llfolderview.cpp 159 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713
  1. /**
  2. * @file llfolderview.cpp
  3. * @brief Implementation of the folder view collection of classes.
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewergpl$
  6. *
  7. * Copyright (c) 2001-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llfolderview.h"
  34. #include "llcachename.h"
  35. #include "llcallbacklist.h"
  36. #include "llfasttimer.h"
  37. #include "llmenugl.h"
  38. #include "llnotifications.h"
  39. #include "llscrollcontainer.h"
  40. #include "lltrans.h"
  41. #include "lluictrlfactory.h"
  42. #include "llagent.h"
  43. #include "llagentwearables.h"
  44. #include "llaisapi.h" // For AISAPI::isAvailable()
  45. #include "llappearancemgr.h"
  46. #include "llappviewer.h"
  47. #include "llfloaterproperties.h"
  48. #include "hbfloaterthumbnail.h"
  49. #include "llinventoryactions.h" // For init_inventory_panel_actions()
  50. #include "llinventorybridge.h"
  51. #include "hbinventoryclipboard.h"
  52. #include "llinventorymodelfetch.h"
  53. #include "llmarketplacefunctions.h"
  54. #include "llpreview.h"
  55. #include "lltooldraganddrop.h"
  56. #include "llviewercontrol.h"
  57. #include "llviewerfoldertype.h"
  58. #include "llviewermenu.h" // For gMenuHolderp
  59. #include "llviewerregion.h"
  60. #include "llviewerwindow.h"
  61. #include "llvoavatarself.h"
  62. #if LL_WINDOWS
  63. // Warning C4355: 'this' : used in base member initializer list
  64. #pragma warning(disable : 4355)
  65. #endif
  66. constexpr S32 LEFT_PAD = 5;
  67. constexpr S32 LEFT_INDENTATION = 10;
  68. constexpr S32 ICON_PAD = 2;
  69. constexpr S32 ICON_WIDTH = 16;
  70. constexpr S32 TEXT_PAD = 1;
  71. constexpr S32 ARROW_SIZE = 12;
  72. constexpr S32 RENAME_HEIGHT_PAD = 6;
  73. constexpr S32 AUTO_OPEN_STACK_DEPTH = 16;
  74. constexpr S32 MIN_ITEM_WIDTH_VISIBLE = ICON_WIDTH + ICON_PAD + ARROW_SIZE +
  75. TEXT_PAD + /*first few characters*/ 40;
  76. constexpr S32 MINIMUM_RENAMER_WIDTH = 80;
  77. constexpr F32 FOLDER_CLOSE_TIME_CONSTANT = 0.02f;
  78. constexpr F32 FOLDER_OPEN_TIME_CONSTANT = 0.03f;
  79. constexpr S32 MAX_FOLDER_ITEM_OVERLAP = 2;
  80. enum
  81. {
  82. SIGNAL_NO_KEYBOARD_FOCUS = 1,
  83. SIGNAL_KEYBOARD_FOCUS = 2
  84. };
  85. // Static meember variables
  86. LLFolderView::instances_map_t LLFolderView::sInstances;
  87. F32 LLFolderView::sAutoOpenTime = 1.f;
  88. LLUUID LLFolderViewFolder::sLastOpenId;
  89. LLUUID LLFolderViewEventListener::sLastDragTipID;
  90. std::string LLFolderViewEventListener::sLastDragTipMsg;
  91. bool LLFolderViewEventListener::sLastDragTipDrop = false;
  92. const LLFontGL* LLFolderViewItem::sFont = NULL;
  93. const LLFontGL* LLFolderViewItem::sFontItalic = NULL;
  94. F32 LLFolderViewItem::sFontLineHeight = 0.f;
  95. S32 LLFolderViewItem::sFontLineHeightRounded = 0;
  96. LLColor4 LLFolderViewItem::sFgColor;
  97. LLColor4 LLFolderViewItem::sHighlightBgColor;
  98. LLColor4 LLFolderViewItem::sHighlightFgColor;
  99. LLColor4 LLFolderViewItem::sContextMenuBgColor;
  100. LLColor4 LLFolderViewItem::sFilterBGColor;
  101. LLColor4 LLFolderViewItem::sFilterTextColor;
  102. LLColor4 LLFolderViewItem::sSuffixColor;
  103. LLColor4 LLFolderViewItem::sSearchStatusColor;
  104. LLUIImagePtr LLFolderViewItem::sArrowImage;
  105. LLWString LLFolderViewItem::sLoadingStr;
  106. // This is used to keep track of existing folder view items and avoid a crash
  107. // bug due to a race condition (see in doIdle()). HB
  108. static fast_hset<LLFolderViewItem*> sFolderViewItems;
  109. static const std::string LL_INVENTORY_PANEL_TAG = "inventory_panel";
  110. static LLRegisterWidget<LLInventoryPanel> r(LL_INVENTORY_PANEL_TAG);
  111. //-----------------------------------------------------------------------------
  112. // LLFolderViewItem class
  113. //-----------------------------------------------------------------------------
  114. //static
  115. void LLFolderViewItem::initClass()
  116. {
  117. sFont = LLFontGL::getFontSansSerifSmall();
  118. sFontItalic = LLFontGL::getFont("SansSerif", "Little", LLFontGL::ITALIC);
  119. sFontLineHeight = sFont->getLineHeight();
  120. if (sFontItalic)
  121. {
  122. sFontLineHeight = llmax(sFontLineHeight, sFontItalic->getLineHeight());
  123. }
  124. sFontLineHeightRounded = ll_roundp(sFontLineHeight);
  125. sArrowImage = LLUI::getUIImage("folder_arrow.tga");
  126. sLoadingStr = LLTrans::getWString("LoadingData");
  127. connectRefreshCachedSettingsSafe("MenuItemEnabledColor");
  128. connectRefreshCachedSettingsSafe("MenuItemHighlightBgColor");
  129. connectRefreshCachedSettingsSafe("MenuItemHighlightFgColor");
  130. connectRefreshCachedSettingsSafe("MenuPopupBgColor");
  131. connectRefreshCachedSettingsSafe("FilterBackgroundColor");
  132. connectRefreshCachedSettingsSafe("FilterTextColor");
  133. connectRefreshCachedSettingsSafe("InventoryItemSuffixColor");
  134. connectRefreshCachedSettingsSafe("InventorySearchStatusColor");
  135. refreshCachedSettings();
  136. }
  137. //static
  138. void LLFolderViewItem::cleanupClass()
  139. {
  140. sArrowImage = NULL;
  141. }
  142. //static
  143. void LLFolderViewItem::connectRefreshCachedSettingsSafe(const char* name)
  144. {
  145. LLControlVariable* controlp = gColors.getControl(name);
  146. if (!controlp)
  147. {
  148. llwarns << "Setting name not found: " << name << llendl;
  149. return;
  150. }
  151. controlp->getSignal()->connect(boost::bind(&LLFolderViewItem::refreshCachedSettings));
  152. }
  153. //static
  154. void LLFolderViewItem::refreshCachedSettings()
  155. {
  156. sFgColor = gColors.getColor("MenuItemEnabledColor");
  157. sHighlightBgColor = gColors.getColor("MenuItemHighlightBgColor");
  158. sHighlightFgColor = gColors.getColor("MenuItemHighlightFgColor");
  159. sContextMenuBgColor = gColors.getColor("MenuPopupBgColor");
  160. sFilterBGColor = gColors.getColor("FilterBackgroundColor");
  161. sFilterTextColor = gColors.getColor("FilterTextColor");
  162. sSuffixColor = gColors.getColor("InventoryItemSuffixColor");
  163. sSearchStatusColor = gColors.getColor("InventorySearchStatusColor");
  164. }
  165. // Default constructor
  166. // NOTE: Optimize this, we call it a *lot* when opening a large inventory
  167. LLFolderViewItem::LLFolderViewItem(const std::string& name, LLUIImagePtr icon,
  168. S32 creation_date,
  169. LLFolderView* root,
  170. LLFolderViewEventListener* listener)
  171. : LLUICtrl(name, LLRect(0, 0, 0, 0), true, NULL, NULL,
  172. FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT),
  173. mLabel(name),
  174. mWLabel(utf8str_to_wstring(name)),
  175. mLabelWidth(0),
  176. mCreationDate(creation_date),
  177. mParentFolder(NULL),
  178. mListener(listener),
  179. mIsSelected(false),
  180. mIsCurSelection(false),
  181. mSelectPending(false),
  182. mDoubleClickDisabled(false),
  183. mLabelStyle(LLFontGL::NORMAL),
  184. mIcon(icon),
  185. mHasVisibleChildren(false),
  186. mIndentation(0),
  187. mFiltered(false),
  188. mLastFilterGeneration(-1),
  189. mStringMatchOffset(std::string::npos),
  190. mControlLabelRotation(0.f),
  191. mRoot(root),
  192. mDragAndDropTarget(false),
  193. mIsLoading(false),
  194. mHasDescription(true)
  195. {
  196. sFolderViewItems.insert(this);
  197. // Possible optimization: only call refreshFromListener():
  198. refresh();
  199. setTabStop(false);
  200. }
  201. //virtual
  202. LLFolderViewItem::~LLFolderViewItem()
  203. {
  204. sFolderViewItems.erase(this);
  205. delete mListener;
  206. mListener = NULL;
  207. }
  208. // Returns true if this object is a child (or grandchild, etc.) of
  209. // potential_ancestor.
  210. bool LLFolderViewItem::isDescendantOf(const LLFolderViewFolder* potential_ancestor)
  211. {
  212. LLFolderViewItem* root = this;
  213. while (root->mParentFolder)
  214. {
  215. if (root->mParentFolder == potential_ancestor)
  216. {
  217. return true;
  218. }
  219. root = root->mParentFolder;
  220. }
  221. return false;
  222. }
  223. LLFolderViewItem* LLFolderViewItem::getNextOpenNode(bool include_children)
  224. {
  225. if (!mParentFolder)
  226. {
  227. return NULL;
  228. }
  229. LLFolderViewItem* itemp =
  230. mParentFolder->getNextFromChild(this, include_children);
  231. while (itemp && !itemp->getVisible())
  232. {
  233. LLFolderViewItem* next_itemp =
  234. itemp->mParentFolder->getNextFromChild(itemp, include_children);
  235. if (itemp == next_itemp)
  236. {
  237. // Hit last item
  238. return itemp->getVisible() ? itemp : this;
  239. }
  240. itemp = next_itemp;
  241. }
  242. return itemp;
  243. }
  244. LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(bool include_children)
  245. {
  246. if (!mParentFolder)
  247. {
  248. return NULL;
  249. }
  250. LLFolderViewItem* itemp =
  251. mParentFolder->getPreviousFromChild(this, include_children);
  252. while (itemp && !itemp->getVisible())
  253. {
  254. LLFolderViewItem* next_itemp =
  255. itemp->mParentFolder->getPreviousFromChild(itemp,
  256. include_children);
  257. if (itemp == next_itemp)
  258. {
  259. // Hit first item
  260. return itemp->getVisible() ? itemp : this;
  261. }
  262. itemp = next_itemp;
  263. }
  264. return itemp;
  265. }
  266. // Is this item something we think we should be showing ? For example, if we
  267. // have not gotten around to filtering it yet, then the answer is yes until we
  268. // find out otherwise.
  269. //virtual
  270. bool LLFolderViewItem::potentiallyVisible()
  271. {
  272. // We have not been checked against min required filter or we have and we
  273. // passed
  274. return getFiltered() ||
  275. mLastFilterGeneration < mRoot->getFilter()->getMinRequiredGeneration();
  276. }
  277. //virtual
  278. bool LLFolderViewItem::getFiltered()
  279. {
  280. return mFiltered &&
  281. mLastFilterGeneration >= mRoot->getFilter()->getMinRequiredGeneration();
  282. }
  283. //virtual
  284. bool LLFolderViewItem::getFiltered(S32 filter_generation)
  285. {
  286. return mFiltered && mLastFilterGeneration >= filter_generation;
  287. }
  288. //virtual
  289. void LLFolderViewItem::setFiltered(bool filtered, S32 filter_generation)
  290. {
  291. mFiltered = filtered;
  292. mLastFilterGeneration = filter_generation;
  293. }
  294. const LLFontGL* LLFolderViewItem::getRenderFont(U32& style)
  295. {
  296. static LLCachedControl<bool> use_true_italics(gSavedSettings,
  297. "InventoryUseItalicsFont");
  298. style = mLabelStyle;
  299. if (use_true_italics && (style & LLFontGL::ITALIC) && sFontItalic)
  300. {
  301. // Use a true italic font instead of slanting the default font. HB
  302. style &= ~LLFontGL::ITALIC;
  303. return sFontItalic;
  304. }
  305. return sFont;
  306. }
  307. // Refresh information from the listener
  308. void LLFolderViewItem::refreshFromListener()
  309. {
  310. if (mListener)
  311. {
  312. mHasDescription = false;
  313. mLabel = mListener->getDisplayName();
  314. mWLabel = utf8str_to_wstring(mLabel);
  315. mIcon = mListener->getIcon();
  316. time_t creation_date = mListener->getCreationDate();
  317. if ((time_t)mCreationDate != creation_date)
  318. {
  319. mCreationDate = mListener->getCreationDate();
  320. dirtyFilter();
  321. }
  322. mLabelStyle = mListener->getLabelStyle();
  323. mLabelSuffix = mListener->getLabelSuffix();
  324. mWLabelSuffix = utf8str_to_wstring(mLabelSuffix);
  325. LLInventoryItem* item = gInventory.getItem(mListener->getUUID());
  326. if (item)
  327. {
  328. std::string desc = item->getDescription();
  329. if (!desc.empty())
  330. {
  331. LLStringUtil::trim(desc);
  332. if (!desc.empty())
  333. {
  334. LLStringUtil::toUpper(desc);
  335. if (desc != "(NO DESCRIPTION)")
  336. {
  337. mHasDescription = true;
  338. }
  339. }
  340. }
  341. }
  342. }
  343. }
  344. //virtual
  345. void LLFolderViewItem::refresh()
  346. {
  347. refreshFromListener();
  348. std::string searchable_label(mLabel);
  349. searchable_label.append(mLabelSuffix);
  350. LLStringUtil::toUpper(searchable_label);
  351. if (mSearchableLabel.compare(searchable_label))
  352. {
  353. mSearchableLabel.assign(searchable_label);
  354. dirtyFilter();
  355. // Some part of label has changed, so overall width has potentially
  356. // changed
  357. if (mParentFolder)
  358. {
  359. mParentFolder->requestArrange();
  360. }
  361. }
  362. U32 style;
  363. const LLFontGL* fontp = getRenderFont(style);
  364. S32 label_width = fontp->getWidth(mLabel);
  365. if (mLabelSuffix.size())
  366. {
  367. label_width += fontp->getWidth(mLabelSuffix);
  368. }
  369. mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + label_width;
  370. }
  371. void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
  372. {
  373. functor(mListener);
  374. }
  375. // This method is called when items are added or view filters change. It is
  376. // implemented here but called by derived classes when folding the views.
  377. void LLFolderViewItem::filterFromRoot()
  378. {
  379. LLFolderViewItem* root = getRoot();
  380. if (root)
  381. {
  382. root->filter(*((LLFolderView*)root)->getFilter());
  383. }
  384. }
  385. // This method is called when the folder view is dirty. It is implemented here
  386. // but called by derived classes when folding the views.
  387. void LLFolderViewItem::arrangeFromRoot()
  388. {
  389. LLFolderViewItem* root = getRoot();
  390. if (root)
  391. {
  392. root->arrange(NULL, NULL, 0);
  393. }
  394. }
  395. // This method clears the currently selected item, and records the specified
  396. // selected item appropriately for display and use in the UI. If open is true,
  397. // then folders are opened up along the way to the selection.
  398. void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection,
  399. bool openitem,
  400. bool take_keyboard_focus)
  401. {
  402. getRoot()->setSelection(selection, openitem, take_keyboard_focus);
  403. }
  404. // Helper method to change the selection from the root.
  405. void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection,
  406. bool selected)
  407. {
  408. getRoot()->changeSelection(selection, selected);
  409. }
  410. void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection)
  411. {
  412. std::vector<LLFolderViewItem*> selected_items;
  413. getRoot()->extendSelection(selection, NULL, selected_items);
  414. }
  415. EInventorySortGroup LLFolderViewItem::getSortGroup() const
  416. {
  417. return SG_ITEM;
  418. }
  419. //virtual
  420. bool LLFolderViewItem::addToFolder(LLFolderViewFolder* folder,
  421. LLFolderView* root)
  422. {
  423. if (!folder || !root || !getListener())
  424. {
  425. return false;
  426. }
  427. mParentFolder = folder;
  428. root->addItemID(getListener()->getUUID(), this);
  429. return folder->addItem(this);
  430. }
  431. // Finds width and height of this object and its children. Also makes sure that
  432. // this view and its children are the right size.
  433. //virtual
  434. S32 LLFolderViewItem::arrange(S32* width, S32* height, S32 filter_generation)
  435. {
  436. if (mParentFolder)
  437. {
  438. mIndentation = mParentFolder->getIndentation() + LEFT_INDENTATION;
  439. }
  440. else
  441. {
  442. mIndentation = 0;
  443. }
  444. if (width)
  445. {
  446. *width = llmax(*width, mLabelWidth + mIndentation);
  447. }
  448. S32 item_height = getItemHeight();
  449. if (height)
  450. {
  451. *height = item_height;
  452. }
  453. return item_height;
  454. }
  455. //virtual
  456. S32 LLFolderViewItem::getItemHeight()
  457. {
  458. S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight();
  459. return llmax(icon_height, sFontLineHeightRounded) + ICON_PAD;
  460. }
  461. //virtual
  462. void LLFolderViewItem::filter(LLInventoryFilter& filter)
  463. {
  464. bool filtered = mListener && filter.check(this);
  465. // If our visibility will change as a result of this filter, then we need
  466. // to be rearranged in our parent folder
  467. if (getVisible() != filtered)
  468. {
  469. if (mParentFolder)
  470. {
  471. mParentFolder->requestArrange();
  472. }
  473. }
  474. setFiltered(filtered, filter.getCurrentGeneration());
  475. mStringMatchOffset = filter.getStringMatchOffset();
  476. filter.decrementFilterCount();
  477. }
  478. //virtual
  479. void LLFolderViewItem::dirtyFilter()
  480. {
  481. mLastFilterGeneration = -1;
  482. // Bubble up dirty flag all the way to root
  483. if (getParentFolder())
  484. {
  485. getParentFolder()->setCompletedFilterGeneration(-1, true);
  486. }
  487. }
  488. // *TODO: This can be optimized a lot by simply recording that it is selected
  489. // in the appropriate places, and assuming that set selection means 'deselect'
  490. // for a leaf item. Do this optimization after multiple selection is
  491. // implemented to make sure it all plays nice together.
  492. bool LLFolderViewItem::setSelection(LLFolderViewItem* selection, bool openitem,
  493. bool take_keyboard_focus)
  494. {
  495. if (selection == this && !mIsSelected)
  496. {
  497. selectItem();
  498. if (mListener)
  499. {
  500. mListener->selectItem();
  501. }
  502. }
  503. else if (mIsSelected) // Deselect everything else.
  504. {
  505. deselectItem();
  506. }
  507. return mIsSelected;
  508. }
  509. bool LLFolderViewItem::changeSelection(LLFolderViewItem* selection,
  510. bool selected)
  511. {
  512. if (selection == this && mIsSelected != selected)
  513. {
  514. if (mIsSelected)
  515. {
  516. deselectItem();
  517. }
  518. else
  519. {
  520. selectItem();
  521. }
  522. if (mListener)
  523. {
  524. mListener->selectItem();
  525. }
  526. return true;
  527. }
  528. return false;
  529. }
  530. void LLFolderViewItem::deselectItem()
  531. {
  532. llassert(mIsSelected);
  533. mIsSelected = false;
  534. // Update ancestors' count of selected descendents.
  535. LLFolderViewFolder* parent_folder = getParentFolder();
  536. if (parent_folder)
  537. {
  538. parent_folder->recursiveIncrementNumDescendantsSelected(-1);
  539. }
  540. }
  541. void LLFolderViewItem::selectItem()
  542. {
  543. llassert(!mIsSelected);
  544. mIsSelected = true;
  545. // Update ancestors' count of selected descendents.
  546. LLFolderViewFolder* parent_folder = getParentFolder();
  547. if (parent_folder)
  548. {
  549. parent_folder->recursiveIncrementNumDescendantsSelected(1);
  550. }
  551. }
  552. bool LLFolderViewItem::isMovable()
  553. {
  554. return !mListener || mListener->isItemMovable();
  555. }
  556. bool LLFolderViewItem::isRemovable()
  557. {
  558. return !mListener || mListener->isItemRemovable();
  559. }
  560. void LLFolderViewItem::destroyView()
  561. {
  562. if (mParentFolder)
  563. {
  564. // removeView() deletes me
  565. mParentFolder->removeView(this);
  566. }
  567. }
  568. // Call through to the viewed object and return true if it can be removed.
  569. bool LLFolderViewItem::remove()
  570. {
  571. if (!isRemovable())
  572. {
  573. return false;
  574. }
  575. if (mListener)
  576. {
  577. return mListener->removeItem();
  578. }
  579. return true;
  580. }
  581. // Build an appropriate context menu for the item.
  582. void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
  583. {
  584. if (mListener)
  585. {
  586. mListener->buildContextMenu(menu, flags);
  587. }
  588. LLViewerRegion* regionp = gAgent.getRegion();
  589. if (!regionp || !regionp->bakesOnMeshEnabled())
  590. {
  591. LLMenuItemGL* item = menu.getChild<LLMenuItemGL>("New Universal",
  592. true, false);
  593. if (item)
  594. {
  595. item->setVisible(false);
  596. }
  597. }
  598. }
  599. void LLFolderViewItem::openItem()
  600. {
  601. if (mListener)
  602. {
  603. mListener->openItem();
  604. }
  605. }
  606. void LLFolderViewItem::preview()
  607. {
  608. if (mListener)
  609. {
  610. mListener->previewItem();
  611. }
  612. }
  613. void LLFolderViewItem::rename(const std::string& new_name)
  614. {
  615. if (!new_name.empty())
  616. {
  617. mLabel = new_name;
  618. mWLabel = utf8str_to_wstring(new_name);
  619. if (mListener)
  620. {
  621. mListener->renameItem(new_name);
  622. if (mParentFolder)
  623. {
  624. mParentFolder->resort(this);
  625. }
  626. }
  627. }
  628. }
  629. std::string LLFolderViewItem::getSearchableData()
  630. {
  631. std::string searchable;
  632. U32 flags = mRoot->getSearchType();
  633. if (flags == 0 || (flags & 1))
  634. {
  635. searchable = mSearchableLabel;
  636. }
  637. bool want_desc = (flags & 2) != 0 && mHasDescription;
  638. if (!want_desc)
  639. {
  640. // Get rid of cached data to save memory.
  641. mSearchableDesc.clear();
  642. }
  643. bool want_creator = (flags & 4) != 0;
  644. if (!want_creator)
  645. {
  646. // Get rid of cached data to save memory.
  647. mSearchableCreator.clear();
  648. }
  649. if ((want_desc || want_creator) && mListener)
  650. {
  651. bool fetch_desc = want_desc && mSearchableDesc.empty();
  652. bool fetch_creator = want_creator && mSearchableCreator.empty();
  653. if (fetch_desc || fetch_creator)
  654. {
  655. LLInventoryItem* item = gInventory.getItem(mListener->getUUID());
  656. if (item)
  657. {
  658. if (fetch_desc)
  659. {
  660. mSearchableDesc = item->getDescription();
  661. if (mSearchableDesc.empty())
  662. {
  663. want_desc = false;
  664. }
  665. else
  666. {
  667. LLStringUtil::toUpper(mSearchableDesc);
  668. }
  669. }
  670. if (fetch_creator)
  671. {
  672. const LLUUID& creator_id = item->getCreatorUUID();
  673. if (creator_id.isNull())
  674. {
  675. mSearchableCreator = '?';
  676. }
  677. else if (gCacheNamep &&
  678. gCacheNamep->getFullName(creator_id,
  679. mSearchableCreator))
  680. {
  681. if (mSearchableCreator.empty())
  682. {
  683. mSearchableCreator = '?';
  684. }
  685. else
  686. {
  687. LLStringUtil::toUpper(mSearchableCreator);
  688. }
  689. }
  690. else
  691. {
  692. mSearchableCreator.clear();
  693. want_creator = false;
  694. }
  695. }
  696. }
  697. }
  698. if (want_desc)
  699. {
  700. if (!searchable.empty())
  701. {
  702. searchable += ' ';
  703. }
  704. searchable += mSearchableDesc;
  705. }
  706. if (want_creator)
  707. {
  708. if (!searchable.empty())
  709. {
  710. searchable += ' ';
  711. }
  712. searchable += mSearchableCreator;
  713. }
  714. }
  715. return searchable;
  716. }
  717. std::string LLFolderViewItem::getName() const
  718. {
  719. if (mListener)
  720. {
  721. return mListener->getName();
  722. }
  723. return mLabel;
  724. }
  725. // LLView functionality
  726. bool LLFolderViewItem::handleRightMouseDown(S32 x, S32 y, MASK mask)
  727. {
  728. if (!mIsSelected)
  729. {
  730. setSelectionFromRoot(this, false);
  731. }
  732. make_ui_sound("UISndClick");
  733. return true;
  734. }
  735. bool LLFolderViewItem::handleMouseDown(S32 x, S32 y, MASK mask)
  736. {
  737. // No handler needed for focus lost since this class has no state that
  738. // depends on it.
  739. gFocusMgr.setMouseCapture(this);
  740. if (!mIsSelected)
  741. {
  742. if (mask & MASK_CONTROL)
  743. {
  744. changeSelectionFromRoot(this, !mIsSelected);
  745. }
  746. else if (mask & MASK_SHIFT)
  747. {
  748. extendSelectionFromRoot(this);
  749. }
  750. else
  751. {
  752. setSelectionFromRoot(this, false);
  753. }
  754. mRoot->setGotLeftMouseClick();
  755. make_ui_sound("UISndClick");
  756. }
  757. else
  758. {
  759. mSelectPending = true;
  760. }
  761. if (isMovable())
  762. {
  763. S32 screen_x, screen_y;
  764. localPointToScreen(x, y, &screen_x, &screen_y);
  765. gToolDragAndDrop.setDragStart(screen_x, screen_y);
  766. }
  767. return true;
  768. }
  769. bool LLFolderViewItem::handleHover(S32 x, S32 y, MASK mask)
  770. {
  771. if (!hasMouseCapture() || !isMovable())
  772. {
  773. mRoot->setShowSelectionContext(false);
  774. gViewerWindowp->setCursor(UI_CURSOR_ARROW);
  775. // Let the parent handle this then...
  776. return false;
  777. }
  778. bool can_drag = true;
  779. S32 screen_x, screen_y;
  780. localPointToScreen(x, y, &screen_x, &screen_y);
  781. if (gToolDragAndDrop.isOverThreshold(screen_x, screen_y))
  782. {
  783. if (mRoot->getCurSelectedItem())
  784. {
  785. LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD;
  786. // *TODO: push this into listener and remove dependency on llagent
  787. if (mListener &&
  788. gInventory.isObjectDescendentOf(mListener->getUUID(),
  789. gInventory.getRootFolderID()))
  790. {
  791. src = LLToolDragAndDrop::SOURCE_AGENT;
  792. }
  793. else if (mListener &&
  794. gInventory.isObjectDescendentOf(mListener->getUUID(),
  795. gInventory.getLibraryRootFolderID()))
  796. {
  797. src = LLToolDragAndDrop::SOURCE_LIBRARY;
  798. }
  799. can_drag = mRoot->startDrag(src);
  800. if (can_drag)
  801. {
  802. // if (mListener) mListener->startDrag();
  803. // RN: when starting drag and drop, clear out last auto-open
  804. mRoot->autoOpenTest(NULL);
  805. mRoot->setShowSelectionContext(true);
  806. // Release keyboard focus, so that if stuff is dropped into the
  807. // world, pressing the delete key would not blow away the
  808. // inventory item.
  809. gFocusMgr.setKeyboardFocus(NULL);
  810. return gToolDragAndDrop.handleHover(x, y, mask);
  811. }
  812. }
  813. }
  814. if (can_drag)
  815. {
  816. gViewerWindowp->setCursor(UI_CURSOR_ARROW);
  817. }
  818. else
  819. {
  820. gViewerWindowp->setCursor(UI_CURSOR_NOLOCKED);
  821. }
  822. return true;
  823. }
  824. bool LLFolderViewItem::handleDoubleClick(S32 x, S32 y, MASK mask)
  825. {
  826. if (!mDoubleClickDisabled)
  827. {
  828. preview();
  829. }
  830. return true;
  831. }
  832. bool LLFolderViewItem::handleScrollWheel(S32 x, S32 y, S32 clicks)
  833. {
  834. return getParent() && getParent()->handleScrollWheel(x, y, clicks);
  835. }
  836. bool LLFolderViewItem::handleMouseUp(S32 x, S32 y, MASK mask)
  837. {
  838. // If mouse has not moved since mouse down...
  839. if (pointInView(x, y) && mSelectPending)
  840. {
  841. // ...then select
  842. if (mask & MASK_CONTROL)
  843. {
  844. changeSelectionFromRoot(this, !mIsSelected);
  845. }
  846. else if (mask & MASK_SHIFT)
  847. {
  848. extendSelectionFromRoot(this);
  849. }
  850. else
  851. {
  852. setSelectionFromRoot(this, false);
  853. }
  854. }
  855. mSelectPending = false;
  856. if (hasMouseCapture())
  857. {
  858. getRoot()->setShowSelectionContext(false);
  859. gFocusMgr.setMouseCapture(NULL);
  860. }
  861. return true;
  862. }
  863. //virtual
  864. bool LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
  865. EDragAndDropType cargo_type,
  866. void* cargo_data, EAcceptance* accept,
  867. std::string& tooltip_msg)
  868. {
  869. bool accepted = false;
  870. bool handled = false;
  871. if (mListener)
  872. {
  873. accepted = mListener->dragOrDrop(mask, drop, cargo_type, cargo_data,
  874. tooltip_msg);
  875. handled = accepted;
  876. if (accepted)
  877. {
  878. mDragAndDropTarget = true;
  879. *accept = ACCEPT_YES_MULTI;
  880. }
  881. else
  882. {
  883. *accept = ACCEPT_NO;
  884. }
  885. LLFolderViewEventListener::dragOrDropTip(drop, tooltip_msg);
  886. }
  887. if (mParentFolder && !handled)
  888. {
  889. handled = mParentFolder->handleDragAndDropFromChild(mask, drop,
  890. cargo_type,
  891. cargo_data,
  892. accept,
  893. tooltip_msg);
  894. }
  895. if (handled)
  896. {
  897. LL_DEBUGS("UserInput") << "dragAndDrop handled with: drop = "
  898. << (drop ? "true" : "false") << " - accepted = "
  899. << (accepted ? "true" : "false") << LL_ENDL;
  900. }
  901. return handled;
  902. }
  903. void LLFolderViewItem::draw()
  904. {
  905. bool up_to_date = mListener && mListener->isUpToDate();
  906. if (sArrowImage &&
  907. // If we fetched our children and some of them have passed the filter...
  908. ((up_to_date && hasVisibleChildren()) ||
  909. // ... or we know we have children but have not fetched them (does not
  910. // obey filter)
  911. (!up_to_date && mListener && mListener->hasChildren())))
  912. {
  913. gl_draw_scaled_rotated_image(mIndentation,
  914. getRect().getHeight() - ARROW_SIZE -
  915. TEXT_PAD, ARROW_SIZE, ARROW_SIZE,
  916. mControlLabelRotation,
  917. sArrowImage->getImage(), sFgColor);
  918. }
  919. F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD +
  920. mIndentation);
  921. // If we have keyboard focus, draw selection filled
  922. bool show_context = getRoot()->getShowSelectionContext();
  923. bool filled = show_context || (gFocusMgr.getKeyboardFocus() == getRoot());
  924. // Always render "current" item, only render other selected items if
  925. // mShowSingleSelection is false
  926. if (mIsSelected)
  927. {
  928. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  929. LLColor4 bg_color = sHighlightBgColor;
  930. if (!mIsCurSelection)
  931. {
  932. // Do time-based fade of extra objects
  933. F32 fade_time = getRoot()->getSelectionFadeElapsedTime();
  934. if (getRoot()->getShowSingleSelection())
  935. {
  936. // Fading out
  937. bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f,
  938. bg_color.mV[VALPHA], 0.f);
  939. }
  940. else
  941. {
  942. // Fading in
  943. bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f,
  944. bg_color.mV[VALPHA]);
  945. }
  946. }
  947. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth() - 2,
  948. llfloor(getRect().getHeight() - sFontLineHeight - ICON_PAD),
  949. bg_color, filled);
  950. if (mIsCurSelection)
  951. {
  952. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth() - 2,
  953. llfloor(getRect().getHeight() - sFontLineHeight -
  954. ICON_PAD),
  955. sHighlightFgColor, false);
  956. }
  957. if (getRect().getHeight() > sFontLineHeightRounded + ICON_PAD + 2)
  958. {
  959. gl_rect_2d(0,
  960. llfloor(getRect().getHeight() - sFontLineHeight -
  961. ICON_PAD) - 2,
  962. getRect().getWidth() - 2, 2, sHighlightFgColor, false);
  963. if (show_context)
  964. {
  965. gl_rect_2d(0,
  966. llfloor(getRect().getHeight() -
  967. sFontLineHeight - ICON_PAD) - 2,
  968. getRect().getWidth() - 2, 2, sHighlightBgColor,
  969. true);
  970. }
  971. }
  972. }
  973. if (mDragAndDropTarget)
  974. {
  975. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  976. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth() - 2,
  977. llfloor(getRect().getHeight() - sFontLineHeight - ICON_PAD),
  978. sHighlightBgColor, false);
  979. if (getRect().getHeight() > sFontLineHeightRounded + ICON_PAD + 2)
  980. {
  981. gl_rect_2d(0,
  982. llfloor(getRect().getHeight() - sFontLineHeight - ICON_PAD) - 2,
  983. getRect().getWidth() - 2, 2, sHighlightBgColor, false);
  984. }
  985. mDragAndDropTarget = false;
  986. }
  987. if (mIcon)
  988. {
  989. mIcon->draw(mIndentation + ARROW_SIZE + TEXT_PAD,
  990. getRect().getHeight() - mIcon->getHeight());
  991. }
  992. if (!mLabel.empty())
  993. {
  994. U32 style;
  995. const LLFontGL* fontp = getRenderFont(style);
  996. // Highlight filtered text
  997. LLColor4 color = ((mIsSelected && filled) ? sHighlightFgColor : sFgColor);
  998. F32 right_x;
  999. F32 y = (F32)getRect().getHeight() - sFontLineHeight - (F32)TEXT_PAD;
  1000. static LLCachedControl<F32> message_wait_time(gSavedSettings,
  1001. "FolderLoadingMessageWaitTime");
  1002. if (mIsLoading &&
  1003. mTimeSinceRequestStart.getElapsedTimeF32() >= message_wait_time)
  1004. {
  1005. fontp->render(sLoadingStr, 0, text_left, y, sSearchStatusColor,
  1006. LLFontGL::LEFT, LLFontGL::BOTTOM, style, S32_MAX,
  1007. S32_MAX, &right_x, false);
  1008. text_left = right_x;
  1009. }
  1010. fontp->render(mWLabel, 0, text_left, y, color, LLFontGL::LEFT,
  1011. LLFontGL::BOTTOM, style, S32_MAX, S32_MAX, &right_x,
  1012. false);
  1013. if (!mLabelSuffix.empty())
  1014. {
  1015. fontp->render(mWLabelSuffix, 0, right_x, y, sSuffixColor,
  1016. LLFontGL::LEFT, LLFontGL::BOTTOM, style, S32_MAX,
  1017. S32_MAX, &right_x, false);
  1018. }
  1019. if (mStringMatchOffset != std::string::npos)
  1020. {
  1021. // Do not draw backgrounds for zero-length strings
  1022. std::string combined_string = mLabel + mLabelSuffix;
  1023. S32 filter_string_length = mRoot->getFilterSubString().size();
  1024. std::string combined_string_upper = combined_string;
  1025. LLStringUtil::toUpper(combined_string_upper);
  1026. if (filter_string_length > 0 && (mRoot->getSearchType() & 1) &&
  1027. combined_string_upper.find(mRoot->getFilterSubString()) == mStringMatchOffset)
  1028. {
  1029. S32 left = ll_round(text_left) +
  1030. fontp->getWidth(combined_string, 0,
  1031. mStringMatchOffset) - 1;
  1032. S32 right = left +
  1033. fontp->getWidth(combined_string, mStringMatchOffset,
  1034. filter_string_length) + 2;
  1035. S32 bottom = llfloor(getRect().getHeight() - sFontLineHeight - 3);
  1036. S32 top = getRect().getHeight();
  1037. LLRect box_rect(left, top, right, bottom);
  1038. LLUIImage::sRoundedSquare->draw(box_rect, sFilterBGColor);
  1039. F32 match_string_left = text_left +
  1040. fontp->getWidthF32(combined_string, 0,
  1041. mStringMatchOffset);
  1042. F32 y = (F32)getRect().getHeight() - sFontLineHeight -
  1043. (F32)TEXT_PAD;
  1044. fontp->renderUTF8(combined_string, mStringMatchOffset,
  1045. match_string_left, y, sFilterTextColor,
  1046. LLFontGL::LEFT, LLFontGL::BOTTOM, style,
  1047. filter_string_length, S32_MAX, &right_x,
  1048. false);
  1049. }
  1050. }
  1051. }
  1052. if (sDebugRects)
  1053. {
  1054. drawDebugRect();
  1055. }
  1056. }
  1057. //-----------------------------------------------------------------------------
  1058. // LLFolderViewFolder class
  1059. //-----------------------------------------------------------------------------
  1060. // Default constructor
  1061. LLFolderViewFolder::LLFolderViewFolder(const std::string& name,
  1062. LLUIImagePtr icon,
  1063. LLFolderView* root,
  1064. LLFolderViewEventListener* listener)
  1065. : LLFolderViewItem(name, icon, 0, root, listener), // 0 = no create time
  1066. mNumDescendantsSelected(0),
  1067. mIsOpen(false),
  1068. mRegisterLastOpen(true),
  1069. mForceFetched(false),
  1070. mCurHeight(0.f),
  1071. mTargetHeight(0.f),
  1072. mAutoOpenCountdown(0.f),
  1073. mSubtreeCreationDate(0),
  1074. mAmTrash(-1),
  1075. mAmCOF(-1),
  1076. mAmMarket(-1),
  1077. mLastArrangeGeneration(-1),
  1078. mLastCalculatedWidth(0),
  1079. mCompletedFilterGeneration(-1),
  1080. mMostFilteredDescendantGeneration(-1)
  1081. {
  1082. mType = std::string("(folder)");
  1083. }
  1084. //virtual
  1085. LLFolderViewFolder::~LLFolderViewFolder()
  1086. {
  1087. // The LLView base class takes care of object destruction. make sure that
  1088. // we do not have mouse or keyboard focus
  1089. gFocusMgr.releaseFocusIfNeeded(this); // Calls onCommit()
  1090. }
  1091. // Returns true on success
  1092. //virtual
  1093. bool LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder,
  1094. LLFolderView* root)
  1095. {
  1096. if (!folder || !root || !getListener())
  1097. {
  1098. return false;
  1099. }
  1100. mParentFolder = folder;
  1101. root->addItemID(getListener()->getUUID(), this);
  1102. return folder->addFolder(this);
  1103. }
  1104. // Finds width and height of this object and its children. Also makes sure that
  1105. // this view and its children are the right size.
  1106. //virtual
  1107. S32 LLFolderViewFolder::arrange(S32* width, S32* height, S32 filter_generation)
  1108. {
  1109. mHasVisibleChildren = hasFilteredDescendants(filter_generation);
  1110. LLInventoryFilter::EFolderShow show_folder_state =
  1111. getRoot()->getShowFolderState();
  1112. // Calculate height as a single item (without any children), and reshapes
  1113. // rectangle to match
  1114. LLFolderViewItem::arrange(width, height, filter_generation);
  1115. // Clamp existing animated height so as to never get smaller than a single
  1116. // item
  1117. mCurHeight = llmax((F32)*height, mCurHeight);
  1118. // Initialize running height value as height of single item in case we have
  1119. // no children
  1120. *height = getItemHeight();
  1121. F32 running_height = (F32)*height;
  1122. F32 target_height = (F32)*height;
  1123. // Are my children visible ?
  1124. if (needsArrange())
  1125. {
  1126. // Set last arrange generation first, in case children are animating
  1127. // and need to be arranged again
  1128. mLastArrangeGeneration = mRoot->getArrangeGeneration();
  1129. if (mIsOpen)
  1130. {
  1131. // Add sizes of children
  1132. S32 parent_item_height = getRect().getHeight();
  1133. for (folders_t::iterator fit = mFolders.begin(),
  1134. end = mFolders.end();
  1135. fit != end; ++fit)
  1136. {
  1137. LLFolderViewFolder* folderp = (*fit);
  1138. bool visible =
  1139. show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS ||
  1140. (folderp->getFiltered(filter_generation) ||
  1141. // Passed filter or has descendants that passed filter
  1142. folderp->hasFilteredDescendants(filter_generation));
  1143. folderp->setVisible(visible);
  1144. if (!visible)
  1145. {
  1146. continue;
  1147. }
  1148. S32 child_width = *width;
  1149. S32 child_height = 0;
  1150. S32 child_top = parent_item_height - ll_roundp(running_height);
  1151. target_height += folderp->arrange(&child_width, &child_height,
  1152. filter_generation);
  1153. running_height += (F32)child_height;
  1154. *width = llmax(*width, child_width);
  1155. folderp->setOrigin(0,
  1156. child_top - folderp->getRect().getHeight());
  1157. }
  1158. for (items_t::iterator iit = mItems.begin(), end = mItems.end();
  1159. iit != end; ++iit)
  1160. {
  1161. LLFolderViewItem* itemp = *iit;
  1162. bool visible = itemp->getFiltered(filter_generation);
  1163. itemp->setVisible(visible);
  1164. if (!visible)
  1165. {
  1166. continue;
  1167. }
  1168. S32 child_width = *width;
  1169. S32 child_height = 0;
  1170. S32 child_top = parent_item_height - ll_roundp(running_height);
  1171. target_height += itemp->arrange(&child_width, &child_height,
  1172. filter_generation);
  1173. // Do not change width, as this item is as wide as its parent
  1174. // folder by construction
  1175. itemp->reshape(itemp->getRect().getWidth(), child_height);
  1176. running_height += (F32)child_height;
  1177. *width = llmax(*width, child_width);
  1178. itemp->setOrigin(0, child_top - itemp->getRect().getHeight());
  1179. }
  1180. }
  1181. mTargetHeight = target_height;
  1182. // Cache this width so next time we can just return it
  1183. mLastCalculatedWidth = *width;
  1184. }
  1185. else
  1186. {
  1187. // Just use existing width
  1188. *width = mLastCalculatedWidth;
  1189. }
  1190. // Animate current height towards target height
  1191. if (fabsf(mCurHeight - mTargetHeight) > 1.f)
  1192. {
  1193. mCurHeight =
  1194. lerp(mCurHeight, mTargetHeight,
  1195. LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT
  1196. : FOLDER_CLOSE_TIME_CONSTANT));
  1197. requestArrange();
  1198. // Hide child elements that fall out of current animated height
  1199. for (folders_t::iterator iter = mFolders.begin();
  1200. iter != mFolders.end(); )
  1201. {
  1202. folders_t::iterator fit = iter++;
  1203. // Number of pixels that bottom of folder label is from top of
  1204. // parent folder
  1205. if (getRect().getHeight() - (*fit)->getRect().mTop +
  1206. (*fit)->getItemHeight() > ll_roundp(mCurHeight) +
  1207. MAX_FOLDER_ITEM_OVERLAP)
  1208. {
  1209. // Hide if beyond current folder height
  1210. (*fit)->setVisible(false);
  1211. }
  1212. }
  1213. for (items_t::iterator iter = mItems.begin();
  1214. iter != mItems.end(); )
  1215. {
  1216. items_t::iterator iit = iter++;
  1217. // Number of pixels that bottom of item label is from top of parent
  1218. // folder
  1219. if (getRect().getHeight() - (*iit)->getRect().mBottom
  1220. > ll_roundp(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
  1221. {
  1222. (*iit)->setVisible(false);
  1223. }
  1224. }
  1225. }
  1226. else
  1227. {
  1228. mCurHeight = mTargetHeight;
  1229. }
  1230. // Do not change width as this item is already as wide as its parent folder
  1231. reshape(getRect().getWidth(), ll_roundp(mCurHeight));
  1232. // Pass current height value back to parent
  1233. *height = ll_roundp(mCurHeight);
  1234. return ll_roundp(mTargetHeight);
  1235. }
  1236. bool LLFolderViewFolder::needsArrange()
  1237. {
  1238. return mLastArrangeGeneration < mRoot->getArrangeGeneration();
  1239. }
  1240. void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation,
  1241. bool recurse_up)
  1242. {
  1243. mMostFilteredDescendantGeneration =
  1244. llmin(mMostFilteredDescendantGeneration, generation);
  1245. mCompletedFilterGeneration = generation;
  1246. // Only aggregate up if we are a lower (older) value
  1247. if (recurse_up && mParentFolder &&
  1248. generation < mParentFolder->getCompletedFilterGeneration())
  1249. {
  1250. mParentFolder->setCompletedFilterGeneration(generation, true);
  1251. }
  1252. }
  1253. //virtual
  1254. void LLFolderViewFolder::filter(LLInventoryFilter& filter)
  1255. {
  1256. S32 filter_generation = filter.getCurrentGeneration();
  1257. // If failed to pass filter newer than must_pass_generation you will
  1258. // automatically fail this time, so we only check against items that have
  1259. // passed the filter
  1260. S32 must_pass_generation = filter.getMustPassGeneration();
  1261. // If we have already been filtered against this generation, skip out
  1262. if (getCompletedFilterGeneration() >= filter_generation)
  1263. {
  1264. return;
  1265. }
  1266. // Filter folder itself
  1267. if (getLastFilterGeneration() < filter_generation)
  1268. {
  1269. // Folder has been compared to a valid precursor filter...
  1270. if (getLastFilterGeneration() >= must_pass_generation &&
  1271. // ... and did not pass the filter.
  1272. !mFiltered)
  1273. {
  1274. // Go ahead and flag this folder as done
  1275. mLastFilterGeneration = filter_generation;
  1276. }
  1277. else
  1278. {
  1279. // Filter self only on first pass through
  1280. LLFolderViewItem::filter(filter);
  1281. }
  1282. }
  1283. // All descendants have been filtered later than must pass generation but
  1284. // none passed
  1285. if (getCompletedFilterGeneration() >= must_pass_generation &&
  1286. !hasFilteredDescendants(must_pass_generation))
  1287. {
  1288. // Do not traverse children if we've already filtered them since
  1289. // must_pass_generation and came back with nothing
  1290. return;
  1291. }
  1292. // We entered here with at least one filter iteration left check to see if
  1293. // we have any more before continuing on to children
  1294. if (filter.getFilterCount() < 0)
  1295. {
  1296. return;
  1297. }
  1298. // When applying a filter, matching folders get their contents downloaded
  1299. // first
  1300. if (filter.isNotDefault() &&
  1301. getFiltered(filter.getMinRequiredGeneration()) &&
  1302. (mListener && !gInventory.isCategoryComplete(mListener->getUUID())))
  1303. {
  1304. LLInventoryModelFetch::getInstance()->start(mListener->getUUID());
  1305. }
  1306. // Now query children
  1307. for (folders_t::iterator iter = mFolders.begin();
  1308. iter != mFolders.end(); )
  1309. {
  1310. folders_t::iterator fit = iter++;
  1311. // Have we run out of iterations this frame ?
  1312. if (filter.getFilterCount() < 0)
  1313. {
  1314. break;
  1315. }
  1316. // mMostFilteredDescendantGeneration might have been reset in which
  1317. // case we need to update it even for folders that do not need to be
  1318. // filtered anymore
  1319. if ((*fit)->getCompletedFilterGeneration() >= filter_generation)
  1320. {
  1321. // Track latest generation to pass any child items
  1322. if ((*fit)->getFiltered() ||
  1323. (*fit)->hasFilteredDescendants(filter.getMinRequiredGeneration()))
  1324. {
  1325. mMostFilteredDescendantGeneration = filter_generation;
  1326. if (mRoot->needsAutoSelect())
  1327. {
  1328. (*fit)->setOpenArrangeRecursively(true);
  1329. }
  1330. }
  1331. // Just skip it, it has already been filtered
  1332. continue;
  1333. }
  1334. // Update this folders filter status (and children)
  1335. (*fit)->filter(filter);
  1336. // Track latest generation to pass any child items
  1337. if ((*fit)->getFiltered() ||
  1338. (*fit)->hasFilteredDescendants(filter_generation))
  1339. {
  1340. mMostFilteredDescendantGeneration = filter_generation;
  1341. if (mRoot->needsAutoSelect())
  1342. {
  1343. (*fit)->setOpenArrangeRecursively(true);
  1344. }
  1345. }
  1346. }
  1347. for (items_t::iterator iter = mItems.begin();
  1348. iter != mItems.end(); )
  1349. {
  1350. items_t::iterator iit = iter++;
  1351. if (filter.getFilterCount() < 0)
  1352. {
  1353. break;
  1354. }
  1355. if ((*iit)->getLastFilterGeneration() >= filter_generation)
  1356. {
  1357. if ((*iit)->getFiltered())
  1358. {
  1359. mMostFilteredDescendantGeneration = filter_generation;
  1360. }
  1361. continue;
  1362. }
  1363. if ((*iit)->getLastFilterGeneration() >= must_pass_generation &&
  1364. !(*iit)->getFiltered(must_pass_generation))
  1365. {
  1366. // Failed to pass an earlier filter that was a subset of the
  1367. // current one go ahead and flag this item as done
  1368. (*iit)->setFiltered(false, filter_generation);
  1369. continue;
  1370. }
  1371. (*iit)->filter(filter);
  1372. if ((*iit)->getFiltered(filter.getMinRequiredGeneration()))
  1373. {
  1374. mMostFilteredDescendantGeneration = filter_generation;
  1375. }
  1376. }
  1377. // If we did not use all filter iterations that means we filtered all of
  1378. // our descendants instead of exhausting the filter count for this frame.
  1379. if (filter.getFilterCount() > 0)
  1380. {
  1381. // Flag this folder as having completed filter pass for all descendants
  1382. // with false = do not recurse up to root
  1383. setCompletedFilterGeneration(filter_generation, false);
  1384. }
  1385. }
  1386. //virtual
  1387. void LLFolderViewFolder::setFiltered(bool filtered, S32 filter_generation)
  1388. {
  1389. // If this folder is now filtered, but was not before (it just passed).
  1390. if (filtered && !mFiltered)
  1391. {
  1392. // Reset current height, because last time we drew it it might have had
  1393. // more visible items than now.
  1394. mCurHeight = 0.f;
  1395. }
  1396. LLFolderViewItem::setFiltered(filtered, filter_generation);
  1397. }
  1398. //virtual
  1399. void LLFolderViewFolder::dirtyFilter()
  1400. {
  1401. // We are a folder, so invalidate our completed generation
  1402. setCompletedFilterGeneration(-1, false);
  1403. LLFolderViewItem::dirtyFilter();
  1404. }
  1405. bool LLFolderViewFolder::hasFilteredDescendants(S32 filter_generation)
  1406. {
  1407. static LLCachedControl<bool> hide_cof(gSavedSettings,
  1408. "HideCurrentOutfitFolder");
  1409. if (hide_cof && isCOF())
  1410. {
  1411. return false;
  1412. }
  1413. static LLCachedControl<bool> hide_mp(gSavedSettings,
  1414. "HideMarketplaceFolder");
  1415. if (hide_mp && isMarketplace())
  1416. {
  1417. return false;
  1418. }
  1419. return mMostFilteredDescendantGeneration >= filter_generation;
  1420. }
  1421. bool LLFolderViewFolder::hasFilteredDescendants()
  1422. {
  1423. return hasFilteredDescendants(mRoot->getFilter()->getCurrentGeneration());
  1424. }
  1425. void LLFolderViewFolder::recursiveIncrementNumDescendantsSelected(S32 increment)
  1426. {
  1427. LLFolderViewFolder* parent_folder = this;
  1428. do
  1429. {
  1430. parent_folder->mNumDescendantsSelected += increment;
  1431. // Make sure we do not have negative values.
  1432. llassert(parent_folder->mNumDescendantsSelected >= 0);
  1433. parent_folder = parent_folder->getParentFolder();
  1434. }
  1435. while (parent_folder);
  1436. }
  1437. // Passes selection information on to children and record selection
  1438. // information if necessary.
  1439. //virtual
  1440. bool LLFolderViewFolder::setSelection(LLFolderViewItem* selection,
  1441. bool openitem,
  1442. bool take_keyboard_focus)
  1443. {
  1444. bool rv = false;
  1445. if (selection == this)
  1446. {
  1447. if (!isSelected())
  1448. {
  1449. selectItem();
  1450. }
  1451. if (mListener)
  1452. {
  1453. mListener->selectItem();
  1454. }
  1455. rv = true;
  1456. }
  1457. else
  1458. {
  1459. if (isSelected())
  1460. {
  1461. deselectItem();
  1462. }
  1463. rv = false;
  1464. }
  1465. bool child_selected = false;
  1466. for (folders_t::iterator iter = mFolders.begin();
  1467. iter != mFolders.end(); )
  1468. {
  1469. folders_t::iterator fit = iter++;
  1470. if ((*fit)->setSelection(selection, openitem, take_keyboard_focus))
  1471. {
  1472. rv = true;
  1473. child_selected = true;
  1474. }
  1475. }
  1476. for (items_t::iterator iter = mItems.begin();
  1477. iter != mItems.end(); )
  1478. {
  1479. items_t::iterator iit = iter++;
  1480. if ((*iit)->setSelection(selection, openitem, take_keyboard_focus))
  1481. {
  1482. rv = true;
  1483. child_selected = true;
  1484. }
  1485. }
  1486. if (openitem && child_selected)
  1487. {
  1488. setOpenArrangeRecursively(true);
  1489. }
  1490. return rv;
  1491. }
  1492. // This method is used to change the selection of an item. Recursively traverse
  1493. // all children; if 'selection' is 'this' then change the select status if
  1494. // necessary. Returns true if the selection state of this folder or of a child
  1495. // was changed.
  1496. bool LLFolderViewFolder::changeSelection(LLFolderViewItem* selection,
  1497. bool selected)
  1498. {
  1499. bool rv = false;
  1500. if (selection == this)
  1501. {
  1502. if (isSelected() != selected)
  1503. {
  1504. rv = true;
  1505. if (selected)
  1506. {
  1507. selectItem();
  1508. }
  1509. else
  1510. {
  1511. deselectItem();
  1512. }
  1513. }
  1514. if (mListener && selected)
  1515. {
  1516. mListener->selectItem();
  1517. }
  1518. }
  1519. for (folders_t::iterator iter = mFolders.begin();
  1520. iter != mFolders.end(); )
  1521. {
  1522. folders_t::iterator fit = iter++;
  1523. if ((*fit)->changeSelection(selection, selected))
  1524. {
  1525. rv = true;
  1526. }
  1527. }
  1528. for (items_t::iterator iter = mItems.begin();
  1529. iter != mItems.end(); )
  1530. {
  1531. items_t::iterator iit = iter++;
  1532. if ((*iit)->changeSelection(selection, selected))
  1533. {
  1534. rv = true;
  1535. }
  1536. }
  1537. return rv;
  1538. }
  1539. void LLFolderViewFolder::extendSelection(LLFolderViewItem* selection,
  1540. LLFolderViewItem* last_selected,
  1541. std::vector<LLFolderViewItem*>& selected_items)
  1542. {
  1543. // Pass on to child folders first
  1544. for (folders_t::iterator iter = mFolders.begin();
  1545. iter != mFolders.end(); )
  1546. {
  1547. folders_t::iterator fit = iter++;
  1548. (*fit)->extendSelection(selection, last_selected, selected_items);
  1549. }
  1550. // Handle selection of our immediate children...
  1551. bool reverse_select = false;
  1552. bool found_last_selected = false;
  1553. bool found_selection = false;
  1554. std::vector<LLFolderViewItem*> items_to_select;
  1555. LLFolderViewItem* item;
  1556. // ... Folders first...
  1557. for (folders_t::iterator iter = mFolders.begin();
  1558. iter != mFolders.end(); )
  1559. {
  1560. folders_t::iterator fit = iter++;
  1561. item = (*fit);
  1562. if (item == selection)
  1563. {
  1564. found_selection = true;
  1565. }
  1566. else if (item == last_selected)
  1567. {
  1568. found_last_selected = true;
  1569. if (found_selection)
  1570. {
  1571. reverse_select = true;
  1572. }
  1573. }
  1574. if (found_selection || found_last_selected)
  1575. {
  1576. // Deselect currently selected items so they can be pushed back on
  1577. // queue
  1578. if (item->isSelected())
  1579. {
  1580. item->changeSelection(item, false);
  1581. }
  1582. items_to_select.push_back(item);
  1583. }
  1584. if (found_selection && found_last_selected)
  1585. {
  1586. break;
  1587. }
  1588. }
  1589. if (!(found_selection && found_last_selected))
  1590. {
  1591. // ,,, Then items
  1592. for (items_t::iterator iter = mItems.begin();
  1593. iter != mItems.end(); )
  1594. {
  1595. items_t::iterator iit = iter++;
  1596. item = (*iit);
  1597. if (item == selection)
  1598. {
  1599. found_selection = true;
  1600. }
  1601. else if (item == last_selected)
  1602. {
  1603. found_last_selected = true;
  1604. if (found_selection)
  1605. {
  1606. reverse_select = true;
  1607. }
  1608. }
  1609. if (found_selection || found_last_selected)
  1610. {
  1611. // Deselect currently selected items so they can be pushed back
  1612. // on queue
  1613. if (item->isSelected())
  1614. {
  1615. item->changeSelection(item, false);
  1616. }
  1617. items_to_select.push_back(item);
  1618. }
  1619. if (found_selection && found_last_selected)
  1620. {
  1621. break;
  1622. }
  1623. }
  1624. }
  1625. if (found_last_selected && found_selection)
  1626. {
  1627. // We have a complete selection inside this folder
  1628. for (S32 index = reverse_select ? items_to_select.size() - 1 : 0;
  1629. reverse_select ? index >= 0 : index < (S32)items_to_select.size();
  1630. reverse_select ? index-- : index++)
  1631. {
  1632. LLFolderViewItem* item = items_to_select[index];
  1633. if (item->changeSelection(item, true))
  1634. {
  1635. selected_items.push_back(item);
  1636. }
  1637. }
  1638. }
  1639. else if (found_selection)
  1640. {
  1641. // Last selection was not in this folder....go ahead and select just
  1642. // the new item
  1643. if (selection->changeSelection(selection, true))
  1644. {
  1645. selected_items.push_back(selection);
  1646. }
  1647. }
  1648. }
  1649. void LLFolderViewFolder::recursiveDeselect(bool deselect_self)
  1650. {
  1651. if (isSelected() && deselect_self)
  1652. {
  1653. deselectItem();
  1654. }
  1655. if (0 == mNumDescendantsSelected)
  1656. {
  1657. return;
  1658. }
  1659. // Deselect all items in this folder.
  1660. for (items_t::iterator iter = mItems.begin();
  1661. iter != mItems.end(); )
  1662. {
  1663. items_t::iterator iit = iter++;
  1664. LLFolderViewItem* item = *iit;
  1665. if (item && item->isSelected())
  1666. {
  1667. item->deselectItem();
  1668. }
  1669. }
  1670. // Recursively deselect all folders in this folder.
  1671. for (folders_t::iterator iter = mFolders.begin();
  1672. iter != mFolders.end(); )
  1673. {
  1674. folders_t::iterator fit = iter++;
  1675. LLFolderViewFolder* folder = *fit;
  1676. if (folder)
  1677. {
  1678. folder->recursiveDeselect(true);
  1679. }
  1680. }
  1681. }
  1682. void LLFolderViewFolder::destroyView()
  1683. {
  1684. if (!getRoot()) return; // Paranoia
  1685. for (items_t::iterator iter = mItems.begin();
  1686. iter != mItems.end(); )
  1687. {
  1688. items_t::iterator iit = iter++;
  1689. LLFolderViewItem* item = *iit;
  1690. if (item && item->getListener())
  1691. {
  1692. getRoot()->removeItemID(item->getListener()->getUUID());
  1693. }
  1694. }
  1695. std::for_each(mItems.begin(), mItems.end(), DeletePointer());
  1696. mItems.clear();
  1697. while (!mFolders.empty())
  1698. {
  1699. LLFolderViewFolder* folderp = mFolders.back();
  1700. if (folderp)
  1701. {
  1702. folderp->destroyView(); // Removes entry from mFolders
  1703. }
  1704. }
  1705. deleteAllChildren();
  1706. if (mParentFolder)
  1707. {
  1708. mParentFolder->removeView(this);
  1709. }
  1710. }
  1711. // Remove the specified item (and any children) if possible. Return true if the
  1712. // item was deleted.
  1713. bool LLFolderViewFolder::removeItem(LLFolderViewItem* item)
  1714. {
  1715. if (item->remove())
  1716. {
  1717. #if 0 // RN: this seem unneccessary as remove() moves to trash
  1718. removeView(item);
  1719. #endif
  1720. return true;
  1721. }
  1722. return false;
  1723. }
  1724. // Simply remove the view (and any children). Do not bother telling the
  1725. // listeners
  1726. void LLFolderViewFolder::removeView(LLFolderViewItem* item)
  1727. {
  1728. if (!item || item->getParentFolder() != this)
  1729. {
  1730. return;
  1731. }
  1732. // Deselect without traversing hierarchy
  1733. if (item->isSelected())
  1734. {
  1735. item->deselectItem();
  1736. }
  1737. getRoot()->removeFromSelectionList(item);
  1738. extractItem(item);
  1739. delete item;
  1740. }
  1741. // Removes the specified item from the folder, but does not delete it.
  1742. void LLFolderViewFolder::extractItem(LLFolderViewItem* item)
  1743. {
  1744. items_t::iterator it = std::find(mItems.begin(), mItems.end(), item);
  1745. if (it == mItems.end())
  1746. {
  1747. // This is an evil downcast. However, it is only doing pointer
  1748. // comparison to find if (which it should be) the item is in the
  1749. // container, so it is pretty safe.
  1750. LLFolderViewFolder* f = static_cast<LLFolderViewFolder*>(item);
  1751. folders_t::iterator ft;
  1752. ft = std::find(mFolders.begin(), mFolders.end(), f);
  1753. if (ft != mFolders.end())
  1754. {
  1755. if (*ft && (*ft)->numSelected())
  1756. {
  1757. recursiveIncrementNumDescendantsSelected(-(*ft)->numSelected());
  1758. }
  1759. mFolders.erase(ft);
  1760. }
  1761. }
  1762. else
  1763. {
  1764. if (*it && (*it)->isSelected())
  1765. {
  1766. recursiveIncrementNumDescendantsSelected(-1);
  1767. }
  1768. mItems.erase(it);
  1769. }
  1770. // Item has been removed, need to update filter
  1771. dirtyFilter();
  1772. // Because an item is going away regardless of filter status, force
  1773. // rearrange
  1774. requestArrange();
  1775. if (getRoot() && item->getListener())
  1776. {
  1777. getRoot()->removeItemID(item->getListener()->getUUID());
  1778. }
  1779. removeChild(item);
  1780. }
  1781. // This function is called by a child that needs to be resorted.
  1782. // This is only called for renaming an object because it would not work for
  1783. // date.
  1784. void LLFolderViewFolder::resort(LLFolderViewItem* item)
  1785. {
  1786. mItems.sort(mSortFunction);
  1787. mFolders.sort(mSortFunction);
  1788. }
  1789. bool LLFolderViewFolder::isTrash() const
  1790. {
  1791. if (mListener && mAmTrash == -1)
  1792. {
  1793. const LLUUID& trash_id =
  1794. gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH, false);
  1795. if (trash_id.notNull())
  1796. {
  1797. mAmTrash = mListener->getUUID() == trash_id ? 1 : 0;
  1798. }
  1799. }
  1800. return mAmTrash == 1;
  1801. }
  1802. bool LLFolderViewFolder::isCOF() const
  1803. {
  1804. if (mListener && mAmCOF == -1)
  1805. {
  1806. const LLUUID cof_id = LLAppearanceMgr::getCOF();
  1807. if (cof_id.notNull())
  1808. {
  1809. mAmCOF = mListener->getUUID() == cof_id ? 1 : 0;
  1810. }
  1811. }
  1812. return mAmCOF == 1;
  1813. }
  1814. bool LLFolderViewFolder::isMarketplace() const
  1815. {
  1816. if (mListener && mAmMarket == -1)
  1817. {
  1818. const LLUUID& market_id = LLMarketplace::getMPL();
  1819. if (market_id.notNull())
  1820. {
  1821. mAmMarket = mListener->getUUID() == market_id ? 1 : 0;
  1822. }
  1823. }
  1824. return mAmMarket == 1;
  1825. }
  1826. void LLFolderViewFolder::sortBy(U32 order)
  1827. {
  1828. if (!mSortFunction.updateSort(order))
  1829. {
  1830. // No changes.
  1831. return;
  1832. }
  1833. // Propegate this change to sub folders
  1834. for (folders_t::iterator iter = mFolders.begin(); iter != mFolders.end(); )
  1835. {
  1836. folders_t::iterator fit = iter++;
  1837. (*fit)->sortBy(order);
  1838. }
  1839. mFolders.sort(mSortFunction);
  1840. mItems.sort(mSortFunction);
  1841. if (order & LLInventoryFilter::SO_DATE)
  1842. {
  1843. time_t latest = 0;
  1844. if (!mItems.empty())
  1845. {
  1846. LLFolderViewItem* item = *(mItems.begin());
  1847. latest = item->getCreationDate();
  1848. }
  1849. if (!mFolders.empty())
  1850. {
  1851. LLFolderViewFolder* folder = *(mFolders.begin());
  1852. if (folder->getCreationDate() > latest)
  1853. {
  1854. latest = folder->getCreationDate();
  1855. }
  1856. }
  1857. mSubtreeCreationDate = latest;
  1858. }
  1859. }
  1860. void LLFolderViewFolder::setItemSortOrder(U32 ordering)
  1861. {
  1862. if (mSortFunction.updateSort(ordering))
  1863. {
  1864. for (folders_t::iterator iter = mFolders.begin();
  1865. iter != mFolders.end(); )
  1866. {
  1867. folders_t::iterator fit = iter++;
  1868. (*fit)->setItemSortOrder(ordering);
  1869. }
  1870. mFolders.sort(mSortFunction);
  1871. mItems.sort(mSortFunction);
  1872. }
  1873. }
  1874. EInventorySortGroup LLFolderViewFolder::getSortGroup() const
  1875. {
  1876. if (isTrash())
  1877. {
  1878. return SG_TRASH_FOLDER;
  1879. }
  1880. if (mListener && mListener->getPreferredType() != LLFolderType::FT_NONE)
  1881. {
  1882. return SG_SYSTEM_FOLDER;
  1883. }
  1884. return SG_NORMAL_FOLDER;
  1885. }
  1886. bool LLFolderViewFolder::isMovable()
  1887. {
  1888. if (mListener)
  1889. {
  1890. if (!mListener->isItemMovable())
  1891. {
  1892. return false;
  1893. }
  1894. for (items_t::iterator iter = mItems.begin(); iter != mItems.end(); )
  1895. {
  1896. items_t::iterator iit = iter++;
  1897. if (!(*iit)->isMovable())
  1898. {
  1899. return false;
  1900. }
  1901. }
  1902. for (folders_t::iterator iter = mFolders.begin();
  1903. iter != mFolders.end(); )
  1904. {
  1905. folders_t::iterator fit = iter++;
  1906. if (!(*fit)->isMovable())
  1907. {
  1908. return false;
  1909. }
  1910. }
  1911. }
  1912. return true;
  1913. }
  1914. bool LLFolderViewFolder::isRemovable()
  1915. {
  1916. if (mListener)
  1917. {
  1918. if (!(mListener->isItemRemovable()))
  1919. {
  1920. return false;
  1921. }
  1922. for (items_t::iterator iter = mItems.begin();
  1923. iter != mItems.end(); )
  1924. {
  1925. items_t::iterator iit = iter++;
  1926. if (!(*iit)->isRemovable())
  1927. {
  1928. return false;
  1929. }
  1930. }
  1931. for (folders_t::iterator iter = mFolders.begin();
  1932. iter != mFolders.end(); )
  1933. {
  1934. folders_t::iterator fit = iter++;
  1935. if (!(*fit)->isRemovable())
  1936. {
  1937. return false;
  1938. }
  1939. }
  1940. }
  1941. return true;
  1942. }
  1943. // This is an internal method used for adding items to folders.
  1944. //virtual
  1945. bool LLFolderViewFolder::addItem(LLFolderViewItem* item)
  1946. {
  1947. if (!item) return false; // Paranoia
  1948. items_t::iterator it = std::lower_bound(mItems.begin(), mItems.end(),
  1949. item, mSortFunction);
  1950. mItems.insert(it, item);
  1951. if (item->isSelected())
  1952. {
  1953. recursiveIncrementNumDescendantsSelected(1);
  1954. }
  1955. item->setRect(LLRect(0, 0, getRect().getWidth(), 0));
  1956. item->setVisible(false);
  1957. addChild(item);
  1958. item->dirtyFilter();
  1959. requestArrange();
  1960. return true;
  1961. }
  1962. // This is an internal method used for adding items to folders.
  1963. //virtual
  1964. bool LLFolderViewFolder::addFolder(LLFolderViewFolder* folder)
  1965. {
  1966. folders_t::iterator it = std::lower_bound(mFolders.begin(), mFolders.end(),
  1967. folder, mSortFunction);
  1968. mFolders.insert(it, folder);
  1969. if (folder->numSelected())
  1970. {
  1971. recursiveIncrementNumDescendantsSelected(folder->numSelected());
  1972. }
  1973. folder->setOrigin(0, 0);
  1974. folder->reshape(getRect().getWidth(), 0);
  1975. folder->setVisible(false);
  1976. addChild(folder);
  1977. folder->dirtyFilter();
  1978. // Rearrange all descendants too, as our indentation level might have
  1979. // changed
  1980. folder->requestArrange(true);
  1981. return true;
  1982. }
  1983. void LLFolderViewFolder::requestArrange(bool include_descendants)
  1984. {
  1985. mLastArrangeGeneration = -1;
  1986. // Flag all items up to root
  1987. if (mParentFolder)
  1988. {
  1989. mParentFolder->requestArrange();
  1990. }
  1991. if (include_descendants)
  1992. {
  1993. for (folders_t::iterator iter = mFolders.begin();
  1994. iter != mFolders.end();
  1995. ++iter)
  1996. {
  1997. (*iter)->requestArrange(true);
  1998. }
  1999. }
  2000. }
  2001. void LLFolderViewFolder::toggleOpen()
  2002. {
  2003. if (mRegisterLastOpen && !mIsOpen)
  2004. {
  2005. LLFolderViewEventListener* listenerp = getListener();
  2006. if (listenerp)
  2007. {
  2008. const LLUUID& id = listenerp->getUUID();
  2009. if (id.notNull())
  2010. {
  2011. sLastOpenId = id;
  2012. }
  2013. }
  2014. }
  2015. setOpen(!mIsOpen);
  2016. // *HACK: sadly, folders do not properly retain their thumbnails Id after a
  2017. // relog (the transmitted "inventory skeleton" does not have them, and when
  2018. // the transmitted data leads to discard the cached one, e.g. due to a
  2019. // version mismatch, we loose the thumbnail Id), so we need to refresh the
  2020. // folder data once after a relog; we do so by force-fetching (in a non
  2021. // recursive way) the contents of any newly opened folder. HB
  2022. if (!mForceFetched && mRoot->showThumbnails())
  2023. {
  2024. mForceFetched = true;
  2025. LLFolderViewEventListener* listenerp = getListener();
  2026. if (listenerp)
  2027. {
  2028. LLInventoryModelFetch::forceFetchFolder(listenerp->getUUID());
  2029. }
  2030. }
  2031. }
  2032. // Force a folder open or closed
  2033. void LLFolderViewFolder::setOpen(bool openitem)
  2034. {
  2035. setOpenArrangeRecursively(openitem);
  2036. }
  2037. void LLFolderViewFolder::setOpenArrangeRecursively(bool openitem,
  2038. ERecurseType recurse)
  2039. {
  2040. bool was_open = mIsOpen;
  2041. mIsOpen = openitem;
  2042. if (!was_open && openitem)
  2043. {
  2044. if (mListener)
  2045. {
  2046. mListener->openItem();
  2047. }
  2048. }
  2049. if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN)
  2050. {
  2051. for (folders_t::iterator iter = mFolders.begin();
  2052. iter != mFolders.end(); )
  2053. {
  2054. folders_t::iterator fit = iter++;
  2055. (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN);
  2056. }
  2057. }
  2058. if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN))
  2059. {
  2060. mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP);
  2061. }
  2062. if (was_open != mIsOpen)
  2063. {
  2064. requestArrange();
  2065. }
  2066. }
  2067. bool LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, bool drop,
  2068. EDragAndDropType c_type,
  2069. void* cargo_data,
  2070. EAcceptance* accept,
  2071. std::string& tooltip_msg)
  2072. {
  2073. bool accepted = mListener &&
  2074. mListener->dragOrDrop(mask, drop, c_type, cargo_data,
  2075. tooltip_msg);
  2076. if (accepted)
  2077. {
  2078. mDragAndDropTarget = true;
  2079. *accept = ACCEPT_YES_MULTI;
  2080. }
  2081. else
  2082. {
  2083. *accept = ACCEPT_NO;
  2084. }
  2085. LLFolderViewEventListener::dragOrDropTip(drop, tooltip_msg);
  2086. // Drag and drop to child item, so clear pending auto-opens
  2087. getRoot()->autoOpenTest(NULL);
  2088. return true;
  2089. }
  2090. void LLFolderViewFolder::openItem()
  2091. {
  2092. toggleOpen();
  2093. }
  2094. void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor)
  2095. {
  2096. functor.doFolder(this);
  2097. for (folders_t::iterator iter = mFolders.begin(); iter != mFolders.end(); )
  2098. {
  2099. folders_t::iterator fit = iter++;
  2100. (*fit)->applyFunctorRecursively(functor);
  2101. }
  2102. for (items_t::iterator iter = mItems.begin(); iter != mItems.end(); )
  2103. {
  2104. items_t::iterator iit = iter++;
  2105. functor.doItem((*iit));
  2106. }
  2107. }
  2108. void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
  2109. {
  2110. functor(mListener);
  2111. for (folders_t::iterator iter = mFolders.begin(); iter != mFolders.end(); )
  2112. {
  2113. folders_t::iterator fit = iter++;
  2114. (*fit)->applyListenerFunctorRecursively(functor);
  2115. }
  2116. for (items_t::iterator iter = mItems.begin(); iter != mItems.end(); )
  2117. {
  2118. items_t::iterator iit = iter++;
  2119. (*iit)->applyListenerFunctorRecursively(functor);
  2120. }
  2121. }
  2122. // LLView functionality
  2123. bool LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
  2124. EDragAndDropType cargo_type,
  2125. void* cargo_data,
  2126. EAcceptance* accept,
  2127. std::string& tooltip_msg)
  2128. {
  2129. LLFolderView* root_view = getRoot();
  2130. bool handled = false;
  2131. if (mIsOpen)
  2132. {
  2133. handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
  2134. cargo_data, accept,
  2135. tooltip_msg) != NULL;
  2136. }
  2137. if (!handled)
  2138. {
  2139. bool accepted = mListener &&
  2140. mListener->dragOrDrop(mask, drop, cargo_type,
  2141. cargo_data,
  2142. tooltip_msg);
  2143. if (accepted)
  2144. {
  2145. mDragAndDropTarget = true;
  2146. *accept = ACCEPT_YES_MULTI;
  2147. }
  2148. else
  2149. {
  2150. *accept = ACCEPT_NO;
  2151. }
  2152. LLFolderViewEventListener::dragOrDropTip(drop, tooltip_msg);
  2153. if (!drop && accepted)
  2154. {
  2155. root_view->autoOpenTest(this);
  2156. }
  2157. LL_DEBUGS("UserInput") << "dragAndDrop handled with: drop = "
  2158. << (drop ? "true" : "false") << " - accepted = "
  2159. << (accepted ? "true" : "false") << LL_ENDL;
  2160. }
  2161. return true;
  2162. }
  2163. bool LLFolderViewFolder::handleRightMouseDown(S32 x, S32 y, MASK mask)
  2164. {
  2165. bool handled = false;
  2166. // Fetch contents of this folder, as context menu can depend on contents
  2167. // still, user would have to open context menu again to see the changes
  2168. gInventory.fetchDescendentsOf(mListener->getUUID());
  2169. if (mIsOpen)
  2170. {
  2171. handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
  2172. }
  2173. if (!handled)
  2174. {
  2175. handled = LLFolderViewItem::handleRightMouseDown(x, y, mask);
  2176. }
  2177. return handled;
  2178. }
  2179. bool LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask)
  2180. {
  2181. bool handled = LLView::handleHover(x, y, mask);
  2182. if (!handled)
  2183. {
  2184. // This does not do child processing
  2185. handled = LLFolderViewItem::handleHover(x, y, mask);
  2186. }
  2187. #if 0
  2188. if (x < LEFT_INDENTATION + mIndentation &&
  2189. x > mIndentation - LEFT_PAD && y > getRect().getHeight() -)
  2190. {
  2191. gViewerWindowp->setCursor(UI_CURSOR_ARROW);
  2192. handled = true;
  2193. }
  2194. #endif
  2195. return handled;
  2196. }
  2197. bool LLFolderViewFolder::handleMouseDown(S32 x, S32 y, MASK mask)
  2198. {
  2199. bool handled = false;
  2200. if (mIsOpen)
  2201. {
  2202. handled = childrenHandleMouseDown(x, y, mask) != NULL;
  2203. }
  2204. if (!handled)
  2205. {
  2206. if (x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD)
  2207. {
  2208. toggleOpen();
  2209. handled = true;
  2210. }
  2211. else
  2212. {
  2213. // Do normal selection logic
  2214. handled = LLFolderViewItem::handleMouseDown(x, y, mask);
  2215. }
  2216. }
  2217. return handled;
  2218. }
  2219. bool LLFolderViewFolder::handleDoubleClick(S32 x, S32 y, MASK mask)
  2220. {
  2221. bool handled = false;
  2222. if (mIsOpen)
  2223. {
  2224. handled = childrenHandleDoubleClick(x, y, mask) != NULL;
  2225. }
  2226. if (!handled)
  2227. {
  2228. if (x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD)
  2229. {
  2230. // Do not select when user double-clicks plus sign so as not to
  2231. // contradict single-click behavior
  2232. toggleOpen();
  2233. }
  2234. else
  2235. {
  2236. setSelectionFromRoot(this, false);
  2237. toggleOpen();
  2238. }
  2239. handled = true;
  2240. }
  2241. return handled;
  2242. }
  2243. void LLFolderViewFolder::draw()
  2244. {
  2245. if (mAutoOpenCountdown != 0.f)
  2246. {
  2247. mControlLabelRotation = mAutoOpenCountdown * -90.f;
  2248. }
  2249. else if (mIsOpen)
  2250. {
  2251. mControlLabelRotation = lerp(mControlLabelRotation, -90.f,
  2252. LLCriticalDamp::getInterpolant(0.04f));
  2253. }
  2254. else
  2255. {
  2256. mControlLabelRotation = lerp(mControlLabelRotation, 0.f,
  2257. LLCriticalDamp::getInterpolant(0.025f));
  2258. }
  2259. bool possibly_has_children = false;
  2260. bool up_to_date = mListener && mListener->isUpToDate();
  2261. // We know we have children but have not fetched them (does not obey
  2262. // filter)
  2263. if (!up_to_date && mListener && mListener->hasChildren())
  2264. {
  2265. possibly_has_children = true;
  2266. }
  2267. bool loading = mIsOpen && possibly_has_children && !up_to_date;
  2268. if (loading && !mIsLoading)
  2269. {
  2270. // Measure how long we have been in the loading state
  2271. mTimeSinceRequestStart.reset();
  2272. }
  2273. mIsLoading = loading;
  2274. LLFolderViewItem::draw();
  2275. // Draw children if root folder, or any other folder that is open or
  2276. // animating to closed state
  2277. if (mIsOpen || mCurHeight != mTargetHeight || getRoot() == this)
  2278. {
  2279. LLView::draw();
  2280. }
  2281. }
  2282. time_t LLFolderViewFolder::getCreationDate() const
  2283. {
  2284. return llmax<time_t>(mCreationDate, mSubtreeCreationDate);
  2285. }
  2286. //virtual
  2287. bool LLFolderViewFolder::potentiallyVisible()
  2288. {
  2289. // Folder should be visible by its own filter status..
  2290. return LLFolderViewItem::potentiallyVisible() ||
  2291. // Or one or more of its descendants have passed the minimum filter
  2292. // requirement...
  2293. hasFilteredDescendants(mRoot->getFilter()->getMinRequiredGeneration()) ||
  2294. // Or not all of its descendants have been checked against minimum
  2295. // filter requirement
  2296. getCompletedFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration();
  2297. }
  2298. // This does prefix traversal, as folders are listed above their contents
  2299. LLFolderViewItem* LLFolderViewFolder::getNextFromChild(LLFolderViewItem* item,
  2300. bool include_children)
  2301. {
  2302. bool found_item = false;
  2303. LLFolderViewItem* result = NULL;
  2304. // When not starting from a given item, start at beginning
  2305. if (!item)
  2306. {
  2307. found_item = true;
  2308. }
  2309. // Find current item among children
  2310. folders_t::iterator fit = mFolders.begin();
  2311. folders_t::iterator fend = mFolders.end();
  2312. items_t::iterator iit = mItems.begin();
  2313. items_t::iterator iend = mItems.end();
  2314. // If not trivially starting at the beginning, we have to find the current
  2315. // item
  2316. if (!found_item)
  2317. {
  2318. // First, look among folders, since they are always above items
  2319. for ( ; fit != fend; ++fit)
  2320. {
  2321. if (item == (*fit))
  2322. {
  2323. found_item = true;
  2324. // If we are on downwards traversal
  2325. if (include_children && (*fit)->isOpen())
  2326. {
  2327. // Look for first descendant
  2328. return (*fit)->getNextFromChild(NULL, true);
  2329. }
  2330. // Otherwise advance to next folder
  2331. ++fit;
  2332. include_children = true;
  2333. break;
  2334. }
  2335. }
  2336. // Did not find in folders ? Check items...
  2337. if (!found_item)
  2338. {
  2339. for ( ; iit != iend; ++iit)
  2340. {
  2341. if (item == (*iit))
  2342. {
  2343. found_item = true;
  2344. // Point to next item
  2345. ++iit;
  2346. break;
  2347. }
  2348. }
  2349. }
  2350. }
  2351. if (!found_item)
  2352. {
  2353. // You should never call this method with an item that is not a child
  2354. // so we should always find something
  2355. llassert(false);
  2356. return NULL;
  2357. }
  2358. // At this point, either iit or fit point to a candidate "next" item if
  2359. // both are out of range, we need to punt up to our parent.
  2360. // Now, starting from found folder, continue through folders searching for
  2361. // next visible folder
  2362. while (fit != fend && !(*fit)->getVisible())
  2363. {
  2364. // Turn on downwards traversal for next folder
  2365. ++fit;
  2366. }
  2367. if (fit != fend)
  2368. {
  2369. result = *fit;
  2370. }
  2371. else
  2372. {
  2373. // Otherwise, scan for next visible item
  2374. while (iit != iend && !(*iit)->getVisible())
  2375. {
  2376. ++iit;
  2377. }
  2378. // Check to see if we have a valid item
  2379. if (iit != iend)
  2380. {
  2381. result = *iit;
  2382. }
  2383. }
  2384. if (!result && mParentFolder)
  2385. {
  2386. // If there are no siblings or children to go to, recurse up one level
  2387. // in the tree and skip children for this folder, as we've already
  2388. // discounted them
  2389. result = mParentFolder->getNextFromChild(this, false);
  2390. }
  2391. return result;
  2392. }
  2393. // This does postfix traversal, as folders are listed above their contents
  2394. LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild(LLFolderViewItem* item,
  2395. bool include_children)
  2396. {
  2397. bool found_item = false;
  2398. LLFolderViewItem* result = NULL;
  2399. // When not starting from a given item, start at end
  2400. if (!item)
  2401. {
  2402. found_item = true;
  2403. }
  2404. // Find current item among children
  2405. folders_t::reverse_iterator fit = mFolders.rbegin();
  2406. folders_t::reverse_iterator fend = mFolders.rend();
  2407. items_t::reverse_iterator iit = mItems.rbegin();
  2408. items_t::reverse_iterator iend = mItems.rend();
  2409. // If not trivially starting at the end, we have to find the current item.
  2410. if (!found_item)
  2411. {
  2412. // First, look among items, since they are always below the folders
  2413. for ( ; iit != iend; ++iit)
  2414. {
  2415. if (item == (*iit))
  2416. {
  2417. found_item = true;
  2418. // Point to next item
  2419. ++iit;
  2420. break;
  2421. }
  2422. }
  2423. // Did not find in items ? Check folders...
  2424. if (!found_item)
  2425. {
  2426. for ( ; fit != fend; ++fit)
  2427. {
  2428. if (item == (*fit))
  2429. {
  2430. found_item = true;
  2431. // Point to next folder
  2432. ++fit;
  2433. break;
  2434. }
  2435. }
  2436. }
  2437. }
  2438. if (!found_item)
  2439. {
  2440. // You should never call this method with an item that is not a child
  2441. // so we should always find something
  2442. llassert(false);
  2443. return NULL;
  2444. }
  2445. // At this point, either iit or fit point to a candidate "next" item if
  2446. // both are out of range, we need to punt up to our parent.
  2447. // Now, starting from found item, continue through items searching for next
  2448. // visible item
  2449. while (iit != iend && !(*iit)->getVisible())
  2450. {
  2451. ++iit;
  2452. }
  2453. if (iit != iend)
  2454. {
  2455. // We found an appropriate item
  2456. result = *iit;
  2457. }
  2458. else
  2459. {
  2460. // Otherwise, scan for next visible folder
  2461. while (fit != fend && !(*fit)->getVisible())
  2462. {
  2463. ++fit;
  2464. }
  2465. // Check to see if we have a valid folder
  2466. if (fit != fend)
  2467. {
  2468. // Try selecting child element of this folder
  2469. if ((*fit)->isOpen())
  2470. {
  2471. result = (*fit)->getPreviousFromChild(NULL);
  2472. }
  2473. else
  2474. {
  2475. result = *fit;
  2476. }
  2477. }
  2478. }
  2479. if (!result)
  2480. {
  2481. // If there are no siblings or children to go to, recurse up one level
  2482. // in the tree which gets back to this folder, which will only be
  2483. // visited if it is a valid, visible item
  2484. result = this;
  2485. }
  2486. return result;
  2487. }
  2488. //---------------------------------------------------------------------------
  2489. // Tells all folders in a folderview to sort their items
  2490. // (and only their items, not folders) by a certain function.
  2491. class LLSetItemSortFunction final : public LLFolderViewFunctor
  2492. {
  2493. public:
  2494. LL_INLINE LLSetItemSortFunction(U32 ordering)
  2495. : mSortOrder(ordering)
  2496. {
  2497. }
  2498. LL_INLINE ~LLSetItemSortFunction() override
  2499. {
  2500. }
  2501. // Set the sort order.
  2502. LL_INLINE void doFolder(LLFolderViewFolder* folder) override
  2503. {
  2504. folder->setItemSortOrder(mSortOrder);
  2505. }
  2506. // Do nothing.
  2507. LL_INLINE void doItem(LLFolderViewItem*) override {}
  2508. public:
  2509. U32 mSortOrder;
  2510. };
  2511. //---------------------------------------------------------------------------
  2512. //virtual
  2513. void LLOpenFolderByID::doFolder(LLFolderViewFolder* folder)
  2514. {
  2515. if (folder->getListener() && folder->getListener()->getUUID() == mID)
  2516. {
  2517. folder->setOpenArrangeRecursively(true,
  2518. LLFolderViewFolder::RECURSE_UP);
  2519. }
  2520. }
  2521. //---------------------------------------------------------------------------
  2522. // Tells all folders in a folderview to close themselves
  2523. // For efficiency, calls setOpenArrangeRecursively().
  2524. // The calling function must then call:
  2525. // LLFolderView* root = getRoot();
  2526. // if (root)
  2527. // {
  2528. // root->arrange(NULL, NULL);
  2529. // root->scrollToShowSelection();
  2530. // }
  2531. // to patch things up.
  2532. class LLCloseAllFoldersFunctor : public LLFolderViewFunctor
  2533. {
  2534. public:
  2535. LL_INLINE LLCloseAllFoldersFunctor(bool close)
  2536. : mOpen(!close)
  2537. {
  2538. }
  2539. LL_INLINE ~LLCloseAllFoldersFunctor() override {}
  2540. void doFolder(LLFolderViewFolder* folder) override;
  2541. void doItem(LLFolderViewItem* item) override;
  2542. public:
  2543. bool mOpen;
  2544. };
  2545. // Set the sort order.
  2546. void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
  2547. {
  2548. folder->setOpenArrangeRecursively(mOpen);
  2549. }
  2550. // Do nothing.
  2551. void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
  2552. {
  2553. }
  2554. //-----------------------------------------------------------------------------
  2555. // LLFolderView class
  2556. //-----------------------------------------------------------------------------
  2557. //static
  2558. LLFolderView* LLFolderView::getInstance(const LLUUID& id)
  2559. {
  2560. instances_map_t::iterator it = sInstances.find(id);
  2561. return it == sInstances.end() ? NULL : it->second;
  2562. }
  2563. LLFolderView::LLFolderView(const std::string& name, LLUIImagePtr folder_icon,
  2564. const LLRect& rect, const LLUUID& source_id,
  2565. LLPanel* parent_panel)
  2566. : LLFolderViewFolder(name, folder_icon, this, NULL),
  2567. mParentPanel(parent_panel),
  2568. mScrollContainer(NULL),
  2569. mContextMenuCreated(false),
  2570. mAllowMultiSelect(true),
  2571. mSourceID(source_id),
  2572. mFolderViewId(source_id),
  2573. mRenameItem(NULL),
  2574. mNeedsScroll(false),
  2575. mLastScrollItem(NULL),
  2576. mCanAutoSelect(true),
  2577. mNeedsAutoSelect(false),
  2578. mNeedsAutoRename(false),
  2579. // This gets overridden by a preference shortly after:
  2580. mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME),
  2581. mSearchType(1),
  2582. mFilter(name),
  2583. mShowSelectionContext(false),
  2584. mShowSingleSelection(false),
  2585. mArrangeGeneration(0),
  2586. mUserData(NULL),
  2587. mSelectCallback(NULL),
  2588. mSignalSelectCallback(0),
  2589. mMinWidth(0),
  2590. mHasCapture(false),
  2591. mDragAndDropThisFrame(false),
  2592. mShowThumbnails(false),
  2593. mGotLeftMouseClick(false)
  2594. {
  2595. if (mFolderViewId.isNull())
  2596. {
  2597. mFolderViewId.generate();
  2598. }
  2599. sInstances[mFolderViewId] = this;
  2600. LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(),
  2601. rect.mLeft + getRect().getWidth(), rect.mBottom);
  2602. setRect(rect);
  2603. reshape(rect.getWidth(), rect.getHeight());
  2604. mIsOpen = true; // This view is always open.
  2605. mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH);
  2606. mAutoOpenCandidate = NULL;
  2607. mAutoOpenTimer.stop();
  2608. mKeyboardSelection = false;
  2609. mIndentation = -LEFT_INDENTATION; // Children start at indentation 0
  2610. gIdleCallbacks.addFunction(idle, this);
  2611. // Clear label. Go ahead and render root folder as usual; just make sure
  2612. // the label ("Inventory Folder") never shows up
  2613. mLabel.clear();
  2614. mWLabel.clear();
  2615. mRenamer = new LLLineEditor("ren", getRect(), LLStringUtil::null, sFont,
  2616. DB_INV_ITEM_NAME_STR_LEN, commitRename, NULL,
  2617. NULL, this,
  2618. LLLineEditor::prevalidatePrintableNotPipe);
  2619. // Escape is handled by reverting the rename, not commiting it (default
  2620. // behaviour)
  2621. mRenamer->setCommitOnFocusLost(true);
  2622. mRenamer->setVisible(false);
  2623. addChild(mRenamer);
  2624. setTabStop(true);
  2625. }
  2626. //virtual
  2627. LLFolderView::~LLFolderView()
  2628. {
  2629. sInstances.erase(mFolderViewId);
  2630. // The release focus call can potentially call the scrollcontainer, which
  2631. // can potentially be called with a partly destroyed scollcontainer. Just
  2632. // null it out here, and no worries about calling into the invalid scroll
  2633. // container. Same with the renamer.
  2634. mScrollContainer = NULL;
  2635. mRenameItem = NULL;
  2636. mRenamer = NULL;
  2637. gFocusMgr.releaseFocusIfNeeded(this);
  2638. mAutoOpenItems.removeAllNodes();
  2639. gIdleCallbacks.deleteFunction(idle, this);
  2640. deleteViewByHandle(mPopupMenuHandle);
  2641. if (mRenamer == gFocusMgr.getTopCtrl())
  2642. {
  2643. gFocusMgr.setTopCtrl(NULL);
  2644. }
  2645. mAutoOpenItems.removeAllNodes();
  2646. clearSelection();
  2647. mItems.clear();
  2648. mFolders.clear();
  2649. mItemMap.clear();
  2650. }
  2651. LLMenuGL* LLFolderView::getContextMenu()
  2652. {
  2653. LLMenuGL* menup = (LLMenuGL*)mPopupMenuHandle.get();
  2654. if (menup || mContextMenuCreated) // Do not re-create a deleted menu.
  2655. {
  2656. return menup;
  2657. }
  2658. mContextMenuCreated = true;
  2659. menup = LLUICtrlFactory::getInstance()->buildMenu("menu_inventory.xml",
  2660. mParentPanel);
  2661. if (!menup)
  2662. {
  2663. menup = new LLMenuGL(LLStringUtil::null);
  2664. }
  2665. menup->setBackgroundColor(sContextMenuBgColor);
  2666. menup->setVisible(false);
  2667. mPopupMenuHandle = menup->getHandle();
  2668. return menup;
  2669. }
  2670. void LLFolderView::checkTreeResortForModelChanged()
  2671. {
  2672. if (mSortOrder & LLInventoryFilter::SO_DATE &&
  2673. !(mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME))
  2674. {
  2675. // This is the case where something got added or removed. If we are
  2676. // date sorting everything including folders, then we need to rebuild
  2677. // the whole tree. Just set to something not SO_DATE to force the
  2678. // folder most resent date resort.
  2679. mSortOrder = mSortOrder & ~LLInventoryFilter::SO_DATE;
  2680. setSortOrder(mSortOrder | LLInventoryFilter::SO_DATE);
  2681. }
  2682. }
  2683. void LLFolderView::setSortOrder(U32 order)
  2684. {
  2685. if (order != mSortOrder)
  2686. {
  2687. LL_FAST_TIMER(FTM_SORT);
  2688. mSortOrder = order;
  2689. for (folders_t::iterator iter = mFolders.begin();
  2690. iter != mFolders.end(); )
  2691. {
  2692. folders_t::iterator fit = iter++;
  2693. (*fit)->sortBy(order);
  2694. }
  2695. arrangeAll();
  2696. }
  2697. }
  2698. U32 LLFolderView::getSortOrder() const
  2699. {
  2700. return mSortOrder;
  2701. }
  2702. U32 LLFolderView::toggleSearchType(std::string toggle)
  2703. {
  2704. if (toggle == "name")
  2705. {
  2706. if (mSearchType & 1)
  2707. {
  2708. mSearchType &= 6;
  2709. }
  2710. else
  2711. {
  2712. mSearchType |= 1;
  2713. }
  2714. }
  2715. else if (toggle == "description")
  2716. {
  2717. if (mSearchType & 2)
  2718. {
  2719. mSearchType &= 5;
  2720. }
  2721. else
  2722. {
  2723. mSearchType |= 2;
  2724. }
  2725. }
  2726. else if (toggle == "creator")
  2727. {
  2728. if (mSearchType & 4)
  2729. {
  2730. mSearchType &= 3;
  2731. }
  2732. else
  2733. {
  2734. mSearchType |= 4;
  2735. }
  2736. }
  2737. if (mSearchType == 0)
  2738. {
  2739. mSearchType = 1;
  2740. }
  2741. if (getFilterSubString().length())
  2742. {
  2743. mFilter.setModified(LLInventoryFilter::FILTER_RESTART);
  2744. }
  2745. return mSearchType;
  2746. }
  2747. U32 LLFolderView::getSearchType() const
  2748. {
  2749. return mSearchType;
  2750. }
  2751. //virtual
  2752. bool LLFolderView::addFolder(LLFolderViewFolder* folder)
  2753. {
  2754. if (!folder) return false; // Paranoia
  2755. // Enforce sort order of "My inventory" followed by Library
  2756. if (folder->getListener() &&
  2757. folder->getListener()->getUUID() == gInventory.getLibraryRootFolderID())
  2758. {
  2759. mFolders.push_back(folder);
  2760. }
  2761. else
  2762. {
  2763. mFolders.insert(mFolders.begin(), folder);
  2764. }
  2765. if (folder->numSelected())
  2766. {
  2767. recursiveIncrementNumDescendantsSelected(folder->numSelected());
  2768. }
  2769. folder->setOrigin(0, 0);
  2770. folder->reshape(getRect().getWidth(), 0);
  2771. folder->setVisible(false);
  2772. addChild(folder);
  2773. folder->dirtyFilter();
  2774. folder->requestArrange();
  2775. return true;
  2776. }
  2777. void LLFolderView::closeAllFolders()
  2778. {
  2779. // Close all the folders
  2780. setOpenArrangeRecursively(false, LLFolderViewFolder::RECURSE_DOWN);
  2781. }
  2782. void LLFolderView::openFolder(const std::string& foldername)
  2783. {
  2784. LLFolderViewFolder* inv = getChild<LLFolderViewFolder>(foldername.c_str());
  2785. if (inv)
  2786. {
  2787. setSelection(inv, false, false);
  2788. inv->setOpen(true);
  2789. }
  2790. }
  2791. void LLFolderView::openFolder(const LLUUID& cat_id)
  2792. {
  2793. LLFolderViewFolder* inv =
  2794. dynamic_cast<LLFolderViewFolder*>(getItemByID(cat_id));
  2795. if (inv)
  2796. {
  2797. setSelection(inv, false, false);
  2798. inv->setOpen(true);
  2799. }
  2800. }
  2801. void LLFolderView::setOpenArrangeRecursively(bool openitem,
  2802. ERecurseType recurse)
  2803. {
  2804. // Call base class to do proper recursion
  2805. LLFolderViewFolder::setOpenArrangeRecursively(openitem, recurse);
  2806. // Make sure root folder is always open
  2807. mIsOpen = true;
  2808. }
  2809. // This view grows and shinks to enclose all of its children items and folders.
  2810. //virtual
  2811. S32 LLFolderView::arrange(S32* unused_width, S32* unused_height,
  2812. S32 filter_generation)
  2813. {
  2814. LL_FAST_TIMER(FTM_ARRANGE);
  2815. filter_generation = mFilter.getMinRequiredGeneration();
  2816. mMinWidth = 0;
  2817. mHasVisibleChildren = hasFilteredDescendants(filter_generation);
  2818. // Arrange always finishes, so optimistically set the arrange generation to
  2819. // the most current
  2820. mLastArrangeGeneration = mRoot->getArrangeGeneration();
  2821. LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState();
  2822. S32 total_width = LEFT_PAD;
  2823. S32 running_height = 0;
  2824. S32 target_height = running_height;
  2825. S32 parent_item_height = getRect().getHeight();
  2826. for (folders_t::iterator iter = mFolders.begin();
  2827. iter != mFolders.end(); )
  2828. {
  2829. folders_t::iterator fit = iter++;
  2830. LLFolderViewFolder* folderp = *fit;
  2831. bool visible =
  2832. // Always show folders ?
  2833. show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS ||
  2834. // Passed filter...
  2835. folderp->getFiltered(filter_generation) ||
  2836. // ...or has descendants that passed filter
  2837. folderp->hasFilteredDescendants(filter_generation);
  2838. folderp->setVisible(visible);
  2839. if (visible)
  2840. {
  2841. S32 child_top = parent_item_height - running_height;
  2842. S32 child_height = 0;
  2843. S32 child_width = 0;
  2844. target_height += folderp->arrange(&child_width, &child_height,
  2845. filter_generation);
  2846. mMinWidth = llmax(mMinWidth, child_width);
  2847. total_width = llmax(total_width, child_width);
  2848. running_height += child_height;
  2849. folderp->setOrigin(ICON_PAD,
  2850. child_top - (*fit)->getRect().getHeight());
  2851. }
  2852. }
  2853. for (items_t::iterator iter = mItems.begin(); iter != mItems.end(); )
  2854. {
  2855. items_t::iterator iit = iter++;
  2856. LLFolderViewItem* itemp = *iit;
  2857. bool visible = itemp->getFiltered(filter_generation);
  2858. itemp->setVisible(visible);
  2859. if (visible)
  2860. {
  2861. S32 child_top = parent_item_height - running_height;
  2862. S32 child_width = 0;
  2863. S32 child_height = 0;
  2864. target_height += itemp->arrange(&child_width, &child_height,
  2865. filter_generation);
  2866. itemp->reshape(itemp->getRect().getWidth(), child_height);
  2867. mMinWidth = llmax(mMinWidth, child_width);
  2868. total_width = llmax(total_width, child_width);
  2869. running_height += child_height;
  2870. itemp->setOrigin(ICON_PAD,
  2871. child_top - itemp->getRect().getHeight());
  2872. }
  2873. }
  2874. S32 dummy_s32;
  2875. bool dummy_bool;
  2876. S32 min_width;
  2877. mScrollContainer->calcVisibleSize(&min_width, &dummy_s32, &dummy_bool,
  2878. &dummy_bool);
  2879. reshape(llmax(min_width, total_width), running_height);
  2880. S32 new_min_width;
  2881. mScrollContainer->calcVisibleSize(&new_min_width, &dummy_s32, &dummy_bool,
  2882. &dummy_bool);
  2883. if (new_min_width != min_width)
  2884. {
  2885. reshape(llmax(min_width, total_width), running_height);
  2886. }
  2887. mTargetHeight = (F32)target_height;
  2888. return ll_roundp(mTargetHeight);
  2889. }
  2890. const std::string LLFolderView::getFilterSubString(bool trim)
  2891. {
  2892. return mFilter.getFilterSubString(trim);
  2893. }
  2894. //virtual
  2895. void LLFolderView::filter(LLInventoryFilter& filter)
  2896. {
  2897. LL_FAST_TIMER(FTM_FILTER);
  2898. static LLCachedControl<S32> filter_items_per_frame(gSavedSettings,
  2899. "FilterItemsPerFrame");
  2900. filter.setFilterCount(llclamp((S32)filter_items_per_frame, 1, 5000));
  2901. if (getCompletedFilterGeneration() < filter.getCurrentGeneration())
  2902. {
  2903. mFiltered = false;
  2904. mMinWidth = 0;
  2905. LLFolderViewFolder::filter(filter);
  2906. }
  2907. }
  2908. void LLFolderView::reshape(S32 width, S32 height, bool called_from_parent)
  2909. {
  2910. S32 min_width = 0;
  2911. S32 dummy_height;
  2912. bool dummy_bool;
  2913. if (mScrollContainer)
  2914. {
  2915. mScrollContainer->calcVisibleSize(&min_width, &dummy_height,
  2916. &dummy_bool, &dummy_bool);
  2917. }
  2918. width = llmax(mMinWidth, min_width);
  2919. LLView::reshape(width, height, called_from_parent);
  2920. }
  2921. void LLFolderView::addToSelectionList(LLFolderViewItem* item)
  2922. {
  2923. if (item->isSelected())
  2924. {
  2925. removeFromSelectionList(item);
  2926. }
  2927. if (!mSelectedItems.empty())
  2928. {
  2929. mSelectedItems.back()->setIsCurSelection(false);
  2930. }
  2931. item->setIsCurSelection(true);
  2932. mSelectedItems.push_back(item);
  2933. }
  2934. void LLFolderView::removeFromSelectionList(LLFolderViewItem* item)
  2935. {
  2936. if (mSelectedItems.size())
  2937. {
  2938. mSelectedItems.back()->setIsCurSelection(false);
  2939. }
  2940. selected_items_t::iterator item_iter;
  2941. for (item_iter = mSelectedItems.begin();
  2942. item_iter != mSelectedItems.end(); )
  2943. {
  2944. if (*item_iter == item)
  2945. {
  2946. item_iter = mSelectedItems.erase(item_iter);
  2947. }
  2948. else
  2949. {
  2950. ++item_iter;
  2951. }
  2952. }
  2953. if (mSelectedItems.size())
  2954. {
  2955. mSelectedItems.back()->setIsCurSelection(true);
  2956. }
  2957. }
  2958. LLFolderViewItem* LLFolderView::getCurSelectedItem()
  2959. {
  2960. if (mSelectedItems.size())
  2961. {
  2962. LLFolderViewItem* itemp = mSelectedItems.back();
  2963. llassert(itemp->getIsCurSelection());
  2964. return itemp;
  2965. }
  2966. return NULL;
  2967. }
  2968. // Record the selected item and pass it down the hierachy.
  2969. //virtual
  2970. bool LLFolderView::setSelection(LLFolderViewItem* selection, bool openitem,
  2971. bool take_keyboard_focus)
  2972. {
  2973. if (selection == this)
  2974. {
  2975. return false;
  2976. }
  2977. if (selection && take_keyboard_focus)
  2978. {
  2979. setFocus(true);
  2980. }
  2981. // Clear selection down here because change of keyboard focus can
  2982. // potentially affect selection
  2983. clearSelection();
  2984. if (selection)
  2985. {
  2986. addToSelectionList(selection);
  2987. }
  2988. bool rv = LLFolderViewFolder::setSelection(selection, openitem,
  2989. take_keyboard_focus);
  2990. if (openitem && selection)
  2991. {
  2992. selection->getParentFolder()->requestArrange();
  2993. }
  2994. llassert(mSelectedItems.size() <= 1);
  2995. mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS
  2996. : SIGNAL_NO_KEYBOARD_FOCUS;
  2997. return rv;
  2998. }
  2999. bool LLFolderView::changeSelection(LLFolderViewItem* selection, bool selected)
  3000. {
  3001. // Cannot select the root folder
  3002. if (!selection || selection == this)
  3003. {
  3004. return false;
  3005. }
  3006. if (!mAllowMultiSelect)
  3007. {
  3008. clearSelection();
  3009. }
  3010. selected_items_t::iterator item_iter;
  3011. for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();
  3012. ++item_iter)
  3013. {
  3014. if (*item_iter == selection)
  3015. {
  3016. break;
  3017. }
  3018. }
  3019. bool on_list = item_iter != mSelectedItems.end();
  3020. if (selected && !on_list)
  3021. {
  3022. addToSelectionList(selection);
  3023. }
  3024. if (!selected && on_list)
  3025. {
  3026. removeFromSelectionList(selection);
  3027. }
  3028. bool rv = LLFolderViewFolder::changeSelection(selection, selected);
  3029. mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS;
  3030. return rv;
  3031. }
  3032. void LLFolderView::extendSelection(LLFolderViewItem* selection,
  3033. LLFolderViewItem* last_selected,
  3034. std::vector<LLFolderViewItem*>& items)
  3035. {
  3036. // Now store resulting selection
  3037. if (mAllowMultiSelect)
  3038. {
  3039. LLFolderViewItem *cur_selection = getCurSelectedItem();
  3040. LLFolderViewFolder::extendSelection(selection, cur_selection, items);
  3041. for (S32 i = 0, count = items.size(); i < count; ++i)
  3042. {
  3043. addToSelectionList(items[i]);
  3044. }
  3045. }
  3046. else
  3047. {
  3048. setSelection(selection, false, false);
  3049. }
  3050. mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS;
  3051. }
  3052. void LLFolderView::sanitizeSelection()
  3053. {
  3054. // Store off current item in case it is automatically deselected and we
  3055. // want to preserve context
  3056. LLFolderViewItem* orig_selected = getCurSelectedItem();
  3057. // Cache "Show all folders" filter setting
  3058. bool show_all_folders =
  3059. getRoot()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS;
  3060. std::vector<LLFolderViewItem*> items_to_remove;
  3061. selected_items_t::iterator item_iter;
  3062. for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();
  3063. ++item_iter)
  3064. {
  3065. LLFolderViewItem* item = *item_iter;
  3066. // Ensure that each ancestor is open and potentially passes filtering.
  3067. // Initialize from filter state for this item
  3068. bool visible = item->potentiallyVisible();
  3069. // Modify with parent open and filters states
  3070. LLFolderViewFolder* parent_folder = item->getParentFolder();
  3071. if (parent_folder)
  3072. {
  3073. if (show_all_folders)
  3074. {
  3075. // "Show all folders" is on, so this folder is visible
  3076. visible = true;
  3077. }
  3078. else
  3079. {
  3080. // Move up through parent folders and see what is visible
  3081. while (parent_folder)
  3082. {
  3083. visible = visible && parent_folder->isOpen() &&
  3084. parent_folder->potentiallyVisible();
  3085. parent_folder = parent_folder->getParentFolder();
  3086. }
  3087. }
  3088. }
  3089. // Deselect item if any ancestor is closed or did not pass filter
  3090. // requirements.
  3091. if (!visible)
  3092. {
  3093. items_to_remove.push_back(item);
  3094. }
  3095. // Disallow nested selections (i.e. folder items plus one or more
  3096. // ancestors) could check cached mum selections count and only iterate
  3097. // if there are any but that may be a premature optimization.
  3098. selected_items_t::iterator other_item_iter;
  3099. for (other_item_iter = mSelectedItems.begin();
  3100. other_item_iter != mSelectedItems.end(); ++other_item_iter)
  3101. {
  3102. LLFolderViewItem* other_item = *other_item_iter;
  3103. for (parent_folder = other_item->getParentFolder(); parent_folder;
  3104. parent_folder = parent_folder->getParentFolder())
  3105. {
  3106. if (parent_folder == item)
  3107. {
  3108. // This is a descendent of the current folder, remove from
  3109. // list
  3110. items_to_remove.push_back(other_item);
  3111. break;
  3112. }
  3113. }
  3114. }
  3115. }
  3116. std::vector<LLFolderViewItem*>::iterator item_it;
  3117. for (item_it = items_to_remove.begin(); item_it != items_to_remove.end();
  3118. ++item_it)
  3119. {
  3120. // Toggle selection (also removes from list)
  3121. changeSelection(*item_it, false);
  3122. }
  3123. // If nothing selected after prior constraints...
  3124. if (mSelectedItems.empty())
  3125. {
  3126. // ... select first available parent of original selection, or "My
  3127. // Inventory" otherwise
  3128. LLFolderViewItem* new_selection = NULL;
  3129. if (orig_selected)
  3130. {
  3131. for (LLFolderViewFolder* parent = orig_selected->getParentFolder();
  3132. parent; parent = parent->getParentFolder())
  3133. {
  3134. if (parent->potentiallyVisible())
  3135. {
  3136. // Give initial selection to first ancestor folder that
  3137. // potentially passes the filter
  3138. if (!new_selection)
  3139. {
  3140. new_selection = parent;
  3141. }
  3142. // If any ancestor folder of original item is closed, move
  3143. // the selection up to the highest closed
  3144. if (!parent->isOpen())
  3145. {
  3146. new_selection = parent;
  3147. }
  3148. }
  3149. }
  3150. }
  3151. else
  3152. {
  3153. // Nothing selected to start with, so pick "My Inventory" as best
  3154. // guess
  3155. new_selection = getItemByID(gInventory.getRootFolderID());
  3156. }
  3157. if (new_selection)
  3158. {
  3159. setSelection(new_selection, false, false);
  3160. }
  3161. }
  3162. }
  3163. void LLFolderView::clearSelection()
  3164. {
  3165. if (mSelectedItems.size() > 0)
  3166. {
  3167. recursiveDeselect(false);
  3168. mSelectedItems.clear();
  3169. }
  3170. }
  3171. bool LLFolderView::getSelectionList(uuid_list_t& selection)
  3172. {
  3173. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3174. end = mSelectedItems.end();
  3175. it != end; ++it)
  3176. {
  3177. LLFolderViewItem* item = *it;
  3178. if (item && item->getListener())
  3179. {
  3180. selection.emplace(item->getListener()->getUUID());
  3181. }
  3182. }
  3183. return !selection.empty();
  3184. }
  3185. bool LLFolderView::getSelection(uuid_vec_t& selection)
  3186. {
  3187. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3188. end = mSelectedItems.end();
  3189. it != end; ++it)
  3190. {
  3191. LLFolderViewItem* item = *it;
  3192. if (item && item->getListener())
  3193. {
  3194. selection.emplace_back(item->getListener()->getUUID());
  3195. }
  3196. }
  3197. return !selection.empty();
  3198. }
  3199. bool LLFolderView::startDrag(LLToolDragAndDrop::ESource source)
  3200. {
  3201. bool can_drag = true;
  3202. if (!mSelectedItems.empty())
  3203. {
  3204. std::vector<EDragAndDropType> types;
  3205. uuid_vec_t cargo_ids;
  3206. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3207. end = mSelectedItems.end();
  3208. it != end; ++it)
  3209. {
  3210. LLFolderViewItem* item = *it;
  3211. EDragAndDropType type = DAD_NONE;
  3212. LLUUID id;
  3213. can_drag = can_drag && item && item->getListener() &&
  3214. item->getListener()->startDrag(&type, &id);
  3215. types.push_back(type);
  3216. cargo_ids.emplace_back(id);
  3217. }
  3218. gToolDragAndDrop.beginMultiDrag(types, cargo_ids, source, mSourceID);
  3219. }
  3220. return can_drag;
  3221. }
  3222. void LLFolderView::commitRename(LLUICtrl* renamer, void* user_data)
  3223. {
  3224. LLFolderView* root = reinterpret_cast<LLFolderView*>(user_data);
  3225. if (root)
  3226. {
  3227. root->finishRenamingItem();
  3228. }
  3229. }
  3230. void LLFolderView::draw()
  3231. {
  3232. // If cursor has moved off of me during drag and drop, close all auto
  3233. // opened folders
  3234. if (!mDragAndDropThisFrame)
  3235. {
  3236. closeAutoOpenedFolders();
  3237. }
  3238. if (this == gFocusMgr.getKeyboardFocus() && !getVisible())
  3239. {
  3240. gFocusMgr.setKeyboardFocus(NULL);
  3241. }
  3242. // While dragging, update selection rendering to reflect single/multi drag
  3243. // status
  3244. if (gToolDragAndDrop.hasMouseCapture())
  3245. {
  3246. EAcceptance last_accept = gToolDragAndDrop.getLastAccept();
  3247. if (last_accept == ACCEPT_YES_SINGLE ||
  3248. last_accept == ACCEPT_YES_COPY_SINGLE)
  3249. {
  3250. setShowSingleSelection(true);
  3251. }
  3252. else
  3253. {
  3254. setShowSingleSelection(false);
  3255. }
  3256. mHasCapture = true;
  3257. }
  3258. else
  3259. {
  3260. if (mHasCapture)
  3261. {
  3262. // Cancel any drag message tip since we just lost mouse capture
  3263. LLFolderViewEventListener::cancelTip(true);
  3264. }
  3265. mHasCapture = false;
  3266. setShowSingleSelection(false);
  3267. }
  3268. if (mSearchTimer.getElapsedTimeF32() > LLUI::sTypeAheadTimeout)
  3269. {
  3270. mSearchString.clear();
  3271. }
  3272. LLFolderViewFolder::draw();
  3273. mDragAndDropThisFrame = false;
  3274. }
  3275. void LLFolderView::rememberMarketplaceFolders()
  3276. {
  3277. // Clear old data and flags
  3278. mMarketplaceFolders.clear();
  3279. mWillModifyListing = mWillUnlistIfRemoved = mWillDeleteListingIfRemoved =
  3280. false;
  3281. // Get the Markeplace Listings folder UUID, is any
  3282. const LLUUID& market_id = LLMarketplace::getMPL();
  3283. if (market_id.isNull())
  3284. {
  3285. // No Martketplace Listings folder: do not bother...
  3286. return;
  3287. }
  3288. LLMarketplaceData* marketdata = LLMarketplaceData::getInstance();
  3289. LLUUID id, parent_id;
  3290. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3291. end = mSelectedItems.end();
  3292. it != end; ++it)
  3293. {
  3294. LLFolderViewItem* item = *it;
  3295. if (!item) continue; // Paranoia
  3296. if (item && item->getListener())
  3297. {
  3298. id = item->getListener()->getUUID();
  3299. if (!LLMarketplace::contains(id))
  3300. {
  3301. continue;
  3302. }
  3303. bool in_marketplace = false;
  3304. LLViewerInventoryCategory* cat = gInventory.getCategory(id);
  3305. if (cat)
  3306. {
  3307. in_marketplace = true;
  3308. mMarketplaceFolders.emplace(id);
  3309. parent_id = cat->getParentUUID();
  3310. if (parent_id.notNull() && LLMarketplace::contains(parent_id))
  3311. {
  3312. mMarketplaceFolders.emplace(parent_id);
  3313. }
  3314. }
  3315. else
  3316. {
  3317. LLViewerInventoryItem* item = gInventory.getItem(id);
  3318. if (item)
  3319. {
  3320. id = item->getParentUUID();
  3321. if (id.notNull())
  3322. {
  3323. in_marketplace = true;
  3324. mMarketplaceFolders.emplace(id);
  3325. }
  3326. }
  3327. }
  3328. if (in_marketplace)
  3329. {
  3330. // Check what could happen to our listings...
  3331. if (marketdata->isInActiveFolder(id) ||
  3332. marketdata->isListedAndActive(id))
  3333. {
  3334. mWillModifyListing = true;
  3335. if (marketdata->isListed(id) ||
  3336. marketdata->isVersionFolder(id))
  3337. {
  3338. mWillUnlistIfRemoved = true;
  3339. }
  3340. }
  3341. else if (marketdata->isListed(id))
  3342. {
  3343. mWillDeleteListingIfRemoved = true;
  3344. }
  3345. }
  3346. }
  3347. }
  3348. }
  3349. void LLFolderView::updateMarketplaceFolders()
  3350. {
  3351. for (uuid_list_t::iterator it = mMarketplaceFolders.begin(),
  3352. end = mMarketplaceFolders.end();
  3353. it != end; ++it)
  3354. {
  3355. const LLUUID& cat_id = *it;
  3356. LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
  3357. if (cat)
  3358. {
  3359. LLMarketplace::updateCategory(cat_id);
  3360. gInventory.notifyObservers();
  3361. }
  3362. }
  3363. mMarketplaceFolders.clear();
  3364. }
  3365. void LLFolderView::finishRenamingItem()
  3366. {
  3367. if (!mRenamer)
  3368. {
  3369. return;
  3370. }
  3371. if (mRenameItem)
  3372. {
  3373. mRenameItem->rename(mRenamer->getText());
  3374. }
  3375. mRenamer->setCommitOnFocusLost(false);
  3376. mRenamer->setFocus(false);
  3377. mRenamer->setVisible(false);
  3378. mRenamer->setCommitOnFocusLost(true);
  3379. gFocusMgr.setTopCtrl(NULL);
  3380. if (mRenameItem)
  3381. {
  3382. setSelectionFromRoot(mRenameItem, true);
  3383. mRenameItem = NULL;
  3384. }
  3385. // List is re-sorted alphabeticly, so scroll to make sure the selected item
  3386. // is visible.
  3387. scrollToShowSelection();
  3388. // Update renamed market place listing folders if any
  3389. updateMarketplaceFolders();
  3390. }
  3391. void LLFolderView::closeRenamer()
  3392. {
  3393. // Will commit current name (which could be same as original name)
  3394. mRenamer->setFocus(false);
  3395. mRenamer->setVisible(false);
  3396. gFocusMgr.setTopCtrl(NULL);
  3397. if (mRenameItem)
  3398. {
  3399. setSelectionFromRoot(mRenameItem, true);
  3400. mRenameItem = NULL;
  3401. }
  3402. }
  3403. bool removeSelectedItemsCallback(const LLSD& notification,
  3404. const LLSD& response)
  3405. {
  3406. if (LLNotification::getSelectedOption(notification, response) == 0) // Yes
  3407. {
  3408. const LLUUID& id = notification["payload"]["folderview_id"].asUUID();
  3409. LLFolderView* view = LLFolderView::getInstance(id);
  3410. if (view)
  3411. {
  3412. view->removeSelectedItems(false);
  3413. }
  3414. }
  3415. return false;
  3416. }
  3417. void LLFolderView::removeSelectedItems(bool confirm)
  3418. {
  3419. if (getVisible() && getEnabled())
  3420. {
  3421. // Keep track of the selected market place listing folders if any
  3422. rememberMarketplaceFolders();
  3423. if (confirm && (mWillUnlistIfRemoved || mWillDeleteListingIfRemoved))
  3424. {
  3425. LLSD payload;
  3426. payload["folderview_id"] = mFolderViewId;
  3427. std::string type = mWillUnlistIfRemoved ? "ConfirmMerchantUnlist"
  3428. : "ConfirmListingCutOrDelete";
  3429. gNotifications.add(type, LLSD(), payload,
  3430. removeSelectedItemsCallback);
  3431. return;
  3432. }
  3433. // Just in case we are removing the renaming item.
  3434. mRenameItem = NULL;
  3435. // Create a temporary structure which we will use to remove items,
  3436. // since the removal will futz with internal data structures.
  3437. std::vector<LLFolderViewItem*> items;
  3438. S32 count = mSelectedItems.size();
  3439. if (count <= 0) return;
  3440. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3441. end = mSelectedItems.end();
  3442. it != end; ++it)
  3443. {
  3444. LLFolderViewItem* item = *it;
  3445. if (item->isRemovable())
  3446. {
  3447. items.push_back(item);
  3448. }
  3449. else
  3450. {
  3451. llinfos << "Cannot delete " << item->getName() << llendl;
  3452. return;
  3453. }
  3454. }
  3455. // Iterate through the new container.
  3456. bool has_focus = gFocusMgr.childHasKeyboardFocus(this);
  3457. count = items.size();
  3458. LLUUID new_selection_id;
  3459. if (count == 1)
  3460. {
  3461. LLFolderViewItem* item_to_delete = items[0];
  3462. LLFolderViewFolder* parent = item_to_delete->getParentFolder();
  3463. LLFolderViewItem* new_selection =
  3464. item_to_delete->getNextOpenNode(false);
  3465. if (!new_selection)
  3466. {
  3467. new_selection = item_to_delete->getPreviousOpenNode(false);
  3468. }
  3469. if (parent && parent->removeItem(item_to_delete))
  3470. {
  3471. // Change selection on successful delete
  3472. if (new_selection)
  3473. {
  3474. setSelectionFromRoot(new_selection,
  3475. new_selection->isOpen(), has_focus);
  3476. }
  3477. else
  3478. {
  3479. setSelectionFromRoot(NULL, has_focus);
  3480. }
  3481. }
  3482. arrangeAll();
  3483. }
  3484. else if (count > 1)
  3485. {
  3486. std::vector<LLFolderViewEventListener*> listeners;
  3487. LLFolderViewEventListener* listener;
  3488. LLFolderViewItem* last_item = items[count - 1];
  3489. LLFolderViewItem* new_selection =
  3490. last_item->getNextOpenNode(false);
  3491. while (new_selection && new_selection->isSelected())
  3492. {
  3493. new_selection = new_selection->getNextOpenNode(false);
  3494. }
  3495. if (!new_selection)
  3496. {
  3497. new_selection = last_item->getPreviousOpenNode(false);
  3498. while (new_selection && new_selection->isSelected())
  3499. {
  3500. new_selection = new_selection->getPreviousOpenNode(false);
  3501. }
  3502. }
  3503. if (new_selection)
  3504. {
  3505. setSelectionFromRoot(new_selection, new_selection->isOpen(),
  3506. has_focus);
  3507. }
  3508. else
  3509. {
  3510. setSelectionFromRoot(NULL, has_focus);
  3511. }
  3512. for (S32 i = 0; i < count; ++i)
  3513. {
  3514. LLFolderViewItem* item = items[i];
  3515. if (!item) continue; // Paranoia
  3516. listener = item->getListener();
  3517. if (listener &&
  3518. std::find(listeners.begin(), listeners.end(),
  3519. listener) == listeners.end())
  3520. {
  3521. listeners.push_back(listener);
  3522. }
  3523. }
  3524. listener = listeners[0];
  3525. if (listener)
  3526. {
  3527. listener->removeBatch(listeners);
  3528. }
  3529. }
  3530. arrangeAll();
  3531. scrollToShowSelection();
  3532. // Update deleted market place listing folders if any
  3533. updateMarketplaceFolders();
  3534. }
  3535. }
  3536. void LLFolderView::openSelectedItems()
  3537. {
  3538. if (!getVisible() || !getEnabled())
  3539. {
  3540. return;
  3541. }
  3542. if (mSelectedItems.size() == 1)
  3543. {
  3544. mSelectedItems.front()->openItem();
  3545. return;
  3546. }
  3547. S32 left, top;
  3548. gFloaterViewp->getNewFloaterPosition(&left, &top);
  3549. LLMultiPreview* multi_previewp =
  3550. new LLMultiPreview(LLRect(left, top, left + 300, top - 100));
  3551. gFloaterViewp->getNewFloaterPosition(&left, &top);
  3552. LLMultiProperties* multi_propertiesp =
  3553. new LLMultiProperties(LLRect(left, top, left + 300, top - 100));
  3554. {
  3555. LLHostFloater host;
  3556. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3557. end = mSelectedItems.end();
  3558. it != end; ++it)
  3559. {
  3560. LLFolderViewItem* item = *it;
  3561. if (!item) continue; // Paranoia
  3562. // IT_{OBJECT,ATTACHMENT} creates LLProperties floaters; others
  3563. // create LLPreviews. Put each one in the right type of container.
  3564. bool is_prop = false;
  3565. LLFolderViewEventListener* listener = item->getListener();
  3566. if (listener)
  3567. {
  3568. LLInventoryType::EType type = listener->getInventoryType();
  3569. is_prop = type == LLInventoryType::IT_OBJECT ||
  3570. type == LLInventoryType::IT_ATTACHMENT;
  3571. }
  3572. if (is_prop)
  3573. {
  3574. host.set(multi_propertiesp);
  3575. }
  3576. else
  3577. {
  3578. host.set(multi_previewp);
  3579. }
  3580. item->openItem();
  3581. }
  3582. }
  3583. // NOTE: LLMulti* will safely auto-delete when opened without any children
  3584. multi_previewp->open();
  3585. multi_propertiesp->open();
  3586. }
  3587. void LLFolderView::propertiesSelectedItems()
  3588. {
  3589. if (!getVisible() || !getEnabled())
  3590. {
  3591. return;
  3592. }
  3593. if (mSelectedItems.size() == 1)
  3594. {
  3595. LLFolderViewItem* folder_item = mSelectedItems.front();
  3596. if (folder_item && folder_item->getListener())
  3597. {
  3598. folder_item->getListener()->showProperties();
  3599. }
  3600. return;
  3601. }
  3602. S32 left, top;
  3603. gFloaterViewp->getNewFloaterPosition(&left, &top);
  3604. LLMultiProperties* multi_propertiesp =
  3605. new LLMultiProperties(LLRect(left, top, left + 100, top - 100));
  3606. {
  3607. LLHostFloater host(multi_propertiesp);
  3608. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3609. end = mSelectedItems.end();
  3610. it != end; ++it)
  3611. {
  3612. LLFolderViewItem* item = *it;
  3613. if (item && item->getListener())
  3614. {
  3615. item->getListener()->showProperties();
  3616. }
  3617. }
  3618. }
  3619. multi_propertiesp->open();
  3620. }
  3621. void LLFolderView::autoOpenItem(LLFolderViewFolder* item)
  3622. {
  3623. if (mAutoOpenItems.check() == item ||
  3624. mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH)
  3625. {
  3626. return;
  3627. }
  3628. // Close auto-opened folders
  3629. LLFolderViewFolder* close_item = mAutoOpenItems.check();
  3630. while (close_item && close_item != item->getParentFolder())
  3631. {
  3632. mAutoOpenItems.pop();
  3633. close_item->setOpenArrangeRecursively(false);
  3634. close_item = mAutoOpenItems.check();
  3635. }
  3636. item->requestArrange();
  3637. mAutoOpenItems.push(item);
  3638. item->setOpen(true);
  3639. scrollToShowItem(item);
  3640. }
  3641. void LLFolderView::closeAutoOpenedFolders()
  3642. {
  3643. while (mAutoOpenItems.check())
  3644. {
  3645. LLFolderViewFolder* close_item = mAutoOpenItems.pop();
  3646. close_item->setOpen(false);
  3647. }
  3648. if (mAutoOpenCandidate)
  3649. {
  3650. mAutoOpenCandidate->setAutoOpenCountdown(0.f);
  3651. }
  3652. mAutoOpenCandidate = NULL;
  3653. mAutoOpenTimer.stop();
  3654. }
  3655. bool LLFolderView::autoOpenTest(LLFolderViewFolder* folder)
  3656. {
  3657. if (folder && mAutoOpenCandidate == folder)
  3658. {
  3659. if (mAutoOpenTimer.getStarted())
  3660. {
  3661. if (!mAutoOpenCandidate->isOpen())
  3662. {
  3663. F32 t = clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f,
  3664. sAutoOpenTime, 0.f, 1.f);
  3665. mAutoOpenCandidate->setAutoOpenCountdown(t);
  3666. }
  3667. if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime)
  3668. {
  3669. autoOpenItem(folder);
  3670. mAutoOpenTimer.stop();
  3671. return true;
  3672. }
  3673. }
  3674. return false;
  3675. }
  3676. // Otherwise new candidate, restart timer
  3677. if (mAutoOpenCandidate)
  3678. {
  3679. mAutoOpenCandidate->setAutoOpenCountdown(0.f);
  3680. }
  3681. mAutoOpenCandidate = folder;
  3682. mAutoOpenTimer.start();
  3683. return false;
  3684. }
  3685. bool LLFolderView::canCopy() const
  3686. {
  3687. if (!getVisible() || !getEnabled() || mSelectedItems.empty())
  3688. {
  3689. return false;
  3690. }
  3691. for (selected_items_t::const_iterator it = mSelectedItems.begin(),
  3692. end = mSelectedItems.end();
  3693. it != end; ++it)
  3694. {
  3695. const LLFolderViewItem* item = *it;
  3696. if (!item || !item->getListener() ||
  3697. !item->getListener()->isItemCopyable())
  3698. {
  3699. return false;
  3700. }
  3701. }
  3702. return true;
  3703. }
  3704. void LLFolderView::copy()
  3705. {
  3706. // Clear the inventory clipboard
  3707. HBInventoryClipboard::reset();
  3708. if (getVisible() && getEnabled() && mSelectedItems.size())
  3709. {
  3710. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3711. end = mSelectedItems.end();
  3712. it != end; ++it)
  3713. {
  3714. LLFolderViewItem* item = *it;
  3715. if (!item) continue;
  3716. LLFolderViewEventListener* listener = item->getListener();
  3717. if (listener)
  3718. {
  3719. listener->copyToClipboard();
  3720. }
  3721. }
  3722. }
  3723. mSearchString.clear();
  3724. }
  3725. bool LLFolderView::canCut() const
  3726. {
  3727. if (!getVisible() || !getEnabled() || mSelectedItems.size() == 0)
  3728. {
  3729. return false;
  3730. }
  3731. for (selected_items_t::const_iterator it = mSelectedItems.begin(),
  3732. end = mSelectedItems.end();
  3733. it != end; ++it)
  3734. {
  3735. LLFolderViewItem* item = *it;
  3736. if (!item || !item->getListener() ||
  3737. !item->getListener()->isItemMovable())
  3738. {
  3739. return false;
  3740. }
  3741. }
  3742. return true;
  3743. }
  3744. void LLFolderView::cut()
  3745. {
  3746. doCut(true);
  3747. }
  3748. bool cutSelectedItemsCallback(const LLSD& notification, const LLSD& response)
  3749. {
  3750. if (LLNotification::getSelectedOption(notification, response) == 0) // yes
  3751. {
  3752. const LLUUID& id = notification["payload"]["folderview_id"].asUUID();
  3753. LLFolderView* view = LLFolderView::getInstance(id);
  3754. if (view)
  3755. {
  3756. view->doCut(false);
  3757. }
  3758. }
  3759. return false;
  3760. }
  3761. void LLFolderView::doCut(bool confirm)
  3762. {
  3763. if (confirm)
  3764. {
  3765. // Check for possible Marketplace Listings changes and warn if needed
  3766. rememberMarketplaceFolders();
  3767. std::string type;
  3768. if (mWillUnlistIfRemoved)
  3769. {
  3770. type = "ConfirmMerchantUnlist";
  3771. }
  3772. else if (mWillDeleteListingIfRemoved)
  3773. {
  3774. type = "ConfirmListingCutOrDelete";
  3775. }
  3776. else if (mWillModifyListing)
  3777. {
  3778. type = "ConfirmMerchantActiveChange";
  3779. }
  3780. if (!type.empty())
  3781. {
  3782. LLSD payload;
  3783. payload["folderview_id"] = mFolderViewId;
  3784. gNotifications.add(type, LLSD(), payload,
  3785. cutSelectedItemsCallback);
  3786. return;
  3787. }
  3788. }
  3789. // Clear the inventory clipboard
  3790. HBInventoryClipboard::reset();
  3791. if (getVisible() && getEnabled() && mSelectedItems.size())
  3792. {
  3793. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3794. end = mSelectedItems.end();
  3795. it != end; ++it)
  3796. {
  3797. LLFolderViewItem* item = *it;
  3798. if (!item) continue;
  3799. LLFolderViewEventListener* listener = item->getListener();
  3800. if (listener)
  3801. {
  3802. listener->cutToClipboard();
  3803. }
  3804. }
  3805. }
  3806. mSearchString.clear();
  3807. }
  3808. bool LLFolderView::canPaste() const
  3809. {
  3810. if (!getVisible() || !getEnabled() || mSelectedItems.size() == 0)
  3811. {
  3812. return false;
  3813. }
  3814. for (selected_items_t::const_iterator it = mSelectedItems.begin(),
  3815. end = mSelectedItems.end();
  3816. it != end; ++it)
  3817. {
  3818. const LLFolderViewItem* item = *it;
  3819. if (!item)
  3820. {
  3821. return false;
  3822. }
  3823. const LLFolderViewEventListener* listener = item->getListener();
  3824. if (!listener || !listener->isClipboardPasteable())
  3825. {
  3826. const LLFolderViewFolder* folderp = item->getParentFolder();
  3827. if (!folderp)
  3828. {
  3829. return false;
  3830. }
  3831. listener = folderp->getListener();
  3832. if (!listener || !listener->isClipboardPasteable())
  3833. {
  3834. return false;
  3835. }
  3836. }
  3837. }
  3838. return true;
  3839. }
  3840. void LLFolderView::paste()
  3841. {
  3842. doPaste(true);
  3843. }
  3844. bool pasteSelectedItemCallback(const LLSD& notification, const LLSD& response)
  3845. {
  3846. if (LLNotification::getSelectedOption(notification, response) == 0) // Yes
  3847. {
  3848. const LLUUID& id = notification["payload"]["folderview_id"].asUUID();
  3849. LLFolderView* view = LLFolderView::getInstance(id);
  3850. if (view)
  3851. {
  3852. view->doPaste(false);
  3853. }
  3854. }
  3855. return false;
  3856. }
  3857. void LLFolderView::doPaste(bool confirm)
  3858. {
  3859. // Keep track of the selected market place listing folders if any
  3860. rememberMarketplaceFolders();
  3861. if (confirm && mWillModifyListing)
  3862. {
  3863. LLSD payload;
  3864. payload["folderview_id"] = mFolderViewId;
  3865. gNotifications.add("ConfirmMerchantActiveChange", LLSD(), payload,
  3866. pasteSelectedItemCallback);
  3867. return;
  3868. }
  3869. if (getVisible() && getEnabled())
  3870. {
  3871. // Find a set of unique folders to paste into
  3872. fast_hset<LLFolderViewItem*> folder_set;
  3873. for (selected_items_t::iterator it = mSelectedItems.begin(),
  3874. end = mSelectedItems.end();
  3875. it != end; ++it)
  3876. {
  3877. LLFolderViewItem* item = *it;
  3878. if (!item) continue; // Paranoia
  3879. LLFolderViewEventListener* listener = item->getListener();
  3880. if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY)
  3881. {
  3882. item = item->getParentFolder();
  3883. }
  3884. folder_set.insert(item);
  3885. }
  3886. for (fast_hset<LLFolderViewItem*>::iterator it = folder_set.begin(),
  3887. end = folder_set.end();
  3888. it != end; ++it)
  3889. {
  3890. LLFolderViewItem* item = *it;
  3891. if (item) // Paranoia
  3892. {
  3893. LLFolderViewEventListener* listener = item->getListener();
  3894. if (listener && listener->isClipboardPasteable())
  3895. {
  3896. listener->pasteFromClipboard();
  3897. }
  3898. }
  3899. }
  3900. }
  3901. mSearchString.clear();
  3902. // Update deleted market place listing folders if any
  3903. updateMarketplaceFolders();
  3904. }
  3905. bool startRenamingSelectedItemCallback(const LLSD& notification,
  3906. const LLSD& response)
  3907. {
  3908. if (LLNotification::getSelectedOption(notification, response) == 0) // Yes
  3909. {
  3910. const LLUUID& id = notification["payload"]["folderview_id"].asUUID();
  3911. LLFolderView* view = LLFolderView::getInstance(id);
  3912. if (view)
  3913. {
  3914. view->startRenamingSelectedItem(false);
  3915. }
  3916. }
  3917. return false;
  3918. }
  3919. // Public rename functionality: can only start the process
  3920. void LLFolderView::startRenamingSelectedItem(bool confirm)
  3921. {
  3922. // Keep track of the selected market place listing folders if any
  3923. rememberMarketplaceFolders();
  3924. if (confirm && mWillModifyListing)
  3925. {
  3926. LLSD payload;
  3927. payload["folderview_id"] = mFolderViewId;
  3928. gNotifications.add("ConfirmMerchantActiveChange", LLSD(), payload,
  3929. startRenamingSelectedItemCallback);
  3930. return;
  3931. }
  3932. // Make sure selection is visible
  3933. scrollToShowSelection();
  3934. S32 count = mSelectedItems.size();
  3935. LLFolderViewItem* item = NULL;
  3936. if (count > 0)
  3937. {
  3938. item = mSelectedItems.front();
  3939. }
  3940. if (getVisible() && getEnabled() && count == 1 && item &&
  3941. item->getListener() && item->getListener()->isItemRenameable())
  3942. {
  3943. mRenameItem = item;
  3944. S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 +
  3945. item->getIndentation();
  3946. S32 y = llfloor(item->getRect().getHeight() - sFontLineHeight - 2);
  3947. item->localPointToScreen(x, y, &x, &y);
  3948. screenPointToLocal(x, y, &x, &y);
  3949. mRenamer->setOrigin(x, y);
  3950. S32 scroller_height = 0;
  3951. S32 scroller_width = gViewerWindowp->getWindowWidth();
  3952. bool dummy_bool;
  3953. if (mScrollContainer)
  3954. {
  3955. mScrollContainer->calcVisibleSize(&scroller_width,
  3956. &scroller_height,
  3957. &dummy_bool, &dummy_bool);
  3958. }
  3959. S32 width = llmax(llmin(item->getRect().getWidth() - x,
  3960. scroller_width - x - getRect().mLeft),
  3961. MINIMUM_RENAMER_WIDTH);
  3962. S32 height = llfloor(sFontLineHeight + RENAME_HEIGHT_PAD);
  3963. mRenamer->reshape(width, height);
  3964. mRenamer->setText(item->getName());
  3965. mRenamer->selectAll();
  3966. mRenamer->setVisible(true);
  3967. // Set focus will fail unless item is visible
  3968. mRenamer->setFocus(true);
  3969. mRenamer->setLostTopCallback(onRenamerLost);
  3970. gFocusMgr.setTopCtrl(mRenamer);
  3971. }
  3972. }
  3973. void LLFolderView::setFocus(bool focus)
  3974. {
  3975. if (focus && !hasFocus())
  3976. {
  3977. grabMenuHandler();
  3978. }
  3979. LLFolderViewFolder::setFocus(focus);
  3980. }
  3981. bool LLFolderView::handleKeyHere(KEY key, MASK mask)
  3982. {
  3983. bool handled = false;
  3984. // SL-51858: key presses are not being passed to the pop-up menu. A proper
  3985. // fix is non-trivial, so instead just close the menu.
  3986. LLMenuGL* menup = (LLMenuGL*)mPopupMenuHandle.get();
  3987. if (menup && menup->isOpen())
  3988. {
  3989. gMenuHolderp->hideMenus();
  3990. }
  3991. switch (key)
  3992. {
  3993. case KEY_F2:
  3994. mSearchString.clear();
  3995. startRenamingSelectedItem();
  3996. handled = true;
  3997. break;
  3998. case KEY_RETURN:
  3999. if (mask == MASK_NONE)
  4000. {
  4001. if (mRenameItem && mRenamer->getVisible())
  4002. {
  4003. finishRenamingItem();
  4004. mSearchString.clear();
  4005. handled = true;
  4006. }
  4007. else
  4008. {
  4009. LLFolderView::openSelectedItems();
  4010. handled = true;
  4011. }
  4012. }
  4013. break;
  4014. case KEY_ESCAPE:
  4015. if (mask == MASK_NONE)
  4016. {
  4017. if (mRenameItem && mRenamer->getVisible())
  4018. {
  4019. closeRenamer();
  4020. handled = true;
  4021. }
  4022. mSearchString.clear();
  4023. }
  4024. break;
  4025. case KEY_PAGE_UP:
  4026. mSearchString.clear();
  4027. mScrollContainer->pageUp(30);
  4028. handled = true;
  4029. break;
  4030. case KEY_PAGE_DOWN:
  4031. mSearchString.clear();
  4032. mScrollContainer->pageDown(30);
  4033. handled = true;
  4034. break;
  4035. case KEY_HOME:
  4036. mSearchString.clear();
  4037. mScrollContainer->goToTop();
  4038. handled = true;
  4039. break;
  4040. case KEY_END:
  4041. mSearchString.clear();
  4042. mScrollContainer->goToBottom();
  4043. break;
  4044. case KEY_DOWN:
  4045. if (mScrollContainer && mSelectedItems.size() > 0)
  4046. {
  4047. LLFolderViewItem* last_selected = getCurSelectedItem();
  4048. if (!mKeyboardSelection)
  4049. {
  4050. setSelection(last_selected, false, true);
  4051. mKeyboardSelection = true;
  4052. }
  4053. LLFolderViewItem* next = NULL;
  4054. if (mask & MASK_SHIFT)
  4055. {
  4056. // Do not shift select down to children of folders (they
  4057. // are implicitly selected through parent)
  4058. next = last_selected->getNextOpenNode(false);
  4059. if (next)
  4060. {
  4061. if (next->isSelected())
  4062. {
  4063. // Shrink selection
  4064. changeSelectionFromRoot(last_selected, false);
  4065. }
  4066. else if (last_selected->getParentFolder() ==
  4067. next->getParentFolder())
  4068. {
  4069. // Grow selection
  4070. changeSelectionFromRoot(next, true);
  4071. }
  4072. }
  4073. }
  4074. else
  4075. {
  4076. next = last_selected->getNextOpenNode();
  4077. if (next)
  4078. {
  4079. if (next == last_selected)
  4080. {
  4081. return false;
  4082. }
  4083. setSelection(next, false, true);
  4084. }
  4085. }
  4086. scrollToShowSelection();
  4087. mSearchString.clear();
  4088. handled = true;
  4089. }
  4090. break;
  4091. case KEY_UP:
  4092. if ((mSelectedItems.size() > 0) && mScrollContainer)
  4093. {
  4094. LLFolderViewItem* last_selected = mSelectedItems.back();
  4095. if (!mKeyboardSelection)
  4096. {
  4097. setSelection(last_selected, false, true);
  4098. mKeyboardSelection = true;
  4099. }
  4100. LLFolderViewItem* prev = NULL;
  4101. if (mask & MASK_SHIFT)
  4102. {
  4103. // Do not shift select down to children of folders (they
  4104. // are implicitly selected through parent)
  4105. prev = last_selected->getPreviousOpenNode(false);
  4106. if (prev)
  4107. {
  4108. if (prev->isSelected())
  4109. {
  4110. // Shrink selection
  4111. changeSelectionFromRoot(last_selected, false);
  4112. }
  4113. else if (last_selected->getParentFolder() ==
  4114. prev->getParentFolder())
  4115. {
  4116. // Grow selection
  4117. changeSelectionFromRoot(prev, true);
  4118. }
  4119. }
  4120. }
  4121. else
  4122. {
  4123. prev = last_selected->getPreviousOpenNode();
  4124. if (prev)
  4125. {
  4126. if (prev == this)
  4127. {
  4128. return false;
  4129. }
  4130. setSelection(prev, false, true);
  4131. }
  4132. }
  4133. scrollToShowSelection();
  4134. mSearchString.clear();
  4135. handled = true;
  4136. }
  4137. break;
  4138. case KEY_RIGHT:
  4139. if (mSelectedItems.size())
  4140. {
  4141. LLFolderViewItem* last_selected = getCurSelectedItem();
  4142. last_selected->setOpen(true);
  4143. mSearchString.clear();
  4144. handled = true;
  4145. }
  4146. break;
  4147. case KEY_LEFT:
  4148. if (mSelectedItems.size())
  4149. {
  4150. LLFolderViewItem* last_selected = getCurSelectedItem();
  4151. LLFolderViewItem* parent_folder =
  4152. last_selected->getParentFolder();
  4153. if (!last_selected->isOpen() &&
  4154. parent_folder && parent_folder->getParentFolder())
  4155. {
  4156. setSelection(parent_folder, false, true);
  4157. }
  4158. else
  4159. {
  4160. last_selected->setOpen(false);
  4161. }
  4162. mSearchString.clear();
  4163. scrollToShowSelection();
  4164. handled = true;
  4165. }
  4166. }
  4167. if (!handled && hasFocus() && key == KEY_BACKSPACE)
  4168. {
  4169. mSearchTimer.reset();
  4170. if (mSearchString.size())
  4171. {
  4172. mSearchString.erase(mSearchString.size() - 1, 1);
  4173. }
  4174. search(getCurSelectedItem(), mSearchString);
  4175. handled = true;
  4176. }
  4177. return handled;
  4178. }
  4179. bool LLFolderView::handleUnicodeCharHere(llwchar uni_char)
  4180. {
  4181. if (uni_char < 0x20 || uni_char == 0x7F) // Control character or DEL
  4182. {
  4183. return false;
  4184. }
  4185. if (uni_char > 0x7f)
  4186. {
  4187. llwarns << "Cannot handle non-ASCII yet, aborting" << llendl;
  4188. return false;
  4189. }
  4190. bool handled = false;
  4191. if (gFocusMgr.childHasKeyboardFocus(getRoot()))
  4192. {
  4193. // SL-51858: key presses are not being passed to the pop-up menu. A
  4194. // proper fix is non-trivial so instead just close the menu.
  4195. LLMenuGL* menup = (LLMenuGL*)mPopupMenuHandle.get();
  4196. if (menup && menup->isOpen())
  4197. {
  4198. gMenuHolderp->hideMenus();
  4199. }
  4200. // Do text search
  4201. if (mSearchTimer.getElapsedTimeF32() > LLUI::sTypeAheadTimeout)
  4202. {
  4203. mSearchString.clear();
  4204. }
  4205. mSearchTimer.reset();
  4206. if (mSearchString.size() < 128)
  4207. {
  4208. mSearchString += uni_char;
  4209. }
  4210. search(getCurSelectedItem(), mSearchString);
  4211. handled = true;
  4212. }
  4213. return handled;
  4214. }
  4215. bool LLFolderView::canDoDelete() const
  4216. {
  4217. if (mSelectedItems.empty())
  4218. {
  4219. return false;
  4220. }
  4221. for (selected_items_t::const_iterator it = mSelectedItems.begin(),
  4222. end = mSelectedItems.end();
  4223. it != end; ++it)
  4224. {
  4225. LLFolderViewItem* item = *it;
  4226. if (!item || !item->getListener() ||
  4227. !item->getListener()->isItemRemovable())
  4228. {
  4229. return false;
  4230. }
  4231. }
  4232. return true;
  4233. }
  4234. void LLFolderView::doDelete()
  4235. {
  4236. if (mSelectedItems.size() > 0)
  4237. {
  4238. removeSelectedItems();
  4239. }
  4240. }
  4241. bool LLFolderView::handleMouseDown(S32 x, S32 y, MASK mask)
  4242. {
  4243. mKeyboardSelection = false;
  4244. mSearchString.clear();
  4245. setFocus(true);
  4246. return LLView::handleMouseDown(x, y, mask);
  4247. }
  4248. void LLFolderView::onFocusLost()
  4249. {
  4250. releaseMenuHandler();
  4251. LLUICtrl::onFocusLost();
  4252. }
  4253. bool LLFolderView::search(LLFolderViewItem* first_item,
  4254. const std::string& search_string, bool backward)
  4255. {
  4256. // Get first selected item
  4257. LLFolderViewItem* search_item = first_item;
  4258. // Make sure search string is upper case
  4259. std::string upper_case_string = search_string;
  4260. LLStringUtil::toUpper(upper_case_string);
  4261. // If nothing selected, select first item in folder
  4262. if (!search_item)
  4263. {
  4264. // Start from first item
  4265. search_item = getNextFromChild(NULL);
  4266. }
  4267. // Search over all open nodes for first substring match (with wrapping)
  4268. bool found = false;
  4269. LLFolderViewItem* original_search_item = search_item;
  4270. do
  4271. {
  4272. // Wrap at end
  4273. if (!search_item)
  4274. {
  4275. if (backward)
  4276. {
  4277. search_item = getPreviousFromChild(NULL);
  4278. }
  4279. else
  4280. {
  4281. search_item = getNextFromChild(NULL);
  4282. }
  4283. if (!search_item || search_item == original_search_item)
  4284. {
  4285. break;
  4286. }
  4287. }
  4288. std::string current_item_label(search_item->getSearchableData());
  4289. S32 search_string_length = llmin(upper_case_string.size(),
  4290. current_item_label.size());
  4291. if (!current_item_label.compare(0, search_string_length,
  4292. upper_case_string))
  4293. {
  4294. found = true;
  4295. break;
  4296. }
  4297. if (backward)
  4298. {
  4299. search_item = search_item->getPreviousOpenNode();
  4300. }
  4301. else
  4302. {
  4303. search_item = search_item->getNextOpenNode();
  4304. }
  4305. }
  4306. while (search_item != original_search_item);
  4307. if (found)
  4308. {
  4309. setSelection(search_item, false, true);
  4310. scrollToShowSelection();
  4311. }
  4312. return found;
  4313. }
  4314. bool LLFolderView::handleDoubleClick(S32 x, S32 y, MASK mask)
  4315. {
  4316. // Skip LLFolderViewFolder::handleDoubleClick()
  4317. return LLView::handleDoubleClick(x, y, mask);
  4318. }
  4319. bool LLFolderView::handleRightMouseDown(S32 x, S32 y, MASK mask)
  4320. {
  4321. // If the context menu has not yet been created for this item, this call
  4322. // will create it now. HB
  4323. LLMenuGL* menup = getContextMenu();
  4324. // All user operations move keyboard focus to inventory this way, we know
  4325. // when to stop auto-updating a search
  4326. setFocus(true);
  4327. bool handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
  4328. S32 count = mSelectedItems.size();
  4329. if (handled && count > 0 && menup)
  4330. {
  4331. const LLView::child_list_t* list = menup->getChildList();
  4332. for (LLView::child_list_t::const_iterator it = list->begin(),
  4333. end = list->end();
  4334. it != end; ++it)
  4335. {
  4336. (*it)->setVisible(true);
  4337. (*it)->setEnabled(true);
  4338. }
  4339. // Successively filter out invalid options
  4340. U32 flags = FIRST_SELECTED_ITEM;
  4341. U32 multi_select_flag = 0x0;
  4342. if (mSelectedItems.size() > 1)
  4343. {
  4344. multi_select_flag = ITEM_IN_MULTI_SELECTION;
  4345. }
  4346. for (selected_items_t::iterator it = mSelectedItems.begin(),
  4347. end = mSelectedItems.end();
  4348. it != end; ++it)
  4349. {
  4350. (*it)->buildContextMenu(*menup, flags);
  4351. flags = multi_select_flag;
  4352. }
  4353. menup->arrange();
  4354. menup->updateParent(gMenuHolderp);
  4355. LLMenuGL::showPopup(this, menup, x, y);
  4356. }
  4357. else
  4358. {
  4359. if (menup && menup->getVisible())
  4360. {
  4361. menup->setVisible(false);
  4362. }
  4363. setSelection(NULL, false, true);
  4364. }
  4365. return handled;
  4366. }
  4367. bool LLFolderView::handleHover(S32 x, S32 y, MASK mask)
  4368. {
  4369. return LLView::handleHover(x, y, mask);
  4370. }
  4371. bool LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
  4372. EDragAndDropType cargo_type,
  4373. void* cargo_data, EAcceptance* accept,
  4374. std::string& tooltip_msg)
  4375. {
  4376. mDragAndDropThisFrame = true;
  4377. bool handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type,
  4378. cargo_data, accept, tooltip_msg);
  4379. if (handled)
  4380. {
  4381. LL_DEBUGS("UserInput") << "dragAndDrop handled with: drop = "
  4382. << (drop ? "true" : "false") << " - accepted = "
  4383. << (*accept != ACCEPT_NO ? "true" : "false")
  4384. << LL_ENDL;
  4385. }
  4386. return handled;
  4387. }
  4388. bool LLFolderView::handleScrollWheel(S32 x, S32 y, S32 clicks)
  4389. {
  4390. return mScrollContainer &&
  4391. mScrollContainer->handleScrollWheel(x, y, clicks);
  4392. }
  4393. void LLFolderView::deleteAllChildren()
  4394. {
  4395. if (mRenamer == gFocusMgr.getTopCtrl())
  4396. {
  4397. gFocusMgr.setTopCtrl(NULL);
  4398. }
  4399. if (deleteViewByHandle(mPopupMenuHandle))
  4400. {
  4401. mPopupMenuHandle = LLHandle<LLView>();
  4402. }
  4403. mRenamer = NULL;
  4404. mRenameItem = NULL;
  4405. clearSelection();
  4406. LLView::deleteAllChildren();
  4407. }
  4408. void LLFolderView::scrollToShowSelection()
  4409. {
  4410. if (mSelectedItems.size())
  4411. {
  4412. mNeedsScroll = true;
  4413. }
  4414. }
  4415. // If the parent is scroll containter, scrolls it to make the selection is
  4416. // maximally visible.
  4417. void LLFolderView::scrollToShowItem(LLFolderViewItem* item)
  4418. {
  4419. // Do not scroll to items when mouse is being used to scroll/drag and drop
  4420. if (gFocusMgr.childHasMouseCapture(mScrollContainer))
  4421. {
  4422. mNeedsScroll = false;
  4423. return;
  4424. }
  4425. if (item && mScrollContainer)
  4426. {
  4427. LLRect local_rect = item->getRect();
  4428. // Item position relative to display area of scroller:
  4429. LLRect item_scrolled_rect;
  4430. S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight();
  4431. // When navigating with keyboard, only move top of folders on screen,
  4432. // otherwise show whole folder
  4433. S32 max_height_to_show =
  4434. gFocusMgr.childHasKeyboardFocus(this) ?
  4435. llmax(icon_height, sFontLineHeightRounded) + ICON_PAD :
  4436. local_rect.getHeight();
  4437. item->localPointToOtherView(item->getIndentation(),
  4438. llmax(0,
  4439. local_rect.getHeight() -
  4440. max_height_to_show),
  4441. &item_scrolled_rect.mLeft,
  4442. &item_scrolled_rect.mBottom,
  4443. mScrollContainer);
  4444. item->localPointToOtherView(local_rect.getWidth(),
  4445. local_rect.getHeight(),
  4446. &item_scrolled_rect.mRight,
  4447. &item_scrolled_rect.mTop,
  4448. mScrollContainer);
  4449. item_scrolled_rect.mRight =
  4450. llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE,
  4451. item_scrolled_rect.mRight);
  4452. LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() -
  4453. item_scrolled_rect.mLeft,
  4454. mScrollContainer->getRect().getHeight() -
  4455. item_scrolled_rect.mTop - 1);
  4456. S32 max_scroll_offset = getVisibleRect().getHeight() -
  4457. item_scrolled_rect.getHeight();
  4458. // If we are scrolling to focus on a new item
  4459. if (item != mLastScrollItem ||
  4460. // Or the item has just appeared on screen and it was not on screen
  4461. // before
  4462. (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset &&
  4463. (mLastScrollOffset.mY < 0 ||
  4464. mLastScrollOffset.mY > max_scroll_offset)))
  4465. {
  4466. // We now have a position on screen that we want to keep stable
  4467. // offset of selection relative to top of visible area
  4468. mLastScrollOffset = scroll_offset;
  4469. mLastScrollItem = item;
  4470. }
  4471. mScrollContainer->scrollToShowRect(item_scrolled_rect,
  4472. mLastScrollOffset);
  4473. // After scrolling, store new offset; in case we do not have room to
  4474. // maintain the original position.
  4475. LLCoordGL new_item_left_top;
  4476. item->localPointToOtherView(item->getIndentation(),
  4477. item->getRect().getHeight(),
  4478. &new_item_left_top.mX,
  4479. &new_item_left_top.mY,
  4480. mScrollContainer);
  4481. mLastScrollOffset.set(-mScrollContainer->getBorderWidth() -
  4482. new_item_left_top.mX,
  4483. mScrollContainer->getRect().getHeight() -
  4484. new_item_left_top.mY - 1);
  4485. }
  4486. }
  4487. LLRect LLFolderView::getVisibleRect()
  4488. {
  4489. S32 visible_height = mScrollContainer->getRect().getHeight();
  4490. S32 visible_width = mScrollContainer->getRect().getWidth();
  4491. LLRect visible_rect;
  4492. visible_rect.setLeftTopAndSize(-getRect().mLeft,
  4493. visible_height - getRect().mBottom,
  4494. visible_width, visible_height);
  4495. return visible_rect;
  4496. }
  4497. bool LLFolderView::getShowSelectionContext()
  4498. {
  4499. if (mShowSelectionContext)
  4500. {
  4501. return true;
  4502. }
  4503. LLMenuGL* menup = (LLMenuGL*)mPopupMenuHandle.get();
  4504. return menup && menup->getVisible();
  4505. }
  4506. void LLFolderView::setShowSingleSelection(bool show)
  4507. {
  4508. if (show != mShowSingleSelection)
  4509. {
  4510. mMultiSelectionFadeTimer.reset();
  4511. mShowSingleSelection = show;
  4512. }
  4513. }
  4514. void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp)
  4515. {
  4516. mItemMap[id] = itemp;
  4517. }
  4518. void LLFolderView::removeItemID(const LLUUID& id)
  4519. {
  4520. mItemMap.erase(id);
  4521. }
  4522. LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id)
  4523. {
  4524. if (id.isNull())
  4525. {
  4526. return this;
  4527. }
  4528. item_map_t::iterator map_it = mItemMap.find(id);
  4529. if (map_it != mItemMap.end())
  4530. {
  4531. return map_it->second;
  4532. }
  4533. return NULL;
  4534. }
  4535. // Main idle routine
  4536. void LLFolderView::doIdle()
  4537. {
  4538. LL_FAST_TIMER(FTM_INVENTORY);
  4539. mFilter.clearModified();
  4540. bool filter_modified_and_active =
  4541. mFilter.isNotDefault() &&
  4542. mCompletedFilterGeneration < mFilter.getCurrentGeneration();
  4543. mNeedsAutoSelect = mCanAutoSelect && filter_modified_and_active &&
  4544. !gFocusMgr.childHasKeyboardFocus(this) &&
  4545. !gFocusMgr.getMouseCapture();
  4546. // Filter to determine visiblity before arranging
  4547. filterFromRoot();
  4548. // Automatically show matching items, and select first one. Do this every
  4549. // frame until user puts keyboard focus into the inventory window signaling
  4550. // the end of the automatic update.
  4551. // Only do this when mNeedsFilter is set, meaning filtered items have
  4552. // potentially changed
  4553. if (mNeedsAutoSelect)
  4554. {
  4555. LL_FAST_TIMER(FTM_AUTO_SELECT);
  4556. // Select new item only if a filtered item not currently selected
  4557. LLFolderViewItem* selected_itemp =
  4558. mSelectedItems.empty() ? NULL : mSelectedItems.back();
  4559. if (selected_itemp && !sFolderViewItems.count(selected_itemp))
  4560. {
  4561. // There is a crash bug due to a race condition: when a folder view
  4562. // item is destroyed, its address may still appear in
  4563. // mSelectedItems a couple of doIdle()s later, even if you
  4564. // explicitely clear this list and dirty the filters in the
  4565. // destructor... This code avoids the crash bug. HB
  4566. llwarns << "Invalid folder view item (" << selected_itemp
  4567. << ") in selection: clearing the latter." << llendl;
  4568. dirtyFilter();
  4569. clearSelection();
  4570. requestArrange();
  4571. }
  4572. else if (!selected_itemp || !selected_itemp->getFiltered())
  4573. {
  4574. // Select first filtered item
  4575. LLSelectFirstFilteredItem filter;
  4576. applyFunctorRecursively(filter);
  4577. }
  4578. scrollToShowSelection();
  4579. }
  4580. bool is_visible = isInVisibleChain();
  4581. if (is_visible)
  4582. {
  4583. sanitizeSelection();
  4584. if (needsArrange())
  4585. {
  4586. arrangeFromRoot();
  4587. }
  4588. }
  4589. if (mSelectedItems.size() && mNeedsScroll)
  4590. {
  4591. scrollToShowItem(mSelectedItems.back());
  4592. // Continue scrolling until animated layout change is done
  4593. if ((!needsArrange() || !is_visible) &&
  4594. getCompletedFilterGeneration() >=
  4595. mFilter.getMinRequiredGeneration())
  4596. {
  4597. mNeedsScroll = false;
  4598. }
  4599. }
  4600. if (!mSignalSelectCallback)
  4601. {
  4602. mGotLeftMouseClick = false;
  4603. return;
  4604. }
  4605. static LLCachedControl<bool> thumbnails(gSavedSettings,
  4606. "AutoShowInventoryThumbnails");
  4607. if (thumbnails && mGotLeftMouseClick && mShowThumbnails &&
  4608. mSelectedItems.size() == 1 && AISAPI::isAvailable())
  4609. {
  4610. LLFolderViewItem* itemp = mSelectedItems.back();
  4611. if (itemp && itemp->getListener())
  4612. {
  4613. HBFloaterThumbnail::showInstance(itemp->getListener()->getUUID(),
  4614. mSourceID, this);
  4615. }
  4616. }
  4617. mGotLeftMouseClick = false;
  4618. if (mSelectCallback)
  4619. {
  4620. // RN: we use keyboard focus as a proxy for user-explicit actions
  4621. bool take_focus = mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS;
  4622. mSelectCallback(this, take_focus, mUserData);
  4623. }
  4624. mSignalSelectCallback = 0;
  4625. }
  4626. //static
  4627. void LLFolderView::idle(void* user_data)
  4628. {
  4629. LLFolderView* self = (LLFolderView*)user_data;
  4630. if (self)
  4631. {
  4632. // Do the real idle
  4633. self->doIdle();
  4634. }
  4635. }
  4636. bool LLInventorySort::updateSort(U32 order)
  4637. {
  4638. if (order == mSortOrder)
  4639. {
  4640. return false;
  4641. }
  4642. mSortOrder = order;
  4643. mByDate = order & LLInventoryFilter::SO_DATE;
  4644. mSystemToTop = order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP;
  4645. mFoldersByName = order & LLInventoryFilter::SO_FOLDERS_BY_NAME;
  4646. return true;
  4647. }
  4648. bool LLInventorySort::operator()(const LLFolderViewItem* const& a,
  4649. const LLFolderViewItem* const& b)
  4650. {
  4651. // We sort by name if we are not sorting by date OR if these are folders
  4652. // and we are sorting folders by name.
  4653. bool by_name = !mByDate ||
  4654. (mFoldersByName && a->getSortGroup() != SG_ITEM);
  4655. if (a->getSortGroup() != b->getSortGroup())
  4656. {
  4657. if (mSystemToTop)
  4658. {
  4659. // Group order is System Folders, Trash, Normal Folders, Items
  4660. return a->getSortGroup() < b->getSortGroup();
  4661. }
  4662. else if (mByDate)
  4663. {
  4664. // Trash needs to go to the bottom if we are sorting by date
  4665. if (a->getSortGroup() == SG_TRASH_FOLDER ||
  4666. b->getSortGroup() == SG_TRASH_FOLDER)
  4667. {
  4668. return b->getSortGroup() == SG_TRASH_FOLDER;
  4669. }
  4670. }
  4671. }
  4672. if (by_name)
  4673. {
  4674. S32 compare = LLStringUtil::compareDict(a->getLabel(), b->getLabel());
  4675. return compare < 0 ||
  4676. (compare == 0 && a->getCreationDate() > b->getCreationDate());
  4677. }
  4678. // This is very very slow. The getCreationDate() is log(n) in number of
  4679. // inventory items.
  4680. time_t first_create = a->getCreationDate();
  4681. time_t second_create = b->getCreationDate();
  4682. if (first_create == second_create)
  4683. {
  4684. return LLStringUtil::compareDict(a->getLabel(), b->getLabel()) < 0;
  4685. }
  4686. return first_create > second_create;
  4687. }
  4688. //static
  4689. void LLFolderView::onRenamerLost(LLUICtrl* renamer, void* user_data)
  4690. {
  4691. renamer->setVisible(false);
  4692. }
  4693. //-----------------------------------------------------------------------------
  4694. // LLFolderViewEventListener class
  4695. //-----------------------------------------------------------------------------
  4696. void LLFolderViewEventListener::arrangeAndSet(LLFolderViewItem* focusp,
  4697. bool set_selection,
  4698. bool take_keyboard_focus)
  4699. {
  4700. if (!focusp) return;
  4701. LLFolderView* rootp = focusp->getRoot();
  4702. if (focusp->getParentFolder())
  4703. {
  4704. focusp->getParentFolder()->requestArrange();
  4705. }
  4706. if (set_selection)
  4707. {
  4708. focusp->setSelectionFromRoot(focusp, true, take_keyboard_focus);
  4709. if (rootp)
  4710. {
  4711. rootp->scrollToShowSelection();
  4712. }
  4713. }
  4714. }
  4715. //static
  4716. void LLFolderViewEventListener::cancelTip(bool not_drop_msg)
  4717. {
  4718. if (sLastDragTipID.notNull() && !(not_drop_msg && sLastDragTipDrop))
  4719. {
  4720. LLNotificationPtr n = gNotifications.find(sLastDragTipID);
  4721. if (n)
  4722. {
  4723. gNotifications.cancel(n);
  4724. }
  4725. sLastDragTipID.setNull();
  4726. sLastDragTipMsg.clear();
  4727. sLastDragTipDrop = false;
  4728. }
  4729. }
  4730. //static
  4731. void LLFolderViewEventListener::dragOrDropTip(bool drop,
  4732. const std::string& tooltip_msg)
  4733. {
  4734. if (tooltip_msg.empty())
  4735. {
  4736. if (drop)
  4737. {
  4738. cancelTip(); // Drag and drop action ended without warning
  4739. }
  4740. return;
  4741. }
  4742. if (!sLastDragTipMsg.empty())
  4743. {
  4744. // If the last notification has expired, clear its data so to display
  4745. // it again now if needed.
  4746. LLNotificationPtr n = gNotifications.find(sLastDragTipID);
  4747. if (!n || n->isExpired() || n->isCancelled())
  4748. {
  4749. sLastDragTipMsg.clear();
  4750. sLastDragTipID.setNull();
  4751. sLastDragTipDrop = false;
  4752. }
  4753. }
  4754. if (sLastDragTipMsg != tooltip_msg)
  4755. {
  4756. cancelTip();
  4757. LLSD subs;
  4758. subs["MESSAGE"] = tooltip_msg;
  4759. LLNotificationPtr n = gNotifications.add("LongGenericMessageTip",
  4760. subs);
  4761. if (n)
  4762. {
  4763. sLastDragTipID = n->getID();
  4764. sLastDragTipMsg = tooltip_msg;
  4765. sLastDragTipDrop = drop;
  4766. }
  4767. }
  4768. }
  4769. //-----------------------------------------------------------------------------
  4770. // LLInventoryFilter class
  4771. //-----------------------------------------------------------------------------
  4772. LLInventoryFilter::LLInventoryFilter(const std::string& name)
  4773. : mName(name),
  4774. mModified(false),
  4775. mNeedTextRebuild(true),
  4776. mOrder(SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately
  4777. mFilterGeneration(0),
  4778. mNextFilterGeneration(1),
  4779. mMinRequiredGeneration(0),
  4780. mMustPassGeneration(S32_MAX),
  4781. mFilterBehavior(FILTER_NONE),
  4782. mFilterCount(0),
  4783. mFilterSubType(-1),
  4784. mHideLibrary(false),
  4785. mFilterWorn(false),
  4786. mFilterLastOpen(false),
  4787. mFilterShowLinks(false),
  4788. mSubStringMatchOffset(0)
  4789. {
  4790. mFilterOps.mFilterTypes = 0xffffffff;
  4791. mFilterOps.mMinDate = time_min();
  4792. mFilterOps.mMaxDate = time_max();
  4793. mFilterOps.mHoursAgo = 0;
  4794. mFilterOps.mShowFolderState = SHOW_NON_EMPTY_FOLDERS;
  4795. mFilterOps.mPermissions = PERM_NONE;
  4796. mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff");
  4797. // Copy mFilterOps into mDefaultFilterOps
  4798. markDefault();
  4799. }
  4800. bool LLInventoryFilter::check(LLFolderViewItem* item)
  4801. {
  4802. if (!item) return false;
  4803. LLFolderViewEventListener* listener = item->getListener();
  4804. if (!listener) return false;
  4805. const LLUUID& item_id = listener->getUUID();
  4806. // When filtering is active and we do not explicitly search for a link,
  4807. // omit links.
  4808. if (!mFilterShowLinks)
  4809. {
  4810. const LLInventoryObject* obj = gInventory.getObject(item_id);
  4811. if (isActive() && obj && obj->getIsLinkType() &&
  4812. mFilterSubString.find("(LINK)") == std::string::npos)
  4813. {
  4814. return false;
  4815. }
  4816. }
  4817. static LLCachedControl<bool> hide_cof(gSavedSettings,
  4818. "HideCurrentOutfitFolder");
  4819. if (hide_cof && !LLFolderType::getCanDeleteCOF() &&
  4820. gInventory.isInCOF(item_id))
  4821. {
  4822. return false;
  4823. }
  4824. static LLCachedControl<bool> hide_mp(gSavedSettings,
  4825. "HideMarketplaceFolder");
  4826. if (hide_mp && gInventory.isInMarketPlace(item_id))
  4827. {
  4828. return false;
  4829. }
  4830. if (mHideLibrary &&
  4831. gInventory.isObjectDescendentOf(item_id,
  4832. gInventory.getLibraryRootFolderID()))
  4833. {
  4834. return false;
  4835. }
  4836. LLInventoryType::EType object_type = listener->getInventoryType();
  4837. if (object_type != LLInventoryType::IT_NONE &&
  4838. !(0x1 << object_type & mFilterOps.mFilterTypes))
  4839. {
  4840. return false;
  4841. }
  4842. if (mFilterSubString.empty())
  4843. {
  4844. mSubStringMatchOffset = std::string::npos;
  4845. }
  4846. else
  4847. {
  4848. std::string search_string = mFilterSubString;
  4849. if (search_string != "(LINK)" && search_string.find("(LINK)"))
  4850. {
  4851. LLStringUtil::replaceString(search_string, "(LINK)", "");
  4852. }
  4853. mSubStringMatchOffset = item->getSearchableData().find(search_string);
  4854. if (mSubStringMatchOffset == std::string::npos)
  4855. {
  4856. return false;
  4857. }
  4858. }
  4859. if (mFilterSubType != -1 && listener->getSubType() != mFilterSubType)
  4860. {
  4861. return false;
  4862. }
  4863. if (mFilterWorn && !gAgentWearables.isWearingItem(item_id) &&
  4864. !(isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item_id)))
  4865. {
  4866. return false;
  4867. }
  4868. if (mFilterLastOpen && mLastOpenID.notNull() &&
  4869. !gInventory.isObjectDescendentOf(item_id, mLastOpenID))
  4870. {
  4871. return false;
  4872. }
  4873. if ((listener->getPermissionMask() & mFilterOps.mPermissions) !=
  4874. mFilterOps.mPermissions)
  4875. {
  4876. return false;
  4877. }
  4878. time_t earliest = time_corrected() - mFilterOps.mHoursAgo * 3600;
  4879. if (mFilterOps.mMinDate > time_min() && mFilterOps.mMinDate < earliest)
  4880. {
  4881. earliest = mFilterOps.mMinDate;
  4882. }
  4883. else if (!mFilterOps.mHoursAgo)
  4884. {
  4885. earliest = 0;
  4886. }
  4887. if (listener->getCreationDate() < earliest ||
  4888. listener->getCreationDate() > mFilterOps.mMaxDate)
  4889. {
  4890. return false;
  4891. }
  4892. static LLCachedControl<bool> hide_empty_folders(gSavedSettings,
  4893. "HideEmptySystemFolders");
  4894. if (object_type == LLInventoryType::IT_CATEGORY && hide_empty_folders)
  4895. {
  4896. if (LLViewerFolderType::lookupIsHiddenIfEmpty(listener->getPreferredType()))
  4897. {
  4898. // Force the fetching of those folders so they are hidden if they
  4899. // really are empty...
  4900. gInventory.fetchDescendentsOf(item_id);
  4901. return false;
  4902. }
  4903. }
  4904. return true;
  4905. }
  4906. // Has user modified default filter params ?
  4907. bool LLInventoryFilter::isNotDefault()
  4908. {
  4909. return mFilterOps.mFilterTypes != mDefaultFilterOps.mFilterTypes ||
  4910. mFilterSubType != -1 || mFilterWorn || mFilterLastOpen ||
  4911. mFilterSubString.size() || mHideLibrary ||
  4912. mFilterOps.mPermissions != mDefaultFilterOps.mPermissions ||
  4913. mFilterOps.mMinDate != mDefaultFilterOps.mMinDate ||
  4914. mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate ||
  4915. mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo;
  4916. }
  4917. bool LLInventoryFilter::isActive()
  4918. {
  4919. return mFilterOps.mFilterTypes != 0xffffffff || mFilterWorn ||
  4920. mFilterLastOpen || mFilterSubString.size() ||
  4921. mFilterOps.mPermissions != PERM_NONE ||
  4922. mFilterOps.mMinDate != time_min() ||
  4923. mFilterOps.mMaxDate != time_max() ||
  4924. mFilterOps.mHoursAgo != 0;
  4925. }
  4926. void LLInventoryFilter::setFilterTypes(U32 types)
  4927. {
  4928. if (mFilterOps.mFilterTypes != types)
  4929. {
  4930. // Keep current items only if no type bits getting turned off
  4931. bool fewer_bits_set = (mFilterOps.mFilterTypes & ~types) != 0;
  4932. bool more_bits_set = (~mFilterOps.mFilterTypes & types) != 0;
  4933. mFilterOps.mFilterTypes = types;
  4934. if (more_bits_set && fewer_bits_set)
  4935. {
  4936. // Neither less or more restrive, both simultaneously, so we need
  4937. // to filter from scratch
  4938. setModified(FILTER_RESTART);
  4939. }
  4940. else if (more_bits_set)
  4941. {
  4942. // Target is only one of all requested types so more type
  4943. // bits == less restrictive
  4944. setModified(FILTER_LESS_RESTRICTIVE);
  4945. }
  4946. else if (fewer_bits_set)
  4947. {
  4948. setModified(FILTER_MORE_RESTRICTIVE);
  4949. }
  4950. }
  4951. }
  4952. void LLInventoryFilter::setFilterSubString(const std::string& str)
  4953. {
  4954. if (mFilterSubString != str)
  4955. {
  4956. std::string upper = str;
  4957. LLStringUtil::toUpper(upper);
  4958. // Check whether the new search string contains "(LINK)" and not the
  4959. // old one, or vice-versa...
  4960. bool had_link = mFilterSubString.find("(LINK)") != std::string::npos;
  4961. bool has_link = upper.find("(LINK)") != std::string::npos;
  4962. size_t old_size = mFilterSubString.size();
  4963. size_t new_size = upper.size();
  4964. // Hitting BACKSPACE, for example
  4965. bool looser = had_link == has_link && old_size >= new_size &&
  4966. !mFilterSubString.substr(0, new_size).compare(upper);
  4967. // Appending new characters
  4968. bool stricter = had_link == has_link && old_size < new_size &&
  4969. !upper.substr(0, old_size).compare(mFilterSubString);
  4970. mFilterSubString = upper;
  4971. LLStringUtil::trimHead(mFilterSubString);
  4972. if (looser)
  4973. {
  4974. setModified(FILTER_LESS_RESTRICTIVE);
  4975. }
  4976. else if (stricter)
  4977. {
  4978. setModified(FILTER_MORE_RESTRICTIVE);
  4979. }
  4980. else
  4981. {
  4982. setModified(FILTER_RESTART);
  4983. }
  4984. }
  4985. }
  4986. void LLInventoryFilter::setFilterPermissions(PermissionMask perms)
  4987. {
  4988. if (mFilterOps.mPermissions != perms)
  4989. {
  4990. // Keep current items only if no perm bits getting turned off
  4991. bool fewer_bits_set = (mFilterOps.mPermissions & ~perms) != 0;
  4992. bool more_bits_set = (~mFilterOps.mPermissions & perms) != 0;
  4993. mFilterOps.mPermissions = perms;
  4994. if (more_bits_set && fewer_bits_set)
  4995. {
  4996. setModified(FILTER_RESTART);
  4997. }
  4998. else if (more_bits_set)
  4999. {
  5000. // Target must have all requested permission bits, so more bits
  5001. // means more restrictive
  5002. setModified(FILTER_MORE_RESTRICTIVE);
  5003. }
  5004. else if (fewer_bits_set)
  5005. {
  5006. setModified(FILTER_LESS_RESTRICTIVE);
  5007. }
  5008. }
  5009. }
  5010. void LLInventoryFilter::setDateRange(time_t min_date, time_t max_date)
  5011. {
  5012. mFilterOps.mHoursAgo = 0;
  5013. if (mFilterOps.mMinDate != min_date)
  5014. {
  5015. mFilterOps.mMinDate = min_date;
  5016. setModified();
  5017. }
  5018. if (mFilterOps.mMaxDate != llmax(mFilterOps.mMinDate, max_date))
  5019. {
  5020. mFilterOps.mMaxDate = llmax(mFilterOps.mMinDate, max_date);
  5021. setModified();
  5022. }
  5023. }
  5024. void LLInventoryFilter::setDateRangeLastLogoff(bool sl)
  5025. {
  5026. if (sl && !isSinceLogoff())
  5027. {
  5028. setDateRange(mLastLogoff, time_max());
  5029. setModified();
  5030. }
  5031. if (!sl && isSinceLogoff())
  5032. {
  5033. setDateRange(0, time_max());
  5034. setModified();
  5035. }
  5036. }
  5037. bool LLInventoryFilter::isSinceLogoff()
  5038. {
  5039. return mFilterOps.mMinDate == (time_t)mLastLogoff &&
  5040. mFilterOps.mMaxDate == (time_t)time_max();
  5041. }
  5042. void LLInventoryFilter::setHoursAgo(U32 hours)
  5043. {
  5044. if (mFilterOps.mHoursAgo != hours)
  5045. {
  5046. // *NOTE: need to cache last filter time, in case filter goes stale
  5047. bool looser = mFilterOps.mMinDate == time_min() &&
  5048. mFilterOps.mMaxDate == time_max() &&
  5049. hours > mFilterOps.mHoursAgo;
  5050. bool stricter = mFilterOps.mMinDate == time_min() &&
  5051. mFilterOps.mMaxDate == time_max() &&
  5052. hours <= mFilterOps.mHoursAgo;
  5053. mFilterOps.mHoursAgo = hours;
  5054. mFilterOps.mMinDate = time_min();
  5055. mFilterOps.mMaxDate = time_max();
  5056. if (looser)
  5057. {
  5058. setModified(FILTER_LESS_RESTRICTIVE);
  5059. }
  5060. else if (stricter)
  5061. {
  5062. setModified(FILTER_MORE_RESTRICTIVE);
  5063. }
  5064. else
  5065. {
  5066. setModified(FILTER_RESTART);
  5067. }
  5068. }
  5069. }
  5070. void LLInventoryFilter::setShowFolderState(EFolderShow state)
  5071. {
  5072. if (mFilterOps.mShowFolderState != state)
  5073. {
  5074. mFilterOps.mShowFolderState = state;
  5075. if (state == SHOW_NON_EMPTY_FOLDERS)
  5076. {
  5077. // Showing fewer folders than before
  5078. setModified(FILTER_MORE_RESTRICTIVE);
  5079. }
  5080. else if (state == SHOW_ALL_FOLDERS)
  5081. {
  5082. // Showing same folders as before and then some
  5083. setModified(FILTER_LESS_RESTRICTIVE);
  5084. }
  5085. else
  5086. {
  5087. setModified();
  5088. }
  5089. }
  5090. }
  5091. void LLInventoryFilter::setSortOrder(U32 order)
  5092. {
  5093. if (mOrder != order)
  5094. {
  5095. mOrder = order;
  5096. setModified();
  5097. }
  5098. }
  5099. void LLInventoryFilter::markDefault()
  5100. {
  5101. mDefaultFilterOps = mFilterOps;
  5102. }
  5103. void LLInventoryFilter::resetDefault()
  5104. {
  5105. mFilterOps = mDefaultFilterOps;
  5106. setModified();
  5107. }
  5108. void LLInventoryFilter::setModified(EFilterBehavior behavior)
  5109. {
  5110. mModified = true;
  5111. mNeedTextRebuild = true;
  5112. mFilterGeneration = mNextFilterGeneration++;
  5113. if (mFilterBehavior == FILTER_NONE)
  5114. {
  5115. mFilterBehavior = behavior;
  5116. }
  5117. else if (mFilterBehavior != behavior)
  5118. {
  5119. // Trying to do both less restrictive and more restrictive filter
  5120. // basically means restart from scratch
  5121. mFilterBehavior = FILTER_RESTART;
  5122. }
  5123. if (isNotDefault())
  5124. {
  5125. // Tf not keeping current filter results, update last valid as well
  5126. switch (mFilterBehavior)
  5127. {
  5128. case FILTER_RESTART:
  5129. mMustPassGeneration = mFilterGeneration;
  5130. mMinRequiredGeneration = mFilterGeneration;
  5131. break;
  5132. case FILTER_LESS_RESTRICTIVE:
  5133. mMustPassGeneration = mFilterGeneration;
  5134. break;
  5135. case FILTER_MORE_RESTRICTIVE:
  5136. mMinRequiredGeneration = mFilterGeneration;
  5137. // Must have passed either current filter generation
  5138. // (meaningless, as it has not been run yet) or some older
  5139. // generation, so keep the value
  5140. mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration);
  5141. break;
  5142. default:
  5143. llerrs << "Bad filter behavior specified" << llendl;
  5144. }
  5145. }
  5146. else
  5147. {
  5148. // Shortcut disabled filters to show everything immediately
  5149. mMinRequiredGeneration = 0;
  5150. mMustPassGeneration = S32_MAX;
  5151. }
  5152. }
  5153. bool LLInventoryFilter::isFilterWith(LLInventoryType::EType t)
  5154. {
  5155. return (mFilterOps.mFilterTypes & (0x01 << t)) != 0;
  5156. }
  5157. std::string LLInventoryFilter::getFilterText()
  5158. {
  5159. if (!mNeedTextRebuild)
  5160. {
  5161. return mFilterText;
  5162. }
  5163. mNeedTextRebuild = false;
  5164. std::string filtered_types;
  5165. std::string not_filtered_types;
  5166. bool filtered_by_type = false;
  5167. bool filtered_by_all_types = true;
  5168. S32 num_filter_types = 0;
  5169. mFilterText.clear();
  5170. if (isFilterWith(LLInventoryType::IT_ANIMATION))
  5171. {
  5172. filtered_types += " Animations,";
  5173. filtered_by_type = true;
  5174. ++num_filter_types;
  5175. }
  5176. else
  5177. {
  5178. not_filtered_types += " Animations,";
  5179. filtered_by_all_types = false;
  5180. }
  5181. if (isFilterWith(LLInventoryType::IT_CALLINGCARD))
  5182. {
  5183. filtered_types += " Calling Cards,";
  5184. filtered_by_type = true;
  5185. ++num_filter_types;
  5186. }
  5187. else
  5188. {
  5189. not_filtered_types += " Calling Cards,";
  5190. filtered_by_all_types = false;
  5191. }
  5192. if (isFilterWith(LLInventoryType::IT_WEARABLE))
  5193. {
  5194. filtered_types += " Clothing,";
  5195. filtered_by_type = true;
  5196. ++num_filter_types;
  5197. }
  5198. else
  5199. {
  5200. not_filtered_types += " Clothing,";
  5201. filtered_by_all_types = false;
  5202. }
  5203. if (isFilterWith(LLInventoryType::IT_GESTURE))
  5204. {
  5205. filtered_types += " Gestures,";
  5206. filtered_by_type = true;
  5207. ++num_filter_types;
  5208. }
  5209. else
  5210. {
  5211. not_filtered_types += " Gestures,";
  5212. filtered_by_all_types = false;
  5213. }
  5214. if (isFilterWith(LLInventoryType::IT_LANDMARK))
  5215. {
  5216. filtered_types += " Landmarks,";
  5217. filtered_by_type = true;
  5218. ++num_filter_types;
  5219. }
  5220. else
  5221. {
  5222. not_filtered_types += " Landmarks,";
  5223. filtered_by_all_types = false;
  5224. }
  5225. if (isFilterWith(LLInventoryType::IT_NOTECARD))
  5226. {
  5227. filtered_types += " Notecards,";
  5228. filtered_by_type = true;
  5229. ++num_filter_types;
  5230. }
  5231. else
  5232. {
  5233. not_filtered_types += " Notecards,";
  5234. filtered_by_all_types = false;
  5235. }
  5236. if (isFilterWith(LLInventoryType::IT_OBJECT) &&
  5237. isFilterWith(LLInventoryType::IT_ATTACHMENT))
  5238. {
  5239. filtered_types += " Objects,";
  5240. filtered_by_type = true;
  5241. ++num_filter_types;
  5242. }
  5243. else
  5244. {
  5245. not_filtered_types += " Objects,";
  5246. filtered_by_all_types = false;
  5247. }
  5248. if (isFilterWith(LLInventoryType::IT_LSL))
  5249. {
  5250. filtered_types += " Scripts,";
  5251. filtered_by_type = true;
  5252. ++num_filter_types;
  5253. }
  5254. else
  5255. {
  5256. not_filtered_types += " Scripts,";
  5257. filtered_by_all_types = false;
  5258. }
  5259. if (isFilterWith(LLInventoryType::IT_SOUND))
  5260. {
  5261. filtered_types += " Sounds,";
  5262. filtered_by_type = true;
  5263. ++num_filter_types;
  5264. }
  5265. else
  5266. {
  5267. not_filtered_types += " Sounds,";
  5268. filtered_by_all_types = false;
  5269. }
  5270. if (isFilterWith(LLInventoryType::IT_TEXTURE))
  5271. {
  5272. filtered_types += " Textures,";
  5273. filtered_by_type = true;
  5274. ++num_filter_types;
  5275. }
  5276. else
  5277. {
  5278. not_filtered_types += " Textures,";
  5279. filtered_by_all_types = false;
  5280. }
  5281. if (isFilterWith(LLInventoryType::IT_SNAPSHOT))
  5282. {
  5283. filtered_types += " Snapshots,";
  5284. filtered_by_type = true;
  5285. ++num_filter_types;
  5286. }
  5287. else
  5288. {
  5289. not_filtered_types += " Snapshots,";
  5290. filtered_by_all_types = false;
  5291. }
  5292. if (isFilterWith(LLInventoryType::IT_SETTINGS))
  5293. {
  5294. filtered_types += " Settings,";
  5295. filtered_by_type = true;
  5296. ++num_filter_types;
  5297. }
  5298. else
  5299. {
  5300. not_filtered_types += " Settings,";
  5301. filtered_by_all_types = false;
  5302. }
  5303. if (!LLInventoryModelFetch::getInstance()->backgroundFetchActive() &&
  5304. filtered_by_type && !filtered_by_all_types)
  5305. {
  5306. mFilterText += " - ";
  5307. if (num_filter_types < 5)
  5308. {
  5309. mFilterText += filtered_types;
  5310. }
  5311. else
  5312. {
  5313. mFilterText += "No ";
  5314. mFilterText += not_filtered_types;
  5315. }
  5316. // Remove the ',' at the end
  5317. mFilterText.erase(mFilterText.size() - 1, 1);
  5318. }
  5319. if (isSinceLogoff())
  5320. {
  5321. mFilterText += " - Since Logoff";
  5322. }
  5323. if (getFilterWorn())
  5324. {
  5325. mFilterText += " - Worn";
  5326. }
  5327. if (getFilterLastOpen())
  5328. {
  5329. mFilterText += " - Last Open";
  5330. }
  5331. return mFilterText;
  5332. }
  5333. void LLInventoryFilter::toLLSD(LLSD& data)
  5334. {
  5335. data["filter_types"] = (LLSD::Integer)getFilterTypes();
  5336. data["min_date"] = (LLSD::Integer)getMinDate();
  5337. data["max_date"] = (LLSD::Integer)getMaxDate();
  5338. data["hours_ago"] = (LLSD::Integer)getHoursAgo();
  5339. data["show_folder_state"] = (LLSD::Integer)getShowFolderState();
  5340. data["permissions"] = (LLSD::Integer)getFilterPermissions();
  5341. data["substring"] = (LLSD::String)getFilterSubString();
  5342. data["sort_order"] = (LLSD::Integer)getSortOrder();
  5343. data["since_logoff"] = (LLSD::Boolean)isSinceLogoff();
  5344. }
  5345. void LLInventoryFilter::fromLLSD(LLSD& data)
  5346. {
  5347. if (data.has("filter_types"))
  5348. {
  5349. setFilterTypes((U32)data["filter_types"].asInteger());
  5350. }
  5351. if (data.has("min_date") && data.has("max_date"))
  5352. {
  5353. setDateRange(data["min_date"].asInteger(), data["max_date"].asInteger());
  5354. }
  5355. if (data.has("hours_ago"))
  5356. {
  5357. setHoursAgo((U32)data["hours_ago"].asInteger());
  5358. }
  5359. if (data.has("show_folder_state"))
  5360. {
  5361. setShowFolderState((EFolderShow)data["show_folder_state"].asInteger());
  5362. }
  5363. if (data.has("permissions"))
  5364. {
  5365. setFilterPermissions((PermissionMask)data["permissions"].asInteger());
  5366. }
  5367. if (data.has("substring"))
  5368. {
  5369. setFilterSubString(std::string(data["substring"].asString()));
  5370. }
  5371. if (data.has("sort_order"))
  5372. {
  5373. setSortOrder((U32)data["sort_order"].asInteger());
  5374. }
  5375. if (data.has("since_logoff"))
  5376. {
  5377. setDateRangeLastLogoff((bool)data["since_logoff"].asBoolean());
  5378. }
  5379. }
  5380. //-----------------------------------------------------------------------------
  5381. // LLSelectFirstFilteredItem class
  5382. //-----------------------------------------------------------------------------
  5383. void LLSelectFirstFilteredItem::doItem(LLFolderViewItem* item)
  5384. {
  5385. if (item && item->getFiltered() && !mItemSelected)
  5386. {
  5387. LLFolderView* rootp = item->getRoot();
  5388. if (rootp)
  5389. {
  5390. rootp->setSelection(item, false, false);
  5391. }
  5392. LLFolderViewFolder* parentp = item->getParentFolder();
  5393. if (parentp)
  5394. {
  5395. parentp->setOpenArrangeRecursively(true,
  5396. LLFolderViewFolder::RECURSE_UP);
  5397. }
  5398. if (rootp)
  5399. {
  5400. rootp->scrollToShowSelection();
  5401. }
  5402. mItemSelected = true;
  5403. }
  5404. }
  5405. void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder)
  5406. {
  5407. if (folder && folder->getFiltered() && !mItemSelected)
  5408. {
  5409. LLFolderView* rootp = folder->getRoot();
  5410. if (rootp)
  5411. {
  5412. rootp->setSelection(folder, false, false);
  5413. }
  5414. LLFolderViewFolder* parentp = folder->getParentFolder();
  5415. if (parentp)
  5416. {
  5417. parentp->setOpenArrangeRecursively(true,
  5418. LLFolderViewFolder::RECURSE_UP);
  5419. }
  5420. if (rootp)
  5421. {
  5422. rootp->scrollToShowSelection();
  5423. }
  5424. mItemSelected = true;
  5425. }
  5426. }
  5427. //-----------------------------------------------------------------------------
  5428. // LLInventoryPanel class
  5429. //-----------------------------------------------------------------------------
  5430. // Bridge to support knowing when the inventory has changed.
  5431. class LLInventoryPanelObserver final : public LLInventoryObserver
  5432. {
  5433. public:
  5434. LLInventoryPanelObserver(LLInventoryPanel* ip)
  5435. : mIP(ip)
  5436. {
  5437. }
  5438. ~LLInventoryPanelObserver() override
  5439. {
  5440. }
  5441. LL_INLINE void changed(U32 mask) override
  5442. {
  5443. mIP->modelChanged(mask);
  5444. }
  5445. protected:
  5446. LLInventoryPanel* mIP;
  5447. };
  5448. LLInventoryPanel::LLInventoryPanel(const std::string& name,
  5449. const std::string& sort_order_setting,
  5450. const LLRect& rect,
  5451. LLInventoryModel* inventory,
  5452. bool allow_multi_select,
  5453. bool disable_double_click,
  5454. bool show_thumbnails)
  5455. : LLPanel(name, rect, true),
  5456. mInventory(inventory),
  5457. mInventoryObserver(NULL),
  5458. mFolders(NULL),
  5459. mScroller(NULL),
  5460. mAllowMultiSelect(allow_multi_select),
  5461. mDoubleClickDisabled(disable_double_click),
  5462. mSortOrderSetting(sort_order_setting),
  5463. mLastOpenLocked(false),
  5464. mShowThumbnails(show_thumbnails)
  5465. {
  5466. setBackgroundColor(gColors.getColor("InventoryBackgroundColor"));
  5467. setBackgroundVisible(true);
  5468. setBackgroundOpaque(true);
  5469. }
  5470. bool LLInventoryPanel::postBuild()
  5471. {
  5472. init_inventory_panel_actions(this);
  5473. LLRect folder_rect(0, 0, getRect().getWidth(), 0);
  5474. mFolders = new LLFolderView(getName(), NULL, folder_rect, LLUUID::null,
  5475. this);
  5476. mFolders->setAllowMultiSelect(mAllowMultiSelect);
  5477. mFolders->setShowThumbnails(mShowThumbnails);
  5478. // Scroller
  5479. LLRect scroller_view_rect = getRect();
  5480. scroller_view_rect.translate(-scroller_view_rect.mLeft,
  5481. -scroller_view_rect.mBottom);
  5482. mScroller = new LLScrollableContainer("Inventory Scroller",
  5483. scroller_view_rect, mFolders);
  5484. mScroller->setFollowsAll();
  5485. mScroller->setReserveScrollCorner(true);
  5486. addChild(mScroller);
  5487. mFolders->setScrollContainer(mScroller);
  5488. // Set up the callbacks from the inventory we are viewing, and then build
  5489. // everything.
  5490. mInventoryObserver = new LLInventoryPanelObserver(this);
  5491. mInventory->addObserver(mInventoryObserver);
  5492. rebuildViewsFor(LLUUID::null);
  5493. // A bit of a hack to make sure the inventory is open.
  5494. mFolders->openFolder("My Inventory");
  5495. if (!mSortOrderSetting.empty())
  5496. {
  5497. setSortOrder(gSavedSettings.getU32(mSortOrderSetting.c_str()));
  5498. }
  5499. else
  5500. {
  5501. setSortOrder(gSavedSettings.getU32("InventorySortOrder"));
  5502. }
  5503. mFolders->setSortOrder(mFolders->getFilter()->getSortOrder());
  5504. return true;
  5505. }
  5506. //virtual
  5507. LLInventoryPanel::~LLInventoryPanel()
  5508. {
  5509. // Should this be a global setting ?
  5510. U32 sort_order = mFolders->getSortOrder();
  5511. if (!mSortOrderSetting.empty())
  5512. {
  5513. gSavedSettings.setU32(mSortOrderSetting.c_str(), sort_order);
  5514. }
  5515. // LLView destructor will take care of the sub-views.
  5516. mInventory->removeObserver(mInventoryObserver);
  5517. delete mInventoryObserver;
  5518. mScroller = NULL;
  5519. if (mShowThumbnails)
  5520. {
  5521. // Close the temporary thumbnail view floater, if open.
  5522. HBFloaterThumbnail::hideInstance();
  5523. }
  5524. }
  5525. //virtual
  5526. const std::string& LLInventoryPanel::getTag() const
  5527. {
  5528. return LL_INVENTORY_PANEL_TAG;
  5529. }
  5530. //virtual
  5531. LLXMLNodePtr LLInventoryPanel::getXML(bool save_children) const
  5532. {
  5533. LLXMLNodePtr node = LLPanel::getXML(false); // Do not print out children
  5534. node->setName(LL_INVENTORY_PANEL_TAG);
  5535. node->createChild("allow_multi_select",
  5536. true)->setBoolValue(mFolders->getAllowMultiSelect());
  5537. return node;
  5538. }
  5539. LLView* LLInventoryPanel::fromXML(LLXMLNodePtr node, LLView* parentp,
  5540. LLUICtrlFactory*)
  5541. {
  5542. std::string name = LL_INVENTORY_PANEL_TAG;
  5543. node->getAttributeString("name", name);
  5544. LLRect rect;
  5545. createRect(node, rect, parentp, LLRect());
  5546. std::string sort_order;
  5547. node->getAttributeString("sort_order", sort_order);
  5548. bool allow_multi_select = true;
  5549. node->getAttributeBool("allow_multi_select", allow_multi_select);
  5550. bool disable_double_click = false;
  5551. node->getAttributeBool("disable_double_click", disable_double_click);
  5552. bool show_thumbnails = false;
  5553. node->getAttributeBool("show_thumbnails", show_thumbnails);
  5554. LLInventoryPanel* self = new LLInventoryPanel(name, sort_order, rect,
  5555. &gInventory,
  5556. allow_multi_select,
  5557. disable_double_click,
  5558. show_thumbnails);
  5559. self->initFromXML(node, parentp);
  5560. self->postBuild();
  5561. return self;
  5562. }
  5563. void LLInventoryPanel::draw()
  5564. {
  5565. // Select the desired item (in case it was not loaded when the selection
  5566. // was requested)
  5567. if (mSelectThisID.notNull())
  5568. {
  5569. setSelection(mSelectThisID, false);
  5570. }
  5571. LLPanel::draw();
  5572. }
  5573. void LLInventoryPanel::setFilterTypes(U32 filter_types)
  5574. {
  5575. mFolders->getFilter()->setFilterTypes(filter_types);
  5576. }
  5577. void LLInventoryPanel::setFilterHideLibrary(bool hide)
  5578. {
  5579. mFolders->getFilter()->setFilterHideLibrary(hide);
  5580. }
  5581. void LLInventoryPanel::setFilterSubType(S32 subtype)
  5582. {
  5583. mFolders->getFilter()->setFilterSubType(subtype);
  5584. }
  5585. void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask)
  5586. {
  5587. mFolders->getFilter()->setFilterPermissions(filter_perm_mask);
  5588. }
  5589. void LLInventoryPanel::setFilterSubString(const std::string& string)
  5590. {
  5591. mFolders->getFilter()->setFilterSubString(string);
  5592. }
  5593. void LLInventoryPanel::setFilterWorn(bool worn)
  5594. {
  5595. mFolders->getFilter()->setFilterWorn(worn);
  5596. }
  5597. void LLInventoryPanel::setFilterLastOpen(bool b)
  5598. {
  5599. mFolders->getFilter()->setFilterLastOpen(b);
  5600. }
  5601. void LLInventoryPanel::setFilterShowLinks(bool b)
  5602. {
  5603. mFolders->getFilter()->setFilterShowLinks(b);
  5604. }
  5605. void LLInventoryPanel::setSortOrder(U32 order)
  5606. {
  5607. mFolders->getFilter()->setSortOrder(order);
  5608. if (mFolders->getFilter()->isModified())
  5609. {
  5610. mFolders->setSortOrder(order);
  5611. // Try to keep selection onscreen, even if it was not to start with
  5612. mFolders->scrollToShowSelection();
  5613. }
  5614. }
  5615. void LLInventoryPanel::setSinceLogoff(bool sl)
  5616. {
  5617. mFolders->getFilter()->setDateRangeLastLogoff(sl);
  5618. }
  5619. void LLInventoryPanel::setHoursAgo(U32 hours)
  5620. {
  5621. mFolders->getFilter()->setHoursAgo(hours);
  5622. }
  5623. void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show)
  5624. {
  5625. mFolders->getFilter()->setShowFolderState(show);
  5626. }
  5627. LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState()
  5628. {
  5629. return mFolders->getFilter()->getShowFolderState();
  5630. }
  5631. void LLInventoryPanel::modelChanged(U32 mask)
  5632. {
  5633. LL_FAST_TIMER(FTM_REFRESH);
  5634. uuid_list_t::const_iterator id_it, id_end;
  5635. bool handled = false;
  5636. if (mask & LLInventoryObserver::LABEL)
  5637. {
  5638. handled = true;
  5639. // Label change: empty out the display name for each object in this
  5640. // change set.
  5641. const uuid_list_t& changed_items = gInventory.getChangedIDs();
  5642. LLFolderViewItem* view = NULL;
  5643. LLInvFVBridge* bridge = NULL;
  5644. for (id_it = changed_items.begin(), id_end = changed_items.end();
  5645. id_it != id_end; ++id_it)
  5646. {
  5647. view = mFolders->getItemByID(*id_it);
  5648. if (view)
  5649. {
  5650. // Request refresh on this item (also flags for filtering)
  5651. bridge = (LLInvFVBridge*)view->getListener();
  5652. if (bridge)
  5653. {
  5654. // Clear the display name first, so it gets properly
  5655. // re-built during refresh()
  5656. bridge->clearDisplayName();
  5657. }
  5658. view->refresh();
  5659. }
  5660. }
  5661. }
  5662. if (mask & LLInventoryObserver::REBUILD)
  5663. {
  5664. handled = true;
  5665. // Icon change for each object in this change set.
  5666. const uuid_list_t& changed_items = gInventory.getChangedIDs();
  5667. for (id_it = changed_items.begin(), id_end = changed_items.end();
  5668. id_it != id_end; ++id_it)
  5669. {
  5670. // Sync view with model
  5671. LLInventoryModel* model = getModel();
  5672. if (model)
  5673. {
  5674. LLInventoryObject* model_item = model->getObject(*id_it);
  5675. LLFolderViewItem* view_item = mFolders->getItemByID(*id_it);
  5676. if (model_item && view_item)
  5677. {
  5678. view_item->destroyView();
  5679. }
  5680. buildNewViews(*id_it);
  5681. }
  5682. }
  5683. }
  5684. if ((mask & (LLInventoryObserver::STRUCTURE | LLInventoryObserver::ADD |
  5685. LLInventoryObserver::REMOVE)) != 0)
  5686. {
  5687. handled = true;
  5688. // Record which folders are open by uuid.
  5689. LLInventoryModel* model = getModel();
  5690. if (model)
  5691. {
  5692. const uuid_list_t& changed_items = gInventory.getChangedIDs();
  5693. for (id_it = changed_items.begin(), id_end = changed_items.end();
  5694. id_it != id_end; ++id_it)
  5695. {
  5696. // Sync view with model
  5697. LLInventoryObject* model_item = model->getObject(*id_it);
  5698. LLFolderViewItem* view_item = mFolders->getItemByID(*id_it);
  5699. if (model_item)
  5700. {
  5701. if (!view_item)
  5702. {
  5703. // This object was just created, need to build a view
  5704. // for it
  5705. if ((mask & LLInventoryObserver::ADD) !=
  5706. LLInventoryObserver::ADD)
  5707. {
  5708. llwarns << *id_it
  5709. << " is in model but not in view, but ADD flag not set"
  5710. << llendl;
  5711. }
  5712. buildNewViews(*id_it);
  5713. // Select any newly created object that has the auto
  5714. // rename at top of folder root set
  5715. if (mFolders->getRoot()->needsAutoRename())
  5716. {
  5717. setSelection(*id_it, false);
  5718. }
  5719. }
  5720. else
  5721. {
  5722. // This object was probably moved, check its parent
  5723. #if 0 // Inventory links cause many such warnings while they
  5724. // do not correspond to an actual issue...
  5725. if ((mask & LLInventoryObserver::STRUCTURE) !=
  5726. LLInventoryObserver::STRUCTURE)
  5727. {
  5728. llwarns << *id_it
  5729. << " is in model and in view, but STRUCTURE flag not set"
  5730. << llendl;
  5731. }
  5732. #endif
  5733. LLFolderViewFolder* new_parent =
  5734. (LLFolderViewFolder*)mFolders->getItemByID(model_item->getParentUUID());
  5735. if (new_parent)
  5736. {
  5737. if (view_item->getParentFolder() != new_parent)
  5738. {
  5739. view_item->getParentFolder()->extractItem(view_item);
  5740. view_item->addToFolder(new_parent, mFolders);
  5741. }
  5742. }
  5743. else
  5744. {
  5745. llwarns << model_item->getParentUUID()
  5746. << ": parent folder gone ! Destroying orphan view."
  5747. << llendl;
  5748. view_item->destroyView();
  5749. }
  5750. }
  5751. }
  5752. else if (view_item)
  5753. {
  5754. if ((mask & LLInventoryObserver::REMOVE) !=
  5755. LLInventoryObserver::REMOVE)
  5756. {
  5757. llwarns << *id_it
  5758. << " is not in model but in view, but REMOVE flag not set"
  5759. << llendl;
  5760. }
  5761. // Item in view but not model, need to delete view
  5762. view_item->destroyView();
  5763. }
  5764. else
  5765. {
  5766. llwarns << *id_it
  5767. << ": Item does not exist in either view or model, but notification triggered"
  5768. << llendl;
  5769. }
  5770. }
  5771. }
  5772. }
  5773. if (!handled)
  5774. {
  5775. // It is a small change that only requires a refresh.
  5776. // *TODO: figure out a more efficient way to do the refresh since it is
  5777. // expensive on large inventories.
  5778. mFolders->refresh();
  5779. }
  5780. }
  5781. void LLInventoryPanel::rebuildViewsFor(const LLUUID& id)
  5782. {
  5783. LLFolderViewItem* old_view = mFolders->getItemByID(id);
  5784. if (old_view && id.notNull())
  5785. {
  5786. old_view->destroyView();
  5787. }
  5788. buildNewViews(id);
  5789. }
  5790. void LLInventoryPanel::buildNewViews(const LLUUID& id)
  5791. {
  5792. LLFolderViewFolder* parent_folder = NULL;
  5793. LLInventoryObject* objectp = gInventory.getObject(id);
  5794. if (mFolders->getListener() && id == mFolders->getListener()->getUUID())
  5795. {
  5796. parent_folder = mFolders;
  5797. }
  5798. else if (objectp)
  5799. {
  5800. const LLUUID& parent_id = objectp->getParentUUID();
  5801. parent_folder = (LLFolderViewFolder*)mFolders->getItemByID(parent_id);
  5802. if (parent_folder)
  5803. {
  5804. LLFolderViewItem* itemp = NULL;
  5805. if (objectp->getType() <= LLAssetType::AT_NONE ||
  5806. objectp->getType() >= LLAssetType::AT_COUNT)
  5807. {
  5808. llwarns_once << "Called with unsupported asset type: "
  5809. << (S32)objectp->getType() << llendl;
  5810. }
  5811. else if (objectp->getType() == LLAssetType::AT_CATEGORY &&
  5812. objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)
  5813. {
  5814. // Build new view for category
  5815. LLInvFVBridge* new_listener =
  5816. LLInvFVBridge::createBridge(objectp->getType(),
  5817. objectp->getType(),
  5818. LLInventoryType::IT_CATEGORY,
  5819. this, objectp->getUUID());
  5820. if (new_listener)
  5821. {
  5822. LLFolderViewFolder* folderp =
  5823. new LLFolderViewFolder(new_listener->getDisplayName(),
  5824. new_listener->getIcon(),
  5825. mFolders, new_listener);
  5826. folderp->setItemSortOrder(mFolders->getSortOrder());
  5827. itemp = folderp;
  5828. }
  5829. }
  5830. else
  5831. {
  5832. // Build new view for item
  5833. LLInventoryItem* item = (LLInventoryItem*)objectp;
  5834. LLInvFVBridge* new_listener =
  5835. LLInvFVBridge::createBridge(item->getType(),
  5836. item->getActualType(),
  5837. item->getInventoryType(), this,
  5838. item->getUUID(),
  5839. item->getFlags());
  5840. if (new_listener)
  5841. {
  5842. itemp = new LLFolderViewItem(new_listener->getDisplayName(),
  5843. new_listener->getIcon(),
  5844. new_listener->getCreationDate(),
  5845. mFolders,
  5846. new_listener);
  5847. if (itemp && mDoubleClickDisabled)
  5848. {
  5849. itemp->disableDoubleClick();
  5850. }
  5851. }
  5852. }
  5853. if (itemp)
  5854. {
  5855. itemp->addToFolder(parent_folder, mFolders);
  5856. }
  5857. }
  5858. }
  5859. // If this is a folder, recursively add all the children
  5860. if (id.isNull() ||
  5861. (objectp && objectp->getType() == LLAssetType::AT_CATEGORY))
  5862. {
  5863. LLViewerInventoryCategory::cat_array_t* categories;
  5864. LLViewerInventoryItem::item_array_t* items;
  5865. #if LL_DEBUG
  5866. mInventory->lockDirectDescendentArrays(id, categories, items);
  5867. #else
  5868. mInventory->getDirectDescendentsOf(id, categories, items);
  5869. #endif
  5870. if (categories)
  5871. {
  5872. for (S32 i = 0, count = categories->size(); i < count; ++i)
  5873. {
  5874. LLInventoryCategory* cat = (*categories)[i];
  5875. if (cat) // Paranoia
  5876. {
  5877. buildNewViews(cat->getUUID());
  5878. }
  5879. }
  5880. }
  5881. if (items && parent_folder)
  5882. {
  5883. for (S32 i = 0, count = items->size(); i < count; ++i)
  5884. {
  5885. LLInventoryItem* item = (*items)[i];
  5886. if (item) // Paranoia
  5887. {
  5888. buildNewViews(item->getUUID());
  5889. }
  5890. }
  5891. }
  5892. #if LL_DEBUG
  5893. mInventory->unlockDirectDescendentArrays(id);
  5894. #endif
  5895. }
  5896. }
  5897. void LLInventoryPanel::openSelected()
  5898. {
  5899. LLFolderViewItem* folder_item = mFolders->getCurSelectedItem();
  5900. if (folder_item)
  5901. {
  5902. LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener();
  5903. if (bridge)
  5904. {
  5905. bridge->openItem();
  5906. }
  5907. }
  5908. }
  5909. bool LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask)
  5910. {
  5911. if (!gWindowp) return true; // Paranoia
  5912. // Has everything been fetched in the inventory ?
  5913. bool fetched = LLInventoryModelFetch::getInstance()->isEverythingFetched();
  5914. if (!fetched)
  5915. {
  5916. // Force the hourglass cursor
  5917. gWindowp->setCursor(UI_CURSOR_WORKING);
  5918. // Prevent any changes to cursor done by LLView::handleHover() to avoid
  5919. // occasionnal flickering. HB
  5920. gWindowp->freezeCursor(true);
  5921. }
  5922. bool handled = LLView::handleHover(x, y, mask);
  5923. if (fetched)
  5924. {
  5925. if (!handled)
  5926. {
  5927. // Restore the arrow cursor
  5928. gWindowp->setCursor(UI_CURSOR_ARROW);
  5929. }
  5930. }
  5931. else
  5932. {
  5933. gWindowp->freezeCursor(false);
  5934. }
  5935. return handled;
  5936. }
  5937. bool LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
  5938. EDragAndDropType cargo_type,
  5939. void* cargo_data, EAcceptance* accept,
  5940. std::string& tooltip_msg)
  5941. {
  5942. bool handled = LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type,
  5943. cargo_data, accept, tooltip_msg);
  5944. if (handled)
  5945. {
  5946. mFolders->setDragAndDropThisFrame();
  5947. }
  5948. return handled;
  5949. }
  5950. void LLInventoryPanel::openAllFolders()
  5951. {
  5952. mFolders->setOpenArrangeRecursively(true,
  5953. LLFolderViewFolder::RECURSE_DOWN);
  5954. mFolders->arrangeAll();
  5955. }
  5956. void LLInventoryPanel::closeAllFolders()
  5957. {
  5958. mFolders->setOpenArrangeRecursively(false,
  5959. LLFolderViewFolder::RECURSE_DOWN);
  5960. mFolders->arrangeAll();
  5961. }
  5962. void LLInventoryPanel::openDefaultFolderForType(LLAssetType::EType type)
  5963. {
  5964. const LLUUID& category_id =
  5965. mInventory->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(type));
  5966. LLOpenFolderByID opener(category_id);
  5967. mFolders->applyFunctorRecursively(opener);
  5968. }
  5969. void LLInventoryPanel::setSelection(const LLUUID& obj_id,
  5970. bool take_keyboard_focus)
  5971. {
  5972. LLFolderViewItem* itemp = mFolders->getItemByID(obj_id);
  5973. if (itemp && itemp->getListener())
  5974. {
  5975. itemp->getListener()->arrangeAndSet(itemp, true, take_keyboard_focus);
  5976. mSelectThisID.setNull();
  5977. return;
  5978. }
  5979. // Save the desired item to be selected later (if/when ready)
  5980. mSelectThisID = obj_id;
  5981. }
  5982. void LLInventoryPanel::clearSelection()
  5983. {
  5984. mFolders->clearSelection();
  5985. mSelectThisID.setNull();
  5986. }
  5987. bool LLInventoryPanel::makeLastOpenCurrent()
  5988. {
  5989. if ((mLastOpenID.notNull() && gInventory.getCategory(mLastOpenID)) &&
  5990. (mLastOpenLocked || LLFolderViewFolder::sLastOpenId.isNull() ||
  5991. mLastOpenID == LLFolderViewFolder::sLastOpenId ||
  5992. !gInventory.getCategory(LLFolderViewFolder::sLastOpenId)))
  5993. {
  5994. return false;
  5995. }
  5996. mLastOpenID = LLFolderViewFolder::sLastOpenId;
  5997. mFolders->openFolder(mLastOpenID);
  5998. return true;
  5999. }