guiTreeViewCtrl.cpp 170 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "gui/controls/guiTreeViewCtrl.h"
  24. #include "core/frameAllocator.h"
  25. #include "core/strings/findMatch.h"
  26. #include "gui/containers/guiScrollCtrl.h"
  27. #include "gui/worldEditor/editorIconRegistry.h"
  28. #include "console/consoleTypes.h"
  29. #include "console/console.h"
  30. #include "gui/core/guiTypes.h"
  31. #include "gfx/gfxDrawUtil.h"
  32. #include "gui/controls/guiTextEditCtrl.h"
  33. #ifdef TORQUE_TOOLS
  34. #include "gui/editor/editorFunctions.h"
  35. #endif
  36. #include "console/engineAPI.h"
  37. #include "T3D/entity.h"
  38. IMPLEMENT_CONOBJECT(GuiTreeViewCtrl);
  39. ConsoleDocClass( GuiTreeViewCtrl,
  40. "@brief Hierarchical list of text items with optional icons.\n\n"
  41. "Can also be used to inspect SimObject hierarchies, primarily within editors.\n\n"
  42. "GuiTreeViewCtrls can either display arbitrary user-defined trees or can be used to display SimObject hierarchies where "
  43. "each parent node in the tree is a SimSet or SimGroup and each leaf node is a SimObject.\n\n"
  44. "Each item in the tree has a text and a value. For trees that display SimObject hierarchies, the text for each item "
  45. "is automatically derived from objects while the value for each item is the ID of the respective SimObject. For trees "
  46. "that are not tied to SimObjects, both text and value of each item are set by the user.\n\n"
  47. "Additionally, items in the tree can have icons.\n\n"
  48. "Each item in the tree has a distinct numeric ID that is unique within its tree. The ID of the root item, which is always "
  49. "present on a tree, is 0.\n\n"
  50. "@tsexample\n"
  51. "new GuiTreeViewCtrl(DatablockEditorTree)\n"
  52. "{\n"
  53. " tabSize = \"16\";\n"
  54. " textOffset = \"2\";\n"
  55. " fullRowSelect = \"0\";\n"
  56. " itemHeight = \"21\";\n"
  57. " destroyTreeOnSleep = \"0\";\n"
  58. " MouseDragging = \"0\";\n"
  59. " MultipleSelections = \"1\";\n"
  60. " DeleteObjectAllowed = \"1\";\n"
  61. " DragToItemAllowed = \"0\";\n"
  62. " ClearAllOnSingleSelection = \"1\";\n"
  63. " showRoot = \"1\";\n"
  64. " internalNamesOnly = \"0\";\n"
  65. " objectNamesOnly = \"0\";\n"
  66. " compareToObjectID = \"0\";\n"
  67. " Profile = \"GuiTreeViewProfile\";\n"
  68. " tooltipprofile = \"GuiToolTipProfile\";\n"
  69. " hovertime = \"1000\";\n"
  70. "};\n"
  71. "@endtsexample\n\n"
  72. "@ingroup GuiContainers\n");
  73. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onDeleteObject, bool, ( SimObject* object ), ( object ), "" );
  74. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, isValidDragTarget, bool, ( S32 id, const char* value ), ( id, value ), "" );
  75. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onDefineIcons, void, (), (), "" );
  76. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onAddGroupSelected, void, ( SimGroup* group ), ( group ), "" );
  77. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onAddSelection, void, ( S32 itemOrObjectId, bool isLastSelection ), ( itemOrObjectId, isLastSelection ), "" );
  78. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onSelect, void, ( S32 itemOrObjectId ), ( itemOrObjectId ), "" );
  79. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onInspect, void, ( S32 itemOrObjectId ), ( itemOrObjectId ), "" );
  80. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onRemoveSelection, void, ( S32 itemOrObjectId ), ( itemOrObjectId ), "" );
  81. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onUnselect, void, ( S32 itemOrObjectId ), ( itemOrObjectId ), "" );
  82. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onDeleteSelection, void, (), (), "" );
  83. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onObjectDeleteCompleted, void, (), (), "" );
  84. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onKeyDown, void, ( S32 modifier, S32 keyCode ), ( modifier, keyCode ), "" );
  85. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onMouseUp, void, ( S32 hitItemId, S32 mouseClickCount ), ( hitItemId, mouseClickCount ), "" );
  86. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onMouseDragged, void, (), (), "" );
  87. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onRightMouseDown, void, ( S32 itemId, const Point2I& mousePos, SimObject* object ), ( itemId, mousePos, object ), "" );
  88. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onRightMouseUp, void, ( S32 itemId, const Point2I& mousePos, SimObject* object ), ( itemId, mousePos, object ), "" );
  89. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onBeginReparenting, void, (), (), "" );
  90. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onEndReparenting, void, (), (), "" );
  91. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onReparent, void, ( S32 itemOrObjectId, S32 oldParentItemOrObjectId, S32 newParentItemOrObjectId ), ( itemOrObjectId, oldParentItemOrObjectId, newParentItemOrObjectId ), "" );
  92. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onDragDropped, void, (), (), "" );
  93. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onAddMultipleSelectionBegin, void, (), (), "" );
  94. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onAddMultipleSelectionEnd, void, (), (), "" );
  95. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, canRenameObject, bool, ( SimObject* object ), ( object ), "" );
  96. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, handleRenameObject, bool, ( const char* newName, SimObject* object ), ( newName, object ), "" );
  97. IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onClearSelection, void, (), (), "" );
  98. static S32 QSORT_CALLBACK itemCompareCaseSensitive( const void *a, const void *b )
  99. {
  100. GuiTreeViewCtrl::Item* itemA = *( ( GuiTreeViewCtrl::Item** ) a );
  101. GuiTreeViewCtrl::Item* itemB = *( ( GuiTreeViewCtrl::Item** ) b );
  102. char bufferA[ 1024 ];
  103. char bufferB[ 1024 ];
  104. itemA->getDisplayText( sizeof( bufferA ), bufferA );
  105. itemB->getDisplayText( sizeof( bufferB ), bufferB );
  106. return dStrnatcmp( bufferA, bufferB );
  107. }
  108. static S32 QSORT_CALLBACK itemCompareCaseInsensitive( const void *a, const void *b )
  109. {
  110. GuiTreeViewCtrl::Item* itemA = *( ( GuiTreeViewCtrl::Item** ) a );
  111. GuiTreeViewCtrl::Item* itemB = *( ( GuiTreeViewCtrl::Item** ) b );
  112. char bufferA[ 1024 ];
  113. char bufferB[ 1024 ];
  114. itemA->getDisplayText( sizeof( bufferA ), bufferA );
  115. itemB->getDisplayText( sizeof( bufferB ), bufferB );
  116. return dStrnatcasecmp( bufferA, bufferB );
  117. }
  118. static void itemSortList( GuiTreeViewCtrl::Item*& firstChild, bool caseSensitive, bool traverseHierarchy, bool parentsFirst )
  119. {
  120. // Sort the children.
  121. // Do this in a separate scope, so we release the buffers before
  122. // recursing.
  123. {
  124. Vector< GuiTreeViewCtrl::Item* > parents;
  125. Vector< GuiTreeViewCtrl::Item* > items;
  126. // Put all items into the two vectors.
  127. for( GuiTreeViewCtrl::Item* item = firstChild; item != NULL; item = item->mNext )
  128. if( parentsFirst && item->isParent() )
  129. parents.push_back( item );
  130. else
  131. items.push_back( item );
  132. // Sort both vectors.
  133. dQsort( parents.address(), parents.size(), sizeof( GuiTreeViewCtrl::Item* ), caseSensitive ? itemCompareCaseSensitive : itemCompareCaseInsensitive );
  134. dQsort( items.address(), items.size(), sizeof( GuiTreeViewCtrl::Item* ), caseSensitive ? itemCompareCaseSensitive : itemCompareCaseInsensitive );
  135. // Wipe current child chain then reconstruct it in reverse
  136. // as we prepend items.
  137. firstChild = NULL;
  138. // Add child items.
  139. for( U32 i = items.size(); i > 0; -- i )
  140. {
  141. GuiTreeViewCtrl::Item* child = items[ i - 1 ];
  142. child->mNext = firstChild;
  143. if( firstChild )
  144. firstChild->mPrevious = child;
  145. firstChild = child;
  146. }
  147. // Add parent child items, if requested.
  148. for( U32 i = parents.size(); i > 0; -- i )
  149. {
  150. GuiTreeViewCtrl::Item* child = parents[ i - 1 ];
  151. child->mNext = firstChild;
  152. if( firstChild )
  153. firstChild->mPrevious = child;
  154. firstChild = child;
  155. }
  156. firstChild->mPrevious = NULL;
  157. }
  158. // Traverse hierarchy, if requested.
  159. if( traverseHierarchy )
  160. {
  161. GuiTreeViewCtrl::Item* child = firstChild;
  162. while( child )
  163. {
  164. if( child->isParent() )
  165. child->sort( caseSensitive, traverseHierarchy, parentsFirst );
  166. child = child->mNext;
  167. }
  168. }
  169. }
  170. //=============================================================================
  171. // GuiTreeViewCtrl::Item.
  172. //=============================================================================
  173. // MARK: ---- GuiTreeViewCtrl::Item ----
  174. //-----------------------------------------------------------------------------
  175. GuiTreeViewCtrl::Item::Item( GuiTreeViewCtrl* parent, GuiControlProfile *pProfile )
  176. {
  177. AssertFatal( pProfile != NULL , "Cannot create a tree item without a valid tree and control profile!");
  178. mParentControl = parent;
  179. mState = 0;
  180. mId = -1;
  181. mTabLevel = 0;
  182. mIcon = 0;
  183. mDataRenderWidth = 0;
  184. mParent = NULL;
  185. mChild = NULL;
  186. mNext = NULL;
  187. mPrevious = NULL;
  188. mProfile = pProfile;
  189. mScriptInfo.mNormalImage = BmpCon;
  190. mScriptInfo.mExpandedImage = BmpExp;
  191. mScriptInfo.mText = NULL;
  192. mScriptInfo.mValue = NULL;
  193. }
  194. //-----------------------------------------------------------------------------
  195. GuiTreeViewCtrl::Item::~Item()
  196. {
  197. _disconnectMonitors();
  198. }
  199. //-----------------------------------------------------------------------------
  200. void GuiTreeViewCtrl::Item::_connectMonitors()
  201. {
  202. if( mInspectorInfo.mObject != NULL )
  203. {
  204. SimSet* set = dynamic_cast< SimSet* >( mInspectorInfo.mObject.getPointer() );
  205. if( set )
  206. set->getSetModificationSignal().notify( mParentControl, &GuiTreeViewCtrl::_onInspectorSetObjectModified );
  207. }
  208. }
  209. //-----------------------------------------------------------------------------
  210. void GuiTreeViewCtrl::Item::_disconnectMonitors()
  211. {
  212. if( mInspectorInfo.mObject != NULL )
  213. {
  214. SimSet* set = dynamic_cast< SimSet* >( mInspectorInfo.mObject.getPointer() );
  215. if( set )
  216. set->getSetModificationSignal().remove( mParentControl, &GuiTreeViewCtrl::_onInspectorSetObjectModified );
  217. }
  218. }
  219. //-----------------------------------------------------------------------------
  220. void GuiTreeViewCtrl::Item::setNormalImage(S8 id)
  221. {
  222. if(mState.test(InspectorData))
  223. {
  224. Con::errorf("Tried to set normal image %d for item %d, which is InspectorData!", id, mId);
  225. return;
  226. }
  227. mScriptInfo.mNormalImage = id;
  228. }
  229. //-----------------------------------------------------------------------------
  230. void GuiTreeViewCtrl::Item::setExpandedImage(S8 id)
  231. {
  232. if(mState.test(InspectorData))
  233. {
  234. Con::errorf("Tried to set expanded image %d for item %d, which is InspectorData!", id, mId);
  235. return;
  236. }
  237. mScriptInfo.mExpandedImage = id;
  238. }
  239. //-----------------------------------------------------------------------------
  240. void GuiTreeViewCtrl::Item::setText(StringTableEntry txt)
  241. {
  242. if(mState.test(InspectorData))
  243. {
  244. Con::errorf("Tried to set text for item %d, which is InspectorData!", mId);
  245. return;
  246. }
  247. mScriptInfo.mText = txt;
  248. // Update Render Data
  249. if( !mProfile.isNull() )
  250. mDataRenderWidth = getDisplayTextWidth( mProfile->mFont );
  251. }
  252. //-----------------------------------------------------------------------------
  253. void GuiTreeViewCtrl::Item::setValue(StringTableEntry val)
  254. {
  255. if(mState.test(InspectorData))
  256. {
  257. Con::errorf("Tried to set value for item %d, which is InspectorData!", mId);
  258. return;
  259. }
  260. mScriptInfo.mValue = const_cast<char*>(val); // mValue really ought to be a StringTableEntry
  261. // Update Render Data
  262. if( !mProfile.isNull() )
  263. mDataRenderWidth = getDisplayTextWidth( mProfile->mFont );
  264. }
  265. //-----------------------------------------------------------------------------
  266. S8 GuiTreeViewCtrl::Item::getNormalImage() const
  267. {
  268. if(mState.test(InspectorData))
  269. {
  270. Con::errorf("Tried to get the normal image for item %d, which is InspectorData!", mId);
  271. return 0; // fail safe for width determinations
  272. }
  273. return mScriptInfo.mNormalImage;
  274. }
  275. //-----------------------------------------------------------------------------
  276. S8 GuiTreeViewCtrl::Item::getExpandedImage() const
  277. {
  278. if(mState.test(InspectorData))
  279. {
  280. Con::errorf("Tried to get the expanded image for item %d, which is InspectorData!", mId);
  281. return 0; // fail safe for width determinations
  282. }
  283. return mScriptInfo.mExpandedImage;
  284. }
  285. //-----------------------------------------------------------------------------
  286. StringTableEntry GuiTreeViewCtrl::Item::getText()
  287. {
  288. if(mState.test(InspectorData))
  289. {
  290. Con::errorf("Tried to get the text for item %d, which is InspectorData!", mId);
  291. return NULL;
  292. }
  293. return ( mScriptInfo.mText ) ? mScriptInfo.mText : StringTable->EmptyString();
  294. }
  295. //-----------------------------------------------------------------------------
  296. StringTableEntry GuiTreeViewCtrl::Item::getValue()
  297. {
  298. if(mState.test(InspectorData))
  299. {
  300. Con::errorf("Tried to get the value for item %d, which is InspectorData!", mId);
  301. return NULL;
  302. }
  303. return ( mScriptInfo.mValue ) ? mScriptInfo.mValue : StringTable->EmptyString();
  304. }
  305. //-----------------------------------------------------------------------------
  306. void GuiTreeViewCtrl::Item::setObject(SimObject *obj)
  307. {
  308. if(!mState.test(InspectorData))
  309. {
  310. return;
  311. }
  312. _disconnectMonitors();
  313. mInspectorInfo.mObject = obj;
  314. _connectMonitors();
  315. // Update Render Data
  316. if( !mProfile.isNull() )
  317. mDataRenderWidth = getDisplayTextWidth( mProfile->mFont );
  318. }
  319. //-----------------------------------------------------------------------------
  320. SimObject *GuiTreeViewCtrl::Item::getObject()
  321. {
  322. if(!mState.test(InspectorData))
  323. {
  324. return NULL;
  325. }
  326. return mInspectorInfo.mObject;
  327. }
  328. //-----------------------------------------------------------------------------
  329. U32 GuiTreeViewCtrl::Item::getDisplayTextLength()
  330. {
  331. if( mState.test( InspectorData ) )
  332. {
  333. SimObject *obj = getObject();
  334. if( !obj )
  335. return 0;
  336. StringTableEntry name = obj->getName();
  337. StringTableEntry internalName = obj->getInternalName();
  338. StringTableEntry className = obj->getClassName();
  339. if( showInternalNameOnly() )
  340. {
  341. if( internalName && internalName[ 0 ] )
  342. return dStrlen( internalName );
  343. else
  344. return dStrlen( "(none)" );
  345. }
  346. else if( showObjectNameOnly() )
  347. {
  348. if( name && name[ 0 ] )
  349. return dStrlen( name );
  350. else if( mState.test( ShowClassNameForUnnamed ) )
  351. return dStrlen( className );
  352. else
  353. return dStrlen( "(none)" );
  354. }
  355. dsize_t len = 0;
  356. if( mState.test( ShowObjectId ) )
  357. len += dStrlen( obj->getIdString() ) + 2; // '<id>: '
  358. if( mState.test( ShowClassName ) )
  359. {
  360. if( name && name[ 0 ] )
  361. len += dStrlen( className ) + 3; // '<class> - '
  362. else
  363. len += dStrlen( className );
  364. }
  365. if( mState.test( ShowObjectName ) )
  366. {
  367. if( name && name[ 0 ] )
  368. len += dStrlen( name );
  369. else if( mState.test( ShowClassNameForUnnamed ) )
  370. len += dStrlen( className );
  371. }
  372. if( mState.test( ShowInternalName ) )
  373. {
  374. if( internalName && internalName[ 0 ] )
  375. len += dStrlen( internalName ) + 3; // ' [<internalname>]'
  376. }
  377. return len;
  378. }
  379. StringTableEntry pText = getText();
  380. if( pText == NULL )
  381. return 0;
  382. return dStrlen( pText );
  383. }
  384. //-----------------------------------------------------------------------------
  385. void GuiTreeViewCtrl::Item::getDisplayText(U32 bufLen, char *buf)
  386. {
  387. FrameAllocatorMarker txtAlloc;
  388. //if we're doing the special case of forcing the item text, just skip the rest of this junk
  389. if (mState.test(ForceItemName))
  390. {
  391. StringTableEntry text = (mScriptInfo.mText) ? mScriptInfo.mText : StringTable->EmptyString();
  392. dStrncpy(buf, text, bufLen);
  393. return;
  394. }
  395. if( mState.test( InspectorData ) )
  396. {
  397. SimObject *pObject = getObject();
  398. if( pObject )
  399. {
  400. const char* pObjName = pObject->getName();
  401. const char* pInternalName = pObject->getInternalName();
  402. bool hasInternalName = pInternalName && pInternalName[0];
  403. bool hasObjectName = pObjName && pObjName[0];
  404. const char* pClassName = pObject->getClassName();
  405. if( showInternalNameOnly() )
  406. dSprintf( buf, bufLen, "%s", hasInternalName ? pInternalName : "(none)" );
  407. else if( showObjectNameOnly() )
  408. {
  409. if( !hasObjectName && mState.test( ShowClassNameForUnnamed ) )
  410. dSprintf( buf, bufLen, "%s", pClassName );
  411. else
  412. dSprintf( buf, bufLen, "%s", hasObjectName ? pObjName : "(none)" );
  413. }
  414. else
  415. {
  416. char* ptr = buf;
  417. int len = bufLen;
  418. if( mState.test( ShowObjectId ) )
  419. {
  420. S32 n = dSprintf( ptr, len, "%d: ", pObject->getId() );
  421. ptr += n;
  422. len -= n;
  423. }
  424. if( mState.test( ShowClassName ) )
  425. {
  426. S32 n;
  427. if( hasObjectName && mState.test( ShowObjectName ) )
  428. n = dSprintf( ptr, len, "%s - ", pClassName );
  429. else
  430. n = dSprintf( ptr, len, "%s", pClassName );
  431. ptr += n;
  432. len -= n;
  433. }
  434. if( mState.test( ShowObjectName ) )
  435. {
  436. S32 n = 0;
  437. if( hasObjectName )
  438. n = dSprintf( ptr, len, "%s", pObjName );
  439. else if( mState.test( ShowClassNameForUnnamed ) )
  440. n = dSprintf( ptr, len, "%s", pClassName );
  441. ptr += n;
  442. len -= n;
  443. }
  444. if( hasInternalName && mState.test( ShowInternalName ) )
  445. dSprintf( ptr, len, " [%s]", pInternalName );
  446. }
  447. }
  448. else
  449. buf[ 0 ] = '\0';
  450. }
  451. else
  452. {
  453. // Script data! (copy it in)
  454. dStrncpy(buf, getText(), bufLen);
  455. }
  456. }
  457. //-----------------------------------------------------------------------------
  458. S32 GuiTreeViewCtrl::Item::getDisplayTextWidth(GFont *font)
  459. {
  460. if( !font )
  461. return 0;
  462. FrameAllocatorMarker txtAlloc;
  463. U32 bufLen = getDisplayTextLength();
  464. if( bufLen == 0 )
  465. return 0;
  466. // Add space for the string terminator
  467. bufLen++;
  468. char *buf = (char*)txtAlloc.alloc(bufLen);
  469. getDisplayText(bufLen, buf);
  470. return font->getStrWidth(buf);
  471. }
  472. //-----------------------------------------------------------------------------
  473. bool GuiTreeViewCtrl::Item::hasObjectBasedTooltip()
  474. {
  475. if(mState.test(Item::InspectorData))
  476. {
  477. SimObject *pObject = getObject();
  478. if(pObject)
  479. {
  480. const char* pClassName = pObject->getClassName();
  481. // Retrieve custom tooltip string
  482. String method("GetTooltip");
  483. method += pClassName;
  484. if(mParentControl->isMethod(method.c_str()))
  485. {
  486. return true;
  487. }
  488. }
  489. }
  490. return false;
  491. }
  492. //-----------------------------------------------------------------------------
  493. void GuiTreeViewCtrl::Item::getTooltipText(U32 bufLen, char *buf)
  494. {
  495. getDisplayText(bufLen, buf);
  496. if(mState.test(Item::InspectorData))
  497. {
  498. SimObject *pObject = getObject();
  499. if(pObject)
  500. {
  501. const char* pClassName = pObject->getClassName();
  502. // Retrieve custom tooltip string
  503. String method("GetTooltip");
  504. method += pClassName;
  505. if(mParentControl->isMethod(method.c_str()))
  506. {
  507. const char* tooltip = Con::executef( mParentControl, method.c_str(), pObject->getIdString() );
  508. dsize_t len = dStrlen(buf);
  509. S32 newBufLen = bufLen-len;
  510. if(dStrlen(tooltip) > 0 && newBufLen > 0)
  511. {
  512. dSprintf(buf+len, newBufLen, "\n%s", tooltip);
  513. }
  514. }
  515. }
  516. }
  517. }
  518. //-----------------------------------------------------------------------------
  519. bool GuiTreeViewCtrl::Item::isParent() const
  520. {
  521. if(mState.test(VirtualParent))
  522. {
  523. if( !isInspectorData() )
  524. return true;
  525. // Does our object have any children?
  526. if(mInspectorInfo.mObject)
  527. {
  528. SimSet *pSimSet = dynamic_cast<SimSet*>( (SimObject*)mInspectorInfo.mObject);
  529. if ( pSimSet != NULL && pSimSet->size() > 0)
  530. return pSimSet->size();
  531. }
  532. }
  533. // Otherwise, just return whether the child list is populated.
  534. return mChild;
  535. }
  536. //-----------------------------------------------------------------------------
  537. bool GuiTreeViewCtrl::Item::isExpanded() const
  538. {
  539. if(mState.test(InspectorData))
  540. return mInspectorInfo.mObject ? mInspectorInfo.mObject->isExpanded() : false;
  541. else
  542. return mState.test(Expanded);
  543. }
  544. //-----------------------------------------------------------------------------
  545. void GuiTreeViewCtrl::Item::setExpanded(bool f)
  546. {
  547. if( mState.test(InspectorData) )
  548. {
  549. if( !mInspectorInfo.mObject.isNull() )
  550. mInspectorInfo.mObject->setExpanded(f);
  551. }
  552. else
  553. mState.set(Expanded, f);
  554. }
  555. //-----------------------------------------------------------------------------
  556. void GuiTreeViewCtrl::Item::setVirtualParent( bool value )
  557. {
  558. mState.set(VirtualParent, value);
  559. }
  560. //-----------------------------------------------------------------------------
  561. GuiTreeViewCtrl::Item* GuiTreeViewCtrl::Item::findChildByName( const char* name )
  562. {
  563. Item* child = mChild;
  564. while( child )
  565. {
  566. if( dStricmp( child->mScriptInfo.mText, name ) == 0 )
  567. return child;
  568. child = child->mNext;
  569. }
  570. return NULL;
  571. }
  572. //-----------------------------------------------------------------------------
  573. GuiTreeViewCtrl::Item *GuiTreeViewCtrl::Item::findChildByValue(const SimObject *obj)
  574. {
  575. // Iterate over our children and try to find the given
  576. // SimObject
  577. Item *pResultObj = mChild;
  578. while(pResultObj)
  579. {
  580. // CodeReview this check may need to be removed
  581. // if we want to use the tree for data that
  582. // isn't related to SimObject based objects with
  583. // arbitrary values associated with them [5/5/2007 justind]
  584. // Skip non-inspector data stuff.
  585. if(pResultObj->mState.test(InspectorData))
  586. {
  587. if(pResultObj->getObject() == obj)
  588. break; // Whoa.
  589. }
  590. pResultObj = pResultObj->mNext;
  591. }
  592. // If the loop terminated we are NULL, otherwise we have the result in res.
  593. return pResultObj;
  594. }
  595. //-----------------------------------------------------------------------------
  596. GuiTreeViewCtrl::Item *GuiTreeViewCtrl::Item::findChildByValue( StringTableEntry Value )
  597. {
  598. // Iterate over our children and try to find the given Value
  599. // Note : This is a case-insensitive search
  600. Item *pResultObj = mChild;
  601. while(pResultObj)
  602. {
  603. // check the script value of the item against the specified value
  604. if( pResultObj->mScriptInfo.mValue != NULL && dStricmp( pResultObj->mScriptInfo.mValue, Value ) == 0 )
  605. return pResultObj;
  606. pResultObj = pResultObj->mNext;
  607. }
  608. // If the loop terminated we didn't find an item with the specified script value
  609. return NULL;
  610. }
  611. //-----------------------------------------------------------------------------
  612. void GuiTreeViewCtrl::Item::sort( bool caseSensitive, bool traverseHierarchy, bool parentsFirst )
  613. {
  614. itemSortList( mChild, caseSensitive, traverseHierarchy, parentsFirst );
  615. }
  616. //=============================================================================
  617. // GuiTreeViewCtrl.
  618. //=============================================================================
  619. // MARK: ---- GuiTreeViewCtrl ----
  620. //-----------------------------------------------------------------------------
  621. GuiTreeViewCtrl::GuiTreeViewCtrl()
  622. {
  623. VECTOR_SET_ASSOCIATION(mItems);
  624. VECTOR_SET_ASSOCIATION(mVisibleItems);
  625. VECTOR_SET_ASSOCIATION(mSelectedItems);
  626. VECTOR_SET_ASSOCIATION(mSelected);
  627. mItemFreeList = NULL;
  628. mRoot = NULL;
  629. mItemCount = 0;
  630. mSelectedItem = 0;
  631. mStart = 0;
  632. mPossibleRenameItem = NULL;
  633. mRenamingItem = NULL;
  634. mTempItem = NULL;
  635. mRenameCtrl = NULL;
  636. mDraggedToItem = 0;
  637. mCurrentDragCell = 0;
  638. mPreviousDragCell = 0;
  639. mDragMidPoint = NomDragMidPoint;
  640. mMouseDragged = false;
  641. mDebug = false;
  642. // persist info..
  643. mTabSize = 16;
  644. mTextOffset = 2;
  645. mFullRowSelect = false;
  646. mItemHeight = 20;
  647. //
  648. setSize(Point2I(1, 0));
  649. // Set up default state
  650. mFlags.set(ShowTreeLines);
  651. mFlags.set(IsEditable, false);
  652. mDestroyOnSleep = true;
  653. mSupportMouseDragging = true;
  654. mMultipleSelections = true;
  655. mDeleteObjectAllowed = true;
  656. mDragToItemAllowed = true;
  657. mShowRoot = true;
  658. mUseInspectorTooltips = false;
  659. mTooltipOnWidthOnly = false;
  660. mCompareToObjectID = true;
  661. mShowObjectIds = true;
  662. mShowClassNames = true;
  663. mShowObjectNames = true;
  664. mShowInternalNames = true;
  665. mShowClassNameForUnnamedObjects = false;
  666. mFlags.set(RebuildVisible);
  667. mCanRenameObjects = true;
  668. mRenameInternal = false;
  669. mClearAllOnSingleSelection = true;
  670. mBitmapBase = StringTable->EmptyString();
  671. mTexRollover = NULL;
  672. mTexSelected = NULL;
  673. mRenderTooltipDelegate.bind( this, &GuiTreeViewCtrl::renderTooltip );
  674. mDoFilterChildren = true;
  675. }
  676. //-----------------------------------------------------------------------------
  677. GuiTreeViewCtrl::~GuiTreeViewCtrl()
  678. {
  679. _destroyTree();
  680. }
  681. //------------------------------------------------------------------------------
  682. void GuiTreeViewCtrl::initPersistFields()
  683. {
  684. addGroup( "TreeView" );
  685. addField( "tabSize", TypeS32, Offset(mTabSize, GuiTreeViewCtrl));
  686. addField( "textOffset", TypeS32, Offset(mTextOffset, GuiTreeViewCtrl));
  687. addField( "fullRowSelect", TypeBool, Offset(mFullRowSelect, GuiTreeViewCtrl));
  688. addField( "itemHeight", TypeS32, Offset(mItemHeight, GuiTreeViewCtrl));
  689. addField( "destroyTreeOnSleep", TypeBool, Offset(mDestroyOnSleep, GuiTreeViewCtrl),
  690. "If true, the entire tree item hierarchy is deleted when the control goes to sleep." );
  691. addField( "mouseDragging", TypeBool, Offset(mSupportMouseDragging, GuiTreeViewCtrl));
  692. addField( "multipleSelections", TypeBool, Offset(mMultipleSelections, GuiTreeViewCtrl),
  693. "If true, multiple items can be selected concurrently." );
  694. addField( "deleteObjectAllowed", TypeBool, Offset(mDeleteObjectAllowed, GuiTreeViewCtrl));
  695. addField( "dragToItemAllowed", TypeBool, Offset(mDragToItemAllowed, GuiTreeViewCtrl));
  696. addField( "clearAllOnSingleSelection", TypeBool, Offset(mClearAllOnSingleSelection, GuiTreeViewCtrl));
  697. addField( "showRoot", TypeBool, Offset(mShowRoot, GuiTreeViewCtrl),
  698. "If true, the root item is shown in the tree." );
  699. addField( "useInspectorTooltips", TypeBool, Offset(mUseInspectorTooltips, GuiTreeViewCtrl));
  700. addField( "tooltipOnWidthOnly", TypeBool, Offset(mTooltipOnWidthOnly, GuiTreeViewCtrl));
  701. endGroup( "TreeView" );
  702. addGroup( "Inspector Trees" );
  703. addField( "showObjectIds", TypeBool, Offset( mShowObjectIds, GuiTreeViewCtrl ),
  704. "If true, item text labels for objects will include object IDs." );
  705. addField( "showClassNames", TypeBool, Offset( mShowClassNames, GuiTreeViewCtrl ),
  706. "If true, item text labels for objects will include class names." );
  707. addField( "showObjectNames", TypeBool, Offset( mShowObjectNames, GuiTreeViewCtrl ),
  708. "If true, item text labels for objects will include object names." );
  709. addField( "showInternalNames", TypeBool, Offset( mShowInternalNames, GuiTreeViewCtrl ),
  710. "If true, item text labels for obje ts will include internal names." );
  711. addField( "showClassNameForUnnamedObjects", TypeBool, Offset( mShowClassNameForUnnamedObjects, GuiTreeViewCtrl ),
  712. "If true, class names will be used as object names for unnamed objects." );
  713. addField( "compareToObjectID", TypeBool, Offset(mCompareToObjectID, GuiTreeViewCtrl));
  714. addField( "canRenameObjects", TypeBool, Offset(mCanRenameObjects, GuiTreeViewCtrl),
  715. "If true clicking on a selected item ( that is an object and not the root ) will allow you to rename it." );
  716. addField( "renameInternal", TypeBool, Offset(mRenameInternal, GuiTreeViewCtrl),
  717. "If true then object renaming operates on the internalName rather than the object name." );
  718. endGroup( "Inspector Trees" );
  719. Parent::initPersistFields();
  720. }
  721. //------------------------------------------------------------------------------
  722. GuiTreeViewCtrl::Item * GuiTreeViewCtrl::getItem(S32 itemId) const
  723. {
  724. if ( itemId > 0 && itemId <= mItems.size() )
  725. return mItems[itemId-1];
  726. return NULL;
  727. }
  728. //------------------------------------------------------------------------------
  729. GuiTreeViewCtrl::Item * GuiTreeViewCtrl::createItem(S32 icon)
  730. {
  731. Item * pNewItem = NULL;
  732. // grab from the free list?
  733. if( mItemFreeList )
  734. {
  735. pNewItem = mItemFreeList;
  736. mItemFreeList = pNewItem->mNext;
  737. // re-add to vector
  738. mItems[ pNewItem->mId - 1 ] = pNewItem;
  739. }
  740. else
  741. {
  742. pNewItem = new Item( this, mProfile );
  743. AssertFatal( pNewItem != NULL, "Fatal : unable to allocate tree item!");
  744. mItems.push_back( pNewItem );
  745. // set the id
  746. pNewItem->mId = mItems.size();
  747. }
  748. // reset
  749. if (icon)
  750. pNewItem->mIcon = icon;
  751. else
  752. pNewItem->mIcon = Default; //default icon to stick next to an item
  753. pNewItem->mState = Item::ShowObjectId | Item::ShowClassName | Item::ShowObjectName | Item::ShowInternalName;
  754. pNewItem->mTabLevel = 0;
  755. // Null out item pointers
  756. pNewItem->mNext = 0;
  757. pNewItem->mPrevious = 0;
  758. pNewItem->mChild = 0;
  759. pNewItem->mParent = 0;
  760. mItemCount++;
  761. return pNewItem;
  762. }
  763. //------------------------------------------------------------------------------
  764. void GuiTreeViewCtrl::_destroyChildren( Item* item, Item* parent, bool deleteObjects )
  765. {
  766. if ( !item || item == parent || !mItems[item->mId-1] )
  767. return;
  768. // destroy depth first, then siblings from last to first
  769. if ( item->isParent() && item->mChild )
  770. _destroyChildren(item->mChild, item, deleteObjects);
  771. if( item->mNext )
  772. _destroyChildren(item->mNext, parent, deleteObjects);
  773. // destroy the item
  774. _destroyItem( item, deleteObjects );
  775. }
  776. //-----------------------------------------------------------------------------
  777. void GuiTreeViewCtrl::_destroyItem( Item* item, bool deleteObject )
  778. {
  779. if(!item)
  780. return;
  781. if(item->isInspectorData())
  782. {
  783. // make sure the SimObjectPtr is clean!
  784. SimObject *pObject = item->getObject();
  785. if( pObject && pObject->isProperlyAdded() )
  786. {
  787. bool skipDelete = !deleteObject;
  788. if( !skipDelete && isMethod( "onDeleteObject" ) )
  789. skipDelete = onDeleteObject_callback( pObject );
  790. if ( !skipDelete )
  791. pObject->deleteObject();
  792. }
  793. item->setObject( NULL );
  794. }
  795. // Remove item from the selection
  796. if (mSelectedItem == item->mId)
  797. mSelectedItem = 0;
  798. for ( S32 i = 0; i < mSelectedItems.size(); i++ )
  799. {
  800. if ( mSelectedItems[i] == item )
  801. {
  802. mSelectedItems.erase( i );
  803. break;
  804. }
  805. }
  806. item->mState.clear();
  807. // unlink
  808. if( item->mPrevious )
  809. item->mPrevious->mNext = item->mNext;
  810. if( item->mNext )
  811. item->mNext->mPrevious = item->mPrevious;
  812. if( item->mParent && ( item->mParent->mChild == item ) )
  813. item->mParent->mChild = item->mNext;
  814. // remove from vector
  815. mItems[item->mId-1] = 0;
  816. // set as root free item
  817. item->mNext = mItemFreeList;
  818. mItemFreeList = item;
  819. mItemCount--;
  820. }
  821. //------------------------------------------------------------------------------
  822. void GuiTreeViewCtrl::_deleteItem(Item *item)
  823. {
  824. removeItem(item->mId);
  825. }
  826. //------------------------------------------------------------------------------
  827. void GuiTreeViewCtrl::_destroyTree()
  828. {
  829. // clear the item list
  830. for(U32 i = 0; i < mItems.size(); i++)
  831. {
  832. Item *pFreeItem = mItems[ i ];
  833. if( pFreeItem != NULL )
  834. delete pFreeItem;
  835. }
  836. mItems.clear();
  837. // clear the free list
  838. while(mItemFreeList)
  839. {
  840. Item *next = mItemFreeList->mNext;
  841. delete mItemFreeList;
  842. mItemFreeList = next;
  843. }
  844. mVisibleItems.clear();
  845. mSelectedItems.clear();
  846. //
  847. mRoot = NULL;
  848. mItemFreeList = NULL;
  849. mItemCount = 0;
  850. mSelectedItem = 0;
  851. mDraggedToItem = 0;
  852. mRenamingItem = NULL;
  853. mTempItem = NULL;
  854. mPossibleRenameItem = NULL;
  855. }
  856. //------------------------------------------------------------------------------
  857. void GuiTreeViewCtrl::_onInspectorSetObjectModified( SetModification modification, SimSet* set, SimObject* object )
  858. {
  859. // Don't bother searching for the Item to see if it is actually visible and instead just
  860. // mark our tree state as dirty so we get a rebuild on the next render.
  861. mFlags.set( RebuildVisible );
  862. }
  863. //------------------------------------------------------------------------------
  864. GuiTreeViewCtrl::Item* GuiTreeViewCtrl::_findItemByAmbiguousId( S32 itemOrObjectId, bool buildVirtual )
  865. {
  866. Item* item = getItem( itemOrObjectId );
  867. if( item )
  868. return item;
  869. SimObject* object = Sim::findObject( itemOrObjectId );
  870. if( object )
  871. {
  872. // If we should expand virtual trees in order to find the item,
  873. // do so now.
  874. if( buildVirtual )
  875. {
  876. if( mFlags.test( RebuildVisible ) )
  877. buildVisibleTree();
  878. SimGroup* group = object->getGroup();
  879. if( group )
  880. _expandObjectHierarchy( group );
  881. }
  882. if( objectSearch( object, &item ) )
  883. return item;
  884. }
  885. return NULL;
  886. }
  887. //------------------------------------------------------------------------------
  888. void GuiTreeViewCtrl::_expandObjectHierarchy( SimGroup* group )
  889. {
  890. SimGroup* parent = group->getGroup();
  891. if( parent && !parent->isExpanded() )
  892. _expandObjectHierarchy( parent );
  893. if( !group->isExpanded() )
  894. {
  895. Item* item;
  896. if( objectSearch( group, &item ) )
  897. {
  898. item->setExpanded();
  899. onVirtualParentBuild( item, false );
  900. }
  901. }
  902. }
  903. //------------------------------------------------------------------------------
  904. void GuiTreeViewCtrl::_buildItem( Item* item, U32 tabLevel, bool bForceFullUpdate, bool skipFlter )
  905. {
  906. if (!item || !mActive || !isVisible() || !mProfile )
  907. return;
  908. // If it's inspector data, make sure we still have it, if not, kill it.
  909. if(item->isInspectorData() && !item->getObject() )
  910. {
  911. removeItem(item->mId);
  912. return;
  913. }
  914. // If it's a virtual parent, give a chance to update itself...
  915. if(item->mState.test( Item::VirtualParent) )
  916. {
  917. // If it returns false the item has been removed.
  918. if( !onVirtualParentBuild( item, bForceFullUpdate ) )
  919. return;
  920. }
  921. // If we have a filter pattern, sync the item's filtering status to it.
  922. if( !getFilterText().isEmpty() && !skipFlter)
  923. {
  924. // Determine the filtering status by looking for the filter
  925. // text in the item's display text.
  926. char displayText[ 2048 ];
  927. item->getDisplayText( sizeof( displayText ), displayText );
  928. if( !dStristr( displayText, mFilterText ) )
  929. {
  930. //Last check, see if we special-exception this item
  931. if (!mItemFilterExceptionList.contains(item->mId))
  932. item->mState.set(Item::Filtered);
  933. else
  934. item->mState.clear(Item::Filtered);
  935. // If it's not a parent, we're done. Otherwise, there may be children
  936. // that are not filtered so we need to process them first.
  937. if( !item->isParent() )
  938. return;
  939. }
  940. else
  941. {
  942. item->mState.clear(Item::Filtered);
  943. }
  944. }
  945. else
  946. item->mState.clear( Item::Filtered );
  947. //If the item should be hidden from view, check now
  948. if (mHiddenItemsList.contains(item->mId))
  949. item->mState.set(Item::Filtered);
  950. // Is this the root item?
  951. const bool isRoot = item == mRoot;
  952. // Add non-root items or the root if we're supposed to show it.
  953. if( ( mShowRoot || !isRoot ) &&
  954. !item->isFiltered() )
  955. {
  956. item->mTabLevel = tabLevel;
  957. mVisibleItems.push_back( item );
  958. if( mProfile != NULL )
  959. {
  960. mProfile->incLoadCount();
  961. S32 width = mTextOffset + ( mTabSize * item->mTabLevel ) + getInspectorItemIconsWidth( item ) + item->getDisplayTextWidth( mProfile->mFont );
  962. // check image
  963. S32 image = BmpChild;
  964. if ( item->isInspectorData() )
  965. image = item->isExpanded() ? BmpExp : BmpCon;
  966. else
  967. image = item->isExpanded() ? item->getExpandedImage() : item->getNormalImage();
  968. if ( ( image >= 0 ) && ( image < mProfile->mBitmapArrayRects.size() ) )
  969. width += mProfile->mBitmapArrayRects[image].extent.x;
  970. if ( width > mMaxWidth )
  971. mMaxWidth = width;
  972. mProfile->decLoadCount();
  973. }
  974. }
  975. // If expanded or a hidden root, add all the
  976. // children items as well.
  977. if ( item->isExpanded() ||
  978. bForceFullUpdate ||
  979. ( isRoot && !mShowRoot ) )
  980. {
  981. Item* child = item->mChild;
  982. while ( child )
  983. {
  984. // Bit of a hack so we can safely remove items as we
  985. // traverse.
  986. Item *pChildTemp = child;
  987. child = child->mNext;
  988. if (!mItemFilterExceptionList.contains(item->mId) && !mDoFilterChildren && !item->isFiltered())
  989. _buildItem( pChildTemp, tabLevel + 1, bForceFullUpdate, true );
  990. else
  991. _buildItem(pChildTemp, tabLevel + 1, bForceFullUpdate, false);
  992. }
  993. }
  994. }
  995. //------------------------------------------------------------------------------
  996. void GuiTreeViewCtrl::buildVisibleTree(bool bForceFullUpdate)
  997. {
  998. // Recursion Prevention.
  999. if( mFlags.test( BuildingVisTree ) )
  1000. return;
  1001. mFlags.set( BuildingVisTree, true );
  1002. if( mDebug )
  1003. Con::printf( "Rebuilding visible tree" );
  1004. mMaxWidth = 0;
  1005. mVisibleItems.clear();
  1006. // If we're filtering, force a full update.
  1007. if( !mFilterText.isEmpty() )
  1008. bForceFullUpdate = true;
  1009. // Update the flags.
  1010. mFlags.clear(RebuildVisible);
  1011. // build the root items
  1012. Item *traverse = mRoot;
  1013. while(traverse)
  1014. {
  1015. _buildItem(traverse, 0, bForceFullUpdate);
  1016. traverse = traverse->mNext;
  1017. }
  1018. // adjust the GuiArrayCtrl
  1019. mCellSize.set( mMaxWidth + mTextOffset, mItemHeight );
  1020. setSize(Point2I(1, mVisibleItems.size()));
  1021. syncSelection();
  1022. // Done Recursing.
  1023. mFlags.clear( BuildingVisTree );
  1024. }
  1025. //------------------------------------------------------------------------------
  1026. bool GuiTreeViewCtrl::scrollVisible( S32 itemId )
  1027. {
  1028. Item* item = getItem(itemId);
  1029. if(item)
  1030. return scrollVisible(item);
  1031. return false;
  1032. }
  1033. //-----------------------------------------------------------------------------
  1034. bool GuiTreeViewCtrl::scrollVisible( Item *item )
  1035. {
  1036. // Now, make sure it's visible (ie, all parents expanded)
  1037. Item *parent = item->mParent;
  1038. if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
  1039. onVirtualParentExpand(item);
  1040. while(parent)
  1041. {
  1042. parent->setExpanded(true);
  1043. if( !parent->isInspectorData() && parent->mState.test(Item::VirtualParent) )
  1044. onVirtualParentExpand(parent);
  1045. parent = parent->mParent;
  1046. }
  1047. // Get our scroll-pappy, if any.
  1048. GuiScrollCtrl *pScrollParent = dynamic_cast<GuiScrollCtrl*>( getParent() );
  1049. if ( !pScrollParent )
  1050. {
  1051. Con::warnf("GuiTreeViewCtrl::scrollVisible - parent control is not a GuiScrollCtrl!");
  1052. return false;
  1053. }
  1054. // And now, build the visible tree so we know where we have to scroll.
  1055. if( mFlags.test( RebuildVisible ) )
  1056. buildVisibleTree();
  1057. // All done, let's figure out where we have to scroll...
  1058. for(S32 i=0; i<mVisibleItems.size(); i++)
  1059. {
  1060. if(mVisibleItems[i] == item)
  1061. {
  1062. // Fetch X Details.
  1063. const S32 xPos = pScrollParent->getChildRelPos().x;
  1064. const S32 xWidth = ( mMaxWidth - xPos );
  1065. // Scroll to View the Item.
  1066. // Note: Delta X should be 0 so that we maintain the X axis position.
  1067. pScrollParent->scrollRectVisible( RectI( xPos, i * mItemHeight, xWidth, mItemHeight ) );
  1068. return true;
  1069. }
  1070. }
  1071. // If we got here, it's probably bad...
  1072. Con::errorf("GuiTreeViewCtrl::scrollVisible - was unable to find specified item in visible list!");
  1073. return false;
  1074. }
  1075. //------------------------------------------------------------------------------
  1076. S32 GuiTreeViewCtrl::insertItem(S32 parentId, const char * text, const char * value, const char * iconString, S16 normalImage, S16 expandedImage)
  1077. {
  1078. if( ( parentId < 0 ) || ( parentId > mItems.size() ) )
  1079. {
  1080. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::insertItem: invalid parent id!");
  1081. return 0;
  1082. }
  1083. if((parentId != 0) && (mItems[parentId-1] == 0))
  1084. {
  1085. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::insertItem: parent item invalid!");
  1086. return 0;
  1087. }
  1088. const char * pItemText = ( text != NULL ) ? text : "";
  1089. const char * pItemValue = ( value != NULL ) ? value : "";
  1090. S32 icon = getIcon(iconString);
  1091. // create an item (assigns id)
  1092. Item * pNewItem = createItem(icon);
  1093. if( pNewItem == NULL )
  1094. return 0;
  1095. pNewItem->setText( StringTable->insert( pItemText, true ) );
  1096. pNewItem->setValue( StringTable->insert( pItemValue, true ) );
  1097. pNewItem->setNormalImage( normalImage );
  1098. pNewItem->setExpandedImage( expandedImage );
  1099. // root level?
  1100. if(parentId == 0)
  1101. {
  1102. // insert back
  1103. if( mRoot != NULL )
  1104. {
  1105. Item * pTreeTraverse = mRoot;
  1106. while( pTreeTraverse != NULL && pTreeTraverse->mNext != NULL )
  1107. pTreeTraverse = pTreeTraverse->mNext;
  1108. pTreeTraverse->mNext = pNewItem;
  1109. pNewItem->mPrevious = pTreeTraverse;
  1110. }
  1111. else
  1112. mRoot = pNewItem;
  1113. mFlags.set(RebuildVisible);
  1114. }
  1115. else if( mItems.size() >= ( parentId - 1 ) )
  1116. {
  1117. Item * pParentItem = mItems[parentId-1];
  1118. // insert back
  1119. if( pParentItem != NULL && pParentItem->mChild)
  1120. {
  1121. Item * pTreeTraverse = pParentItem->mChild;
  1122. while( pTreeTraverse != NULL && pTreeTraverse->mNext != NULL )
  1123. pTreeTraverse = pTreeTraverse->mNext;
  1124. pTreeTraverse->mNext = pNewItem;
  1125. pNewItem->mPrevious = pTreeTraverse;
  1126. }
  1127. else
  1128. pParentItem->mChild = pNewItem;
  1129. pNewItem->mParent = pParentItem;
  1130. if( pParentItem->isExpanded() )
  1131. mFlags.set(RebuildVisible);
  1132. }
  1133. return pNewItem->mId;
  1134. }
  1135. //------------------------------------------------------------------------------
  1136. bool GuiTreeViewCtrl::removeItem( S32 itemId, bool deleteObjects )
  1137. {
  1138. if( isSelected( itemId ) )
  1139. removeSelection( itemId );
  1140. // tree?
  1141. if(itemId == 0)
  1142. {
  1143. //RD: this does not delete objects and thus isn't coherent with the semantics of this method
  1144. _destroyTree();
  1145. return(true);
  1146. }
  1147. Item * item = getItem(itemId);
  1148. if(!item)
  1149. {
  1150. //Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::removeItem: invalid item id!");
  1151. return false;
  1152. }
  1153. // root?
  1154. if(item == mRoot)
  1155. mRoot = item->mNext;
  1156. // Dispose of any children...
  1157. if (item->mChild)
  1158. _destroyChildren( item->mChild, item, deleteObjects );
  1159. // Kill the item...
  1160. _destroyItem( item, deleteObjects );
  1161. // Update the rendered tree...
  1162. mFlags.set(RebuildVisible);
  1163. return true;
  1164. }
  1165. //-----------------------------------------------------------------------------
  1166. void GuiTreeViewCtrl::removeAllChildren(S32 itemId)
  1167. {
  1168. Item * item = getItem(itemId);
  1169. if(item)
  1170. {
  1171. _destroyChildren(item->mChild, item);
  1172. }
  1173. }
  1174. //------------------------------------------------------------------------------
  1175. const S32 GuiTreeViewCtrl::getFirstRootItem() const
  1176. {
  1177. return (mRoot ? mRoot->mId : 0);
  1178. }
  1179. //------------------------------------------------------------------------------
  1180. S32 GuiTreeViewCtrl::getChildItem(S32 itemId)
  1181. {
  1182. Item * item = getItem(itemId);
  1183. if(!item)
  1184. {
  1185. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getChild: invalid item id!");
  1186. return(0);
  1187. }
  1188. return(item->mChild ? item->mChild->mId : 0);
  1189. }
  1190. //-----------------------------------------------------------------------------
  1191. S32 GuiTreeViewCtrl::getParentItem(S32 itemId)
  1192. {
  1193. Item * item = getItem(itemId);
  1194. if(!item)
  1195. {
  1196. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getParent: invalid item id!");
  1197. return(0);
  1198. }
  1199. return(item->mParent ? item->mParent->mId : 0);
  1200. }
  1201. //-----------------------------------------------------------------------------
  1202. S32 GuiTreeViewCtrl::getNextSiblingItem(S32 itemId)
  1203. {
  1204. Item * item = getItem(itemId);
  1205. if(!item)
  1206. {
  1207. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getNextSibling: invalid item id!");
  1208. return(0);
  1209. }
  1210. return(item->mNext ? item->mNext->mId : 0);
  1211. }
  1212. //-----------------------------------------------------------------------------
  1213. S32 GuiTreeViewCtrl::getPrevSiblingItem(S32 itemId)
  1214. {
  1215. Item * item = getItem(itemId);
  1216. if(!item)
  1217. {
  1218. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getPrevSibling: invalid item id!");
  1219. return(0);
  1220. }
  1221. return(item->mPrevious ? item->mPrevious->mId : 0);
  1222. }
  1223. //------------------------------------------------------------------------------
  1224. bool GuiTreeViewCtrl::isValidDragTarget( Item* item )
  1225. {
  1226. bool isValid = true;
  1227. // First, check if we're just going to override this from manually setting the ForceAllowDrag flag
  1228. // If that's set, we're assuming special circumstances and will just let it go on it's way
  1229. if (item->isDragTargetAllowed())
  1230. return true;
  1231. // If this is inspector data, first make sure the item accepts all
  1232. // selected objects as children. This prevents bad surprises when
  1233. // certain SimSet subclasses reject children and start shoving them
  1234. // off to places of their own choosing.
  1235. if( item->isInspectorData() )
  1236. {
  1237. if( mDebug )
  1238. Con::printf( "Checking %i:%s as drag-parent",
  1239. item->getObject()->getId(), item->getObject()->getClassName() );
  1240. SimSet* set = dynamic_cast< SimSet*>( item->getObject() );
  1241. if( set )
  1242. {
  1243. for( U32 i = 0; i < mSelectedItems.size(); ++ i )
  1244. {
  1245. Item* selectedItem = mSelectedItems[ i ];
  1246. if( mDebug )
  1247. Con::printf( "Checking %i:%s as drag-object",
  1248. selectedItem->getObject()->getId(),
  1249. selectedItem->getObject()->getClassName() );
  1250. if( selectedItem->isInspectorData()
  1251. && !set->acceptsAsChild( selectedItem->getObject() ) )
  1252. return false;
  1253. }
  1254. }
  1255. }
  1256. if( isMethod( "isValidDragTarget" ) )
  1257. {
  1258. // We have a callback. Exclusively leave the decision whether
  1259. // the item is a valid drag target to it.
  1260. isValid = isValidDragTarget_callback( item->mId, getItemValue( item->mId ) );
  1261. }
  1262. else
  1263. {
  1264. // Make the item a valid drag target if it either already is
  1265. // a parent (including VirtualParents) or if dragging to non-parent
  1266. // items is explicitly allowed.
  1267. isValid = item->isParent() || mDragToItemAllowed;
  1268. }
  1269. return isValid;
  1270. }
  1271. //------------------------------------------------------------------------------
  1272. S32 GuiTreeViewCtrl::getItemCount()
  1273. {
  1274. return(mItemCount);
  1275. }
  1276. //-----------------------------------------------------------------------------
  1277. S32 GuiTreeViewCtrl::getSelectedItem()
  1278. {
  1279. return mSelectedItem;
  1280. }
  1281. //------------------------------------------------------------------------------
  1282. void GuiTreeViewCtrl::moveItemUp( S32 itemId )
  1283. {
  1284. GuiTreeViewCtrl::Item* pItem = getItem( itemId );
  1285. if ( !pItem )
  1286. {
  1287. Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemUp: invalid item id!");
  1288. return;
  1289. }
  1290. Item * pParent = pItem->mParent;
  1291. Item * pPrevItem = pItem->mPrevious;
  1292. if ( pPrevItem == NULL || pParent == NULL )
  1293. {
  1294. Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemUp: Unable to move item up, bad data!");
  1295. return;
  1296. }
  1297. // Diddle the linked list!
  1298. if ( pPrevItem->mPrevious )
  1299. pPrevItem->mPrevious->mNext = pItem;
  1300. else if ( pItem->mParent )
  1301. pItem->mParent->mChild = pItem;
  1302. if ( pItem->mNext )
  1303. pItem->mNext->mPrevious = pPrevItem;
  1304. pItem->mPrevious = pPrevItem->mPrevious;
  1305. pPrevItem->mNext = pItem->mNext;
  1306. pItem->mNext = pPrevItem;
  1307. pPrevItem->mPrevious = pItem;
  1308. // Update SimObjects if Appropriate.
  1309. SimObject * pSimObject = NULL;
  1310. SimSet * pParentSet = NULL;
  1311. // Fetch Current Add Set
  1312. if( pParent->isInspectorData() )
  1313. pParentSet = dynamic_cast<SimSet*>( pParent->getObject() );
  1314. else
  1315. {
  1316. // parent is probably script data so we search up the tree for a
  1317. // set to put our object in
  1318. Item * pTraverse = pItem->mParent;
  1319. while ( pTraverse != NULL && !pTraverse->isInspectorData() )
  1320. pTraverse = pTraverse->mParent;
  1321. // found an ancestor who is an inspectorData?
  1322. if (pTraverse != NULL)
  1323. pParentSet = pTraverse->isInspectorData() ? dynamic_cast<SimSet*>( pTraverse->getObject() ) : NULL;
  1324. }
  1325. // Reorder the item and make sure that the children of the item get updated
  1326. // correctly prev item may be script... so find a prevItem if there is.
  1327. // We only need to reorder if there you move it above an inspector item.
  1328. if ( pSimObject != NULL && pParentSet != NULL )
  1329. {
  1330. Item * pTraverse = pItem->mNext;
  1331. while(pTraverse)
  1332. {
  1333. if (pTraverse->isInspectorData())
  1334. break;
  1335. pTraverse = pTraverse->mNext;
  1336. }
  1337. if (pTraverse && pItem->getObject() && pTraverse->getObject())
  1338. pParentSet->reOrder(pItem->getObject(), pTraverse->getObject());
  1339. }
  1340. mFlags.set(RebuildVisible);
  1341. }
  1342. //-----------------------------------------------------------------------------
  1343. void GuiTreeViewCtrl::moveItemDown( S32 itemId )
  1344. {
  1345. GuiTreeViewCtrl::Item* item = getItem( itemId );
  1346. if ( !item )
  1347. {
  1348. Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemDown: invalid item id!");
  1349. return;
  1350. }
  1351. Item* nextItem = item->mNext;
  1352. if ( !nextItem )
  1353. {
  1354. Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemDown: no next sibling?");
  1355. return;
  1356. }
  1357. // Diddle the linked list!
  1358. if ( nextItem->mNext )
  1359. nextItem->mNext->mPrevious = item;
  1360. if ( item->mPrevious )
  1361. item->mPrevious->mNext = nextItem;
  1362. else if ( item->mParent )
  1363. item->mParent->mChild = nextItem;
  1364. item->mNext = nextItem->mNext;
  1365. nextItem->mPrevious = item->mPrevious;
  1366. item->mPrevious = nextItem;
  1367. nextItem->mNext = item;
  1368. // And update the simobjects if apppropriate...
  1369. SimObject * simobj = NULL;
  1370. if (item->isInspectorData())
  1371. simobj = item->getObject();
  1372. SimSet *parentSet = NULL;
  1373. // grab the current parentSet if there is any...
  1374. if(item->mParent->isInspectorData())
  1375. parentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
  1376. else
  1377. {
  1378. // parent is probably script data so we search up the tree for a
  1379. // set to put our object in
  1380. Item * temp = item->mParent;
  1381. while (temp && !temp->isInspectorData())
  1382. temp = temp->mParent;
  1383. // found an ancestor who is an inspectorData?
  1384. parentSet = (temp && temp->isInspectorData()) ? dynamic_cast<SimSet*>(temp->getObject()) : NULL;
  1385. }
  1386. // Reorder the item and make sure that the children of the item get updated
  1387. // correctly prev item may be script... so find a prevItem if there is.
  1388. // We only need to reorder if there you move it above an inspector item.
  1389. if (simobj && parentSet)
  1390. {
  1391. Item * temp = item->mPrevious;
  1392. while(temp)
  1393. {
  1394. if (temp->isInspectorData())
  1395. break;
  1396. temp = temp->mPrevious;
  1397. }
  1398. if (temp && item->getObject() && temp->getObject())
  1399. parentSet->reOrder(temp->getObject(), item->getObject());
  1400. }
  1401. mFlags.set(RebuildVisible);
  1402. }
  1403. //------------------------------------------------------------------------------
  1404. bool GuiTreeViewCtrl::onAdd()
  1405. {
  1406. if( !Parent::onAdd() )
  1407. return false;
  1408. // If we have dynamic fields, convert the "internalNamesOnly" and "objectNamesOnly"
  1409. // legacy fields.
  1410. if( getFieldDictionary() )
  1411. {
  1412. static StringTableEntry sInternalNamesOnly = StringTable->insert( "internalNamesOnly" );
  1413. static StringTableEntry sObjectNamesOnly = StringTable->insert( "objectNamesOnly" );
  1414. const char* internalNamesOnly = getDataField( sInternalNamesOnly, NULL );
  1415. if( internalNamesOnly && internalNamesOnly[ 0 ] && dAtob( internalNamesOnly ) )
  1416. {
  1417. mShowObjectIds = false;
  1418. mShowClassNames = false;
  1419. mShowObjectNames = false;
  1420. mShowInternalNames = true;
  1421. }
  1422. const char* objectNamesOnly = getDataField( sObjectNamesOnly, NULL );
  1423. if( objectNamesOnly && objectNamesOnly[ 0 ] && dAtob( objectNamesOnly ) )
  1424. {
  1425. mShowObjectIds = false;
  1426. mShowClassNames = false;
  1427. mShowObjectNames = true;
  1428. mShowInternalNames = false;
  1429. }
  1430. }
  1431. return true;
  1432. }
  1433. //------------------------------------------------------------------------------
  1434. bool GuiTreeViewCtrl::onWake()
  1435. {
  1436. if(!Parent::onWake() || !mProfile->constructBitmapArray())
  1437. return false;
  1438. // If destroy on sleep, then we have to give things a chance to rebuild.
  1439. if(mDestroyOnSleep)
  1440. {
  1441. onDefineIcons_callback();
  1442. }
  1443. // Update the row height, if appropriate.
  1444. if(mProfile->mAutoSizeHeight)
  1445. {
  1446. // make sure it's big enough for both bitmap AND font...
  1447. mItemHeight = getMax((S32)mFont->getHeight(), (S32)mProfile->mBitmapArrayRects[0].extent.y);
  1448. }
  1449. mFlags.set(RebuildVisible);
  1450. return true;
  1451. }
  1452. //-----------------------------------------------------------------------------
  1453. void GuiTreeViewCtrl::onSleep()
  1454. {
  1455. Parent::onSleep();
  1456. // If appropriate, blast the tree. (We probably rebuild it on wake.)
  1457. if( mDestroyOnSleep )
  1458. _destroyTree();
  1459. if ( mRenameCtrl )
  1460. {
  1461. mRenameCtrl->deleteObject();
  1462. mRenameCtrl = NULL;
  1463. }
  1464. }
  1465. //-----------------------------------------------------------------------------
  1466. bool GuiTreeViewCtrl::buildIconTable(const char * icons)
  1467. {
  1468. // Icons should be designated by the bitmap/png file names (minus the file extensions)
  1469. // and separated by colons (:). This list should be synchronized with the Icons enum.
  1470. // Figure the size of the buffer we need...
  1471. const char* temp = dStrchr( icons, '\t' );
  1472. U32 textLen = temp ? ( temp - icons ) : dStrlen( icons );
  1473. // Allocate temporary space.
  1474. FrameAllocatorMarker txtBuff;
  1475. char* drawText = (char*)txtBuff.alloc(sizeof(char) * (textLen + 4));
  1476. dStrncpy( drawText, icons, textLen );
  1477. drawText[textLen] = '\0';
  1478. U32 numIcons = 0;
  1479. char buf[ 1024 ];
  1480. char* pos = drawText;
  1481. // Count the number of icons and store them.
  1482. while( *pos && numIcons < MaxIcons )
  1483. {
  1484. char* start = pos;
  1485. while( *pos && *pos != ':' )
  1486. pos ++;
  1487. const U32 len = pos - start;
  1488. if( len )
  1489. {
  1490. dStrncpy( buf, start, getMin( sizeof( buf ) / sizeof( buf[ 0 ] ) - 1, len ) );
  1491. buf[ len ] = '\0';
  1492. mIconTable[ numIcons ] = GFXTexHandle( buf, &GFXTexturePersistentProfile, avar( "%s() - mIconTable[%d] (line %d)", __FUNCTION__, numIcons, __LINE__ ) );
  1493. }
  1494. else
  1495. mIconTable[ numIcons ] = GFXTexHandle();
  1496. numIcons ++;
  1497. if( *pos )
  1498. pos ++;
  1499. }
  1500. return true;
  1501. }
  1502. //------------------------------------------------------------------------------
  1503. void GuiTreeViewCtrl::onPreRender()
  1504. {
  1505. Parent::onPreRender();
  1506. S32 nRootItemId = getFirstRootItem();
  1507. if( nRootItemId == 0 )
  1508. return;
  1509. Item *pRootItem = getItem( nRootItemId );
  1510. if( pRootItem == NULL )
  1511. return;
  1512. // Update every render in case new objects are added
  1513. if(mFlags.test(RebuildVisible))
  1514. {
  1515. buildVisibleTree();
  1516. mFlags.clear(RebuildVisible);
  1517. }
  1518. }
  1519. //------------------------------------------------------------------------------
  1520. bool GuiTreeViewCtrl::_hitTest(const Point2I & pnt, Item* & item, BitSet32 & flags)
  1521. {
  1522. // Initialize some things.
  1523. const Point2I pos = globalToLocalCoord(pnt);
  1524. flags.clear();
  1525. item = 0;
  1526. // get the hit cell
  1527. Point2I cell((pos.x < 0 ? -1 : pos.x / mCellSize.x),
  1528. (pos.y < 0 ? -1 : pos.y / mCellSize.y));
  1529. // valid?
  1530. if((cell.x < 0 || cell.x >= mSize.x) ||
  1531. (cell.y < 0 || cell.y >= mSize.y))
  1532. return false;
  1533. flags.set(OnRow);
  1534. // Grab the cell.
  1535. if (cell.y >= mVisibleItems.size())
  1536. return false; //Invalid cell, so don't do anything
  1537. item = mVisibleItems[cell.y];
  1538. S32 min = mTabSize * item->mTabLevel;
  1539. // left of icon/text?
  1540. if(pos.x < min)
  1541. {
  1542. flags.set(OnIndent);
  1543. return true;
  1544. }
  1545. // check image
  1546. S32 image = BmpChild;
  1547. if(item->isInspectorData())
  1548. image = item->isExpanded() ? BmpExp : BmpCon;
  1549. else
  1550. image = item->isExpanded() ? item->getExpandedImage() : item->getNormalImage();
  1551. if((image >= 0) && (image < mProfile->mBitmapArrayRects.size()))
  1552. min += mProfile->mBitmapArrayRects[image].extent.x;
  1553. // Is it on the image?
  1554. if(pos.x < min)
  1555. {
  1556. flags.set(OnImage);
  1557. return(true);
  1558. }
  1559. // Check the icon.
  1560. min += getInspectorItemIconsWidth( item );
  1561. if ( pos.x < min )
  1562. {
  1563. flags.set(OnIcon);
  1564. return true;
  1565. }
  1566. // Check the text.
  1567. min += mProfile->mTextOffset.x;
  1568. FrameAllocatorMarker txtAlloc;
  1569. U32 bufLen = item->getDisplayTextLength() + 1;
  1570. char *buf = (char*)txtAlloc.alloc(bufLen);
  1571. item->getDisplayText(bufLen, buf);
  1572. min += mProfile->mFont->getStrWidth(buf);
  1573. if(pos.x < min)
  1574. flags.set(OnText);
  1575. return true;
  1576. }
  1577. //-----------------------------------------------------------------------------
  1578. S32 GuiTreeViewCtrl::getInspectorItemIconsWidth(Item* & item)
  1579. {
  1580. S32 width = 0;
  1581. if( item->isInspectorData() )
  1582. {
  1583. // Based on code in onRenderCell()
  1584. S32 icon = Lock1;
  1585. S32 icon2 = Hidden;
  1586. if (item->getObject() && item->getObject()->isLocked())
  1587. {
  1588. if (mIconTable[icon])
  1589. {
  1590. width += mIconTable[icon].getWidth();
  1591. }
  1592. }
  1593. if (item->getObject() && item->getObject()->isHidden())
  1594. {
  1595. if (mIconTable[icon2])
  1596. {
  1597. width += mIconTable[icon2].getWidth();
  1598. }
  1599. }
  1600. GFXTexHandle iconHandle;
  1601. if ( ( item->mIcon != -1 ) && mIconTable[item->mIcon] )
  1602. iconHandle = mIconTable[item->mIcon];
  1603. #ifdef TORQUE_TOOLS
  1604. else
  1605. iconHandle = gEditorIcons.findIcon( item->getObject() );
  1606. #endif
  1607. if ( iconHandle.isValid() )
  1608. {
  1609. width += iconHandle.getWidth();
  1610. }
  1611. }
  1612. else
  1613. {
  1614. S32 icon = item->isExpanded() ? item->mScriptInfo.mExpandedImage : item->mScriptInfo.mNormalImage;
  1615. if ( ( icon != -1 ) && mIconTable[icon] )
  1616. {
  1617. width += mIconTable[icon].getWidth();
  1618. }
  1619. }
  1620. return width;
  1621. }
  1622. //-----------------------------------------------------------------------------
  1623. bool GuiTreeViewCtrl::setAddGroup(SimObject * obj)
  1624. {
  1625. // make sure we're talking about a group.
  1626. SimGroup * grp = dynamic_cast<SimGroup*>(obj);
  1627. if(grp)
  1628. {
  1629. onAddGroupSelected_callback( grp );
  1630. return true;
  1631. }
  1632. return false;
  1633. }
  1634. //-----------------------------------------------------------------------------
  1635. void GuiTreeViewCtrl::syncSelection()
  1636. {
  1637. // for each visible item check to see if it is on the mSelected list.
  1638. // if it is then make sure that it is on the mSelectedItems list as well.
  1639. for (S32 i = 0; i < mVisibleItems.size(); i++)
  1640. {
  1641. for (S32 j = 0; j < mSelected.size(); j++)
  1642. {
  1643. if (mVisibleItems[i]->mId == mSelected[j])
  1644. {
  1645. // check to see if it is on the visible items list.
  1646. bool addToSelectedItems = true;
  1647. for (S32 k = 0; k < mSelectedItems.size(); k++)
  1648. {
  1649. if (mSelected[j] == mSelectedItems[k]->mId)
  1650. {
  1651. // don't add it
  1652. addToSelectedItems = false;
  1653. }
  1654. }
  1655. if (addToSelectedItems)
  1656. {
  1657. mVisibleItems[i]->mState.set(Item::Selected, true);
  1658. mSelectedItems.push_front(mVisibleItems[i]);
  1659. break;
  1660. }
  1661. }
  1662. else if (mVisibleItems[i]->isInspectorData())
  1663. {
  1664. if(mCompareToObjectID)
  1665. {
  1666. if (mVisibleItems[i]->getObject() && mVisibleItems[i]->getObject()->getId() == mSelected[j])
  1667. {
  1668. // check to see if it is on the visible items list.
  1669. bool addToSelectedItems = true;
  1670. for (S32 k = 0; k < mSelectedItems.size(); k++)
  1671. {
  1672. if (mSelectedItems[k]->isInspectorData() && mSelectedItems[k]->getObject() )
  1673. {
  1674. if (mSelected[j] == mSelectedItems[k]->getObject()->getId())
  1675. {
  1676. // don't add it
  1677. addToSelectedItems = false;
  1678. }
  1679. }
  1680. else
  1681. {
  1682. if (mSelected[j] == mSelectedItems[k]->mId)
  1683. {
  1684. // don't add it
  1685. addToSelectedItems = false;
  1686. }
  1687. }
  1688. }
  1689. if (addToSelectedItems)
  1690. {
  1691. mVisibleItems[i]->mState.set(Item::Selected, true);
  1692. mSelectedItems.push_front(mVisibleItems[i]);
  1693. break;
  1694. }
  1695. }
  1696. }
  1697. }
  1698. }
  1699. }
  1700. }
  1701. //-----------------------------------------------------------------------------
  1702. void GuiTreeViewCtrl::removeSelection( S32 itemOrObjectId )
  1703. {
  1704. if (mDebug)
  1705. Con::printf( "removeSelection %i", itemOrObjectId );
  1706. Item* item = _findItemByAmbiguousId( itemOrObjectId, false );
  1707. if (!item)
  1708. return;
  1709. // Make sure we have a true item ID even if we started with
  1710. // an object ID.
  1711. S32 itemId = item->getID();
  1712. S32 objectId = -1;
  1713. if ( item->isInspectorData() && item->getObject() )
  1714. objectId = item->getObject()->getId();
  1715. // Remove from vector of selected object ids if it exists there
  1716. if ( objectId != -1 )
  1717. {
  1718. for ( S32 i = 0; i < mSelected.size(); i++ )
  1719. {
  1720. if ( objectId == mSelected[i] || itemId == mSelected[i] )
  1721. {
  1722. mSelected.erase( i );
  1723. break;
  1724. }
  1725. }
  1726. }
  1727. else
  1728. {
  1729. for ( S32 i = 0; i < mSelected.size(); i++ )
  1730. {
  1731. if ( itemId == mSelected[i] )
  1732. {
  1733. mSelected.erase( i );
  1734. break;
  1735. }
  1736. }
  1737. }
  1738. item->mState.set(Item::Selected, false);
  1739. // Remove from vector of selected items if it exists there.
  1740. for ( S32 i = 0; i < mSelectedItems.size(); i++ )
  1741. {
  1742. if ( mSelectedItems[i] == item )
  1743. {
  1744. mSelectedItems.erase( i );
  1745. break;
  1746. }
  1747. }
  1748. // Callback.
  1749. onRemoveSelection( item );
  1750. }
  1751. //-----------------------------------------------------------------------------
  1752. void GuiTreeViewCtrl::addSelection( S32 itemOrObjectId, bool update, bool isLastSelection )
  1753. {
  1754. if (mDebug)
  1755. Con::printf( "addSelection %i", itemOrObjectId );
  1756. Item* item = _findItemByAmbiguousId( itemOrObjectId );
  1757. // Add Item?
  1758. if ( !item || isSelected( item ) || !canAddSelection( item ) )
  1759. {
  1760. // Nope.
  1761. return;
  1762. }
  1763. const S32 itemId = item->getID();
  1764. // Ok, we have an item to select which isn't already selected....
  1765. // Do we want to allow more than one selected item?
  1766. if( !mMultipleSelections )
  1767. clearSelection();
  1768. // Add this object id to the vector of selected objectIds
  1769. // if it is not already.
  1770. bool foundMatch = false;
  1771. for ( S32 i = 0; i < mSelected.size(); i++)
  1772. {
  1773. if ( mSelected[i] == itemId )
  1774. foundMatch = true;
  1775. }
  1776. if ( !foundMatch )
  1777. mSelected.push_front(itemId);
  1778. item->mState.set(Item::Selected, true);
  1779. if( mSelected.size() == 1 )
  1780. {
  1781. onItemSelected( item );
  1782. }
  1783. // Callback Start
  1784. // Set and add the selection to the selected items group
  1785. item->mState.set(Item::Selected, true);
  1786. mSelectedItems.push_front(item);
  1787. if ( item->isInspectorData() &&
  1788. item->getObject() )
  1789. {
  1790. SimObject *obj = item->getObject();
  1791. onAddSelection_callback( obj->getId(), isLastSelection );
  1792. }
  1793. else
  1794. {
  1795. onAddSelection_callback( item->mId, isLastSelection );
  1796. }
  1797. // Callback end
  1798. mFlags.set( RebuildVisible );
  1799. if( update )
  1800. {
  1801. // Also make it so we can see it if we didn't already.
  1802. scrollVisible( item );
  1803. }
  1804. }
  1805. //-----------------------------------------------------------------------------
  1806. void GuiTreeViewCtrl::onItemSelected( Item *item )
  1807. {
  1808. mSelectedItem = item->getID();
  1809. if (item->isInspectorData())
  1810. {
  1811. SimObject* object = item->getObject();
  1812. if( object )
  1813. onSelect_callback( object->getId() );
  1814. if( !item->isParent() && object )
  1815. onInspect_callback( object->getId() );
  1816. }
  1817. else
  1818. {
  1819. onSelect_callback( item->mId );
  1820. if( !item->isParent() )
  1821. onInspect_callback( item->mId );
  1822. }
  1823. }
  1824. //-----------------------------------------------------------------------------
  1825. void GuiTreeViewCtrl::onRemoveSelection( Item *item )
  1826. {
  1827. S32 id = item->mId;
  1828. if( item->isInspectorData() &&
  1829. item->getObject() )
  1830. {
  1831. SimObject* obj = item->getObject();
  1832. id = obj->getId();
  1833. //obj->setSelected( false );
  1834. }
  1835. if( isMethod( "onRemoveSelection" ) )
  1836. onRemoveSelection_callback( id );
  1837. else
  1838. onUnselect_callback( id );
  1839. }
  1840. //-----------------------------------------------------------------------------
  1841. bool GuiTreeViewCtrl::setItemSelected(S32 itemId, bool select)
  1842. {
  1843. Item * item = getItem(itemId);
  1844. if( isSelected( item ) == select )
  1845. return true;
  1846. if (select)
  1847. {
  1848. if (mDebug) Con::printf("setItemSelected called true");
  1849. mSelected.push_front(itemId);
  1850. }
  1851. else
  1852. {
  1853. if (mDebug) Con::printf("setItemSelected called false");
  1854. // remove it from the mSelected list
  1855. for (S32 j = 0; j <mSelected.size(); j++)
  1856. {
  1857. if (item)
  1858. {
  1859. if (item->isInspectorData())
  1860. {
  1861. if (item->getObject())
  1862. {
  1863. if(item->getObject()->getId() == mSelected[j])
  1864. {
  1865. mSelected.erase(j);
  1866. break;
  1867. }
  1868. }
  1869. else
  1870. {
  1871. // Zombie, kill it!
  1872. mSelected.erase(j);
  1873. j--;
  1874. break;
  1875. }
  1876. }
  1877. }
  1878. if (mSelected[j] == itemId)
  1879. {
  1880. mSelected.erase(j);
  1881. break;
  1882. }
  1883. }
  1884. }
  1885. if(!item)
  1886. {
  1887. // maybe what we were passed wasn't an item id but an object id.
  1888. for (S32 i = 0; i <mItems.size(); i++)
  1889. {
  1890. if (mItems[i] != 0)
  1891. {
  1892. if (mItems[i]->isInspectorData())
  1893. {
  1894. if (mItems[i]->getObject())
  1895. {
  1896. if(mItems[i]->getObject()->getId() == itemId)
  1897. {
  1898. item = mItems[i];
  1899. break;
  1900. }
  1901. }
  1902. else
  1903. {
  1904. // It's a zombie, blast it.
  1905. mItems.erase(i);
  1906. i--;
  1907. }
  1908. }
  1909. }
  1910. }
  1911. if (!item)
  1912. {
  1913. //Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemSelected: invalid item id! Perhaps it isn't visible yet.");
  1914. return(false);
  1915. }
  1916. }
  1917. mFlags.set( RebuildVisible );
  1918. if(select)
  1919. {
  1920. addSelection( item->mId );
  1921. onItemSelected( item );
  1922. }
  1923. else
  1924. {
  1925. // deselect the item, if it's present.
  1926. item->mState.set(Item::Selected, false);
  1927. if (item->isInspectorData() && item->getObject())
  1928. onUnselect_callback( item->getObject()->getId() );
  1929. else
  1930. onUnselect_callback( item->mId );
  1931. // remove it from the selected items list
  1932. for (S32 i = 0; i < mSelectedItems.size(); i++)
  1933. {
  1934. if (mSelectedItems[i] == item)
  1935. {
  1936. mSelectedItems.erase(i);
  1937. break;
  1938. }
  1939. }
  1940. }
  1941. setUpdate();
  1942. return(true);
  1943. }
  1944. //-----------------------------------------------------------------------------
  1945. // Given an item's index in the selection list, return its itemId
  1946. S32 GuiTreeViewCtrl::getSelectedItem(S32 index)
  1947. {
  1948. if(index >= 0 && index < getSelectedItemsCount())
  1949. {
  1950. return mSelectedItems[index]->mId;
  1951. }
  1952. return -1;
  1953. }
  1954. //-----------------------------------------------------------------------------
  1955. bool GuiTreeViewCtrl::setItemExpanded(S32 itemId, bool expand)
  1956. {
  1957. Item * item = getItem(itemId);
  1958. if(!item)
  1959. {
  1960. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemExpanded: invalid item id!");
  1961. return(false);
  1962. }
  1963. if(item->isExpanded() == expand)
  1964. return(true);
  1965. // expand parents
  1966. if(expand)
  1967. {
  1968. while(item)
  1969. {
  1970. if(item->mState.test(Item::VirtualParent))
  1971. onVirtualParentExpand(item);
  1972. item->setExpanded(true);
  1973. item = item->mParent;
  1974. }
  1975. }
  1976. else
  1977. {
  1978. if(item->mState.test(Item::VirtualParent))
  1979. onVirtualParentCollapse(item);
  1980. item->setExpanded(false);
  1981. }
  1982. return(true);
  1983. }
  1984. //-----------------------------------------------------------------------------
  1985. bool GuiTreeViewCtrl::setItemValue(S32 itemId, StringTableEntry Value)
  1986. {
  1987. Item * item = getItem(itemId);
  1988. if(!item)
  1989. {
  1990. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemValue: invalid item id!");
  1991. return(false);
  1992. }
  1993. item->setValue( ( Value ) ? Value : "" );
  1994. return(true);
  1995. }
  1996. //-----------------------------------------------------------------------------
  1997. const char * GuiTreeViewCtrl::getItemText(S32 itemId)
  1998. {
  1999. Item * item = getItem(itemId);
  2000. if(!item)
  2001. {
  2002. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getItemText: invalid item id!");
  2003. return("");
  2004. }
  2005. return(item->getText() ? item->getText() : "");
  2006. }
  2007. //-----------------------------------------------------------------------------
  2008. const char * GuiTreeViewCtrl::getItemValue(S32 itemId)
  2009. {
  2010. Item * item = getItem(itemId);
  2011. if(!item)
  2012. {
  2013. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getItemValue: invalid item id!");
  2014. return("");
  2015. }
  2016. if(item->mState.test(Item::InspectorData))
  2017. {
  2018. // If it's InspectorData, we let people use this call to get an object reference.
  2019. return item->mInspectorInfo.mObject->getIdString();
  2020. }
  2021. else
  2022. {
  2023. // Just return the script value...
  2024. return item->getValue();
  2025. }
  2026. }
  2027. //-----------------------------------------------------------------------------
  2028. S32 GuiTreeViewCtrl::getItemAtPosition(Point2I position)
  2029. {
  2030. BitSet32 hitFlags = 0;
  2031. Item* item;
  2032. if (_hitTest(position, item, hitFlags))
  2033. return item->mId;
  2034. else
  2035. return -1;
  2036. }
  2037. //-----------------------------------------------------------------------------
  2038. bool GuiTreeViewCtrl::editItem( S32 itemId, const char* newText, const char* newValue )
  2039. {
  2040. Item* item = getItem( itemId );
  2041. if ( !item )
  2042. {
  2043. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::editItem: invalid item id: %d!", itemId);
  2044. return false;
  2045. }
  2046. if ( item->mState.test(Item::InspectorData) )
  2047. {
  2048. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::editItem: item %d is inspector data and may not be modified!", itemId);
  2049. return false;
  2050. }
  2051. item->setText( StringTable->insert( newText, true ) );
  2052. item->setValue( StringTable->insert( newValue, true ) );
  2053. // Update the widths and such:
  2054. mFlags.set(RebuildVisible);
  2055. return true;
  2056. }
  2057. //-----------------------------------------------------------------------------
  2058. bool GuiTreeViewCtrl::markItem( S32 itemId, bool mark )
  2059. {
  2060. Item *item = getItem( itemId );
  2061. if ( !item )
  2062. {
  2063. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::markItem: invalid item id: %d!", itemId);
  2064. return false;
  2065. }
  2066. item->mState.set(Item::Marked, mark);
  2067. return true;
  2068. }
  2069. //-----------------------------------------------------------------------------
  2070. bool GuiTreeViewCtrl::isItemSelected( S32 itemId )
  2071. {
  2072. for( U32 i = 0, num = mSelectedItems.size(); i < num; ++ i )
  2073. if( mSelectedItems[ i ]->mId == itemId )
  2074. return true;
  2075. return false;
  2076. }
  2077. //-----------------------------------------------------------------------------
  2078. void GuiTreeViewCtrl::deleteSelection()
  2079. {
  2080. onDeleteSelection_callback();
  2081. if (mSelectedItems.empty())
  2082. {
  2083. for (S32 i = 0; i < mSelected.size(); i++)
  2084. {
  2085. S32 objectId = mSelected[i];
  2086. // find the object
  2087. SimObject* obj = Sim::findObject(objectId);
  2088. if ( !obj )
  2089. continue;
  2090. bool skipDelete = onDeleteObject_callback( obj );
  2091. if ( !skipDelete )
  2092. obj->deleteObject();
  2093. }
  2094. }
  2095. else
  2096. {
  2097. Vector<Item*> delSelection;
  2098. delSelection = mSelectedItems;
  2099. mSelectedItems.clear();
  2100. while (!delSelection.empty())
  2101. {
  2102. Item * item = delSelection.front();
  2103. setItemSelected(item->mId,false);
  2104. if ( item->mParent )
  2105. _deleteItem( item );
  2106. delSelection.pop_front();
  2107. }
  2108. }
  2109. mSelected.clear();
  2110. mSelectedItems.clear();
  2111. mSelectedItem = 0;
  2112. onObjectDeleteCompleted_callback();
  2113. }
  2114. //------------------------------------------------------------------------------
  2115. // keyboard movement of items is restricted to just one item at a time
  2116. // if more than one item is selected then movement operations are not performed
  2117. bool GuiTreeViewCtrl::onKeyDown( const GuiEvent& event )
  2118. {
  2119. if ( !mVisible || !mActive || !mAwake )
  2120. return false;
  2121. // All the keyboard functionality requires a selected item, so if none exists...
  2122. // Deal with enter and delete
  2123. if ( event.modifier == 0 )
  2124. {
  2125. if ( event.keyCode == KEY_RETURN )
  2126. {
  2127. execAltConsoleCallback();
  2128. return true;
  2129. }
  2130. if ( event.keyCode == KEY_DELETE && mDeleteObjectAllowed )
  2131. {
  2132. // Don't delete the root!
  2133. if (mSelectedItems.empty())
  2134. return true;
  2135. //this may be fighting with the world editor delete
  2136. deleteSelection();
  2137. return true;
  2138. }
  2139. //call a generic bit of script that will let the subclass know that a key was pressed
  2140. onKeyDown_callback( event.modifier, event.keyCode );
  2141. }
  2142. // only do operations if only one item is selected
  2143. if ( mSelectedItems.empty() || (mSelectedItems.size() > 1))
  2144. return false;
  2145. Item* item = mSelectedItems.first();
  2146. if ( !item )
  2147. return false;
  2148. // The Alt key lets you move items around!
  2149. if ( mFlags.test(IsEditable) && event.modifier & SI_ALT )
  2150. {
  2151. switch ( event.keyCode )
  2152. {
  2153. case KEY_UP:
  2154. // Move us up.
  2155. if ( item->mPrevious )
  2156. {
  2157. moveItemUp( item->mId );
  2158. scrollVisible(item);
  2159. }
  2160. return true;
  2161. case KEY_DOWN:
  2162. // Move the item under us up.
  2163. if ( item->mNext )
  2164. {
  2165. moveItemUp( item->mNext->mId );
  2166. scrollVisible(item);
  2167. }
  2168. return true;
  2169. case KEY_LEFT:
  2170. if ( item->mParent )
  2171. {
  2172. if ( item->mParent->mParent )
  2173. {
  2174. // Ok, we have both an immediate parent, and a grandparent.
  2175. // The goal of left-arrow alt is to become the child of our
  2176. // grandparent, ie, to become a sibling of our parent.
  2177. // First, unlink item from its siblings.
  2178. if ( item->mPrevious )
  2179. item->mPrevious->mNext = item->mNext;
  2180. else
  2181. item->mParent->mChild = item->mNext;
  2182. if ( item->mNext )
  2183. item->mNext->mPrevious = item->mPrevious;
  2184. // Now, relink as the next sibling of our parent.
  2185. item->mPrevious = item->mParent;
  2186. item->mNext = item->mParent->mNext;
  2187. // If there was already a next sibling, deal with that case.
  2188. if ( item->mNext )
  2189. item->mNext->mPrevious = item;
  2190. item->mParent->mNext = item;
  2191. // Snag the current parent set if any...
  2192. SimSet *parentSet = NULL;
  2193. if(item->mParent->isInspectorData())
  2194. parentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
  2195. else
  2196. {
  2197. // parent is probably script data so we search up the tree for a
  2198. // set to put our object in
  2199. Item * temp = item->mParent;
  2200. while (!temp->isInspectorData())
  2201. temp = temp->mParent;
  2202. // found a ancestor who is an inspectorData
  2203. if (temp->isInspectorData())
  2204. parentSet = dynamic_cast<SimSet*>(temp->getObject());
  2205. else parentSet = NULL;
  2206. }
  2207. // Get our active SimObject if any
  2208. SimObject *simObj = NULL;
  2209. if(item->isInspectorData())
  2210. simObj = item->getObject();
  2211. // Remove from the old parentset...
  2212. if(simObj && parentSet) {
  2213. if (parentSet->size()>0)
  2214. {
  2215. SimObject *lastObject = parentSet->last();
  2216. parentSet->removeObject(simObj);
  2217. parentSet->reOrder(lastObject);
  2218. } else
  2219. parentSet->removeObject(simObj);
  2220. }
  2221. // And finally, update our item
  2222. item->mParent = item->mParent->mParent;
  2223. // Snag the newparent set if any...
  2224. SimSet *newParentSet = NULL;
  2225. if(item->mParent->isInspectorData())
  2226. newParentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
  2227. else
  2228. {
  2229. // parent is probably script data so we search up the tree for a
  2230. // set to put our object in
  2231. Item * temp = item->mParent;
  2232. while (!temp->isInspectorData())
  2233. temp = temp->mParent;
  2234. // found a ancestor who is an inspectorData
  2235. if (temp->isInspectorData())
  2236. newParentSet = dynamic_cast<SimSet*>(temp->getObject());
  2237. else newParentSet = NULL;
  2238. }
  2239. if(simObj && newParentSet)
  2240. {
  2241. newParentSet->addObject(simObj);
  2242. Item * temp = item->mNext;
  2243. // item->mNext may be script, so find an inspector item to reorder with if any
  2244. if (temp) {
  2245. do {
  2246. if (temp->isInspectorData())
  2247. break;
  2248. temp = temp->mNext;
  2249. } while (temp);
  2250. if (temp && item->getObject() && temp->getObject()) //do we still have a item->mNext? If not then don't bother reordering
  2251. newParentSet->reOrder(item->getObject(), temp->getObject());
  2252. }
  2253. } else if (!simObj&&newParentSet) {
  2254. // our current item is script data. but it may have children who
  2255. // is inspector data who need an updated set
  2256. if (item->mChild)
  2257. inspectorSearch(item->mChild, item, parentSet, newParentSet);
  2258. }
  2259. // And update everything hurrah.
  2260. buildVisibleTree();
  2261. scrollVisible(item);
  2262. }
  2263. }
  2264. return true;
  2265. case KEY_RIGHT:
  2266. if ( item->mPrevious )
  2267. {
  2268. // Make the item the last child of its previous sibling.
  2269. // First, unlink from the current position in the list
  2270. item->mPrevious->mNext = item->mNext;
  2271. if ( item->mNext )
  2272. item->mNext->mPrevious = item->mPrevious;
  2273. // Get the object we're poking with.
  2274. SimObject *simObj = NULL;
  2275. SimSet *parentSet = NULL;
  2276. if(item->isInspectorData())
  2277. simObj = item->getObject();
  2278. if(item->mParent->isInspectorData())
  2279. parentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
  2280. else {
  2281. // parent is probably script data so we search up the tree for a
  2282. // set to put our object in
  2283. Item * temp = item->mParent;
  2284. while (!temp->isInspectorData())
  2285. temp = temp->mParent;
  2286. // found an ancestor who is an inspectorData
  2287. if (temp->isInspectorData())
  2288. parentSet = dynamic_cast<SimSet*>(temp->getObject());
  2289. }
  2290. // If appropriate, remove from the current SimSet.
  2291. if(parentSet && simObj) {
  2292. if (parentSet->size()>0)
  2293. {
  2294. SimObject *lastObject = parentSet->last();
  2295. parentSet->removeObject(simObj);
  2296. parentSet->reOrder(lastObject);
  2297. } else
  2298. parentSet->removeObject(simObj);
  2299. }
  2300. // Now, make our previous sibling our parent...
  2301. item->mParent = item->mPrevious;
  2302. item->mNext = NULL;
  2303. // And sink us down to the end of its siblings, if appropriate.
  2304. if ( item->mParent->mChild )
  2305. {
  2306. Item* temp = item->mParent->mChild;
  2307. while ( temp->mNext )
  2308. temp = temp->mNext;
  2309. temp->mNext = item;
  2310. item->mPrevious = temp;
  2311. }
  2312. else
  2313. {
  2314. // only child...<sniff>
  2315. item->mParent->mChild = item;
  2316. item->mPrevious = NULL;
  2317. }
  2318. // Make sure the new parent is expanded:
  2319. if ( !item->mParent->mState.test( Item::Expanded ) )
  2320. setItemExpanded( item->mParent->mId, true );
  2321. // Snag the new parent simset if any.
  2322. SimSet *newParentSet = NULL;
  2323. // new parent might be script. so figure out what set we need to add it to.
  2324. if(item->mParent->isInspectorData())
  2325. newParentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
  2326. else
  2327. {
  2328. // parent is probably script data so we search up the tree for a
  2329. // set to put our object in
  2330. if (mDebug) Con::printf("oh nos my parent is script!");
  2331. Item * temp = item->mParent;
  2332. while (!temp->isInspectorData())
  2333. temp = temp->mParent;
  2334. // found a ancestor who is an inspectorData
  2335. if (temp->isInspectorData())
  2336. newParentSet = dynamic_cast<SimSet*>(temp->getObject());
  2337. else newParentSet = NULL;
  2338. }
  2339. // Add the item's SimObject to the new parent simset, at the end.
  2340. if(newParentSet && simObj)
  2341. newParentSet->addObject(simObj);
  2342. else if (!simObj&&newParentSet&&parentSet) {
  2343. // our current item is script data. but it may have children who
  2344. // is inspector data who need an updated set
  2345. if (item->mChild) {
  2346. inspectorSearch(item->mChild, item, parentSet, newParentSet);
  2347. }
  2348. }
  2349. scrollVisible(item);
  2350. }
  2351. return true;
  2352. default:
  2353. break;
  2354. }
  2355. }
  2356. // Explorer-esque navigation...
  2357. switch( event.keyCode )
  2358. {
  2359. case KEY_UP:
  2360. // Select previous visible item:
  2361. if ( item->mPrevious )
  2362. {
  2363. item = item->mPrevious;
  2364. while ( item->isParent() && item->isExpanded() )
  2365. {
  2366. item = item->mChild;
  2367. while ( item->mNext )
  2368. item = item->mNext;
  2369. }
  2370. clearSelection();
  2371. addSelection( item->mId );
  2372. return true;
  2373. }
  2374. // or select parent:
  2375. if ( item->mParent )
  2376. {
  2377. clearSelection();
  2378. addSelection( item->mParent->mId );
  2379. return true;
  2380. }
  2381. return false;
  2382. break;
  2383. case KEY_DOWN:
  2384. // Selected child if it is visible:
  2385. if ( item->isParent() && item->isExpanded() )
  2386. {
  2387. clearSelection();
  2388. addSelection( item->mChild->mId );
  2389. return true;
  2390. }
  2391. // or select next sibling (recursively):
  2392. do
  2393. {
  2394. if ( item->mNext )
  2395. {
  2396. clearSelection();
  2397. addSelection( item->mNext->mId );
  2398. return true;
  2399. }
  2400. item = item->mParent;
  2401. } while ( item );
  2402. return false;
  2403. break;
  2404. case KEY_LEFT:
  2405. // Contract current menu:
  2406. if ( item->isExpanded() )
  2407. {
  2408. setItemExpanded( item->mId, false );
  2409. scrollVisible(item);
  2410. return true;
  2411. }
  2412. // or select parent:
  2413. if ( item->mParent )
  2414. {
  2415. clearSelection();
  2416. addSelection( item->mParent->mId );
  2417. return true;
  2418. }
  2419. return false;
  2420. break;
  2421. case KEY_RIGHT:
  2422. // Expand selected item:
  2423. if ( item->isParent() )
  2424. {
  2425. if ( !item->isExpanded() )
  2426. {
  2427. setItemExpanded( item->mId, true );
  2428. scrollVisible(item);
  2429. return true;
  2430. }
  2431. // or select child:
  2432. clearSelection();
  2433. addSelection( item->mChild->mId );
  2434. return true;
  2435. }
  2436. return false;
  2437. break;
  2438. default:
  2439. break;
  2440. }
  2441. // Not processed, so pass the event on:
  2442. return Parent::onKeyDown( event );
  2443. }
  2444. //------------------------------------------------------------------------------
  2445. // on mouse up look at the current item and check to see if it is valid
  2446. // to move the selected item(s) to it.
  2447. void GuiTreeViewCtrl::onMouseUp(const GuiEvent &event)
  2448. {
  2449. if( !mActive || !mAwake || !mVisible )
  2450. return;
  2451. BitSet32 hitFlags = 0;
  2452. if( isMethod("onMouseUp") )
  2453. {
  2454. Item* item;
  2455. S32 hitItemId = -1;
  2456. if( _hitTest( event.mousePoint, item, hitFlags ) )
  2457. hitItemId = item->mId;
  2458. onMouseUp_callback( hitItemId, event.mouseClickCount );
  2459. }
  2460. mouseUnlock();
  2461. if ( mSelectedItems.empty())
  2462. {
  2463. mDragMidPoint = NomDragMidPoint;
  2464. return;
  2465. }
  2466. hitFlags = 0;
  2467. Item *hitItem;
  2468. bool hitCheck = _hitTest( event.mousePoint, hitItem, hitFlags );
  2469. mRenamingItem = NULL;
  2470. if( hitCheck )
  2471. {
  2472. if ( event.mouseClickCount == 1 && !mMouseDragged && mPossibleRenameItem != NULL )
  2473. {
  2474. if (hitItem == mPossibleRenameItem )
  2475. showItemRenameCtrl(hitItem);
  2476. }
  2477. else // If mouseUp occurs on the same item as mouse down
  2478. {
  2479. bool wasSelected = isSelected(hitItem);
  2480. bool multiSelect = getSelectedItemsCount() > 1;
  2481. if( wasSelected && multiSelect && hitItem == mTempItem )
  2482. {
  2483. clearSelection();
  2484. addSelection( hitItem->mId );
  2485. }
  2486. }
  2487. }
  2488. mPossibleRenameItem = NULL;
  2489. if (!mMouseDragged)
  2490. return;
  2491. Item* newItem = NULL;
  2492. Item* newItem2 = NULL;
  2493. if (mFlags.test(IsEditable))
  2494. {
  2495. Parent::onMouseMove( event );
  2496. hitFlags = 0;
  2497. if( !_hitTest( event.mousePoint, newItem2, hitFlags ) )
  2498. {
  2499. if( !mShowRoot )
  2500. newItem2 = mRoot;
  2501. else
  2502. {
  2503. if( mDebug )
  2504. Con::printf( "Nothing hit" );
  2505. mDragMidPoint = NomDragMidPoint;
  2506. return;
  2507. }
  2508. }
  2509. newItem2->mState.clear(Item::MouseOverBmp | Item::MouseOverText );
  2510. // If the hit item is the visible root, make sure
  2511. // we don't allow dragging above.
  2512. if( newItem2 == mRoot && mDragMidPoint == AbovemDragMidPoint )
  2513. {
  2514. if( mDebug )
  2515. Con::printf( "Rejecting to make child sibling of root" );
  2516. mDragMidPoint = NomDragMidPoint;
  2517. return;
  2518. }
  2519. // if the newItem isn't in the mSelectedItemList then continue.
  2520. Vector<Item *>::iterator k;
  2521. for(k = mSelectedItems.begin(); k != mSelectedItems.end(); k++)
  2522. {
  2523. newItem = newItem2;
  2524. if (*(k) == newItem)
  2525. {
  2526. mDragMidPoint = NomDragMidPoint;
  2527. return;
  2528. }
  2529. Item * temp = *(k);
  2530. Item * grandpaTemp = newItem->mParent;
  2531. // grandpa check, kick out if an item would be its own ancestor
  2532. while (grandpaTemp)
  2533. {
  2534. if (temp == grandpaTemp)
  2535. {
  2536. if (mDebug)
  2537. {
  2538. Con::printf("grandpa check");
  2539. if (temp->isInspectorData())
  2540. Con::printf("temp's name: %s",temp->getObject()->getName());
  2541. if (grandpaTemp->isInspectorData())
  2542. Con::printf("grandpa's name: %s",grandpaTemp->getObject()->getName());
  2543. }
  2544. mDragMidPoint = NomDragMidPoint;
  2545. return;
  2546. }
  2547. grandpaTemp = grandpaTemp->mParent;
  2548. }
  2549. }
  2550. // Notify script for undo.
  2551. onBeginReparenting_callback();
  2552. // Reparent the items.
  2553. reparentItems(mSelectedItems, newItem2);
  2554. onEndReparenting_callback();
  2555. // And update everything.
  2556. scrollVisible(newItem);
  2557. onDragDropped_callback();
  2558. buildVisibleTree(false);
  2559. }
  2560. mDragMidPoint = NomDragMidPoint;
  2561. }
  2562. //------------------------------------------------------------------------------
  2563. void GuiTreeViewCtrl::onMouseDragged(const GuiEvent &event)
  2564. {
  2565. if( mDragStartInSelection )
  2566. onMouseDragged_callback();
  2567. if(!mSupportMouseDragging)
  2568. return;
  2569. if( !mActive || !mAwake || !mVisible )
  2570. return;
  2571. if (mSelectedItems.size() == 0)
  2572. return;
  2573. //Check through to make sure all attempted dragged items even allow it
  2574. for (U32 i = 0; i < mSelectedItems.size(); i++)
  2575. if (!mSelectedItems[i]->isDragAllowed())
  2576. return;
  2577. // Give us a little delta before we actually start a mouse drag so that
  2578. // if the user moves the mouse a little while clicking, he/she does not
  2579. // accidentally trigger a drag.
  2580. if( mFabs( ( mMouseDownPoint - event.mousePoint ).len() ) <= 4.f )
  2581. return;
  2582. Point2I pt = globalToLocalCoord(event.mousePoint);
  2583. Parent::onMouseMove(event);
  2584. mouseLock();
  2585. mMouseDragged = true;
  2586. // If the drag is outside of our visible area,
  2587. // start scrolling.
  2588. GuiScrollCtrl* scrollCtrl = dynamic_cast< GuiScrollCtrl* >( getParent() );
  2589. if( scrollCtrl && !scrollCtrl->isPointVisible( pt ) )
  2590. {
  2591. S32 widthDelta = 0;
  2592. S32 heightDelta = 0;
  2593. if( pt.x < scrollCtrl->getChildRelPos().x )
  2594. widthDelta = pt.x - scrollCtrl->getChildRelPos().x;
  2595. else if( pt.x > scrollCtrl->getChildRelPos().x + scrollCtrl->getContentExtent().x )
  2596. widthDelta = pt.x - scrollCtrl->getChildRelPos().x - scrollCtrl->getContentExtent().x;
  2597. if( pt.y < scrollCtrl->getChildRelPos().y )
  2598. heightDelta = pt.y - scrollCtrl->getChildRelPos().y;
  2599. else if( pt.y > scrollCtrl->getChildRelPos().y + scrollCtrl->getContentExtent().y )
  2600. heightDelta = pt.y - scrollCtrl->getChildRelPos().y - scrollCtrl->getContentExtent().y;
  2601. const F32 SCROLL_RATIO = 0.5f;
  2602. scrollCtrl->scrollDelta( S32( F32( widthDelta ) * SCROLL_RATIO ), S32( F32( heightDelta ) * SCROLL_RATIO ) );
  2603. }
  2604. // whats our mDragMidPoint?
  2605. mCurrentDragCell = mMouseOverCell.y;
  2606. S32 midpCell = mCurrentDragCell * mItemHeight + (mItemHeight/2);
  2607. S32 currentY = pt.y;
  2608. S32 yDiff = currentY-midpCell;
  2609. S32 variance = (mItemHeight/5);
  2610. if( mPreviousDragCell >= 0 && mPreviousDragCell < mVisibleItems.size() )
  2611. mVisibleItems[mPreviousDragCell]->mState.clear( Item::MouseOverBmp | Item::MouseOverText | Item::MouseOverIcon );
  2612. bool hoverItem = false;
  2613. if (mAbs(yDiff) <= variance)
  2614. {
  2615. mDragMidPoint = NomDragMidPoint;
  2616. // highlight the current item
  2617. // hittest to detect whether we are on an item
  2618. // ganked from onMouseMouse
  2619. // used for tracking what our last cell was so we can clear it.
  2620. mPreviousDragCell = mCurrentDragCell;
  2621. if (mCurrentDragCell >= 0)
  2622. {
  2623. Item* item = NULL;
  2624. BitSet32 hitFlags = 0;
  2625. if ( !_hitTest( event.mousePoint, item, hitFlags ) )
  2626. return;
  2627. // If the item is a valid drag target, activate the item
  2628. // highlighting.
  2629. if( isValidDragTarget( item ) )
  2630. {
  2631. hoverItem = true;
  2632. if ( hitFlags.test( OnImage ) )
  2633. item->mState.set( Item::MouseOverBmp );
  2634. if ( hitFlags.test( OnText ) )
  2635. item->mState.set( Item::MouseOverText );
  2636. if ( hitFlags.test( OnIcon ) )
  2637. item->mState.set( Item::MouseOverIcon );
  2638. // Always redraw the entire mouse over item, since we are distinguishing
  2639. // between the bitmap and the text:
  2640. setUpdateRegion( Point2I( mMouseOverCell.x * mCellSize.x, mMouseOverCell.y * mCellSize.y ), mCellSize );
  2641. }
  2642. }
  2643. }
  2644. if ( !hoverItem )
  2645. {
  2646. //above or below an item?
  2647. if (yDiff < 0)
  2648. mDragMidPoint = AbovemDragMidPoint;
  2649. else
  2650. mDragMidPoint = BelowmDragMidPoint;
  2651. }
  2652. }
  2653. //-----------------------------------------------------------------------------
  2654. void GuiTreeViewCtrl::onMiddleMouseDown(const GuiEvent & event)
  2655. {
  2656. //for debugging items
  2657. if (mDebug) {
  2658. Item* item;
  2659. BitSet32 hitFlags = 0;
  2660. _hitTest( event.mousePoint, item, hitFlags );
  2661. Con::printf("debugging %d", item->mId);
  2662. Point2I pt = globalToLocalCoord(event.mousePoint);
  2663. if (item->isInspectorData() && item->getObject()) {
  2664. Con::printf("object data:");
  2665. Con::printf("name:%s",item->getObject()->getName());
  2666. Con::printf("className:%s",item->getObject()->getClassName());
  2667. }
  2668. Con::printf("contents of mSelectedItems:");
  2669. for(S32 i = 0; i < mSelectedItems.size(); i++) {
  2670. if (mSelectedItems[i]->isInspectorData()) {
  2671. Con::printf("%d",mSelectedItems[i]->getObject()->getId());
  2672. } else
  2673. Con::printf("wtf %d", mSelectedItems[i]);
  2674. }
  2675. Con::printf("contents of mSelected");
  2676. for (S32 j = 0; j < mSelected.size(); j++) {
  2677. Con::printf("%d", mSelected[j]);
  2678. }
  2679. mCurrentDragCell = mMouseOverCell.y;
  2680. S32 midpCell = (mCurrentDragCell) * mItemHeight + (mItemHeight/2);
  2681. S32 currentY = pt.y;
  2682. S32 yDiff = currentY-midpCell;
  2683. Con::printf("cell info: (%d,%d) mCurrentDragCell=%d est=(%d,%d,%d) ydiff=%d",pt.x,pt.y,mCurrentDragCell,mCurrentDragCell*mItemHeight, midpCell, (mCurrentDragCell+1)*mItemHeight,yDiff);
  2684. }
  2685. }
  2686. //-----------------------------------------------------------------------------
  2687. void GuiTreeViewCtrl::onMouseDown(const GuiEvent & event)
  2688. {
  2689. if( !mActive || !mAwake || !mVisible )
  2690. {
  2691. Parent::onMouseDown(event);
  2692. return;
  2693. }
  2694. if ( mProfile->mCanKeyFocus )
  2695. setFirstResponder();
  2696. Item * item = 0;
  2697. BitSet32 hitFlags;
  2698. mDragMidPoint = NomDragMidPoint;
  2699. mMouseDragged = false;
  2700. mMouseDownPoint = event.mousePoint;
  2701. //
  2702. if(!_hitTest(event.mousePoint, item, hitFlags))
  2703. return;
  2704. mPossibleRenameItem = NULL;
  2705. mRenamingItem = NULL;
  2706. mTempItem = NULL;
  2707. //
  2708. if( event.modifier & SI_MULTISELECT )
  2709. {
  2710. bool selectFlag = item->mState.test(Item::Selected);
  2711. if (selectFlag == true)
  2712. {
  2713. // already selected, so unselect it and remove it
  2714. removeSelection(item->mId);
  2715. }
  2716. else
  2717. {
  2718. addSelection(item->mId);
  2719. }
  2720. }
  2721. else if( event.modifier & SI_RANGESELECT && mMultipleSelections )
  2722. {
  2723. // is something already selected?
  2724. S32 firstSelectedIndex = 0;
  2725. Item * firstItem = NULL;
  2726. if (!mSelectedItems.empty())
  2727. {
  2728. firstItem = mSelectedItems.front();
  2729. for (S32 i = 0; i < mVisibleItems.size();i++)
  2730. {
  2731. if (mVisibleItems[i] == mSelectedItems.front())
  2732. {
  2733. firstSelectedIndex = i;
  2734. break;
  2735. }
  2736. }
  2737. mCurrentDragCell = mMouseOverCell.y;
  2738. if (mVisibleItems[firstSelectedIndex] != firstItem )
  2739. {
  2740. /*
  2741. Con::printf("something isn't right...");
  2742. if (mVisibleItems[firstSelectedIndex]->isInspectorData())
  2743. Con::printf("visibleItem %s",mVisibleItems[firstSelectedIndex]->getObject()->getName());
  2744. if (firstItem->isInspectorData())
  2745. Con::printf("firstItem %s",firstItem->getObject()->getName());
  2746. */
  2747. }
  2748. else
  2749. {
  2750. // select the cells
  2751. onAddMultipleSelectionBegin_callback();
  2752. if ((mCurrentDragCell) < firstSelectedIndex)
  2753. {
  2754. //select up
  2755. for (S32 j = (mCurrentDragCell); j < firstSelectedIndex; j++)
  2756. {
  2757. if( j != (firstSelectedIndex - 1) )
  2758. addSelection(mVisibleItems[j]->mId, false, false);
  2759. else
  2760. addSelection(mVisibleItems[j]->mId, false);
  2761. }
  2762. }
  2763. else
  2764. {
  2765. // select down
  2766. for (S32 j = firstSelectedIndex+1; j < (mCurrentDragCell+1); j++)
  2767. {
  2768. if( j != mCurrentDragCell )
  2769. addSelection(mVisibleItems[j]->mId, false, false);
  2770. else
  2771. addSelection(mVisibleItems[j]->mId, false);
  2772. }
  2773. }
  2774. // Scroll to view the last selected cell.
  2775. scrollVisible( mVisibleItems[mCurrentDragCell] );
  2776. onAddMultipleSelectionEnd_callback();
  2777. }
  2778. }
  2779. }
  2780. else if ( event.modifier & SI_PRIMARY_ALT )
  2781. {
  2782. if ( item->isInspectorData() && item->getObject() )
  2783. setAddGroup(item->getObject());
  2784. }
  2785. else if ( !hitFlags.test(OnImage) )
  2786. {
  2787. mTempItem = item;
  2788. bool wasSelected = isSelected( item );
  2789. bool multiSelect = getSelectedItemsCount() > 1;
  2790. if( !wasSelected || !multiSelect )
  2791. {
  2792. if ( mClearAllOnSingleSelection )
  2793. clearSelection();
  2794. if ( !wasSelected || mClearAllOnSingleSelection )
  2795. addSelection( item->mId );
  2796. if ( wasSelected &&
  2797. !multiSelect &&
  2798. mCanRenameObjects &&
  2799. hitFlags.test(OnText) &&
  2800. mFlags.test(IsEditable) &&
  2801. item->isInspectorData() &&
  2802. item->getObject() &&
  2803. item->getObject()->isNameChangeAllowed() &&
  2804. item != mRoot &&
  2805. event.mouseClickCount == 1 )
  2806. {
  2807. mPossibleRenameItem = item;
  2808. if ( isMethod( "canRenameObject" ) )
  2809. {
  2810. if( canRenameObject_callback( item->getObject() ) )
  2811. mPossibleRenameItem = NULL;
  2812. }
  2813. }
  2814. }
  2815. }
  2816. if ( ( hitFlags.test( OnText ) || hitFlags.test( OnIcon ) ) &&
  2817. event.mouseClickCount > 1 )
  2818. execAltConsoleCallback();
  2819. // For dragging, note if hit is in selection.
  2820. mDragStartInSelection = isItemSelected( item->mId );
  2821. if(!item->isParent())
  2822. return;
  2823. //
  2824. if ( mFullRowSelect || hitFlags.test( OnImage ) )
  2825. {
  2826. item->setExpanded(!item->isExpanded());
  2827. if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
  2828. onVirtualParentExpand(item);
  2829. //Slightly hacky, but I'm not sure of a better setup until we get major update to the editors
  2830. //We check if our object is an entity, and if it is, we call a 'onInspect' function.
  2831. //This function is pretty much a special notifier to the entity so if it has any behaviors that do special
  2832. //stuff in the editor, it can fire that up
  2833. if (item->isInspectorData())
  2834. {
  2835. Entity* e = dynamic_cast<Entity*>(item->getObject());
  2836. if (e)
  2837. {
  2838. if (item->isExpanded())
  2839. e->onInspect();
  2840. else
  2841. e->onEndInspect();
  2842. }
  2843. }
  2844. mFlags.set( RebuildVisible );
  2845. scrollVisible(item);
  2846. }
  2847. }
  2848. //------------------------------------------------------------------------------
  2849. void GuiTreeViewCtrl::onMouseMove( const GuiEvent &event )
  2850. {
  2851. if ( mMouseOverCell.y >= 0 && mVisibleItems.size() > mMouseOverCell.y)
  2852. mVisibleItems[mMouseOverCell.y]->mState.clear( Item::MouseOverBmp | Item::MouseOverText | Item::MouseOverIcon);
  2853. Parent::onMouseMove( event );
  2854. if ( mMouseOverCell.y >= 0 )
  2855. {
  2856. Item* item = NULL;
  2857. BitSet32 hitFlags = 0;
  2858. if ( !_hitTest( event.mousePoint, item, hitFlags ) )
  2859. return;
  2860. if ( hitFlags.test( OnImage ) )
  2861. item->mState.set( Item::MouseOverBmp );
  2862. if ( hitFlags.test( OnText ) )
  2863. item->mState.set( Item::MouseOverText );
  2864. if ( hitFlags.test( OnIcon ) )
  2865. item->mState.set( Item::MouseOverIcon );
  2866. // Always redraw the entire mouse over item, since we are distinguishing
  2867. // between the bitmap and the text:
  2868. setUpdateRegion( Point2I( mMouseOverCell.x * mCellSize.x, mMouseOverCell.y * mCellSize.y ), mCellSize );
  2869. }
  2870. }
  2871. //------------------------------------------------------------------------------
  2872. void GuiTreeViewCtrl::onMouseEnter( const GuiEvent &event )
  2873. {
  2874. Parent::onMouseEnter( event );
  2875. onMouseMove( event );
  2876. }
  2877. //------------------------------------------------------------------------------
  2878. void GuiTreeViewCtrl::onMouseLeave( const GuiEvent &event )
  2879. {
  2880. if ( mMouseOverCell.y >= 0 && mVisibleItems.size() > mMouseOverCell.y)
  2881. mVisibleItems[mMouseOverCell.y]->mState.clear( Item::MouseOverBmp | Item::MouseOverText | Item::MouseOverIcon );
  2882. Parent::onMouseLeave( event );
  2883. }
  2884. //------------------------------------------------------------------------------
  2885. void GuiTreeViewCtrl::onRightMouseDown(const GuiEvent & event)
  2886. {
  2887. if(!mActive)
  2888. {
  2889. Parent::onRightMouseDown(event);
  2890. return;
  2891. }
  2892. Item * item = NULL;
  2893. BitSet32 hitFlags;
  2894. //
  2895. if(!_hitTest(event.mousePoint, item, hitFlags))
  2896. return;
  2897. //
  2898. if (item->isInspectorData() && item->getObject())
  2899. onRightMouseDown_callback( item->mId, event.mousePoint, item->getObject() );
  2900. else
  2901. onRightMouseDown_callback( item->mId, event.mousePoint );
  2902. }
  2903. //-----------------------------------------------------------------------------
  2904. void GuiTreeViewCtrl::onRightMouseUp(const GuiEvent & event)
  2905. {
  2906. Item *item = NULL;
  2907. BitSet32 hitFlags;
  2908. if ( !_hitTest( event.mousePoint, item, hitFlags ) )
  2909. return;
  2910. if ( hitFlags.test( OnText ) || hitFlags.test( OnIcon ) )
  2911. {
  2912. if ( !isItemSelected( item->getID() ) )
  2913. {
  2914. clearSelection();
  2915. addSelection( item->getID() );
  2916. }
  2917. if (item->isInspectorData() && item->getObject())
  2918. onRightMouseUp_callback( item->mId, event.mousePoint, item->getObject() );
  2919. else
  2920. onRightMouseUp_callback( item->mId, event.mousePoint );
  2921. }
  2922. else
  2923. {
  2924. clearSelection();
  2925. }
  2926. Parent::onRightMouseUp(event);
  2927. }
  2928. //------------------------------------------------------------------------------
  2929. void GuiTreeViewCtrl::onRender(Point2I offset, const RectI &updateRect)
  2930. {
  2931. if ( !mRenamingItem && mRenameCtrl )
  2932. {
  2933. mRenameCtrl->deleteObject();
  2934. mRenameCtrl = NULL;
  2935. }
  2936. // Get all our contents drawn!
  2937. Parent::onRender(offset,updateRect);
  2938. // Deal with drawing the drag & drop line, if any...
  2939. GFX->setClipRect(updateRect);
  2940. // only do it if we have a mDragMidPoint
  2941. if (mDragMidPoint == NomDragMidPoint || !mSupportMouseDragging )
  2942. return;
  2943. ColorI greyLine(128,128,128);
  2944. Point2F squarePt;
  2945. // CodeReview: LineWidth is not supported in Direct3D. This is lame. [5/10/2007 Pat]
  2946. // draw mDragMidPoint lines with a diamond
  2947. if (mDragMidPoint == AbovemDragMidPoint)
  2948. {
  2949. S32 tempY = mItemHeight*mCurrentDragCell+offset.y ;
  2950. squarePt.y = (F32)tempY;
  2951. squarePt.x = 125.f+offset.x;
  2952. GFX->getDrawUtil()->drawLine(0+offset.x, tempY, 250+offset.x, tempY,greyLine);
  2953. GFX->getDrawUtil()->draw2DSquare(squarePt, 6, 90 );
  2954. }
  2955. if (mDragMidPoint == BelowmDragMidPoint)
  2956. {
  2957. S32 tempY2 = mItemHeight*(mCurrentDragCell+1) +offset.y;
  2958. squarePt.y = (F32)tempY2;
  2959. squarePt.x = 125.f+offset.x;
  2960. GFX->getDrawUtil()->drawLine(0+offset.x, tempY2, 250+offset.x, tempY2,greyLine);
  2961. GFX->getDrawUtil()->draw2DSquare(squarePt,6, 90 );
  2962. }
  2963. }
  2964. //-----------------------------------------------------------------------------
  2965. void GuiTreeViewCtrl::onRenderCell(Point2I offset, Point2I cell, bool, bool )
  2966. {
  2967. if( !mVisibleItems.size() )
  2968. return;
  2969. // Do some sanity checking and data retrieval.
  2970. AssertFatal(cell.y < mVisibleItems.size(), "GuiTreeViewCtrl::onRenderCell: invalid cell");
  2971. Item * item = mVisibleItems[cell.y];
  2972. // If there's no object, deal with it.
  2973. if(item->isInspectorData())
  2974. if(!item->getObject())
  2975. return;
  2976. RectI drawRect( offset, mCellSize );
  2977. GFXDrawUtil *drawer = GFX->getDrawUtil();
  2978. drawer->clearBitmapModulation();
  2979. FrameAllocatorMarker txtBuff;
  2980. // Ok, we have the item. There are a few possibilities at this point:
  2981. // - We need to draw inheritance lines and a treeview-chosen icon
  2982. // OR
  2983. // - We have to draw an item-dependent icon
  2984. // - If we're mouseover, we have to highlight it.
  2985. //
  2986. // - We have to draw the text for the item
  2987. // - Taking into account various mouseover states
  2988. // - Taking into account the value (set or not)
  2989. // - If it's an inspector data, we have to do some custom rendering
  2990. // - ADDED: If it is being renamed, we also have custom rendering.
  2991. // Ok, first draw the tab and icon.
  2992. // Do we draw the tree lines?
  2993. if( mFlags.test(ShowTreeLines) )
  2994. {
  2995. drawRect.point.x += ( mTabSize * item->mTabLevel );
  2996. Item* parent = item->mParent;
  2997. for ( S32 i = item->mTabLevel; ( parent && i > 0 ); i-- )
  2998. {
  2999. drawRect.point.x -= mTabSize;
  3000. if ( parent->mNext )
  3001. drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[BmpLine] );
  3002. parent = parent->mParent;
  3003. }
  3004. }
  3005. // Now, the icon...
  3006. drawRect.point.x = offset.x + mTabSize * item->mTabLevel;
  3007. // First, draw the rollover glow, if it's an inner node.
  3008. if ( item->isParent() && item->mState.test( Item::MouseOverBmp ) )
  3009. drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[BmpGlow] );
  3010. // Now, do we draw a treeview-selected item or an item dependent one?
  3011. S32 newOffset = 0; // This is stored so we can render glow, then update render pos.
  3012. S32 bitmap = 0;
  3013. // Ok, draw the treeview lines as appropriate.
  3014. bool drawBitmap = true;
  3015. if ( !item->isParent() )
  3016. {
  3017. if( mFlags.test( ShowTreeLines ) )
  3018. {
  3019. if( ( item->mNext && item->mPrevious )
  3020. || ( item->mNext && item->mParent && ( !_isRootLevelItem( item ) || mShowRoot ) ) )
  3021. bitmap = BmpChild;
  3022. else if( item->mNext && ( !item->mParent || !mShowRoot ) )
  3023. bitmap = BmpFirstChild;
  3024. else if( item->mPrevious || ( item->mParent && !_isRootLevelItem( item ) ) )
  3025. bitmap = BmpLastChild;
  3026. else
  3027. drawBitmap = false;
  3028. }
  3029. else
  3030. drawBitmap = false;
  3031. }
  3032. else
  3033. {
  3034. bitmap = item->isExpanded() ? BmpExp : BmpCon;
  3035. if( mFlags.test( ShowTreeLines ) )
  3036. {
  3037. // Shift indices to show versions with tree lines.
  3038. if ( item->mParent || item->mPrevious )
  3039. bitmap += ( item->mNext ? 3 : 2 );
  3040. else
  3041. bitmap += ( item->mNext ? 1 : 0 );
  3042. }
  3043. }
  3044. if( ( bitmap >= 0 ) && ( bitmap < mProfile->mBitmapArrayRects.size() ) )
  3045. {
  3046. if( drawBitmap )
  3047. drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[bitmap] );
  3048. newOffset = mProfile->mBitmapArrayRects[bitmap].extent.x;
  3049. }
  3050. if(item->isInspectorData())
  3051. {
  3052. // draw lock icon if need be
  3053. S32 icon = Lock1;
  3054. S32 icon2 = Hidden;
  3055. if (item->getObject() && item->getObject()->isLocked())
  3056. {
  3057. if (mIconTable[icon])
  3058. {
  3059. //drawRect.point.x = offset.x + mTabSize * item->mTabLevel + mIconTable[icon].getWidth();
  3060. drawRect.point.x += mIconTable[icon].getWidth();
  3061. drawer->drawBitmap( mIconTable[icon], drawRect.point );
  3062. }
  3063. }
  3064. if (item->getObject() && item->getObject()->isHidden())
  3065. {
  3066. if (mIconTable[icon2])
  3067. {
  3068. //drawRect.point.x = offset.x + mTabSize * item->mTabLevel + mIconTable[icon].getWidth();
  3069. drawRect.point.x += mIconTable[icon2].getWidth();
  3070. drawer->drawBitmap( mIconTable[icon2], drawRect.point );
  3071. }
  3072. }
  3073. SimObject * pObject = item->getObject();
  3074. SimGroup * pGroup = ( pObject == NULL ) ? NULL : dynamic_cast<SimGroup*>( pObject );
  3075. // If this item is a VirtualParent we can use the generic SimGroup123 icons.
  3076. // However if there is already an icon in the EditorIconRegistry for this
  3077. // exact class (not counting parent class icons) we want to use that instead.
  3078. bool hasClassIcon = false;
  3079. #ifdef TORQUE_TOOLS
  3080. hasClassIcon = gEditorIcons.hasIconNoRecurse( pObject );
  3081. #endif
  3082. // draw the icon associated with the item
  3083. if ( !hasClassIcon && item->mState.test(Item::VirtualParent))
  3084. {
  3085. if ( pGroup != NULL)
  3086. {
  3087. //Check if we're a SceneObject, and pick the default icon as appropriate
  3088. if (pObject->getClassName() != String("SimGroup"))
  3089. {
  3090. item->mIcon = Icon31;
  3091. }
  3092. else
  3093. {
  3094. //If we're purely a SimGroup, pick our icon.
  3095. if (item->isExpanded())
  3096. item->mIcon = SimGroup1;
  3097. else
  3098. item->mIcon = SimGroup2;
  3099. }
  3100. }
  3101. else
  3102. item->mIcon = SimGroup2;
  3103. }
  3104. if ( !hasClassIcon && item->mState.test(Item::Marked))
  3105. {
  3106. if (item->isInspectorData())
  3107. {
  3108. if ( pGroup != NULL )
  3109. {
  3110. if (item->isExpanded())
  3111. item->mIcon = SimGroup3;
  3112. else
  3113. item->mIcon = SimGroup4;
  3114. }
  3115. }
  3116. }
  3117. GFXTexHandle iconHandle;
  3118. if ( ( item->mIcon != -1 ) && mIconTable[item->mIcon] )
  3119. iconHandle = mIconTable[item->mIcon];
  3120. #ifdef TORQUE_TOOLS
  3121. else
  3122. iconHandle = gEditorIcons.findIcon( item->getObject() );
  3123. #endif
  3124. if ( iconHandle.isValid() )
  3125. {
  3126. S32 iconHeight = (mItemHeight - iconHandle.getHeight()) / 2;
  3127. S32 oldHeight = drawRect.point.y;
  3128. if(iconHeight > 0)
  3129. drawRect.point.y += iconHeight;
  3130. drawRect.point.x += iconHandle.getWidth();
  3131. drawer->drawBitmap( iconHandle, drawRect.point );
  3132. drawRect.point.y = oldHeight;
  3133. }
  3134. }
  3135. else
  3136. {
  3137. S32 icon = item->isExpanded() ? item->mScriptInfo.mExpandedImage : item->mScriptInfo.mNormalImage;
  3138. if ( icon )
  3139. {
  3140. if (mIconTable[icon])
  3141. {
  3142. S32 iconHeight = (mItemHeight - mIconTable[icon].getHeight()) / 2;
  3143. S32 oldHeight = drawRect.point.y;
  3144. if(iconHeight > 0)
  3145. drawRect.point.y += iconHeight;
  3146. drawRect.point.x += mIconTable[icon].getWidth();
  3147. drawer->drawBitmap( mIconTable[icon], drawRect.point );
  3148. drawRect.point.y = oldHeight;
  3149. }
  3150. }
  3151. }
  3152. // Ok, update offset so we can render some text!
  3153. drawRect.point.x += newOffset;
  3154. // Ok, now we're off to rendering the actual data for the treeview item.
  3155. U32 bufLen = 1024; //item->mDataRenderWidth + 1;
  3156. char *displayText = (char *)txtBuff.alloc(bufLen);
  3157. displayText[bufLen-1] = 0;
  3158. item->getDisplayText(bufLen, displayText);
  3159. // Draw the rollover/selected bitmap, if one was specified.
  3160. drawRect.extent.x = mProfile->mFont->getStrWidth( displayText ) + ( 2 * mTextOffset );
  3161. if ( item->mState.test( Item::Selected ) && mTexSelected )
  3162. drawer->drawBitmapStretch( mTexSelected, drawRect );
  3163. else if ( item->mState.test( Item::MouseOverText ) && mTexRollover )
  3164. drawer->drawBitmapStretch( mTexRollover, drawRect );
  3165. // Offset a bit so as to space text properly.
  3166. drawRect.point.x += mTextOffset;
  3167. // Determine what color the font should be.
  3168. ColorI fontColor;
  3169. fontColor = item->mState.test( Item::Selected ) ? mProfile->mFontColorSEL :
  3170. ( item->mState.test( Item::MouseOverText ) ? mProfile->mFontColorHL : mProfile->mFontColor );
  3171. if (item->mState.test(Item::Selected))
  3172. {
  3173. drawer->drawRectFill(drawRect, mProfile->mFillColorSEL);
  3174. }
  3175. else if (item->mState.test(Item::MouseOverText))
  3176. {
  3177. drawer->drawRectFill(drawRect, mProfile->mFillColorHL);
  3178. }
  3179. if( item->mState.test(Item::MouseOverText) )
  3180. {
  3181. fontColor = mProfile->mFontColorHL;
  3182. }
  3183. drawer->setBitmapModulation( fontColor );
  3184. // Center the text horizontally.
  3185. S32 height = (mItemHeight - mProfile->mFont->getHeight()) / 2;
  3186. if(height > 0)
  3187. drawRect.point.y += height;
  3188. // JDD - offset by two pixels or so to keep the text from rendering RIGHT ONTOP of the outline
  3189. drawRect.point.x += 2;
  3190. drawer->drawText( mProfile->mFont, drawRect.point, displayText, mProfile->mFontColors );
  3191. if ( mRenamingItem == item && mRenameCtrl )
  3192. {
  3193. Point2I ctrPos = globalToLocalCoord( drawRect.point );
  3194. ctrPos.y -= height;
  3195. ctrPos.x -= 2;
  3196. Point2I ctrExtent( getWidth() - ctrPos.x, drawRect.extent.y );
  3197. mRenameCtrl->setPosition( ctrPos );
  3198. mRenameCtrl->setExtent( ctrExtent );
  3199. mRenameCtrl->setVisible( true );
  3200. }
  3201. }
  3202. //------------------------------------------------------------------------------
  3203. bool GuiTreeViewCtrl::renderTooltip( const Point2I &hoverPos, const Point2I& cursorPos, const char* tipText )
  3204. {
  3205. Item* item;
  3206. BitSet32 flags = 0;
  3207. if( _hitTest( cursorPos, item, flags ) && (!item->mTooltip.isEmpty() || mUseInspectorTooltips) )
  3208. {
  3209. bool render = true;
  3210. if( mTooltipOnWidthOnly && !item->hasObjectBasedTooltip() )
  3211. {
  3212. // Only render tooltip if the item's text is cut off with its
  3213. // parent scroll control, unless there is custom object-based
  3214. // tooltip information.
  3215. GuiScrollCtrl *pScrollParent = dynamic_cast<GuiScrollCtrl*>( getParent() );
  3216. if ( pScrollParent )
  3217. {
  3218. Point2I textStart;
  3219. Point2I textExt;
  3220. const Point2I pos = globalToLocalCoord(cursorPos);
  3221. textStart.y = pos.y / mCellSize.y;
  3222. textStart.y *= mCellSize.y;
  3223. // The following is taken from _hitTest()
  3224. textStart.x = mTabSize * item->mTabLevel;
  3225. S32 image = BmpChild;
  3226. if((image >= 0) && (image < mProfile->mBitmapArrayRects.size()))
  3227. textStart.x += mProfile->mBitmapArrayRects[image].extent.x;
  3228. textStart.x += mTextOffset;
  3229. textStart.x += getInspectorItemIconsWidth( item );
  3230. FrameAllocatorMarker txtAlloc;
  3231. U32 bufLen = item->getDisplayTextLength() + 1;
  3232. char *buf = (char*)txtAlloc.alloc(bufLen);
  3233. item->getDisplayText(bufLen, buf);
  3234. textExt.x = mProfile->mFont->getStrWidth(buf);
  3235. textExt.y = mProfile->mFont->getHeight();
  3236. if( pScrollParent->isRectCompletelyVisible(RectI(textStart, textExt)) )
  3237. render = false;
  3238. }
  3239. }
  3240. if( render )
  3241. {
  3242. if( mUseInspectorTooltips )
  3243. {
  3244. char buf[2048];
  3245. item->getTooltipText( sizeof( buf ), buf );
  3246. tipText = buf;
  3247. }
  3248. else
  3249. {
  3250. tipText = item->mTooltip.c_str();
  3251. }
  3252. }
  3253. }
  3254. return defaultTooltipRender( cursorPos, cursorPos, tipText );
  3255. }
  3256. //------------------------------------------------------------------------------
  3257. void GuiTreeViewCtrl::clearSelection()
  3258. {
  3259. if( mDebug ) Con::printf( "clearSelection called" );
  3260. while ( !mSelectedItems.empty() )
  3261. {
  3262. removeSelection( mSelectedItems.last()->mId );
  3263. }
  3264. mSelectedItems.clear();
  3265. mSelected.clear();
  3266. onClearSelection();
  3267. onClearSelection_callback();
  3268. }
  3269. //-----------------------------------------------------------------------------
  3270. void GuiTreeViewCtrl::lockSelection(bool lock)
  3271. {
  3272. for(U32 i = 0; i < mSelectedItems.size(); i++)
  3273. {
  3274. if(mSelectedItems[i]->isInspectorData())
  3275. mSelectedItems[i]->getObject()->setLocked(lock);
  3276. }
  3277. }
  3278. //-----------------------------------------------------------------------------
  3279. void GuiTreeViewCtrl::hideSelection(bool hide)
  3280. {
  3281. for(U32 i = 0; i < mSelectedItems.size(); i++)
  3282. {
  3283. if(mSelectedItems[i]->isInspectorData())
  3284. mSelectedItems[i]->getObject()->setHidden(hide);
  3285. }
  3286. }
  3287. //-----------------------------------------------------------------------------
  3288. void GuiTreeViewCtrl::toggleLockSelection()
  3289. {
  3290. for(U32 i = 0; i < mSelectedItems.size(); i++)
  3291. {
  3292. if( mSelectedItems[i]->isInspectorData() )
  3293. {
  3294. SimObject* object = mSelectedItems[ i ]->getObject();
  3295. object->setLocked( !object->isLocked() );
  3296. }
  3297. }
  3298. }
  3299. //-----------------------------------------------------------------------------
  3300. void GuiTreeViewCtrl::toggleHideSelection()
  3301. {
  3302. for(U32 i = 0; i < mSelectedItems.size(); i++)
  3303. {
  3304. if( mSelectedItems[i]->isInspectorData() )
  3305. {
  3306. SimObject* object = mSelectedItems[ i ]->getObject();
  3307. object->setHidden( !object->isHidden() );
  3308. }
  3309. }
  3310. }
  3311. //------------------------------------------------------------------------------
  3312. // handles icon assignments
  3313. S32 GuiTreeViewCtrl::getIcon(const char * iconString)
  3314. {
  3315. return -1;
  3316. }
  3317. //-----------------------------------------------------------------------------
  3318. GuiTreeViewCtrl::Item* GuiTreeViewCtrl::addInspectorDataItem(Item *parent, SimObject *obj)
  3319. {
  3320. S32 icon = getIcon(obj->getClassName());
  3321. Item *item = createItem(icon);
  3322. item->mState.set(Item::InspectorData);
  3323. // Set the item text label flags.
  3324. if( !mShowObjectIds )
  3325. item->mState.clear( Item::ShowObjectId );
  3326. else
  3327. item->mState.set( Item::ShowObjectId );
  3328. if( !mShowClassNames )
  3329. item->mState.clear( Item::ShowClassName );
  3330. else
  3331. item->mState.set( Item::ShowClassName );
  3332. if( !mShowObjectNames )
  3333. item->mState.clear( Item::ShowObjectName );
  3334. else
  3335. item->mState.set( Item::ShowObjectName );
  3336. if( !mShowInternalNames )
  3337. item->mState.clear( Item::ShowInternalName );
  3338. else
  3339. item->mState.set( Item::ShowInternalName );
  3340. if( mShowClassNameForUnnamedObjects )
  3341. item->mState.set( Item::ShowClassNameForUnnamed );
  3342. // Deal with child objects...
  3343. if(dynamic_cast<SimSet*>(obj))
  3344. item->mState.set(Item::VirtualParent);
  3345. // Actually store the data!
  3346. item->setObject(obj);
  3347. // Now add us to the data structure...
  3348. if(parent)
  3349. {
  3350. // Add as child of parent.
  3351. if(parent->mChild)
  3352. {
  3353. Item * traverse = parent->mChild;
  3354. while(traverse->mNext)
  3355. traverse = traverse->mNext;
  3356. traverse->mNext = item;
  3357. item->mPrevious = traverse;
  3358. }
  3359. else
  3360. parent->mChild = item;
  3361. item->mParent = parent;
  3362. }
  3363. else
  3364. {
  3365. // If no parent, add to root.
  3366. item->mNext = mRoot;
  3367. mRoot = item;
  3368. item->mParent = NULL;
  3369. }
  3370. mFlags.set(RebuildVisible);
  3371. return item;
  3372. }
  3373. //-----------------------------------------------------------------------------
  3374. void GuiTreeViewCtrl::unlinkItem(Item * item)
  3375. {
  3376. if (item->mPrevious)
  3377. item->mPrevious->mNext = item->mNext;
  3378. if (item->mNext)
  3379. item->mNext->mPrevious = item->mPrevious;
  3380. }
  3381. //-----------------------------------------------------------------------------
  3382. bool GuiTreeViewCtrl::childSearch(Item * item, SimObject *obj, bool yourBaby)
  3383. {
  3384. Item * temp = item->mChild;
  3385. while (temp)
  3386. {
  3387. //do you have my baby?
  3388. if (temp->isInspectorData())
  3389. {
  3390. if (temp->getObject() == obj)
  3391. yourBaby = false; //probably a child of an inner script
  3392. }
  3393. yourBaby = childSearch(temp,obj, yourBaby);
  3394. temp = temp->mNext;
  3395. }
  3396. return yourBaby;
  3397. }
  3398. //-----------------------------------------------------------------------------
  3399. void GuiTreeViewCtrl::inspectorSearch(Item * item, Item * parent, SimSet * parentSet, SimSet * newParentSet)
  3400. {
  3401. if (!parentSet||!newParentSet)
  3402. return;
  3403. if (item == parent->mNext)
  3404. return;
  3405. if (item)
  3406. {
  3407. if (item->isInspectorData())
  3408. {
  3409. // remove the object from the parentSet and add it to the newParentSet
  3410. SimObject* simObj = item->getObject();
  3411. if (parentSet->size())
  3412. {
  3413. SimObject *lastObject = parentSet->last();
  3414. parentSet->removeObject(simObj);
  3415. parentSet->reOrder(lastObject);
  3416. }
  3417. else
  3418. parentSet->removeObject(simObj);
  3419. newParentSet->addObject(simObj);
  3420. if (item->mNext)
  3421. {
  3422. inspectorSearch(item->mNext, parent, parentSet, newParentSet);
  3423. return;
  3424. }
  3425. else
  3426. {
  3427. // end of children so backing up
  3428. if (item->mParent == parent)
  3429. return;
  3430. else
  3431. {
  3432. inspectorSearch(item->mParent->mNext, parent, parentSet, newParentSet);
  3433. return;
  3434. }
  3435. }
  3436. }
  3437. if (item->mChild)
  3438. {
  3439. inspectorSearch(item->mChild, parent, parentSet, newParentSet);
  3440. return;
  3441. }
  3442. if (item->mNext)
  3443. {
  3444. inspectorSearch(item->mNext, parent, parentSet, newParentSet);
  3445. return;
  3446. }
  3447. }
  3448. }
  3449. //-----------------------------------------------------------------------------
  3450. bool GuiTreeViewCtrl::objectSearch( const SimObject *object, Item **item )
  3451. {
  3452. for ( U32 i = 0; i < mItems.size(); i++ )
  3453. {
  3454. Item *pItem = mItems[i];
  3455. if ( !pItem )
  3456. continue;
  3457. //A bit hackish, but we make a special exception here for items that are named 'Components', as they're merely
  3458. //virtual parents to act as a container to an Entity's components
  3459. if (pItem->mScriptInfo.mText == StringTable->insert("Components"))
  3460. continue;
  3461. SimObject *pObj = pItem->getObject();
  3462. if ( pObj && pObj == object )
  3463. {
  3464. *item = pItem;
  3465. return true;
  3466. }
  3467. }
  3468. return false;
  3469. }
  3470. //-----------------------------------------------------------------------------
  3471. bool GuiTreeViewCtrl::onVirtualParentBuild(Item *item, bool bForceFullUpdate)
  3472. {
  3473. if(!item->mState.test(Item::InspectorData))
  3474. return true;
  3475. // Blast an item if it doesn't have a corresponding SimObject...
  3476. if(item->mInspectorInfo.mObject == NULL)
  3477. {
  3478. removeItem(item->mId);
  3479. return false;
  3480. }
  3481. // Skip the next stuff unless we're expanded...
  3482. if(!item->isExpanded() && !bForceFullUpdate && !( item == mRoot && !mShowRoot ) )
  3483. return true;
  3484. // Verify that we have all the kids we should in here...
  3485. SimSet *srcObj = dynamic_cast<SimSet*>(&(*item->mInspectorInfo.mObject));
  3486. // If it's not a SimSet... WTF are we doing here?
  3487. if(!srcObj)
  3488. return true;
  3489. // This is slow but probably ok.
  3490. for( SimSet::iterator i = srcObj->begin(); i != srcObj->end(); ++ i )
  3491. {
  3492. SimObject *obj = *i;
  3493. // If we can't find it, add it.
  3494. // unless it has a parent that is a child that is a script
  3495. Item *res = item->findChildByValue(obj);
  3496. bool foundChild = true;
  3497. // search the children. if any of them are the parent of the object then don't add it.
  3498. foundChild = childSearch(item,obj,foundChild);
  3499. if(!res && foundChild)
  3500. {
  3501. if (mDebug) Con::printf( "adding object %i to item %i", obj->getId(), item->mId );
  3502. res = addInspectorDataItem(item, obj);
  3503. }
  3504. if( res )
  3505. res->mState.set( Item::RebuildVisited );
  3506. }
  3507. // Go through our items and purge those that have disappeared from
  3508. // the set.
  3509. //Entities will be a special case here, if we're an entity, skip this step
  3510. if (dynamic_cast<Entity*>(srcObj))
  3511. return true;
  3512. for( Item* ptr = item->mChild; ptr != NULL; )
  3513. {
  3514. Item* next = ptr->mNext;
  3515. if( !ptr->mState.test( Item::RebuildVisited ) )
  3516. {
  3517. if( mDebug ) Con::printf( "removing item %i for object %i that is no longer in the set",
  3518. ptr->mId, ptr->getObject()->getId() );
  3519. removeItem( ptr->mId, false );
  3520. }
  3521. else
  3522. ptr->mState.clear( Item::RebuildVisited );
  3523. ptr = next;
  3524. }
  3525. return true;
  3526. }
  3527. //-----------------------------------------------------------------------------
  3528. bool GuiTreeViewCtrl::onVirtualParentExpand(Item *item)
  3529. {
  3530. // Do nothing...
  3531. return true;
  3532. }
  3533. //-----------------------------------------------------------------------------
  3534. bool GuiTreeViewCtrl::onVirtualParentCollapse(Item *item)
  3535. {
  3536. // Do nothing...
  3537. return true;
  3538. }
  3539. //-----------------------------------------------------------------------------
  3540. void GuiTreeViewCtrl::inspectObject( SimObject* obj, bool okToEdit )
  3541. {
  3542. _destroyTree();
  3543. mFlags.set( IsEditable, okToEdit );
  3544. mFlags.set( IsInspector );
  3545. onDefineIcons_callback();
  3546. addInspectorDataItem( NULL, obj );
  3547. }
  3548. //-----------------------------------------------------------------------------
  3549. S32 GuiTreeViewCtrl::insertObject( S32 parent, SimObject* obj, bool okToEdit )
  3550. {
  3551. mFlags.set( IsEditable, okToEdit );
  3552. mFlags.set( IsInspector );
  3553. //onDefineIcons_callback();
  3554. GuiTreeViewCtrl::Item *item = addInspectorDataItem( getItem(parent), obj );
  3555. return item->getID();
  3556. }
  3557. //-----------------------------------------------------------------------------
  3558. S32 GuiTreeViewCtrl::findItemByName(const char *name)
  3559. {
  3560. for (S32 i = 0; i < mItems.size(); i++)
  3561. {
  3562. if ( !mItems[i] )
  3563. continue;
  3564. if( mItems[i]->mState.test( Item::InspectorData ) )
  3565. continue;
  3566. if (mItems[i] && dStrcmp(mItems[i]->getText(),name) == 0)
  3567. return mItems[i]->mId;
  3568. }
  3569. return 0;
  3570. }
  3571. //-----------------------------------------------------------------------------
  3572. S32 GuiTreeViewCtrl::findItemByValue(const char *name)
  3573. {
  3574. for (S32 i = 0; i < mItems.size(); i++)
  3575. {
  3576. if (!mItems[i])
  3577. continue;
  3578. if( mItems[i]->mState.test( Item::InspectorData ) )
  3579. continue;
  3580. if (mItems[i] && dStrcmp(mItems[i]->getValue(),name) == 0)
  3581. return mItems[i]->mId;
  3582. }
  3583. return 0;
  3584. }
  3585. //-----------------------------------------------------------------------------
  3586. void GuiTreeViewCtrl::sortTree( bool caseSensitive, bool traverseHierarchy, bool parentsFirst )
  3587. {
  3588. itemSortList( mRoot, caseSensitive, traverseHierarchy, parentsFirst );
  3589. }
  3590. //-----------------------------------------------------------------------------
  3591. StringTableEntry GuiTreeViewCtrl::getTextToRoot( S32 itemId, const char * delimiter )
  3592. {
  3593. Item * item = getItem(itemId);
  3594. if(!item)
  3595. {
  3596. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getTextToRoot: invalid start item id!");
  3597. return StringTable->EmptyString();
  3598. }
  3599. if(item->isInspectorData())
  3600. {
  3601. Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getTextToRoot: cannot get text to root of inspector data items");
  3602. return StringTable->EmptyString();
  3603. }
  3604. char bufferOne[1024];
  3605. char bufferTwo[1024];
  3606. char bufferNodeText[128];
  3607. dMemset( bufferOne, 0, sizeof(bufferOne) );
  3608. dMemset( bufferTwo, 0, sizeof(bufferTwo) );
  3609. dStrcpy( bufferOne, item->getText(), 1024 );
  3610. Item *prevNode = item->mParent;
  3611. while ( prevNode )
  3612. {
  3613. dMemset( bufferNodeText, 0, sizeof(bufferNodeText) );
  3614. dStrcpy( bufferNodeText, prevNode->getText(), 128 );
  3615. dSprintf( bufferTwo, 1024, "%s%s%s",bufferNodeText, delimiter, bufferOne );
  3616. dStrcpy( bufferOne, bufferTwo, 1024 );
  3617. dMemset( bufferTwo, 0, sizeof(bufferTwo) );
  3618. prevNode = prevNode->mParent;
  3619. }
  3620. // Return the result, StringTable-ized.
  3621. return StringTable->insert( bufferOne, true );
  3622. }
  3623. //-----------------------------------------------------------------------------
  3624. void GuiTreeViewCtrl::setFilterText( const String& text )
  3625. {
  3626. mFilterText = text;
  3627. // Trigger rebuild.
  3628. mFlags.set( RebuildVisible );
  3629. }
  3630. void GuiTreeViewCtrl::setItemFilterException(U32 item, bool isExempted)
  3631. {
  3632. if (isExempted)
  3633. {
  3634. mItemFilterExceptionList.push_back(item);
  3635. }
  3636. else
  3637. {
  3638. mItemFilterExceptionList.remove(item);
  3639. }
  3640. }
  3641. void GuiTreeViewCtrl::setItemHidden(U32 item, bool isHidden)
  3642. {
  3643. if (isHidden)
  3644. {
  3645. mHiddenItemsList.push_back(item);
  3646. }
  3647. else
  3648. {
  3649. mHiddenItemsList.remove(item);
  3650. }
  3651. }
  3652. void GuiTreeViewCtrl::reparentItems(Vector<Item*> selectedItems, Item* newParent)
  3653. {
  3654. for (S32 i = 0; i < selectedItems.size(); i++)
  3655. {
  3656. Item* item = selectedItems[i];
  3657. if (mDebug)
  3658. Con::printf("----------------------------");
  3659. // clear old highlighting of the item
  3660. item->mState.clear(Item::MouseOverBmp | Item::MouseOverText);
  3661. // move the selected item to the newParent
  3662. Item * oldParent = item->mParent;
  3663. // Snag the current parent set if any for future reference
  3664. SimSet * parentSet = NULL;
  3665. if (oldParent != nullptr && oldParent->isInspectorData())
  3666. {
  3667. parentSet = dynamic_cast<SimSet*>(oldParent->getObject());
  3668. }
  3669. else
  3670. {
  3671. // parent is probably script data so we search up the tree for a
  3672. // set to put our object in
  3673. Item* temp = oldParent;
  3674. while (temp)
  3675. {
  3676. if (temp->isInspectorData())
  3677. break;
  3678. temp = temp->mParent;
  3679. }
  3680. // found an ancestor who is an inspectorData
  3681. if (temp)
  3682. {
  3683. if (temp->isInspectorData())
  3684. parentSet = dynamic_cast<SimSet*>(temp->getObject());
  3685. }
  3686. }
  3687. // unlink from the current position in the list
  3688. unlinkItem(item);
  3689. // update the parent's children
  3690. // check if we an only child
  3691. if (item->mParent && item->mParent->mChild == item)
  3692. {
  3693. if (item->mNext)
  3694. item->mParent->mChild = item->mNext;
  3695. else
  3696. item->mParent->mChild = NULL;
  3697. }
  3698. if (mDragMidPoint != NomDragMidPoint)
  3699. {
  3700. //if it is below an expanded tree, place as last item in the tree
  3701. //if it is below a parent who isn't expanded put below it
  3702. // position the item above or below another item
  3703. if (mDragMidPoint == AbovemDragMidPoint)
  3704. {
  3705. // easier to treat everything as "Below the mDragMidPoint" so make some adjustments
  3706. if (mDebug)
  3707. Con::printf("adding item above mDragMidPoint");
  3708. // above the mid point of an item, so grab either the parent
  3709. // or the previous sibling
  3710. // does the item have a previous sibling?
  3711. if (newParent->mPrevious)
  3712. {
  3713. newParent = newParent->mPrevious;
  3714. if (mDebug)
  3715. Con::printf("treating as if below an item that isn't expanded");
  3716. // otherwise add below that item as a sibling
  3717. item->mParent = newParent->mParent;
  3718. item->mPrevious = newParent;
  3719. item->mNext = newParent->mNext;
  3720. if (newParent->mNext)
  3721. newParent->mNext->mPrevious = item;
  3722. newParent->mNext = item;
  3723. }
  3724. else
  3725. {
  3726. if (mDebug)
  3727. Con::printf("treating as if adding below the parent of the item");
  3728. // instead we add as the first item below the newParent's parent
  3729. item->mParent = newParent->mParent;
  3730. item->mNext = newParent;
  3731. item->mPrevious = NULL;
  3732. newParent->mPrevious = item;
  3733. item->mParent->mChild = item;
  3734. }
  3735. }
  3736. else if (mDragMidPoint == BelowmDragMidPoint)
  3737. {
  3738. if ((newParent->isParent()) && (newParent->isExpanded()))
  3739. {
  3740. if (mDebug)
  3741. Con::printf("adding item to an expanded parent below the mDragMidPoint");
  3742. item->mParent = newParent;
  3743. // then add the new item as a child
  3744. item->mNext = newParent->mChild;
  3745. if (newParent->mChild)
  3746. newParent->mChild->mPrevious = item;
  3747. item->mParent->mChild = item;
  3748. item->mPrevious = NULL;
  3749. }
  3750. else if ((!newParent->mNext) && (newParent->mParent) && (newParent->mParent->mParent))
  3751. {
  3752. // add below it's parent.
  3753. if (mDebug)
  3754. Con::printf("adding below a tree");
  3755. item->mParent = newParent->mParent->mParent;
  3756. item->mNext = newParent->mParent->mNext;
  3757. item->mPrevious = newParent->mParent;
  3758. if (newParent->mParent->mNext)
  3759. newParent->mParent->mNext->mPrevious = item;
  3760. newParent->mParent->mNext = item;
  3761. }
  3762. else
  3763. {
  3764. // adding below item not as a child
  3765. if (mDebug)
  3766. Con::printf("adding item below the mDragMidPoint of an item");
  3767. item->mParent = newParent->mParent;
  3768. // otherwise the item is a sibling
  3769. if (newParent->mNext)
  3770. newParent->mNext->mPrevious = item;
  3771. item->mNext = newParent->mNext;
  3772. item->mPrevious = newParent;
  3773. newParent->mNext = item;
  3774. }
  3775. }
  3776. }
  3777. // if we're not allowed to add to items, then try to add to the parent of the hit item.
  3778. // if we are, just add to the item we hit.
  3779. else
  3780. {
  3781. if (mDebug)
  3782. {
  3783. if (item->isInspectorData() && item->getObject())
  3784. Con::printf("Item: %i", item->getObject()->getId());
  3785. if (newParent->isInspectorData() && newParent->getObject())
  3786. Con::printf("Parent: %i", newParent->getObject()->getId());
  3787. Con::printf("dragged onto an item");
  3788. }
  3789. // If the hit item is not a valid drag target,
  3790. // then try to add to the parent.
  3791. if (!isValidDragTarget(newParent))
  3792. {
  3793. // add to the item's parent.
  3794. if (!newParent->mParent || !newParent->mParent->isParent())
  3795. {
  3796. if (mDebug)
  3797. Con::printf("could not find the parent of that item. dragging to an item is not allowed, kicking out.");
  3798. mDragMidPoint = NomDragMidPoint;
  3799. continue;
  3800. }
  3801. newParent = newParent->mParent;
  3802. }
  3803. // new parent is the item in the current cell
  3804. item->mParent = newParent;
  3805. // adjust children if any
  3806. if (newParent->mChild)
  3807. {
  3808. if (mDebug) Con::printf("not the first child");
  3809. // put it at the top of the list (easier to find if there are many children)
  3810. if (newParent->mChild)
  3811. newParent->mChild->mPrevious = item;
  3812. item->mNext = newParent->mChild;
  3813. newParent->mChild = item;
  3814. item->mPrevious = NULL;
  3815. }
  3816. else
  3817. {
  3818. if (mDebug) Con::printf("first child");
  3819. // only child
  3820. newParent->mChild = item;
  3821. item->mNext = NULL;
  3822. item->mPrevious = NULL;
  3823. }
  3824. }
  3825. // expand the item we added to, if it isn't expanded already
  3826. if (!item->mParent->mState.test(Item::Expanded))
  3827. setItemExpanded(item->mParent->mId, true);
  3828. //----------------------------------------------------------------
  3829. // handle objects
  3830. // Get our active SimObject if any
  3831. SimObject* simObj = NULL;
  3832. if (item->isInspectorData())
  3833. {
  3834. simObj = item->getObject();
  3835. }
  3836. // Remove from the old parentset
  3837. if ((simObj && parentSet) && (oldParent != item->mParent))
  3838. {
  3839. if (mDebug)
  3840. Con::printf("removing item from old parentset");
  3841. // hack to get around the way removeObject takes the last item of the set
  3842. // and moves it into the place of the object we removed
  3843. if (parentSet->size() > 0)
  3844. {
  3845. SimObject* lastObject = parentSet->last();
  3846. parentSet->removeObject(simObj);
  3847. parentSet->reOrder(lastObject);
  3848. }
  3849. else
  3850. {
  3851. parentSet->removeObject(simObj);
  3852. }
  3853. }
  3854. // Snag the newparent set if any...
  3855. SimSet* newParentSet = NULL;
  3856. if (item->mParent->isInspectorData())
  3857. {
  3858. if (mDebug)
  3859. Con::printf("getting a new parent set");
  3860. SimObject* tmpObj = item->mParent->getObject();
  3861. newParentSet = dynamic_cast<SimSet*>(tmpObj);
  3862. }
  3863. else
  3864. {
  3865. // parent is probably script data so we search up the tree for a
  3866. // set to put our object in
  3867. if (mDebug)
  3868. Con::printf("oh nos my parent is script!");
  3869. Item* temp = item->mParent;
  3870. while (temp)
  3871. {
  3872. if (temp->isInspectorData())
  3873. break;
  3874. temp = temp->mParent;
  3875. }
  3876. // found a ancestor who is an inspectorData
  3877. if (temp)
  3878. {
  3879. if (temp->isInspectorData())
  3880. newParentSet = dynamic_cast<SimSet*>(temp->getObject());
  3881. }
  3882. else
  3883. {
  3884. newParentSet = NULL;
  3885. }
  3886. }
  3887. if (simObj && newParentSet)
  3888. {
  3889. if (mDebug)
  3890. Con::printf("simobj and new ParentSet");
  3891. if (oldParent != item->mParent)
  3892. newParentSet->addObject(simObj);
  3893. //order the objects in the simset according to their
  3894. //order in the tree view control
  3895. if (!item->mNext)
  3896. {
  3897. if (item->mPrevious)
  3898. {
  3899. //bring to the end of the set
  3900. SimObject* prevObject = item->mPrevious->getObject();
  3901. if (prevObject && item->getObject())
  3902. {
  3903. newParentSet->reOrder(item->getObject(), prevObject);
  3904. }
  3905. }
  3906. }
  3907. else
  3908. {
  3909. //reorder within the set
  3910. SimObject* nextObject = item->mNext->getObject();
  3911. if (nextObject && item->getObject())
  3912. {
  3913. newParentSet->reOrder(item->getObject(), nextObject);
  3914. }
  3915. }
  3916. }
  3917. else if (!simObj && newParentSet)
  3918. {
  3919. // our current item is script data. but it may have children who
  3920. // is inspector data who need an updated set
  3921. if (mDebug)
  3922. Con::printf("no simobj but new parentSet");
  3923. if (item->mChild)
  3924. inspectorSearch(item->mChild, item, parentSet, newParentSet);
  3925. }
  3926. else if (simObj && !newParentSet)
  3927. {
  3928. if (mDebug)
  3929. Con::printf("simobject and no new parent set");
  3930. }
  3931. else
  3932. if (mDebug)
  3933. Con::printf("no simobject and no new parent set");
  3934. // Notify script.
  3935. if (item->isInspectorData())
  3936. {
  3937. if (item->getObject() && (oldParent && oldParent->getObject()) && item->mParent->getObject())
  3938. onReparent_callback(
  3939. item->getObject()->getId(),
  3940. oldParent->getObject()->getId(),
  3941. item->mParent->getObject()->getId()
  3942. );
  3943. }
  3944. else
  3945. {
  3946. onReparent_callback(
  3947. item->mId,
  3948. oldParent->mId,
  3949. item->mParent->mId
  3950. );
  3951. }
  3952. }
  3953. }
  3954. S32 GuiTreeViewCtrl::getTabLevel(S32 itemId)
  3955. {
  3956. Item* item = getItem(itemId);
  3957. if (item != nullptr)
  3958. {
  3959. return item->mTabLevel;
  3960. }
  3961. return 0;
  3962. }
  3963. //=============================================================================
  3964. // Console Methods.
  3965. //=============================================================================
  3966. // MARK: ---- Console Methods ----
  3967. //-----------------------------------------------------------------------------
  3968. DefineEngineMethod( GuiTreeViewCtrl, findItemByName, S32, ( const char* text ),,
  3969. "Get the ID of the item whose text matches the given @a text.\n\n"
  3970. "@param text Item text to match.\n"
  3971. "@return ID of the item or -1 if no item matches the given text." )
  3972. {
  3973. return object->findItemByName( text );
  3974. }
  3975. //-----------------------------------------------------------------------------
  3976. DefineEngineMethod( GuiTreeViewCtrl, findItemByValue, S32, ( const char* value ),,
  3977. "Get the ID of the item whose value matches @a value.\n\n"
  3978. "@param value Value text to match.\n"
  3979. "@return ID of the item or -1 if no item has the given value." )
  3980. {
  3981. return object->findItemByValue( value );
  3982. }
  3983. //-----------------------------------------------------------------------------
  3984. DefineEngineMethod( GuiTreeViewCtrl, findChildItemByName, S32, ( S32 parentId, const char* childName ),,
  3985. "Get the child item of the given parent item whose text matches @a childName.\n\n"
  3986. "@param parentId Item ID of the parent in which to look for the child.\n"
  3987. "@param childName Text of the child item to find.\n"
  3988. "@return ID of the child item or -1 if no child in @a parentId has the given text @a childName.\n\n"
  3989. "@note This method does not recurse, i.e. it only looks for direct children." )
  3990. {
  3991. if( parentId == 0 )
  3992. {
  3993. if( !object->getRootItem() )
  3994. return 0;
  3995. GuiTreeViewCtrl::Item* root = object->getRootItem();
  3996. while( root )
  3997. {
  3998. if( dStricmp( root->getText(), childName ) == 0 )
  3999. return root->getID();
  4000. root = root->mNext;
  4001. }
  4002. return 0;
  4003. }
  4004. else
  4005. {
  4006. GuiTreeViewCtrl::Item* item = object->getItem( parentId );
  4007. if( !item )
  4008. {
  4009. Con::errorf( "GuiTreeViewCtrl.findChildItemByName - invalid parent ID '%i'", parentId );
  4010. return 0;
  4011. }
  4012. GuiTreeViewCtrl::Item* child = item->findChildByName( childName );
  4013. if( !child )
  4014. return 0;
  4015. return child->mId;
  4016. }
  4017. }
  4018. //-----------------------------------------------------------------------------
  4019. DefineEngineMethod( GuiTreeViewCtrl, insertItem, S32, ( S32 parentId, const char* text, const char* value, const char* icon, S32 normalImage, S32 expandedImage ), ( "", "", 0, 0 ),
  4020. "Add a new item to the tree.\n\n"
  4021. "@param parentId Item ID of parent to which to add the item as a child. 0 is root item.\n"
  4022. "@param text Text to display on the item in the tree.\n"
  4023. "@param value Behind-the-scenes value of the item.\n"
  4024. "@param icon\n"
  4025. "@param normalImage\n"
  4026. "@param expandedImage\n"
  4027. "@return The ID of the newly added item." )
  4028. {
  4029. return object->insertItem( parentId, text, value, icon, normalImage, expandedImage );
  4030. }
  4031. DefineEngineMethod( GuiTreeViewCtrl, insertObject, S32, ( S32 parentId, SimObject* obj, bool OKToEdit ), (false), "Inserts object as a child to the given parent." )
  4032. {
  4033. return object->insertObject(parentId, obj, OKToEdit);
  4034. }
  4035. //-----------------------------------------------------------------------------
  4036. DefineEngineMethod( GuiTreeViewCtrl, lockSelection, void, ( bool lock ), ( true ),
  4037. "Set whether the current selection can be changed by the user or not.\n\n"
  4038. "@param lock If true, the current selection is frozen and cannot be changed. If false, "
  4039. "the selection may be modified." )
  4040. {
  4041. object->lockSelection( lock );
  4042. }
  4043. //-----------------------------------------------------------------------------
  4044. DefineEngineMethod( GuiTreeViewCtrl, hideSelection, void, ( bool state ), ( true ),
  4045. "Call SimObject::setHidden( @a state ) on all objects in the current selection.\n\n"
  4046. "@param state Visibility state to set objects in selection to." )
  4047. {
  4048. object->hideSelection( state );
  4049. }
  4050. //-----------------------------------------------------------------------------
  4051. DefineEngineMethod( GuiTreeViewCtrl, toggleLockSelection, void, (),,
  4052. "Toggle the locked state of all objects in the current selection." )
  4053. {
  4054. object->toggleLockSelection();
  4055. }
  4056. //-----------------------------------------------------------------------------
  4057. DefineEngineMethod( GuiTreeViewCtrl, toggleHideSelection, void, (),,
  4058. "Toggle the hidden state of all objects in the current selection." )
  4059. {
  4060. object->toggleHideSelection();
  4061. }
  4062. //-----------------------------------------------------------------------------
  4063. DefineEngineMethod( GuiTreeViewCtrl, clearSelection, void, (),,
  4064. "Unselect all currently selected items." )
  4065. {
  4066. object->clearSelection();
  4067. }
  4068. //-----------------------------------------------------------------------------
  4069. DefineEngineMethod( GuiTreeViewCtrl, deleteSelection, void, (),,
  4070. "Delete all items/objects in the current selection." )
  4071. {
  4072. object->deleteSelection();
  4073. }
  4074. //-----------------------------------------------------------------------------
  4075. DefineEngineMethod( GuiTreeViewCtrl, addSelection, void, ( S32 id, bool isLastSelection ), ( true ),
  4076. "Add an item/object to the current selection.\n\n"
  4077. "@param id ID of item/object to add to the selection.\n"
  4078. "@param isLastSelection Whether there are more pending items/objects to be added to the selection. If false, "
  4079. "the control will defer refreshing the tree and wait until addSelection() is called with this parameter set "
  4080. "to true." )
  4081. {
  4082. object->addSelection( id, isLastSelection, isLastSelection );
  4083. }
  4084. DefineEngineMethod( GuiTreeViewCtrl, addChildSelectionByValue, void, ( S32 parentId, const char* value), ,
  4085. "Add a child selection by it's value.\n\n"
  4086. "@param parentId Parent TreeItemId.\n"
  4087. "@param value Value to search for.\n")
  4088. {
  4089. GuiTreeViewCtrl::Item* parentItem = object->getItem(parentId);
  4090. GuiTreeViewCtrl::Item* child = parentItem->findChildByValue(value);
  4091. object->addSelection(child->getID());
  4092. }
  4093. DefineEngineMethod( GuiTreeViewCtrl, removeSelection, void, ( S32 itemId), ,
  4094. "Deselect an item or remove it from the selection.\n\n"
  4095. "@param itemId Item Id to deselect.\n")
  4096. {
  4097. object->removeSelection(itemId);
  4098. }
  4099. DefineEngineMethod( GuiTreeViewCtrl, removeChildSelectionByValue, void, ( S32 parentId, const char* value), ,
  4100. "Deselect a child item or remove it from the selection based on its parent and its value.\n\n"
  4101. "@param parentId Parent TreeItemId.\n"
  4102. "@param value Value to search for.\n"
  4103. "@param performCallback True to notify script of the change, false to not.\n")
  4104. {
  4105. GuiTreeViewCtrl::Item* parentItem = object->getItem(parentId);
  4106. if(parentItem)
  4107. {
  4108. GuiTreeViewCtrl::Item* child = parentItem->findChildByValue(value);
  4109. if(child)
  4110. {
  4111. object->removeSelection(child->getID());
  4112. }
  4113. }
  4114. }
  4115. DefineEngineMethod( GuiTreeViewCtrl, selectItem, bool, ( S32 itemID, bool select), (true) ,
  4116. "Select or deselect and item.\n\n"
  4117. "@param itemID TreeItemId of item to select or deselect.\n"
  4118. "@param select True to select the item, false to deselect it.\n"
  4119. "@return True if it was successful, false if not.")
  4120. {
  4121. return object->setItemSelected(itemID, select);
  4122. }
  4123. DefineEngineMethod( GuiTreeViewCtrl, expandItem, bool, ( S32 itemID, bool expand), (true) ,
  4124. "Expand/contract item, item's sub-tree.\n\n"
  4125. "@param itemID TreeItemId of item to expand or contract.\n"
  4126. "@param expand True to expand the item, false to contract it.\n"
  4127. "@return True if it was successful, false if not.")
  4128. {
  4129. return(object->setItemExpanded(itemID, expand));
  4130. }
  4131. DefineEngineMethod( GuiTreeViewCtrl, markItem, bool, ( S32 itemID, bool mark), (true) ,
  4132. "Mark/unmark item.\n\n"
  4133. "@param itemID TreeItemId of item to Mark or unmark.\n"
  4134. "@param mark True to Mark the item, false to unmark it.\n"
  4135. "@return True if it was successful, false if not.")
  4136. {
  4137. return object->markItem(itemID, mark);
  4138. }
  4139. DefineEngineMethod( GuiTreeViewCtrl, scrollVisible, bool, ( S32 itemID), ,
  4140. "Make the given item visible.\n\n"
  4141. "@param itemID TreeItemId of item to scroll to/make visible.\n"
  4142. "@return True if it was successful, false if not.")
  4143. {
  4144. return object->scrollVisible(itemID);
  4145. }
  4146. DefineEngineMethod( GuiTreeViewCtrl, buildIconTable, bool, ( const char* icons), ,
  4147. "Builds an icon table.\n\n"
  4148. "@param icons Name of icons to build, Icons should be designated by the bitmap/png file names (minus the file extensions)"
  4149. "and separated by colons (:). This list should be synchronized with the Icons enum\n"
  4150. "@return True if it was successful, false if not.")
  4151. {
  4152. return object->buildIconTable(icons);
  4153. }
  4154. DefineEngineMethod( GuiTreeViewCtrl, open, void, ( const char * objName, bool okToEdit), (true),
  4155. "Set the root of the tree view to the specified object, or to the root set.\n\n"
  4156. "@param objName Name or id of SimSet or object to set the tree root equal to.\n")
  4157. {
  4158. SimSet *treeRoot = NULL;
  4159. SimObject* target = Sim::findObject(objName);
  4160. if (target)
  4161. treeRoot = dynamic_cast<SimSet*>(target);
  4162. if (! treeRoot)
  4163. Sim::findObject(RootGroupId, treeRoot);
  4164. object->inspectObject(treeRoot,okToEdit);
  4165. }
  4166. DefineEngineMethod( GuiTreeViewCtrl, setItemTooltip, bool, ( S32 itemId, const char* tooltip), ,
  4167. "Set the tooltip to show for the given item.\n\n"
  4168. "@param itemId TreeItemID of item to set the tooltip for.\n"
  4169. "@param tooltip String tooltip to set for the item."
  4170. "@return True if successfully found the item, false if not")
  4171. {
  4172. GuiTreeViewCtrl::Item* item = object->getItem( itemId );
  4173. if( !item )
  4174. {
  4175. Con::errorf( "GuiTreeViewCtrl::setTooltip() - invalid item id '%i'", itemId );
  4176. return false;
  4177. }
  4178. item->mTooltip = tooltip;
  4179. return true;
  4180. }
  4181. DefineEngineMethod( GuiTreeViewCtrl, setItemImages, void, ( S32 itemId, S8 normalImage, S8 expandedImage ), ,
  4182. "Sets the normal and expanded images to show for the given item.\n\n"
  4183. "@param itemId TreeItemID of item to set images for.\n"
  4184. "@param normalImage Normal image to set for the given item."
  4185. "@param expandedImage Expanded image to set for the given item.")
  4186. {
  4187. GuiTreeViewCtrl::Item* item = object->getItem( itemId );
  4188. if( !item )
  4189. {
  4190. Con::errorf( "GuiTreeViewCtrl::setItemImages() - invalid item id '%i'", itemId );
  4191. return;
  4192. }
  4193. item->setNormalImage(normalImage);
  4194. item->setExpandedImage(expandedImage);
  4195. }
  4196. DefineEngineMethod( GuiTreeViewCtrl, isParentItem, bool, ( S32 itemId ), ,
  4197. "Returns true if the given item contains child items.\n\n"
  4198. "@param itemId TreeItemID to check for children.\n"
  4199. "@return True if the given item contains child items, false if not.")
  4200. {
  4201. if( !itemId && object->getItemCount() )
  4202. return true;
  4203. GuiTreeViewCtrl::Item* item = object->getItem( itemId );
  4204. if( !item )
  4205. {
  4206. Con::errorf( "GuiTreeViewCtrl::isParentItem - invalid item id '%i'", itemId );
  4207. return false;
  4208. }
  4209. return item->isParent();
  4210. }
  4211. DefineEngineMethod( GuiTreeViewCtrl, getItemText, const char *, ( S32 itemId ), ,
  4212. "Gets the text for a given item.\n\n"
  4213. "@param itemId TreeItemID to get text of.\n"
  4214. "@return Text for a given item.")
  4215. {
  4216. return(object->getItemText(itemId));
  4217. }
  4218. DefineEngineMethod( GuiTreeViewCtrl, getItemValue, const char *, ( S32 itemId ), ,
  4219. "Gets the value for a given item.\n\n"
  4220. "@param itemId TreeItemID to get value of.\n"
  4221. "@return Value for a given item.")
  4222. {
  4223. return object->getItemValue(itemId);
  4224. }
  4225. DefineEngineMethod( GuiTreeViewCtrl, editItem, bool, ( S32 itemId, const char* newText, const char* newValue ), ,
  4226. "Edits the text and value for a given tree item.\n\n"
  4227. "@param itemId TreeItemID to edit.\n"
  4228. "@return True if successful, false if not.")
  4229. {
  4230. return(object->editItem(itemId, newText, newValue));
  4231. }
  4232. DefineEngineMethod( GuiTreeViewCtrl, removeItem, bool, (S32 itemId, bool deleteObjects), (0, true),
  4233. "Remove an item from the tree with the given id.\n\n"
  4234. "@param itemId TreeItemID of item to remove.\n"
  4235. "@param deleteObjects Whether the object on the item is deleted when the item is.\n"
  4236. "@return True if successful, false if not.")
  4237. {
  4238. return(object->removeItem(itemId, deleteObjects));
  4239. }
  4240. DefineEngineMethod( GuiTreeViewCtrl, removeAllChildren, void, (S32 itemId), ,
  4241. "Remove all children of an item from the tree with the given id.\n\n"
  4242. "@param itemId TreeItemID of item that has children we should remove.\n")
  4243. {
  4244. object->removeAllChildren(itemId);
  4245. }
  4246. DefineEngineMethod( GuiTreeViewCtrl, clear, void, (), ,
  4247. "Empty the tree.\n")
  4248. {
  4249. object->removeItem(0);
  4250. }
  4251. DefineEngineMethod( GuiTreeViewCtrl, getFirstRootItem, S32, (), ,
  4252. "Get id for root item.\n"
  4253. "@return Id for root item.")
  4254. {
  4255. return(object->getFirstRootItem());
  4256. }
  4257. DefineEngineMethod( GuiTreeViewCtrl, getChild, S32, (S32 itemId), ,
  4258. "Get the child of the parent with the given id.\n\n"
  4259. "@param itemId TreeItemID of item that a child we should get.\n"
  4260. "@return Id of child of given item.")
  4261. {
  4262. return(object->getChildItem(itemId));
  4263. }
  4264. DefineEngineMethod( GuiTreeViewCtrl, buildVisibleTree, void, (bool forceFullUpdate), (false),
  4265. "Build the visible tree.\n\n"
  4266. "@param forceFullUpdate True to force a full update of the tree, false to only update the new stuff.\n")
  4267. {
  4268. object->buildVisibleTree( forceFullUpdate );
  4269. }
  4270. //FIXME: [rene 11/09/09 - This clashes with GuiControl.getParent(); bad thing; should be getParentItem]
  4271. DefineEngineMethod( GuiTreeViewCtrl, getParentItem, S32, (S32 itemId), ,
  4272. "Get the parent of a given id in the tree.\n\n"
  4273. "@param itemId TreeItemID of item that has a parent we should get.\n"
  4274. "@return Id of parent of given item.")
  4275. {
  4276. return(object->getParentItem(itemId));
  4277. }
  4278. DefineEngineMethod( GuiTreeViewCtrl, getNextSibling, S32, (S32 itemId), ,
  4279. "Get the next sibling of the given item id in the tree.\n\n"
  4280. "@param itemId TreeItemID of item that we want the next sibling of.\n"
  4281. "@return Id of next sibling of the given item.")
  4282. {
  4283. return(object->getNextSiblingItem(itemId));
  4284. }
  4285. DefineEngineMethod( GuiTreeViewCtrl, getPrevSibling, S32, (S32 itemId), ,
  4286. "Get the previous sibling of the given item id in the tree.\n\n"
  4287. "@param itemId TreeItemID of item that we want the previous sibling of.\n"
  4288. "@return Id of previous sibling of the given item.")
  4289. {
  4290. return(object->getPrevSiblingItem(itemId));
  4291. }
  4292. DefineEngineMethod( GuiTreeViewCtrl, getItemCount, S32, (), ,
  4293. "Get the total number of items in the tree or item count.\n\n"
  4294. "@return total number of items in the tree.")
  4295. {
  4296. return(object->getItemCount());
  4297. }
  4298. DefineEngineMethod( GuiTreeViewCtrl, getSelectedItem, S32, (S32 index), (0),
  4299. "Return the selected item at the given index.\n\n"
  4300. "@param index Given index to look for selected item."
  4301. "@return selected item at the given index.")
  4302. {
  4303. return ( object->getSelectedItem( index ) );
  4304. }
  4305. DefineEngineMethod( GuiTreeViewCtrl, getSelectedObject, S32, (S32 index), (0),
  4306. "Return the currently selected SimObject at the given index in inspector mode or -1.\n\n"
  4307. "@param index Given index to look for selected object."
  4308. "@return currently selected SimObject at the given index in inspector mode or -1.")
  4309. {
  4310. GuiTreeViewCtrl::Item *item = object->getItem( object->getSelectedItem( index ) );
  4311. if( item != NULL && item->isInspectorData() )
  4312. {
  4313. SimObject *obj = item->getObject();
  4314. if( obj != NULL )
  4315. return obj->getId();
  4316. }
  4317. return -1;
  4318. }
  4319. const char* GuiTreeViewCtrl::getSelectedObjectList()
  4320. {
  4321. static const U32 bufSize = 1024;
  4322. char* buff = Con::getReturnBuffer(bufSize);
  4323. dSprintf(buff,bufSize,"");
  4324. const Vector< GuiTreeViewCtrl::Item* > selectedItems = this->getSelectedItems();
  4325. for(S32 i = 0; i < selectedItems.size(); i++)
  4326. {
  4327. GuiTreeViewCtrl::Item *item = selectedItems[i];
  4328. if ( item->isInspectorData() && item->getObject() )
  4329. {
  4330. S32 id = item->getObject()->getId();
  4331. //get the current length of the buffer
  4332. U32 len = dStrlen(buff);
  4333. //the start of the buffer where we want to write
  4334. char* buffPart = buff+len;
  4335. //the size of the remaining buffer (-1 cause dStrlen doesn't count the \0)
  4336. S32 size = bufSize-len-1;
  4337. //write it:
  4338. if(size < 1)
  4339. {
  4340. Con::errorf("GuiTreeViewCtrl::getSelectedItemList - Not enough room to return our object list");
  4341. return buff;
  4342. }
  4343. dSprintf(buffPart,size,"%d ", id);
  4344. }
  4345. }
  4346. return buff;
  4347. }
  4348. DefineEngineMethod( GuiTreeViewCtrl, getSelectedObjectList, const char*, (), ,
  4349. "Returns a space separated list of all selected object ids.\n\n"
  4350. "@return space separated list of all selected object ids.")
  4351. {
  4352. return object->getSelectedObjectList();
  4353. }
  4354. DefineEngineMethod( GuiTreeViewCtrl, moveItemUp, void, (S32 itemId), ,
  4355. "Move the specified item up in the tree.\n\n"
  4356. "@param itemId TreeItemId of item to move up in the tree.")
  4357. {
  4358. object->moveItemUp( itemId );
  4359. }
  4360. DefineEngineMethod( GuiTreeViewCtrl, getSelectedItemsCount, S32, (), ,
  4361. "Get the selected number of items.\n\n"
  4362. "@return number of selected items.")
  4363. {
  4364. return ( object->getSelectedItemsCount() );
  4365. }
  4366. DefineEngineMethod( GuiTreeViewCtrl, moveItemDown, void, (S32 itemId), ,
  4367. "Move the specified item down in the tree.\n\n"
  4368. "@param itemId TreeItemId of item to move down in the tree.")
  4369. {
  4370. object->moveItemDown( itemId );
  4371. }
  4372. //-----------------------------------------------------------------------------
  4373. DefineEngineMethod( GuiTreeViewCtrl, getTextToRoot, const char*, (S32 itemId, const char* delimiter), (""),
  4374. "Gets the text from the current node to the root, concatenating at each branch upward, with a specified delimiter optionally.\n\n"
  4375. "@param itemId TreeItemId of node to start at."
  4376. "@param delimiter (Optional) delimiter to use between each branch concatenation."
  4377. "@return text from the current node to the root.")
  4378. {
  4379. if (!dStrcmp(delimiter, "" ))
  4380. {
  4381. Con::warnf("GuiTreeViewCtrl::getTextToRoot - Invalid number of arguments!");
  4382. return ("");
  4383. }
  4384. return object->getTextToRoot( itemId, delimiter );
  4385. }
  4386. DefineEngineMethod( GuiTreeViewCtrl, getSelectedItemList, const char*, (), ,
  4387. "Returns a space separated list if ids of all selected items.\n\n"
  4388. "@return space separated list of selected item ids.")
  4389. {
  4390. const U32 bufSize = 1024;
  4391. char* buff = Con::getReturnBuffer(bufSize);
  4392. dSprintf(buff, bufSize, "");
  4393. const Vector< S32 >& selected = object->getSelected();
  4394. for(int i = 0; i < selected.size(); i++)
  4395. {
  4396. S32 id = selected[i];
  4397. //get the current length of the buffer
  4398. U32 len = dStrlen(buff);
  4399. //the start of the buffer where we want to write
  4400. char* buffPart = buff+len;
  4401. //the size of the remaining buffer (-1 cause dStrlen doesn't count the \0)
  4402. S32 size = bufSize-len-1;
  4403. //write it:
  4404. if(size < 1)
  4405. {
  4406. Con::errorf("GuiTreeViewCtrl::getSelectedItemList - Not enough room to return our object list");
  4407. return buff;
  4408. }
  4409. dSprintf(buffPart,size,"%d ", id);
  4410. }
  4411. //mSelected
  4412. return buff;
  4413. }
  4414. S32 GuiTreeViewCtrl::findItemByObjectId(S32 iObjId)
  4415. {
  4416. for (S32 i = 0; i < mItems.size(); i++)
  4417. {
  4418. if ( !mItems[i] )
  4419. continue;
  4420. SimObject* pObj = mItems[i]->getObject();
  4421. if( pObj && pObj->getId() == iObjId )
  4422. return mItems[i]->mId;
  4423. }
  4424. return -1;
  4425. }
  4426. //------------------------------------------------------------------------------
  4427. DefineEngineMethod( GuiTreeViewCtrl, findItemByObjectId, S32, (S32 objectId), ,
  4428. "Find an item by its object id and returns the Tree Item ID for it.\n\n"
  4429. "@param objectId Object id you want the item id for."
  4430. "@return Tree Item Id for the given object ID.")
  4431. {
  4432. return(object->findItemByObjectId(objectId));
  4433. }
  4434. //------------------------------------------------------------------------------
  4435. S32 GuiTreeViewCtrl::getItemObject(S32 itemId)
  4436. {
  4437. GuiTreeViewCtrl::Item* item = getItem(itemId);
  4438. if (!item)
  4439. {
  4440. return 0;
  4441. }
  4442. SimObject* pObj = item->getObject();
  4443. if (pObj)
  4444. return pObj->getId();
  4445. return 0;
  4446. }
  4447. //------------------------------------------------------------------------------
  4448. DefineEngineMethod(GuiTreeViewCtrl, getItemObject, S32, (S32 itemId), ,
  4449. "Gets the object for a particular item.\n\n"
  4450. "@param itemId Item id you want the object id for."
  4451. "@return Object Id for the given tree item ID.")
  4452. {
  4453. return(object->getItemObject(itemId));
  4454. }
  4455. //------------------------------------------------------------------------------
  4456. bool GuiTreeViewCtrl::scrollVisibleByObjectId(S32 objID)
  4457. {
  4458. S32 itemID = findItemByObjectId(objID);
  4459. if(itemID == -1)
  4460. {
  4461. // we did not find the item in our current items
  4462. // we should try to find and show the parent of the item.
  4463. SimObject *obj = Sim::findObject(objID);
  4464. if(!obj || !obj->getGroup())
  4465. return false;
  4466. // if we can't show the parent, we fail.
  4467. if(! scrollVisibleByObjectId(obj->getGroup()->getId()) )
  4468. return false;
  4469. // get the parent. expand the parent. rebuild the tree. this ensures that
  4470. // we'll be able to find the child item we're targeting.
  4471. S32 parentID = findItemByObjectId(obj->getGroup()->getId());
  4472. AssertFatal(parentID != -1, "We were able to show the parent, but could not then find the parent. This should not happen.");
  4473. Item *parentItem = getItem(parentID);
  4474. parentItem->setExpanded(true);
  4475. buildVisibleTree();
  4476. // NOW we should be able to find the object. if not... something's wrong.
  4477. itemID = findItemByObjectId(objID);
  4478. AssertWarn(itemID != -1,"GuiTreeViewCtrl::scrollVisibleByObjectId() found the parent, but can't find it's immediate child. This should not happen.");
  4479. if(itemID == -1)
  4480. return false;
  4481. }
  4482. // ok, item found. scroll to it.
  4483. mFlags.set( RebuildVisible );
  4484. scrollVisible(itemID);
  4485. return true;
  4486. }
  4487. //------------------------------------------------------------------------------
  4488. DefineEngineMethod( GuiTreeViewCtrl, scrollVisibleByObjectId, S32, (S32 objectId), ,
  4489. "Show item by object id.\n\n"
  4490. "@param objectId Object id you want to scroll to."
  4491. "@return True if successful, false if not.")
  4492. {
  4493. return(object->scrollVisibleByObjectId(objectId));
  4494. }
  4495. //------------------------------------------------------------------------------
  4496. //FIXME: this clashes with SimSet.sort()
  4497. DefineEngineMethod( GuiTreeViewCtrl, sort, void, (S32 parentId, bool traverseHierarchy, bool parentsFirst, bool caseSensitive), (0, false, false, true),
  4498. "Sorts all items of the given parent (or root). With 'hierarchy', traverses hierarchy."
  4499. "@param parentId TreeItemID of parent/root to sort all the items under. Use 0 to sort the entire tree."
  4500. "@param traverseHierarchy True to traverse the hierarchy, false to not."
  4501. "@param parentsFirst True to sort the parents first."
  4502. "@param caseSensitive True to pay attention to case, false to ignore it.")
  4503. {
  4504. if( !parentId )
  4505. object->sortTree( caseSensitive, traverseHierarchy, parentsFirst );
  4506. else
  4507. {
  4508. GuiTreeViewCtrl::Item* item = object->getItem( parentId );
  4509. if( !item )
  4510. {
  4511. Con::errorf( "GuiTreeViewCtrl::sort - no item '%i' in tree", parentId );
  4512. return;
  4513. }
  4514. item->sort( caseSensitive, traverseHierarchy, parentsFirst );
  4515. }
  4516. }
  4517. void GuiTreeViewCtrl::cancelRename()
  4518. {
  4519. if ( !mRenamingItem || !mRenameCtrl )
  4520. return;
  4521. mRenamingItem = NULL;
  4522. if ( mRenameCtrl )
  4523. mRenameCtrl->clearFirstResponder();
  4524. }
  4525. void GuiTreeViewCtrl::onRenameValidate()
  4526. {
  4527. if ( !mRenamingItem || !mRenameCtrl )
  4528. return;
  4529. char data[ GuiTextCtrl::MAX_STRING_LENGTH+1 ];
  4530. mRenameCtrl->getText( data );
  4531. SimObject *obj = mRenamingItem->getObject();
  4532. mRenamingItem = NULL;
  4533. // Object could have been deleted in the interum.
  4534. if ( !obj )
  4535. return;
  4536. if( isMethod( "handleRenameObject" ) && handleRenameObject_callback( data, obj ) )
  4537. return;
  4538. if ( mRenameInternal )
  4539. obj->setInternalName( data );
  4540. else
  4541. #ifdef TORQUE_TOOLS
  4542. if ( validateObjectName( data, obj ) )
  4543. #endif
  4544. obj->assignName( data );
  4545. }
  4546. void GuiTreeViewCtrl::showItemRenameCtrl( Item* item )
  4547. {
  4548. SimObject *renameObj = item->getObject();
  4549. mRenamingItem = item;
  4550. if ( !mRenameCtrl )
  4551. {
  4552. mRenameCtrl = new GuiTextEditCtrl;
  4553. mRenameCtrl->registerObject();
  4554. addObject( mRenameCtrl );
  4555. if ( mRenameInternal )
  4556. mRenameCtrl->setText( renameObj->getInternalName() );
  4557. else
  4558. mRenameCtrl->setText( renameObj->getName() );
  4559. mRenameCtrl->setFirstResponder();
  4560. mRenameCtrl->setSinkAllKeys(true);
  4561. mRenameCtrl->selectAllText();
  4562. mRenameCtrl->setCursorPos(0);
  4563. char cmd[256];
  4564. dSprintf( cmd, 256, "%i.onRenameValidate();", getId() );
  4565. mRenameCtrl->setField( "validate", cmd );
  4566. dSprintf( cmd, 256, "%i.cancelRename();", getId() );
  4567. mRenameCtrl->setField( "escapeCommand", cmd );
  4568. }
  4569. }
  4570. DefineEngineMethod( GuiTreeViewCtrl, cancelRename, void, (), , "Cancel renaming an item (For internal use).")
  4571. {
  4572. object->cancelRename();
  4573. }
  4574. DefineEngineMethod( GuiTreeViewCtrl, onRenameValidate, void, (), , "Validate the new name for an object (For internal use).")
  4575. {
  4576. object->onRenameValidate();
  4577. }
  4578. DefineEngineMethod( GuiTreeViewCtrl, showItemRenameCtrl, void, (S32 itemId), ,
  4579. "Show the rename text field for the given item (only one at a time)."
  4580. "@param itemId TreeItemId of item to show rename text field for.")
  4581. {
  4582. GuiTreeViewCtrl::Item* item = object->getItem( itemId );
  4583. if( !item )
  4584. {
  4585. Con::errorf( "GuiTreeViewCtrl::showItemRenameCtrl - invalid item id '%i'", itemId );
  4586. return;
  4587. }
  4588. object->showItemRenameCtrl( item );
  4589. }
  4590. DefineEngineMethod( GuiTreeViewCtrl, setDebug, void, (bool value), (true),
  4591. "Enable/disable debug output."
  4592. "@param value True to enable debug output, false to disable it.")
  4593. {
  4594. object->setDebug( value );
  4595. }
  4596. //-----------------------------------------------------------------------------
  4597. DefineEngineMethod( GuiTreeViewCtrl, isItemSelected, bool, ( S32 id ),,
  4598. "Check whether the given item is currently selected in the tree.\n\n"
  4599. "@param id Item/object ID.\n"
  4600. "@return True if the given item/object is currently selected in the tree." )
  4601. {
  4602. const Vector< GuiTreeViewCtrl::Item* >& selectedItems = object->getSelectedItems();
  4603. for( S32 i = 0; i < selectedItems.size(); ++ i )
  4604. if( selectedItems[ i ]->mId == id )
  4605. return true;
  4606. return false;
  4607. }
  4608. //-----------------------------------------------------------------------------
  4609. DefineEngineMethod( GuiTreeViewCtrl, getFilterText, const char*, (),,
  4610. "Get the current filter expression. Only tree items whose text matches this expression "
  4611. "are displayed. By default, the expression is empty and all items are shown.\n\n"
  4612. "@return The current filter pattern or an empty string if no filter pattern is currently active.\n\n"
  4613. "@see setFilterText\n"
  4614. "@see clearFilterText" )
  4615. {
  4616. return object->getFilterText();
  4617. }
  4618. //-----------------------------------------------------------------------------
  4619. DefineEngineMethod( GuiTreeViewCtrl, setFilterText, void, ( const char* pattern ),,
  4620. "Set the pattern by which to filter items in the tree. Only items in the tree whose text "
  4621. "matches this pattern are displayed.\n\n"
  4622. "@param pattern New pattern based on which visible items in the tree should be filtered. If empty, all items become visible.\n\n"
  4623. "@see getFilterText\n"
  4624. "@see clearFilterText" )
  4625. {
  4626. object->setFilterText( pattern );
  4627. }
  4628. DefineEngineMethod(GuiTreeViewCtrl, setFilterChildren, void, (bool doFilterChildren), (true),
  4629. "Set the pattern by which to filter items in the tree. Only items in the tree whose text "
  4630. "matches this pattern are displayed.\n\n"
  4631. "@param pattern New pattern based on which visible items in the tree should be filtered. If empty, all items become visible.\n\n"
  4632. "@see getFilterText\n"
  4633. "@see clearFilterText")
  4634. {
  4635. object->setFilterChildren(doFilterChildren);
  4636. }
  4637. DefineEngineMethod(GuiTreeViewCtrl, setItemFilterException, void, (U32 item, bool isExempt), (0, true),
  4638. "Set the pattern by which to filter items in the tree. Only items in the tree whose text "
  4639. "matches this pattern are displayed.\n\n"
  4640. "@param pattern New pattern based on which visible items in the tree should be filtered. If empty, all items become visible.\n\n"
  4641. "@see getFilterText\n"
  4642. "@see clearFilterText")
  4643. {
  4644. object->setItemFilterException(item, isExempt);
  4645. }
  4646. DefineEngineMethod(GuiTreeViewCtrl, setItemHidden, void, (U32 item, bool hidden), (0, true),
  4647. "Set the pattern by which to filter items in the tree. Only items in the tree whose text "
  4648. "matches this pattern are displayed.\n\n"
  4649. "@param pattern New pattern based on which visible items in the tree should be filtered. If empty, all items become visible.\n\n"
  4650. "@see getFilterText\n"
  4651. "@see clearFilterText")
  4652. {
  4653. object->setItemHidden(item, hidden);
  4654. }
  4655. DefineEngineMethod(GuiTreeViewCtrl, clearHiddenItems, void, (),,
  4656. "Set the pattern by which to filter items in the tree. Only items in the tree whose text "
  4657. "matches this pattern are displayed.\n\n"
  4658. "@param pattern New pattern based on which visible items in the tree should be filtered. If empty, all items become visible.\n\n"
  4659. "@see getFilterText\n"
  4660. "@see clearFilterText")
  4661. {
  4662. object->clearHiddenItems();
  4663. }
  4664. //-----------------------------------------------------------------------------
  4665. DefineEngineMethod( GuiTreeViewCtrl, clearFilterText, void, (),,
  4666. "Clear the current item filtering pattern.\n\n"
  4667. "@see setFilterText\n"
  4668. "@see getFilterText" )
  4669. {
  4670. object->clearFilterText();
  4671. }
  4672. DefineEngineMethod(GuiTreeViewCtrl, getItemAtPosition, S32, (Point2I position), (Point2I::Zero),
  4673. "Get the tree item at the passed in position.\n\n"
  4674. "@param position The position to check for what item is below it.\n"
  4675. "@return The id of the item under the position.")
  4676. {
  4677. return object->getItemAtPosition(position);
  4678. }
  4679. DefineEngineMethod(GuiTreeViewCtrl, reparentItem, void, (S32 itemId, S32 parentId), (0, 0),
  4680. "Check whether the given item is currently selected in the tree.\n\n"
  4681. "@param id Item/object ID.\n"
  4682. "@return True if the given item/object is currently selected in the tree.")
  4683. {
  4684. if (itemId == parentId || itemId < 0 || parentId < 0)
  4685. return;
  4686. const Vector< GuiTreeViewCtrl::Item* > & selectedItems = object->getItems();
  4687. Vector<GuiTreeViewCtrl::Item*> items;
  4688. GuiTreeViewCtrl::Item * parent = nullptr;
  4689. for (S32 i = 0; i < selectedItems.size(); ++i)
  4690. {
  4691. if (selectedItems[i]->mId == itemId)
  4692. {
  4693. items.push_back(selectedItems[i]);
  4694. }
  4695. if (selectedItems[i]->mId == parentId)
  4696. {
  4697. parent = selectedItems[i];
  4698. }
  4699. }
  4700. if (!items.empty() && parent != nullptr)
  4701. {
  4702. object->reparentItems(items, parent);
  4703. }
  4704. }
  4705. DefineEngineMethod(GuiTreeViewCtrl, getTabLevel, S32, (S32 itemId), (0),
  4706. "Get the tree item at the passed in position.\n\n"
  4707. "@param position The position to check for what item is below it.\n"
  4708. "@return The id of the item under the position.")
  4709. {
  4710. return object->getTabLevel(itemId);
  4711. }