llscrolllistctrl.cpp 99 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371
  1. /**
  2. * @file llscrolllistctrl.cpp
  3. * @brief LLScrollListCtrl base class
  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 "linden_common.h"
  33. // "error: 'get_temporary_buffer<...>' is deprecated" seen with clang 18 and
  34. // gcc 12.3 libstdc++ implementation of std::stable_sort(). HB
  35. #if CLANG_VERSION >= 180000
  36. # pragma clang diagnostic ignored "-Wdeprecated-declarations"
  37. #endif
  38. #include <algorithm>
  39. #include "boost/tokenizer.hpp"
  40. #include "llscrolllistctrl.h"
  41. #include "indra_constants.h"
  42. #include "llcheckboxctrl.h"
  43. #include "llclipboard.h"
  44. #include "llcontrol.h"
  45. #include "llkeyboard.h"
  46. #include "llrender.h"
  47. #include "llresizebar.h"
  48. #include "llscrollbar.h"
  49. #include "llstl.h"
  50. #include "llstring.h"
  51. #include "lltimer.h" // For timeToFormattedString()
  52. #include "lluictrlfactory.h"
  53. #include "llwindow.h"
  54. constexpr S32 MIN_COLUMN_WIDTH = 20;
  55. static const std::string LL_SCROLL_LIST_CTRL_TAG = "scroll_list";
  56. static LLRegisterWidget<LLScrollListCtrl> r20(LL_SCROLL_LIST_CTRL_TAG);
  57. // Local structures & classes.
  58. struct SortScrollListItem
  59. {
  60. SortScrollListItem(const std::vector<std::pair<S32, bool> >& sort_orders)
  61. : mSortOrders(sort_orders)
  62. {
  63. }
  64. bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2)
  65. {
  66. // Sort over all columns in order specified by mSortOrders
  67. S32 sort_result = 0;
  68. for (sort_order_t::const_reverse_iterator it = mSortOrders.rbegin(),
  69. rend = mSortOrders.rend();
  70. it != rend; ++it)
  71. {
  72. S32 col_idx = it->first;
  73. bool sort_ascending = it->second;
  74. const LLScrollListCell* cell1 = i1->getColumn(col_idx);
  75. const LLScrollListCell* cell2 = i2->getColumn(col_idx);
  76. // Ascending or descending sort for this column ?
  77. S32 order = sort_ascending ? 1 : -1;
  78. if (cell1 && cell2)
  79. {
  80. sort_result =
  81. order *
  82. LLStringUtil::compareDict(cell1->getValue().asString(),
  83. cell2->getValue().asString());
  84. if (sort_result != 0)
  85. {
  86. break; // We have a sort order !
  87. }
  88. }
  89. }
  90. return sort_result < 0;
  91. }
  92. typedef std::vector<std::pair<S32, bool> > sort_order_t;
  93. const sort_order_t& mSortOrders;
  94. };
  95. //
  96. // LLScrollListIcon
  97. //
  98. LLScrollListIcon::LLScrollListIcon(LLUIImagePtr icon, S32 width)
  99. : LLScrollListCell(width),
  100. mIcon(icon),
  101. mColor(LLColor4::white)
  102. {
  103. }
  104. LLScrollListIcon::LLScrollListIcon(const LLSD& value, S32 width)
  105. : LLScrollListCell(width),
  106. mColor(LLColor4::white)
  107. {
  108. setValue(value);
  109. }
  110. void LLScrollListIcon::setValue(const LLSD& value)
  111. {
  112. if (value.isUUID())
  113. {
  114. // Do not use default image specified by LLUUID::null, use no image in
  115. // that case
  116. LLUUID image_id = value.asUUID();
  117. mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id)
  118. : LLUIImagePtr(NULL);
  119. }
  120. else
  121. {
  122. std::string value_string = value.asString();
  123. if (LLUUID::validate(value_string))
  124. {
  125. setValue(LLUUID(value_string));
  126. }
  127. else if (!value_string.empty())
  128. {
  129. mIcon = LLUI::getUIImage(value.asString());
  130. }
  131. else
  132. {
  133. mIcon = NULL;
  134. }
  135. }
  136. }
  137. void LLScrollListIcon::setColor(const LLColor4& color)
  138. {
  139. mColor = color;
  140. }
  141. S32 LLScrollListIcon::getWidth() const
  142. {
  143. // if no specified fix width, use width of icon
  144. if (LLScrollListCell::getWidth() == 0 && mIcon.notNull())
  145. {
  146. return mIcon->getWidth();
  147. }
  148. return LLScrollListCell::getWidth();
  149. }
  150. void LLScrollListIcon::draw(const LLColor4& color,
  151. const LLColor4& highlight_color) const
  152. {
  153. if (mIcon)
  154. {
  155. mIcon->draw(0, 0, mColor);
  156. }
  157. }
  158. //
  159. // LLScrollListCheck
  160. //
  161. LLScrollListCheck::LLScrollListCheck(LLCheckBoxCtrl* check_box, S32 width)
  162. {
  163. mCheckBox = check_box;
  164. LLRect rect(mCheckBox->getRect());
  165. if (width)
  166. {
  167. rect.mRight = rect.mLeft + width;
  168. mCheckBox->setRect(rect);
  169. setWidth(width);
  170. }
  171. else
  172. {
  173. setWidth(rect.getWidth()); //check_box->getWidth();
  174. }
  175. }
  176. LLScrollListCheck::~LLScrollListCheck()
  177. {
  178. delete mCheckBox;
  179. }
  180. void LLScrollListCheck::draw(const LLColor4& color,
  181. const LLColor4& highlight_color) const
  182. {
  183. mCheckBox->draw();
  184. }
  185. bool LLScrollListCheck::handleClick()
  186. {
  187. if (mCheckBox->getEnabled())
  188. {
  189. mCheckBox->toggle();
  190. }
  191. // Do not change selection when clicking on embedded checkbox
  192. return true;
  193. }
  194. //
  195. // LLScrollListSeparator
  196. //
  197. LLScrollListSeparator::LLScrollListSeparator(S32 width)
  198. : LLScrollListCell(width)
  199. {
  200. }
  201. //virtual
  202. S32 LLScrollListSeparator::getHeight() const
  203. {
  204. return 5;
  205. }
  206. void LLScrollListSeparator::draw(const LLColor4& color,
  207. const LLColor4& highlight_color) const
  208. {
  209. // *FIXME: use dynamic item heights and make separators narrow, and
  210. // inactive
  211. gl_line_2d(5, 8, llmax(5, getWidth() - 5), 8, color);
  212. }
  213. //
  214. // LLScrollListText
  215. //
  216. U32 LLScrollListText::sCount = 0;
  217. LLScrollListText::LLScrollListText(const std::string& text,
  218. const LLFontGL* font, S32 width, U8 style,
  219. LLFontGL::HAlign alignment, LLColor4& color,
  220. bool use_color, bool visible)
  221. : LLScrollListCell(width),
  222. mText(text),
  223. mFont(font),
  224. mColor(color),
  225. mUseColor(use_color),
  226. mFontStyle(style),
  227. mFontAlignment(alignment),
  228. mVisible(visible),
  229. mHighlightCount(0),
  230. mHighlightOffset(0)
  231. {
  232. ++sCount;
  233. }
  234. //virtual
  235. LLScrollListText::~LLScrollListText()
  236. {
  237. --sCount;
  238. }
  239. //virtual
  240. void LLScrollListText::highlightText(S32 offset, S32 num_chars)
  241. {
  242. mHighlightOffset = offset;
  243. mHighlightCount = num_chars;
  244. }
  245. //virtual
  246. S32 LLScrollListText::getHeight() const
  247. {
  248. return ll_roundp(mFont->getLineHeight());
  249. }
  250. void LLScrollListText::draw(const LLColor4& color,
  251. const LLColor4& highlight_color) const
  252. {
  253. LLColor4 display_color;
  254. if (mUseColor)
  255. {
  256. display_color = mColor;
  257. }
  258. else
  259. {
  260. display_color = color;
  261. }
  262. if (mHighlightCount > 0)
  263. {
  264. S32 left = 0;
  265. switch (mFontAlignment)
  266. {
  267. case LLFontGL::LEFT:
  268. left = mFont->getWidth(mText.getString(), 0, mHighlightOffset);
  269. break;
  270. case LLFontGL::RIGHT:
  271. left = getWidth() - mFont->getWidth(mText.getString(),
  272. mHighlightOffset, S32_MAX);
  273. break;
  274. case LLFontGL::HCENTER:
  275. left = (getWidth() - mFont->getWidth(mText.getString())) / 2;
  276. }
  277. LLRect highlight_rect(left - 2, ll_roundp(mFont->getLineHeight()) + 1,
  278. left + mFont->getWidth(mText.getString(),
  279. mHighlightOffset, mHighlightCount) + 1, 1);
  280. LLUIImage::sRoundedSquare->draw(highlight_rect, highlight_color);
  281. }
  282. // Try to draw the entire string
  283. F32 right_x;
  284. U32 string_chars = mText.length();
  285. F32 start_x = 0.f;
  286. switch (mFontAlignment)
  287. {
  288. case LLFontGL::LEFT:
  289. start_x = 0.f;
  290. break;
  291. case LLFontGL::RIGHT:
  292. start_x = (F32)getWidth();
  293. break;
  294. case LLFontGL::HCENTER:
  295. start_x = (F32)getWidth() * 0.5f;
  296. }
  297. mFont->render(mText.getWString(), 0, start_x, 2.f, display_color,
  298. mFontAlignment, LLFontGL::BOTTOM, mFontStyle, string_chars,
  299. getWidth(), &right_x, false, true);
  300. }
  301. LLScrollListDate::LLScrollListDate(const LLDate& date,
  302. const std::string& format,
  303. const LLFontGL* font, S32 width, U8 style,
  304. LLFontGL::HAlign alignment, LLColor4& color,
  305. bool use_color, bool visible)
  306. : LLScrollListText("", font, width, style, alignment, color, use_color,
  307. visible),
  308. mDate(date),
  309. mFormat(format)
  310. {
  311. std::string text;
  312. if (mFormat.empty())
  313. {
  314. text = mDate.asTimeStamp(false);
  315. }
  316. else
  317. {
  318. timeToFormattedString(mDate.secondsSinceEpoch(), mFormat.c_str(),
  319. text);
  320. }
  321. LLScrollListText::setValue(text);
  322. }
  323. //virtual
  324. void LLScrollListDate::setValue(const LLSD& value)
  325. {
  326. mDate = value.asDate();
  327. std::string text;
  328. if (mFormat.empty())
  329. {
  330. text = mDate.asTimeStamp(false);
  331. }
  332. else
  333. {
  334. timeToFormattedString(mDate.secondsSinceEpoch(), mFormat.c_str(),
  335. text);
  336. }
  337. LLScrollListText::setValue(text);
  338. }
  339. //virtual
  340. LLScrollListItem::~LLScrollListItem()
  341. {
  342. std::for_each(mColumns.begin(), mColumns.end(), DeletePointer());
  343. mColumns.clear();
  344. }
  345. void LLScrollListItem::setNumColumns(S32 columns)
  346. {
  347. S32 prev_columns = mColumns.size();
  348. if (columns < prev_columns)
  349. {
  350. std::for_each(mColumns.begin() + columns, mColumns.end(),
  351. DeletePointer());
  352. }
  353. mColumns.resize(columns);
  354. for (S32 col = prev_columns; col < columns; ++col)
  355. {
  356. mColumns[col] = NULL;
  357. }
  358. }
  359. void LLScrollListItem::setColumn(S32 column, LLScrollListCell* cell)
  360. {
  361. if (column < (S32)mColumns.size())
  362. {
  363. delete mColumns[column];
  364. mColumns[column] = cell;
  365. }
  366. else
  367. {
  368. llwarns << "Bad column number: " << column << " - Ignored." << llendl;
  369. llassert(false);
  370. }
  371. }
  372. std::string LLScrollListItem::getContentsCSV() const
  373. {
  374. std::string ret;
  375. S32 count = getNumColumns();
  376. for (S32 i = 0; i < count; ++i)
  377. {
  378. if (i)
  379. {
  380. ret += ",";
  381. }
  382. ret += getColumn(i)->getValue().asString();
  383. }
  384. return ret;
  385. }
  386. void LLScrollListItem::draw(const LLRect& rect,
  387. const LLColor4& fg_color,
  388. const LLColor4& bg_color,
  389. const LLColor4& highlight_color,
  390. S32 column_padding)
  391. {
  392. // Draw background rect
  393. LLRect bg_rect = rect;
  394. {
  395. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  396. gGL.color4fv(bg_color.mV);
  397. gl_rect_2d(bg_rect);
  398. }
  399. S32 cur_x = rect.mLeft;
  400. S32 num_cols = getNumColumns();
  401. S32 cur_col = 0;
  402. for (LLScrollListCell* cell = getColumn(0); cur_col < num_cols;
  403. cell = getColumn(++cur_col))
  404. {
  405. // Two ways a cell could be hidden
  406. if (cell->getWidth() < 0 || !cell->getVisible()) continue;
  407. LLUI::pushMatrix();
  408. {
  409. LLUI::translate((F32) cur_x, (F32) rect.mBottom, 0.0f);
  410. cell->draw(fg_color, highlight_color);
  411. }
  412. LLUI::popMatrix();
  413. cur_x += cell->getWidth() + column_padding;
  414. }
  415. }
  416. //---------------------------------------------------------------------------
  417. // LLScrollListItemComment
  418. //---------------------------------------------------------------------------
  419. LLScrollListItemComment::LLScrollListItemComment(const std::string& comment,
  420. const LLColor4& color)
  421. : LLScrollListItem(false),
  422. mColor(color)
  423. {
  424. static const LLFontGL* font = LLFontGL::getFontSansSerifSmall();
  425. addColumn(comment, font);
  426. }
  427. void LLScrollListItemComment::draw(const LLRect& rect,
  428. const LLColor4& fg_color,
  429. const LLColor4& bg_color,
  430. const LLColor4& highlight_color,
  431. S32 column_padding)
  432. {
  433. LLScrollListCell* cell = getColumn(0);
  434. if (cell)
  435. {
  436. // Two ways a cell could be hidden
  437. if (cell->getWidth() < 0 || !cell->getVisible()) return;
  438. LLUI::pushMatrix();
  439. {
  440. LLUI::translate((F32)rect.mLeft, (F32)rect.mBottom, 0.0f);
  441. // Force first cell to be width of entire item
  442. cell->setWidth(rect.getWidth());
  443. cell->draw(mColor, highlight_color);
  444. }
  445. LLUI::popMatrix();
  446. }
  447. }
  448. //---------------------------------------------------------------------------
  449. // LLScrollListItemSeparator
  450. //---------------------------------------------------------------------------
  451. LLScrollListItemSeparator::LLScrollListItemSeparator()
  452. : LLScrollListItem(false)
  453. {
  454. LLScrollListSeparator* cell = new LLScrollListSeparator(0);
  455. setNumColumns(1);
  456. setColumn(0, cell);
  457. }
  458. void LLScrollListItemSeparator::draw(const LLRect& rect,
  459. const LLColor4& fg_color,
  460. const LLColor4& bg_color,
  461. const LLColor4& highlight_color,
  462. S32 column_padding)
  463. {
  464. // *TODO: move LLScrollListSeparator::draw into here and get rid of it
  465. LLScrollListCell* cell = getColumn(0);
  466. if (cell)
  467. {
  468. // Two ways a cell could be hidden
  469. if (cell->getWidth() < 0 || !cell->getVisible()) return;
  470. LLUI::pushMatrix();
  471. {
  472. LLUI::translate((F32)rect.mLeft, (F32)rect.mBottom, 0.0f);
  473. // Force first cell to be width of entire item
  474. cell->setWidth(rect.getWidth());
  475. cell->draw(fg_color, highlight_color);
  476. }
  477. LLUI::popMatrix();
  478. }
  479. }
  480. //----------------------------------------------------------------------------
  481. // LLScrollListCtrl
  482. //----------------------------------------------------------------------------
  483. LLScrollListCtrl::LLScrollListCtrl(const std::string& name, const LLRect& rect,
  484. void (*commit_callback)(LLUICtrl*, void*),
  485. void* userdata, bool multi_select,
  486. bool show_border)
  487. : LLUICtrl(name, rect, true, commit_callback, userdata),
  488. mLineHeight(0),
  489. mScrollLines(0),
  490. mPageLines(0),
  491. mHeadingHeight(20),
  492. mMaxSelectable(0),
  493. mAllowMultipleSelection(multi_select),
  494. mAllowKeyboardMovement(true),
  495. mCommitOnKeyboardMovement(true),
  496. mCommitOnSelectionChange(false),
  497. mSelectionChanged(false),
  498. mDirty(true),
  499. mNeedsScroll(false),
  500. mCanSelect(true),
  501. mDisplayColumnHeaders(false),
  502. mColumnsDirty(false),
  503. mColumnWidthsDirty(true),
  504. mSorted(true),
  505. mAllowRefresh(true),
  506. mMaxItemCount(INT_MAX),
  507. mBackgroundVisible(true),
  508. mDrawStripes(true),
  509. mBgWriteableColor(LLUI::sScrollBgWriteableColor),
  510. mBgReadOnlyColor(LLUI::sScrollBgReadOnlyColor),
  511. mBgSelectedColor(LLUI::sScrollSelectedBGColor),
  512. mBgStripeColor(LLUI::sScrollBGStripeColor),
  513. mFgSelectedColor(LLUI::sScrollSelectedFGColor),
  514. mFgUnselectedColor(LLUI::sScrollUnselectedColor),
  515. mFgDisabledColor(LLUI::sScrollDisabledColor),
  516. mHighlightedColor(LLUI::sScrollHighlightedColor),
  517. mBorderThickness(2),
  518. mOnDoubleClickCallback(NULL),
  519. mOnMaximumSelectCallback(NULL),
  520. mOnSortChangedCallback(NULL),
  521. mHighlightedItem(-1),
  522. mBorder(NULL),
  523. mSearchColumn(0),
  524. mNumDynamicWidthColumns(0),
  525. mTotalStaticColumnWidth(0),
  526. mTotalColumnPadding(0),
  527. mColumnPadding(5),
  528. mLastSelected(NULL),
  529. mOriginalSelection(-1)
  530. {
  531. mItemListRect.setOriginAndSize(mBorderThickness, mBorderThickness,
  532. getRect().getWidth() - 2 * mBorderThickness,
  533. getRect().getHeight() - 2 * mBorderThickness);
  534. updateLineHeight();
  535. mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0;
  536. // Initialize the scrollbar
  537. LLRect scroll_rect;
  538. scroll_rect.setOriginAndSize(getRect().getWidth() - mBorderThickness -
  539. SCROLLBAR_SIZE,
  540. mItemListRect.mBottom, SCROLLBAR_SIZE,
  541. mItemListRect.getHeight());
  542. mScrollbar = new LLScrollbar("Scrollbar", scroll_rect,
  543. LLScrollbar::VERTICAL, getItemCount(),
  544. mScrollLines, mPageLines,
  545. &LLScrollListCtrl::onScrollChange, this);
  546. mScrollbar->setFollowsRight();
  547. mScrollbar->setFollowsTop();
  548. mScrollbar->setFollowsBottom();
  549. mScrollbar->setEnabled(true);
  550. // Scrollbar is visible only when needed
  551. mScrollbar->setVisible(false);
  552. addChild(mScrollbar);
  553. // Border
  554. if (show_border)
  555. {
  556. LLRect border_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
  557. mBorder = new LLViewBorder("dlg border", border_rect,
  558. LLViewBorder::BEVEL_IN,
  559. LLViewBorder::STYLE_LINE, 1);
  560. addChild(mBorder);
  561. }
  562. }
  563. S32 LLScrollListCtrl::getSearchColumn()
  564. {
  565. // Search for proper search column
  566. if (mSearchColumn < 0)
  567. {
  568. LLScrollListItem* itemp = getFirstData();
  569. if (itemp)
  570. {
  571. for (S32 column = 0; column < getNumColumns(); ++column)
  572. {
  573. LLScrollListCell* cell = itemp->getColumn(column);
  574. if (cell && cell->isText())
  575. {
  576. mSearchColumn = column;
  577. break;
  578. }
  579. }
  580. }
  581. }
  582. return llclamp(mSearchColumn, 0, getNumColumns());
  583. }
  584. LLScrollListCtrl::~LLScrollListCtrl()
  585. {
  586. std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
  587. mItemList.clear();
  588. clearColumns(); // Clears columns and deletes headers
  589. }
  590. bool LLScrollListCtrl::setMaxItemCount(S32 max_count)
  591. {
  592. if (max_count >= getItemCount())
  593. {
  594. mMaxItemCount = max_count;
  595. }
  596. return max_count == mMaxItemCount;
  597. }
  598. // LLScrolListInterface method (was deleteAllItems)
  599. //virtual
  600. void LLScrollListCtrl::clearRows()
  601. {
  602. std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
  603. mItemList.clear();
  604. // Scroll the bar back up to the top.
  605. mScrollbar->setDocParams(0, 0);
  606. mScrollLines = 0;
  607. mLastSelected = NULL;
  608. updateLayout();
  609. mDirty = mSorted = false;
  610. }
  611. LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
  612. {
  613. if (!getCanSelect())
  614. {
  615. return NULL;
  616. }
  617. for (item_list::const_iterator iter = mItemList.begin(),
  618. end = mItemList.end();
  619. iter != end; ++iter)
  620. {
  621. LLScrollListItem* item = *iter;
  622. if (item && item->getSelected())
  623. {
  624. return item;
  625. }
  626. }
  627. return NULL;
  628. }
  629. std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
  630. {
  631. std::vector<LLScrollListItem*> ret;
  632. if (!getCanSelect())
  633. {
  634. return ret;
  635. }
  636. for (item_list::const_iterator iter = mItemList.begin(),
  637. end = mItemList.end();
  638. iter != end; ++iter)
  639. {
  640. LLScrollListItem* item = *iter;
  641. if (item && item->getSelected())
  642. {
  643. ret.push_back(item);
  644. }
  645. }
  646. return ret;
  647. }
  648. uuid_vec_t LLScrollListCtrl::getSelectedIDs()
  649. {
  650. uuid_vec_t ids;
  651. std::vector<LLScrollListItem*> selected = getAllSelected();
  652. for (std::vector<LLScrollListItem*>::iterator it = selected.begin(),
  653. end = selected.end();
  654. it != end; ++it)
  655. {
  656. ids.emplace_back((*it)->getUUID());
  657. }
  658. return ids;
  659. }
  660. S32 LLScrollListCtrl::getNumSelected() const
  661. {
  662. S32 selected = 0;
  663. for (item_list::const_iterator iter = mItemList.begin(),
  664. end = mItemList.end();
  665. iter != end; ++iter)
  666. {
  667. LLScrollListItem* item = *iter;
  668. if (item && item->getSelected())
  669. {
  670. ++selected;
  671. }
  672. }
  673. return selected;
  674. }
  675. S32 LLScrollListCtrl::getFirstSelectedIndex() const
  676. {
  677. if (!getCanSelect())
  678. {
  679. return -1;
  680. }
  681. S32 cur_selected_idx = 0;
  682. item_list::const_iterator iter;
  683. for (item_list::const_iterator it = mItemList.begin(),
  684. end = mItemList.end();
  685. it != end; ++it)
  686. {
  687. LLScrollListItem* item = *it;
  688. if (item && item->getSelected())
  689. {
  690. return cur_selected_idx;
  691. }
  692. ++cur_selected_idx;
  693. }
  694. return -1;
  695. }
  696. LLScrollListItem* LLScrollListCtrl::getFirstData() const
  697. {
  698. return mItemList.empty() ? NULL : mItemList[0];
  699. }
  700. LLScrollListItem* LLScrollListCtrl::getLastData() const
  701. {
  702. size_t count = mItemList.size();
  703. if (count == 0)
  704. {
  705. return NULL;
  706. }
  707. return mItemList[count - 1];
  708. }
  709. std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const
  710. {
  711. std::vector<LLScrollListItem*> ret;
  712. for (item_list::const_iterator it = mItemList.begin(),
  713. end = mItemList.end();
  714. it != end; ++it)
  715. {
  716. LLScrollListItem* item = *it;
  717. if (item)
  718. {
  719. ret.push_back(item);
  720. }
  721. }
  722. return ret;
  723. }
  724. // Returns the first matching item
  725. LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const
  726. {
  727. std::string string_val = sd.asString();
  728. for (item_list::const_iterator it = mItemList.begin(),
  729. end = mItemList.end();
  730. it != end; ++it)
  731. {
  732. LLScrollListItem* item = *it;
  733. // Assumes string representation is good enough for comparison
  734. if (item && item->getValue().asString() == string_val)
  735. {
  736. return item;
  737. }
  738. }
  739. return NULL;
  740. }
  741. void LLScrollListCtrl::reshape(S32 width, S32 height, bool called_from_parent)
  742. {
  743. LLUICtrl::reshape(width, height, called_from_parent);
  744. updateLayout();
  745. }
  746. void LLScrollListCtrl::updateLayout()
  747. {
  748. // Reserve room for column headers, if needed
  749. S32 heading_size = mDisplayColumnHeaders ? mHeadingHeight : 0;
  750. mItemListRect.setOriginAndSize(mBorderThickness, mBorderThickness,
  751. getRect().getWidth() - 2 * mBorderThickness,
  752. getRect().getHeight() -
  753. 2 * mBorderThickness - heading_size);
  754. // How many lines of content in a single "page" ?
  755. mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0;
  756. bool scrollbar_visible = getItemCount() > mPageLines;
  757. if (scrollbar_visible)
  758. {
  759. // Provide space on the right for scrollbar
  760. mItemListRect.mRight = getRect().getWidth() - mBorderThickness -
  761. SCROLLBAR_SIZE;
  762. mScrollbar->reshape(SCROLLBAR_SIZE,
  763. mItemListRect.getHeight() +
  764. (mDisplayColumnHeaders ? mHeadingHeight : 0));
  765. }
  766. mScrollbar->setPageSize(mPageLines);
  767. mScrollbar->setDocSize(getItemCount());
  768. mScrollbar->setVisible(scrollbar_visible);
  769. dirtyColumns();
  770. }
  771. // Attempt to size the control to show all items. Do not make larger than width
  772. // or height.
  773. void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height)
  774. {
  775. S32 height = llmin(getRequiredRect().getHeight(), max_height);
  776. S32 width = getRect().getWidth();
  777. reshape(width, height);
  778. }
  779. LLRect LLScrollListCtrl::getRequiredRect()
  780. {
  781. S32 hsize = mDisplayColumnHeaders ? mHeadingHeight : 0;
  782. S32 height = mLineHeight * getItemCount() + 2 * mBorderThickness + hsize;
  783. S32 width = getRect().getWidth();
  784. return LLRect(0, height, width, 0);
  785. }
  786. bool LLScrollListCtrl::addItem(LLScrollListItem* item, EAddPosition pos,
  787. bool requires_column)
  788. {
  789. bool not_too_big = getItemCount() < mMaxItemCount;
  790. if (not_too_big)
  791. {
  792. switch (pos)
  793. {
  794. case ADD_TOP:
  795. mItemList.push_front(item);
  796. break;
  797. case ADD_SORTED:
  798. {
  799. // Sort by column 0, in ascending order
  800. std::vector<sort_column_t> single_sort_column;
  801. single_sort_column.emplace_back(0, true);
  802. mItemList.push_back(item);
  803. std::stable_sort(mItemList.begin(), mItemList.end(),
  804. SortScrollListItem(single_sort_column));
  805. // ADD_SORTED just sorts by first column...
  806. // this might not match user sort criteria, so flag list as
  807. // being in unsorted state
  808. break;
  809. }
  810. case ADD_BOTTOM:
  811. mItemList.push_back(item);
  812. break;
  813. default:
  814. llwarns << "Invalid position: " << pos << " - For list: "
  815. << getName() << ". Item added at bottom." << llendl;
  816. llassert(false);
  817. mItemList.push_back(item);
  818. }
  819. setSorted(false);
  820. // Create new column on demand
  821. if (mColumns.empty() && requires_column)
  822. {
  823. LLSD new_column;
  824. new_column["name"] = "default_column";
  825. new_column["label"] = "";
  826. new_column["dynamicwidth"] = true;
  827. addColumn(new_column);
  828. }
  829. S32 num_cols = item->getNumColumns();
  830. S32 i = 0;
  831. for (LLScrollListCell* cell = item->getColumn(i); i < num_cols;
  832. cell = item->getColumn(++i))
  833. {
  834. if (i >= (S32)mColumnsIndexed.size()) break;
  835. cell->setWidth(mColumnsIndexed[i]->getWidth());
  836. }
  837. updateLineHeightInsert(item);
  838. updateLayout();
  839. }
  840. return not_too_big;
  841. }
  842. // NOTE: This is *very* expensive for large lists, especially when we are
  843. // dirtying the list every frame while receiving a long list of names.
  844. // *TODO: Use bookkeeping to make this an incremental cost with item additions
  845. S32 LLScrollListCtrl::calcMaxContentWidth()
  846. {
  847. static const LLFontGL* font = LLFontGL::getFontSansSerifSmall();
  848. constexpr S32 HEADING_TEXT_PADDING = 25;
  849. constexpr S32 COLUMN_TEXT_PADDING = 10;
  850. S32 max_item_width = 0;
  851. for (ordered_columns_t::iterator it = mColumnsIndexed.begin(),
  852. end = mColumnsIndexed.end();
  853. it != end; ++it)
  854. {
  855. LLScrollListColumn* column = *it;
  856. if (!column) continue; // Paranoia
  857. if (mColumnWidthsDirty)
  858. {
  859. // Update max content width for this column, by looking at all
  860. // items
  861. S32 new_width = 0;
  862. if (column->mHeader)
  863. {
  864. new_width = font->getWidth(column->mLabel) + mColumnPadding +
  865. HEADING_TEXT_PADDING;
  866. }
  867. for (item_list::iterator it2 = mItemList.begin(),
  868. end2 = mItemList.end();
  869. it2 != end2; ++it2)
  870. {
  871. LLScrollListCell* cellp = (*it2)->getColumn(column->mIndex);
  872. if (!cellp) continue;
  873. new_width =
  874. llmax(font->getWidth(cellp->getValue().asString()) +
  875. mColumnPadding + COLUMN_TEXT_PADDING,
  876. new_width);
  877. }
  878. column->mMaxContentWidth = new_width;
  879. }
  880. max_item_width += column->mMaxContentWidth;
  881. }
  882. mColumnWidthsDirty = false;
  883. return max_item_width;
  884. }
  885. bool LLScrollListCtrl::updateColumnWidths()
  886. {
  887. bool width_changed = false;
  888. for (ordered_columns_t::iterator column_it = mColumnsIndexed.begin(),
  889. end = mColumnsIndexed.end();
  890. column_it != end; ++column_it)
  891. {
  892. LLScrollListColumn* column = *column_it;
  893. if (!column) continue;
  894. // Update column width
  895. S32 new_width = 0;
  896. if (column->mRelWidth >= 0)
  897. {
  898. new_width = ll_roundp(column->mRelWidth *
  899. (mItemListRect.getWidth() -
  900. mTotalStaticColumnWidth -
  901. mTotalColumnPadding));
  902. }
  903. else if (column->mDynamicWidth && mNumDynamicWidthColumns > 0)
  904. {
  905. new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth -
  906. mTotalColumnPadding) / mNumDynamicWidthColumns;
  907. }
  908. else
  909. {
  910. new_width = column->getWidth();
  911. }
  912. if (column->getWidth() != new_width)
  913. {
  914. column->setWidth(new_width);
  915. width_changed = true;
  916. }
  917. }
  918. return width_changed;
  919. }
  920. constexpr S32 SCROLL_LIST_ROW_PAD = 2;
  921. // Line height is the max height of all the cells in all the items.
  922. void LLScrollListCtrl::updateLineHeight()
  923. {
  924. mLineHeight = 0;
  925. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  926. iter != end; ++iter)
  927. {
  928. updateLineHeightInsert(*iter);
  929. }
  930. }
  931. // When the only change to line height is from an insert, we do not need to
  932. // scan the entire list
  933. void LLScrollListCtrl::updateLineHeightInsert(LLScrollListItem* itemp)
  934. {
  935. if (!itemp) return;
  936. for (S32 i = 0, count = itemp->getNumColumns(); i < count; ++i)
  937. {
  938. const LLScrollListCell* cell = itemp->getColumn(i);
  939. if (cell)
  940. {
  941. mLineHeight = llmax(mLineHeight,
  942. cell->getHeight() + SCROLL_LIST_ROW_PAD);
  943. }
  944. }
  945. }
  946. void LLScrollListCtrl::updateColumns(bool force_update)
  947. {
  948. if (!mColumnsDirty && !force_update)
  949. {
  950. return;
  951. }
  952. mColumnsDirty = false;
  953. bool columns_changed_width = updateColumnWidths();
  954. // Update column headers
  955. S32 left = mItemListRect.mLeft;
  956. S32 top = mItemListRect.mTop;
  957. S32 width = mItemListRect.getWidth();
  958. LLColumnHeader* last_header = NULL;
  959. for (size_t i = 0, count = mColumnsIndexed.size(); i < count; ++i)
  960. {
  961. LLScrollListColumn* column = mColumnsIndexed[i];
  962. if (column && column->mHeader && column->getWidth() >= 0)
  963. {
  964. last_header = column->mHeader;
  965. last_header->updateResizeBars();
  966. S32 right = left + column->getWidth();
  967. if (column->mIndex != (S32)mColumnsIndexed.size() - 1)
  968. {
  969. right += mColumnPadding;
  970. }
  971. right = llmax(left, llmin(width, right));
  972. S32 header_width = right - left;
  973. last_header->reshape(header_width, mHeadingHeight);
  974. last_header->translate(left - last_header->getRect().mLeft,
  975. top - last_header->getRect().mBottom);
  976. last_header->setVisible(mDisplayColumnHeaders && header_width > 0);
  977. left = right;
  978. }
  979. }
  980. // Expand last column header we encountered to full list width
  981. if (last_header && last_header->canResize())
  982. {
  983. S32 new_width =
  984. llmax(0, mItemListRect.mRight - last_header->getRect().mLeft);
  985. last_header->reshape(new_width, last_header->getRect().getHeight());
  986. last_header->setVisible(mDisplayColumnHeaders && new_width > 0);
  987. last_header->getColumn()->setWidth(new_width);
  988. }
  989. if (columns_changed_width || force_update)
  990. {
  991. // Propagate column widths to individual cells
  992. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  993. iter != end; ++iter)
  994. {
  995. LLScrollListItem* itemp = *iter;
  996. S32 num_cols = itemp->getNumColumns();
  997. S32 i = 0;
  998. for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols;
  999. cell = itemp->getColumn(++i))
  1000. {
  1001. if (i >= (S32)mColumnsIndexed.size()) break;
  1002. cell->setWidth(mColumnsIndexed[i]->getWidth());
  1003. }
  1004. }
  1005. }
  1006. }
  1007. void LLScrollListCtrl::setDisplayHeading(bool display)
  1008. {
  1009. mDisplayColumnHeaders = display;
  1010. updateLayout();
  1011. }
  1012. void LLScrollListCtrl::setHeadingHeight(S32 heading_height)
  1013. {
  1014. mHeadingHeight = heading_height;
  1015. updateLayout();
  1016. }
  1017. bool LLScrollListCtrl::selectFirstItem()
  1018. {
  1019. bool success = false;
  1020. // Our $%&@#$()^%#$()*^ iterators do not let us check against the first
  1021. // item inside out iteration
  1022. bool first_item = true;
  1023. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  1024. iter != end; ++iter)
  1025. {
  1026. LLScrollListItem* itemp = *iter;
  1027. if (!itemp) continue; // Paranoia
  1028. if (first_item && itemp->getEnabled())
  1029. {
  1030. if (!itemp->getSelected())
  1031. {
  1032. selectItem(itemp);
  1033. }
  1034. success = true;
  1035. mOriginalSelection = 0;
  1036. }
  1037. else
  1038. {
  1039. deselectItem(itemp);
  1040. }
  1041. first_item = false;
  1042. }
  1043. if (mCommitOnSelectionChange)
  1044. {
  1045. commitIfChanged();
  1046. }
  1047. return success;
  1048. }
  1049. // Deselects all other items
  1050. //virtual
  1051. bool LLScrollListCtrl::selectNthItem(S32 target_index)
  1052. {
  1053. S32 count = mItemList.size();
  1054. if (!count || target_index < 0 || target_index >= count)
  1055. {
  1056. return false;
  1057. }
  1058. return selectItemRange(target_index, target_index);
  1059. }
  1060. //virtual
  1061. bool LLScrollListCtrl::selectItemRange(S32 first_index, S32 last_index)
  1062. {
  1063. if (mItemList.empty())
  1064. {
  1065. return false;
  1066. }
  1067. S32 listlen = (S32)mItemList.size();
  1068. first_index = llclamp(first_index, 0, listlen - 1);
  1069. if (last_index < 0)
  1070. {
  1071. last_index = listlen - 1;
  1072. }
  1073. else
  1074. {
  1075. last_index = llclamp(last_index, first_index, listlen - 1);
  1076. }
  1077. bool success = false;
  1078. S32 index = 0;
  1079. for (item_list::iterator iter = mItemList.begin(),
  1080. end = mItemList.end(); iter != end; )
  1081. {
  1082. LLScrollListItem* itemp = *iter;
  1083. if (!itemp)
  1084. {
  1085. iter = mItemList.erase(iter);
  1086. continue;
  1087. }
  1088. if (index >= first_index && index <= last_index)
  1089. {
  1090. if (itemp->getEnabled())
  1091. {
  1092. selectItem(itemp, false);
  1093. success = true;
  1094. }
  1095. }
  1096. else
  1097. {
  1098. deselectItem(itemp);
  1099. }
  1100. ++index;
  1101. ++iter;
  1102. }
  1103. if (mCommitOnSelectionChange)
  1104. {
  1105. commitIfChanged();
  1106. }
  1107. mSearchString.clear();
  1108. return success;
  1109. }
  1110. void LLScrollListCtrl::swapWithNext(S32 index)
  1111. {
  1112. if (index >= (S32)mItemList.size() - 1)
  1113. {
  1114. // At end of list, does not do anything
  1115. return;
  1116. }
  1117. LLScrollListItem* cur_itemp = mItemList[index];
  1118. mItemList[index] = mItemList[index + 1];
  1119. mItemList[index + 1] = cur_itemp;
  1120. }
  1121. void LLScrollListCtrl::swapWithPrevious(S32 index)
  1122. {
  1123. if (index <= 0)
  1124. {
  1125. // At beginning of list, don't do anything
  1126. }
  1127. LLScrollListItem* cur_itemp = mItemList[index];
  1128. mItemList[index] = mItemList[index - 1];
  1129. mItemList[index - 1] = cur_itemp;
  1130. }
  1131. void LLScrollListCtrl::deleteSingleItem(S32 target_index)
  1132. {
  1133. if (target_index < 0 || target_index >= (S32)mItemList.size())
  1134. {
  1135. return;
  1136. }
  1137. LLScrollListItem* itemp = mItemList[target_index];
  1138. if (itemp == mLastSelected)
  1139. {
  1140. mLastSelected = NULL;
  1141. }
  1142. delete itemp;
  1143. mItemList.erase(mItemList.begin() + target_index);
  1144. dirtyColumns();
  1145. }
  1146. void LLScrollListCtrl::deleteItem(LLScrollListItem* item)
  1147. {
  1148. if (item)
  1149. {
  1150. S32 index = getItemIndex(item);
  1151. if (index >= 0)
  1152. {
  1153. deleteSingleItem(index);
  1154. }
  1155. }
  1156. }
  1157. //FIXME: refactor item deletion
  1158. void LLScrollListCtrl::deleteItems(const LLSD& sd)
  1159. {
  1160. item_list::iterator iter;
  1161. for (iter = mItemList.begin(); iter < mItemList.end(); )
  1162. {
  1163. LLScrollListItem* itemp = *iter;
  1164. if (itemp && itemp->getValue().asString() == sd.asString())
  1165. {
  1166. if (itemp == mLastSelected)
  1167. {
  1168. mLastSelected = NULL;
  1169. }
  1170. delete itemp;
  1171. iter = mItemList.erase(iter);
  1172. }
  1173. else
  1174. {
  1175. ++iter;
  1176. }
  1177. }
  1178. dirtyColumns();
  1179. }
  1180. void LLScrollListCtrl::deleteSelectedItems()
  1181. {
  1182. item_list::iterator iter;
  1183. for (iter = mItemList.begin(); iter < mItemList.end(); )
  1184. {
  1185. LLScrollListItem* itemp = *iter;
  1186. if (itemp && itemp->getSelected())
  1187. {
  1188. delete itemp;
  1189. iter = mItemList.erase(iter);
  1190. }
  1191. else
  1192. {
  1193. ++iter;
  1194. }
  1195. }
  1196. mLastSelected = NULL;
  1197. dirtyColumns();
  1198. }
  1199. void LLScrollListCtrl::highlightNthItem(S32 target_index)
  1200. {
  1201. if (mHighlightedItem != target_index)
  1202. {
  1203. mHighlightedItem = target_index;
  1204. }
  1205. }
  1206. S32 LLScrollListCtrl::selectMultiple(uuid_vec_t ids)
  1207. {
  1208. S32 count = 0;
  1209. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  1210. iter != end; ++iter)
  1211. {
  1212. LLScrollListItem* item = *iter;
  1213. uuid_vec_t::iterator iditr;
  1214. uuid_vec_t::iterator end2 = ids.end();
  1215. for (iditr = ids.begin(); iditr != end2; ++iditr)
  1216. {
  1217. if (item && item->getEnabled() && item->getUUID() == (*iditr))
  1218. {
  1219. selectItem(item, false);
  1220. ++count;
  1221. break;
  1222. }
  1223. }
  1224. if (iditr != end2)
  1225. {
  1226. ids.erase(iditr);
  1227. }
  1228. }
  1229. if (mCommitOnSelectionChange)
  1230. {
  1231. commitIfChanged();
  1232. }
  1233. return count;
  1234. }
  1235. S32 LLScrollListCtrl::getItemIndex(LLScrollListItem* target_item) const
  1236. {
  1237. S32 index = 0;
  1238. for (item_list::const_iterator iter = mItemList.begin(),
  1239. end = mItemList.end();
  1240. iter != end; ++iter)
  1241. {
  1242. LLScrollListItem* itemp = *iter;
  1243. if (target_item == itemp)
  1244. {
  1245. return index;
  1246. }
  1247. ++index;
  1248. }
  1249. return -1;
  1250. }
  1251. S32 LLScrollListCtrl::getItemIndex(const LLUUID& target_id) const
  1252. {
  1253. S32 index = 0;
  1254. for (item_list::const_iterator iter = mItemList.begin(),
  1255. end = mItemList.end();
  1256. iter != end; ++iter)
  1257. {
  1258. LLScrollListItem* itemp = *iter;
  1259. if (target_id == itemp->getUUID())
  1260. {
  1261. return index;
  1262. }
  1263. ++index;
  1264. }
  1265. return -1;
  1266. }
  1267. void LLScrollListCtrl::selectPrevItem(bool extend_selection)
  1268. {
  1269. LLScrollListItem* prev_item = NULL;
  1270. if (!getFirstSelected())
  1271. {
  1272. // Select last item
  1273. selectNthItem(getItemCount() - 1);
  1274. }
  1275. else
  1276. {
  1277. for (item_list::iterator iter = mItemList.begin(),
  1278. end = mItemList.end();
  1279. iter != end; ++iter)
  1280. {
  1281. LLScrollListItem* cur_item = *iter;
  1282. if (cur_item->getSelected())
  1283. {
  1284. if (prev_item)
  1285. {
  1286. selectItem(prev_item, !extend_selection);
  1287. }
  1288. else
  1289. {
  1290. reportInvalidInput();
  1291. }
  1292. break;
  1293. }
  1294. // Do not allow navigation to disabled elements
  1295. prev_item = cur_item->getEnabled() ? cur_item : prev_item;
  1296. }
  1297. }
  1298. if (mCommitOnSelectionChange)
  1299. {
  1300. commitIfChanged();
  1301. }
  1302. mSearchString.clear();
  1303. }
  1304. void LLScrollListCtrl::selectNextItem(bool extend_selection)
  1305. {
  1306. LLScrollListItem* next_item = NULL;
  1307. if (!getFirstSelected())
  1308. {
  1309. selectFirstItem();
  1310. }
  1311. else
  1312. {
  1313. for (item_list::reverse_iterator iter = mItemList.rbegin(),
  1314. rend = mItemList.rend();
  1315. iter != rend; ++iter)
  1316. {
  1317. LLScrollListItem* cur_item = *iter;
  1318. if (cur_item->getSelected())
  1319. {
  1320. if (next_item)
  1321. {
  1322. selectItem(next_item, !extend_selection);
  1323. }
  1324. else
  1325. {
  1326. reportInvalidInput();
  1327. }
  1328. break;
  1329. }
  1330. // Do not allow navigation to disabled items
  1331. next_item = cur_item->getEnabled() ? cur_item : next_item;
  1332. }
  1333. }
  1334. if (mCommitOnSelectionChange)
  1335. {
  1336. commitIfChanged();
  1337. }
  1338. mSearchString.clear();
  1339. }
  1340. void LLScrollListCtrl::deselectAllItems(bool no_commit_on_change)
  1341. {
  1342. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  1343. iter != end; ++iter)
  1344. {
  1345. LLScrollListItem* item = *iter;
  1346. deselectItem(item);
  1347. }
  1348. if (mCommitOnSelectionChange && !no_commit_on_change)
  1349. {
  1350. commitIfChanged();
  1351. }
  1352. }
  1353. ////////////////////////////////////////////////////////////////////////////////
  1354. // Use this to add comment text such as "Searching", which ignores column
  1355. // settings of list
  1356. LLScrollListItem* LLScrollListCtrl::addCommentText(const std::string& comment_text,
  1357. EAddPosition pos)
  1358. {
  1359. LLScrollListItem* item = NULL;
  1360. if (getItemCount() < mMaxItemCount)
  1361. {
  1362. // Always draw comment text with "enabled" color
  1363. item = new LLScrollListItemComment(comment_text, mFgUnselectedColor);
  1364. addItem(item, pos, false);
  1365. }
  1366. return item;
  1367. }
  1368. LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos)
  1369. {
  1370. LLScrollListItem* item = new LLScrollListItemSeparator();
  1371. addItem(item, pos, false);
  1372. return item;
  1373. }
  1374. // Selects first enabled item of the given name.
  1375. // Returns false if item not found.
  1376. bool LLScrollListCtrl::selectItemByLabel(const std::string& label,
  1377. bool case_sensitive, S32 column)
  1378. {
  1379. // Ensure that no stale items are selected, even if we don't find a match
  1380. deselectAllItems(true);
  1381. // RN: assume no empty items
  1382. if (label.empty())
  1383. {
  1384. return false;
  1385. }
  1386. std::string target_text = label;
  1387. if (!case_sensitive)
  1388. {
  1389. LLStringUtil::toLower(target_text);
  1390. }
  1391. bool found = false;
  1392. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  1393. iter != end; ++iter)
  1394. {
  1395. LLScrollListItem* item = *iter;
  1396. // Only select enabled items with matching names
  1397. std::string item_text = item->getColumn(column)->getValue().asString();
  1398. if (!case_sensitive)
  1399. {
  1400. LLStringUtil::toLower(item_text);
  1401. }
  1402. found = item->getEnabled() && item_text == target_text;
  1403. if (found)
  1404. {
  1405. selectItem(item);
  1406. break;
  1407. }
  1408. }
  1409. if (mCommitOnSelectionChange)
  1410. {
  1411. commitIfChanged();
  1412. }
  1413. return found;
  1414. }
  1415. LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label,
  1416. bool case_sensitive,
  1417. S32 column) const
  1418. {
  1419. // RN: assume no empty items
  1420. if (label.empty())
  1421. {
  1422. return NULL;
  1423. }
  1424. std::string target_text = label;
  1425. if (!case_sensitive)
  1426. {
  1427. LLStringUtil::toLower(target_text);
  1428. }
  1429. for (item_list::const_iterator iter = mItemList.begin(),
  1430. end = mItemList.end();
  1431. iter != end; ++iter)
  1432. {
  1433. LLScrollListItem* item = *iter;
  1434. // Only select enabled items with matching names
  1435. std::string item_text = item->getColumn(column)->getValue().asString();
  1436. if (!case_sensitive)
  1437. {
  1438. LLStringUtil::toLower(item_text);
  1439. }
  1440. if (item_text == target_text)
  1441. {
  1442. return item;
  1443. }
  1444. }
  1445. return NULL;
  1446. }
  1447. LLScrollListItem* LLScrollListCtrl::getItemByIndex(S32 index) const
  1448. {
  1449. if (index < 0 || mItemList.empty())
  1450. {
  1451. return NULL;
  1452. }
  1453. item_list::const_iterator iter = mItemList.begin();
  1454. item_list::const_iterator end = mItemList.end();
  1455. while (index-- > 0 && iter != end)
  1456. {
  1457. ++iter;
  1458. }
  1459. return iter == end ? NULL : *iter;
  1460. }
  1461. bool LLScrollListCtrl::selectItemByPrefix(const std::string& target,
  1462. bool case_sensitive)
  1463. {
  1464. return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive);
  1465. }
  1466. // Selects first enabled item that has a name where the name's first part
  1467. // matched the target string. Returns false if item not found.
  1468. bool LLScrollListCtrl::selectItemByPrefix(const LLWString& target,
  1469. bool case_sensitive)
  1470. {
  1471. bool found = false;
  1472. LLWString target_trimmed(target);
  1473. S32 target_len = target_trimmed.size();
  1474. if (0 == target_len)
  1475. {
  1476. // Is "" a valid choice?
  1477. for (item_list::iterator iter = mItemList.begin(),
  1478. end = mItemList.end();
  1479. iter != end; ++iter)
  1480. {
  1481. LLScrollListItem* item = *iter;
  1482. // Only select enabled items with matching names
  1483. LLScrollListCell* cellp = item->getColumn(getSearchColumn());
  1484. bool select = cellp ? item->getEnabled() &&
  1485. cellp->getValue().asString()[0] == '\0'
  1486. : false;
  1487. if (select)
  1488. {
  1489. selectItem(item);
  1490. found = true;
  1491. break;
  1492. }
  1493. }
  1494. }
  1495. else
  1496. {
  1497. if (!case_sensitive)
  1498. {
  1499. // Do comparisons in lower case
  1500. LLWStringUtil::toLower(target_trimmed);
  1501. }
  1502. for (item_list::iterator iter = mItemList.begin(),
  1503. end = mItemList.end();
  1504. iter != end; ++iter)
  1505. {
  1506. LLScrollListItem* item = *iter;
  1507. // Only select enabled items with matching names
  1508. LLScrollListCell* cellp = item->getColumn(getSearchColumn());
  1509. if (!cellp)
  1510. {
  1511. continue;
  1512. }
  1513. LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
  1514. if (!case_sensitive)
  1515. {
  1516. LLWStringUtil::toLower(item_label);
  1517. }
  1518. // remove extraneous whitespace from searchable label
  1519. LLWString trimmed_label = item_label;
  1520. LLWStringUtil::trim(trimmed_label);
  1521. bool select = item->getEnabled() &&
  1522. trimmed_label.compare(0, target_trimmed.size(),
  1523. target_trimmed) == 0;
  1524. if (select)
  1525. {
  1526. // find offset of matching text (might have leading whitespace)
  1527. S32 offset = item_label.find(target_trimmed);
  1528. cellp->highlightText(offset, target_trimmed.size());
  1529. selectItem(item);
  1530. found = true;
  1531. break;
  1532. }
  1533. }
  1534. }
  1535. if (mCommitOnSelectionChange)
  1536. {
  1537. commitIfChanged();
  1538. }
  1539. return found;
  1540. }
  1541. const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const
  1542. {
  1543. LLScrollListItem* item = getFirstSelected();
  1544. if (item && item->getColumn(column))
  1545. {
  1546. return item->getColumn(column)->getValue().asString();
  1547. }
  1548. return LLStringUtil::null;
  1549. }
  1550. ////////////////////////////////////////////////////////////////////////////////
  1551. // "StringUUID" interface: use this when you're creating a list that contains
  1552. // non-unique strings each of which has an associated, unique UUID, and only one
  1553. // of which can be selected at a time.
  1554. LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text,
  1555. const LLUUID& id,
  1556. EAddPosition pos,
  1557. bool enabled,
  1558. S32 column_width)
  1559. {
  1560. static const LLFontGL* font = LLFontGL::getFontSansSerifSmall();
  1561. LLScrollListItem* item = NULL;
  1562. if (getItemCount() < mMaxItemCount)
  1563. {
  1564. item = new LLScrollListItem(enabled, NULL, id);
  1565. item->addColumn(item_text, font, column_width);
  1566. addItem(item, pos);
  1567. }
  1568. return item;
  1569. }
  1570. // Select the line or lines that match this UUID
  1571. bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected)
  1572. {
  1573. bool found = false;
  1574. if (selected && !mAllowMultipleSelection)
  1575. {
  1576. deselectAllItems(true);
  1577. }
  1578. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  1579. iter != end; ++iter)
  1580. {
  1581. LLScrollListItem* item = *iter;
  1582. if (item && item->getEnabled() &&
  1583. item->getValue().asString() == value.asString())
  1584. {
  1585. if (selected)
  1586. {
  1587. selectItem(item);
  1588. }
  1589. else
  1590. {
  1591. deselectItem(item);
  1592. }
  1593. found = true;
  1594. break;
  1595. }
  1596. }
  1597. if (mCommitOnSelectionChange)
  1598. {
  1599. commitIfChanged();
  1600. }
  1601. return found;
  1602. }
  1603. bool LLScrollListCtrl::isSelected(const LLSD& value) const
  1604. {
  1605. for (item_list::const_iterator iter = mItemList.begin(),
  1606. end = mItemList.end();
  1607. iter != end; ++iter)
  1608. {
  1609. LLScrollListItem* item = *iter;
  1610. if (item && item->getValue().asString() == value.asString())
  1611. {
  1612. return item->getSelected();
  1613. }
  1614. }
  1615. return false;
  1616. }
  1617. LLUUID LLScrollListCtrl::getStringUUIDSelectedItem() const
  1618. {
  1619. LLScrollListItem* item = getFirstSelected();
  1620. return item ? item->getUUID() : LLUUID::null;
  1621. }
  1622. LLSD LLScrollListCtrl::getSelectedValue()
  1623. {
  1624. LLScrollListItem* item = getFirstSelected();
  1625. return item ? item->getValue() : LLSD();
  1626. }
  1627. void LLScrollListCtrl::drawItems()
  1628. {
  1629. S32 first_line = mScrollLines;
  1630. S32 count = mItemList.size();
  1631. if (first_line >= count)
  1632. {
  1633. return;
  1634. }
  1635. LLGLSUIDefault gls_ui;
  1636. LLLocalClipRect clip(mItemListRect);
  1637. static LLColor4 highlight_color = LLColor4::white;
  1638. highlight_color.mV[VALPHA] =
  1639. clamp_rescale(mSearchTimer.getElapsedTimeF32(),
  1640. LLUI::sTypeAheadTimeout * 0.7f,
  1641. LLUI::sTypeAheadTimeout, 0.4f, 0.f);
  1642. LLRect item_rect;
  1643. LLColor4* fg_color;
  1644. LLColor4* bg_color;
  1645. S32 list_width = mItemListRect.getWidth();
  1646. S32 x = mItemListRect.mLeft;
  1647. S32 y = mItemListRect.mTop - mLineHeight;
  1648. S32 cur_y = y;
  1649. S32 max_columns = 0;
  1650. // Allow for partial line at bottom
  1651. S32 num_page_lines = mPageLines + 1;
  1652. S32 last_line = llmin(count - 1, mScrollLines + num_page_lines);
  1653. for (S32 line = first_line; line <= last_line; ++line)
  1654. {
  1655. LLScrollListItem* item = mItemList[line];
  1656. if (!item) continue;
  1657. item_rect.setOriginAndSize(x, cur_y, list_width, mLineHeight);
  1658. max_columns = llmax(max_columns, item->getNumColumns());
  1659. if (mScrollLines <= line && line < mScrollLines + num_page_lines)
  1660. {
  1661. if (mCanSelect && item->getSelected())
  1662. {
  1663. if (item->getEnabled())
  1664. {
  1665. fg_color = &mFgSelectedColor;
  1666. }
  1667. else
  1668. {
  1669. fg_color = &mFgDisabledColor;
  1670. }
  1671. bg_color = &mBgSelectedColor;
  1672. }
  1673. else if (!item->getEnabled())
  1674. {
  1675. fg_color = &mFgDisabledColor;
  1676. bg_color = &mBgReadOnlyColor;
  1677. }
  1678. else if (mHighlightedItem == line && mCanSelect)
  1679. {
  1680. fg_color = &mFgUnselectedColor;
  1681. bg_color = &mHighlightedColor;
  1682. }
  1683. else if (mDrawStripes && line % 2 == 0)
  1684. {
  1685. fg_color = &mFgUnselectedColor;
  1686. bg_color = &mBgStripeColor;
  1687. }
  1688. else
  1689. {
  1690. fg_color = &mFgUnselectedColor;
  1691. bg_color = &LLColor4::transparent;
  1692. }
  1693. item->draw(item_rect, *fg_color, *bg_color, highlight_color,
  1694. mColumnPadding);
  1695. cur_y -= mLineHeight;
  1696. }
  1697. }
  1698. }
  1699. void LLScrollListCtrl::draw()
  1700. {
  1701. LLLocalClipRect clip(getLocalRect());
  1702. // If user specifies sort, make sure it is maintained
  1703. if (!mSorted && !mSortColumns.empty())
  1704. {
  1705. sortItems();
  1706. }
  1707. if (mNeedsScroll)
  1708. {
  1709. scrollToShowSelected();
  1710. mNeedsScroll = false;
  1711. }
  1712. // Draw background
  1713. if (mBackgroundVisible)
  1714. {
  1715. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  1716. gGL.color4fv(getEnabled() ? mBgWriteableColor.mV : mBgReadOnlyColor.mV);
  1717. const LLRect& rect = getRect();
  1718. LLRect background(0, rect.getHeight(), rect.getWidth(), 0);
  1719. gl_rect_2d(background);
  1720. }
  1721. updateColumns();
  1722. drawItems();
  1723. if (mBorder)
  1724. {
  1725. mBorder->setKeyboardFocusHighlight(gFocusMgr.getKeyboardFocus() == this);
  1726. }
  1727. LLUICtrl::draw();
  1728. }
  1729. void LLScrollListCtrl::setEnabled(bool enabled)
  1730. {
  1731. mCanSelect = enabled;
  1732. setTabStop(enabled);
  1733. mScrollbar->setTabStop(!enabled &&
  1734. mScrollbar->getPageSize() < mScrollbar->getDocSize());
  1735. }
  1736. bool LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
  1737. {
  1738. // Pretend the mouse is over the scrollbar
  1739. return mScrollbar->handleScrollWheel(0, 0, clicks);
  1740. }
  1741. bool LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg,
  1742. LLRect* sticky_rect_screen)
  1743. {
  1744. S32 column_index = getColumnIndexFromOffset(x);
  1745. LLScrollListColumn* columnp = getColumn(column_index);
  1746. if (!columnp) return false;
  1747. // Show tooltip for full name of hovered item if it has been truncated
  1748. LLScrollListItem* hit_item = hitItem(x, y);
  1749. if (hit_item)
  1750. {
  1751. // If the item has a specific tool tip set by XUI use that first
  1752. std::string tooltip = hit_item->getToolTip();
  1753. if (!tooltip.empty())
  1754. {
  1755. msg = tooltip;
  1756. return true;
  1757. }
  1758. LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
  1759. if (!hit_cell)
  1760. {
  1761. return false;
  1762. }
  1763. if (hit_cell && hit_cell->isText())
  1764. {
  1765. S32 rect_left = getColumnOffsetFromIndex(column_index) +
  1766. mItemListRect.mLeft;
  1767. S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item));
  1768. LLRect cell_rect;
  1769. cell_rect.setOriginAndSize(rect_left, rect_bottom,
  1770. rect_left + columnp->getWidth(),
  1771. mLineHeight);
  1772. // Convert rect local to screen coordinates
  1773. localPointToScreen(cell_rect.mLeft, cell_rect.mBottom,
  1774. &(sticky_rect_screen->mLeft),
  1775. &(sticky_rect_screen->mBottom));
  1776. localPointToScreen(cell_rect.mRight, cell_rect.mTop,
  1777. &(sticky_rect_screen->mRight),
  1778. &(sticky_rect_screen->mTop));
  1779. msg = hit_cell->getValue().asString();
  1780. }
  1781. return true;
  1782. }
  1783. // Otherwise, look for a tooltip associated with this column
  1784. LLColumnHeader* headerp = columnp->mHeader;
  1785. if (headerp)
  1786. {
  1787. headerp->handleToolTip(x, y, msg, sticky_rect_screen);
  1788. return !msg.empty();
  1789. }
  1790. return false;
  1791. }
  1792. bool LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask)
  1793. {
  1794. if (!mCanSelect) return false;
  1795. bool selection_changed = false;
  1796. LLScrollListItem* hit_item = hitItem(x, y);
  1797. if (hit_item)
  1798. {
  1799. if (mAllowMultipleSelection)
  1800. {
  1801. if (mask & MASK_SHIFT)
  1802. {
  1803. if (mLastSelected == NULL)
  1804. {
  1805. selectItem(hit_item);
  1806. }
  1807. else
  1808. {
  1809. // Select everything between mLastSelected and hit_item
  1810. bool selecting = false;
  1811. // If we multiselect backwards, we will stomp on
  1812. // mLastSelected, meaning that we never stop selecting
  1813. // until hitting max or the end of the list.
  1814. LLScrollListItem* last_selected = mLastSelected;
  1815. for (item_list::iterator it = mItemList.begin(),
  1816. end = mItemList.end();
  1817. it != end; ++it)
  1818. {
  1819. if (mMaxSelectable > 0 &&
  1820. getAllSelected().size() >= mMaxSelectable)
  1821. {
  1822. if (mOnMaximumSelectCallback)
  1823. {
  1824. mOnMaximumSelectCallback(mCallbackUserData);
  1825. }
  1826. break;
  1827. }
  1828. LLScrollListItem* item = *it;
  1829. if (!item) continue;
  1830. if (item == hit_item || item == last_selected)
  1831. {
  1832. selectItem(item, false);
  1833. selecting = !selecting;
  1834. if (hit_item == last_selected)
  1835. {
  1836. // Stop selecting now, since we just clicked on
  1837. // our last selected item
  1838. selecting = false;
  1839. }
  1840. }
  1841. if (selecting)
  1842. {
  1843. selectItem(item, false);
  1844. }
  1845. }
  1846. }
  1847. }
  1848. else if (mask & MASK_CONTROL)
  1849. {
  1850. if (hit_item->getSelected())
  1851. {
  1852. deselectItem(hit_item);
  1853. }
  1854. else
  1855. {
  1856. if (!(mMaxSelectable > 0 &&
  1857. getAllSelected().size() >= mMaxSelectable))
  1858. {
  1859. selectItem(hit_item, false);
  1860. }
  1861. else
  1862. {
  1863. if (mOnMaximumSelectCallback)
  1864. {
  1865. mOnMaximumSelectCallback(mCallbackUserData);
  1866. }
  1867. }
  1868. }
  1869. }
  1870. else if (mLastSelected != hit_item)
  1871. {
  1872. deselectAllItems(true);
  1873. selectItem(hit_item);
  1874. }
  1875. }
  1876. // This allows to de-select an item in single-selection lists. HB
  1877. else if ((mask & MASK_CONTROL) && hit_item->getSelected())
  1878. {
  1879. deselectItem(hit_item);
  1880. }
  1881. else
  1882. {
  1883. selectItem(hit_item);
  1884. }
  1885. selection_changed = mSelectionChanged;
  1886. if (mCommitOnSelectionChange)
  1887. {
  1888. commitIfChanged();
  1889. }
  1890. // Clear search string on mouse operations
  1891. mSearchString.clear();
  1892. }
  1893. #if 0
  1894. else
  1895. {
  1896. mLastSelected = NULL;
  1897. deselectAllItems(true);
  1898. }
  1899. #endif
  1900. return selection_changed;
  1901. }
  1902. bool LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
  1903. {
  1904. if (childrenHandleMouseDown(x, y, mask) == NULL)
  1905. {
  1906. // Set keyboard focus first, in case click action wants to move focus
  1907. // elsewhere
  1908. setFocus(true);
  1909. // Clear selection changed flag because user is starting a selection
  1910. // operation
  1911. mSelectionChanged = false;
  1912. handleClick(x, y, mask);
  1913. }
  1914. return true;
  1915. }
  1916. bool LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
  1917. {
  1918. if (hasMouseCapture())
  1919. {
  1920. // Release mouse capture immediately so that the "scroll to show
  1921. // selected" logic can work
  1922. gFocusMgr.setMouseCapture(NULL);
  1923. if (mask == MASK_NONE)
  1924. {
  1925. selectItemAt(x, y, mask);
  1926. mNeedsScroll = true;
  1927. }
  1928. }
  1929. // When not committing already on selection change, always commit when
  1930. // mouse operation is completed inside the list (required for combo
  1931. // scrolldown lists, for example), but do not do it when
  1932. // mCommitOnSelectionChange is true, to avoid duplicate onCommit() events.
  1933. if (!mCommitOnSelectionChange && mItemListRect.pointInRect(x, y))
  1934. {
  1935. mDirty |= mSelectionChanged;
  1936. mSelectionChanged = false;
  1937. onCommit();
  1938. }
  1939. return LLUICtrl::handleMouseUp(x, y, mask);
  1940. }
  1941. bool LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
  1942. {
  1943. if (!handleClick(x, y, mask))
  1944. {
  1945. // Offer the click to the children, even if we are not enabled, so that
  1946. // the scroll bars will work.
  1947. if (LLView::childrenHandleDoubleClick(x, y, mask) == NULL)
  1948. {
  1949. if (mCanSelect && mOnDoubleClickCallback)
  1950. {
  1951. mOnDoubleClickCallback(mCallbackUserData);
  1952. }
  1953. }
  1954. }
  1955. return true;
  1956. }
  1957. bool LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask)
  1958. {
  1959. // Which row was clicked on ?
  1960. LLScrollListItem* hit_item = hitItem(x, y);
  1961. if (!hit_item) return false;
  1962. // Get appropriate cell from that row
  1963. S32 column_index = getColumnIndexFromOffset(x);
  1964. LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
  1965. if (!hit_cell) return false;
  1966. // If cell handled click directly (i.e. clicked on an embedded checkbox)
  1967. if (hit_cell->handleClick())
  1968. {
  1969. // If item not currently selected, select it
  1970. if (!hit_item->getSelected())
  1971. {
  1972. selectItemAt(x, y, mask);
  1973. gFocusMgr.setMouseCapture(this);
  1974. mNeedsScroll = true;
  1975. }
  1976. // Propagate value of this cell to other selected items and commit the
  1977. // respective widgets
  1978. LLSD item_value = hit_cell->getValue();
  1979. for (item_list::iterator iter = mItemList.begin(),
  1980. end = mItemList.end();
  1981. iter != end; ++iter)
  1982. {
  1983. LLScrollListItem* item = *iter;
  1984. if (item && item->getSelected())
  1985. {
  1986. LLScrollListCell* cellp = item->getColumn(column_index);
  1987. if (cellp)
  1988. {
  1989. cellp->setValue(item_value);
  1990. cellp->onCommit();
  1991. if (!mLastSelected)
  1992. {
  1993. break;
  1994. }
  1995. }
  1996. }
  1997. }
  1998. // *FIXME: find a better way to signal cell changes
  1999. onCommit();
  2000. // Eat click (e.g. do not trigger double click callback)
  2001. return true;
  2002. }
  2003. else
  2004. {
  2005. // Treat this as a normal single item selection
  2006. selectItemAt(x, y, mask);
  2007. gFocusMgr.setMouseCapture(this);
  2008. mNeedsScroll = true;
  2009. // Do not eat click (allow double click callback)
  2010. return false;
  2011. }
  2012. }
  2013. LLScrollListItem* LLScrollListCtrl::hitItem(S32 x, S32 y)
  2014. {
  2015. // Excludes disabled items.
  2016. LLScrollListItem* hit_item = NULL;
  2017. LLRect item_rect;
  2018. item_rect.setLeftTopAndSize(mItemListRect.mLeft, mItemListRect.mTop,
  2019. mItemListRect.getWidth(), mLineHeight);
  2020. // Allow for partial line at bottom
  2021. S32 num_page_lines = mPageLines + 1;
  2022. S32 line = 0;
  2023. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  2024. iter != end; ++iter)
  2025. {
  2026. LLScrollListItem* item = *iter;
  2027. if (mScrollLines <= line && line < mScrollLines + num_page_lines)
  2028. {
  2029. if (item && item->getEnabled() && item_rect.pointInRect(x, y))
  2030. {
  2031. hit_item = item;
  2032. break;
  2033. }
  2034. item_rect.translate(0, -mLineHeight);
  2035. }
  2036. ++line;
  2037. }
  2038. return hit_item;
  2039. }
  2040. S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x)
  2041. {
  2042. // Which column did we hit ?
  2043. S32 left = 0;
  2044. S32 right = 0;
  2045. S32 width = 0;
  2046. S32 column_index = 0;
  2047. for (ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(),
  2048. end = mColumnsIndexed.end();
  2049. iter != end; ++iter)
  2050. {
  2051. width = (*iter)->getWidth() + mColumnPadding;
  2052. right += width;
  2053. if (left <= x && x < right)
  2054. {
  2055. break;
  2056. }
  2057. // Set left for next column as right of current column
  2058. left = right;
  2059. ++column_index;
  2060. }
  2061. return llclamp(column_index, 0, getNumColumns() - 1);
  2062. }
  2063. S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index)
  2064. {
  2065. S32 column_offset = 0;
  2066. for (ordered_columns_t::const_iterator iter = mColumnsIndexed.begin(),
  2067. end = mColumnsIndexed.end();
  2068. iter != end; ++iter)
  2069. {
  2070. if (index-- <= 0)
  2071. {
  2072. return column_offset;
  2073. }
  2074. column_offset += (*iter)->getWidth() + mColumnPadding;
  2075. }
  2076. // When running off the end, return the rightmost pixel
  2077. return mItemListRect.mRight;
  2078. }
  2079. S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index)
  2080. {
  2081. return (mItemListRect.mTop - index + mScrollLines) * mLineHeight -
  2082. mLineHeight;
  2083. }
  2084. bool LLScrollListCtrl::handleHover(S32 x, S32 y, MASK mask)
  2085. {
  2086. if (hasMouseCapture())
  2087. {
  2088. if (mask == MASK_NONE)
  2089. {
  2090. selectItemAt(x, y, mask);
  2091. mNeedsScroll = true;
  2092. }
  2093. }
  2094. else if (mCanSelect)
  2095. {
  2096. LLScrollListItem* item = hitItem(x, y);
  2097. if (item)
  2098. {
  2099. highlightNthItem(getItemIndex(item));
  2100. }
  2101. else
  2102. {
  2103. highlightNthItem(-1);
  2104. }
  2105. }
  2106. return LLUICtrl::handleHover(x, y, mask);
  2107. }
  2108. bool LLScrollListCtrl::handleKeyHere(KEY key, MASK mask)
  2109. {
  2110. bool handled = false;
  2111. // Not called from parent means we have keyboard focus or a child does
  2112. if (mCanSelect)
  2113. {
  2114. if (mask == MASK_NONE)
  2115. {
  2116. switch (key)
  2117. {
  2118. case KEY_UP:
  2119. if (mAllowKeyboardMovement || hasFocus())
  2120. {
  2121. // commit implicit in call
  2122. selectPrevItem(false);
  2123. mNeedsScroll = true;
  2124. if (mCommitOnKeyboardMovement &&
  2125. !mCommitOnSelectionChange)
  2126. {
  2127. commitIfChanged();
  2128. }
  2129. handled = true;
  2130. }
  2131. break;
  2132. case KEY_DOWN:
  2133. if (mAllowKeyboardMovement || hasFocus())
  2134. {
  2135. // commit implicit in call
  2136. selectNextItem(false);
  2137. mNeedsScroll = true;
  2138. if (mCommitOnKeyboardMovement &&
  2139. !mCommitOnSelectionChange)
  2140. {
  2141. commitIfChanged();
  2142. }
  2143. handled = true;
  2144. }
  2145. break;
  2146. case KEY_PAGE_UP:
  2147. if (mAllowKeyboardMovement || hasFocus())
  2148. {
  2149. selectNthItem(getFirstSelectedIndex() -
  2150. mScrollbar->getPageSize() + 1);
  2151. mNeedsScroll = true;
  2152. if (mCommitOnKeyboardMovement &&
  2153. !mCommitOnSelectionChange)
  2154. {
  2155. commitIfChanged();
  2156. }
  2157. handled = true;
  2158. }
  2159. break;
  2160. case KEY_PAGE_DOWN:
  2161. if (mAllowKeyboardMovement || hasFocus())
  2162. {
  2163. selectNthItem(getFirstSelectedIndex() +
  2164. mScrollbar->getPageSize() - 1);
  2165. mNeedsScroll = true;
  2166. if (mCommitOnKeyboardMovement &&
  2167. !mCommitOnSelectionChange)
  2168. {
  2169. commitIfChanged();
  2170. }
  2171. handled = true;
  2172. }
  2173. break;
  2174. case KEY_HOME:
  2175. if (mAllowKeyboardMovement || hasFocus())
  2176. {
  2177. selectFirstItem();
  2178. mNeedsScroll = true;
  2179. if (mCommitOnKeyboardMovement &&
  2180. !mCommitOnSelectionChange)
  2181. {
  2182. commitIfChanged();
  2183. }
  2184. handled = true;
  2185. }
  2186. break;
  2187. case KEY_END:
  2188. if (mAllowKeyboardMovement || hasFocus())
  2189. {
  2190. selectNthItem(getItemCount() - 1);
  2191. mNeedsScroll = true;
  2192. if (mCommitOnKeyboardMovement &&
  2193. !mCommitOnSelectionChange)
  2194. {
  2195. commitIfChanged();
  2196. }
  2197. handled = true;
  2198. }
  2199. break;
  2200. case KEY_RETURN:
  2201. // JC - Special case: Only claim to have handled it if we
  2202. // are the special non-commit-on-move type AND we are
  2203. // visible
  2204. if (!mCommitOnKeyboardMovement && mask == MASK_NONE)
  2205. {
  2206. onCommit();
  2207. mSearchString.clear();
  2208. handled = true;
  2209. }
  2210. break;
  2211. case KEY_BACKSPACE:
  2212. mSearchTimer.reset();
  2213. if (mSearchString.size())
  2214. {
  2215. mSearchString.erase(mSearchString.size() - 1, 1);
  2216. }
  2217. if (mSearchString.empty())
  2218. {
  2219. if (getFirstSelected())
  2220. {
  2221. LLScrollListCell* cellp =
  2222. getFirstSelected()->getColumn(getSearchColumn());
  2223. if (cellp)
  2224. {
  2225. cellp->highlightText(0, 0);
  2226. }
  2227. }
  2228. }
  2229. else if (selectItemByPrefix(wstring_to_utf8str(mSearchString),
  2230. false))
  2231. {
  2232. mNeedsScroll = true;
  2233. // Update search string only on successful match
  2234. mSearchTimer.reset();
  2235. if (mCommitOnKeyboardMovement &&
  2236. !mCommitOnSelectionChange)
  2237. {
  2238. commitIfChanged();
  2239. }
  2240. }
  2241. break;
  2242. default:
  2243. break;
  2244. }
  2245. }
  2246. // *TODO: multiple: shift-up, shift-down, shift-home, shift-end,
  2247. // select all
  2248. }
  2249. return handled;
  2250. }
  2251. bool LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char)
  2252. {
  2253. if (uni_char < 0x20 || uni_char == 0x7F) // Control character or DEL
  2254. {
  2255. return false;
  2256. }
  2257. bool handled = false;
  2258. // Perform incremental search based on keyboard input
  2259. if (mSearchTimer.getElapsedTimeF32() > LLUI::sTypeAheadTimeout)
  2260. {
  2261. mSearchString.clear();
  2262. }
  2263. // Type ahead search is case insensitive
  2264. uni_char = LLStringOps::toLower((llwchar)uni_char);
  2265. if (selectItemByPrefix(wstring_to_utf8str(mSearchString +
  2266. (llwchar)uni_char), false))
  2267. {
  2268. // Update search string only on successful match
  2269. mNeedsScroll = true;
  2270. mSearchString += uni_char;
  2271. mSearchTimer.reset();
  2272. if (mCommitOnKeyboardMovement && !mCommitOnSelectionChange)
  2273. {
  2274. commitIfChanged();
  2275. }
  2276. handled = true;
  2277. }
  2278. // Handle iterating over same starting character
  2279. else if (isRepeatedChars(mSearchString + (llwchar)uni_char) &&
  2280. !mItemList.empty())
  2281. {
  2282. // Start from last selected item, in case we previously had a
  2283. // successful match against duplicated characters ('AA' matches
  2284. // 'Aaron')
  2285. item_list::iterator start_iter = mItemList.begin();
  2286. S32 first_selected = getFirstSelectedIndex();
  2287. // If we have a selection (> -1) then point iterator at the selected
  2288. // item
  2289. if (first_selected > 0)
  2290. {
  2291. // Point iterator to first selected item
  2292. start_iter += first_selected;
  2293. }
  2294. // Start search at first item after current selection
  2295. item_list::iterator iter = start_iter;
  2296. ++iter;
  2297. if (iter == mItemList.end())
  2298. {
  2299. iter = mItemList.begin();
  2300. }
  2301. bool needs_commit = false;
  2302. // Loop around once, back to previous selection
  2303. while (iter != start_iter)
  2304. {
  2305. LLScrollListItem* item = *iter;
  2306. LLScrollListCell* cellp = NULL;
  2307. if (item)
  2308. {
  2309. cellp = item->getColumn(getSearchColumn());
  2310. }
  2311. if (cellp)
  2312. {
  2313. // Only select enabled items with matching first characters
  2314. LLWString item_label =
  2315. utf8str_to_wstring(cellp->getValue().asString());
  2316. if (item->getEnabled() &&
  2317. LLStringOps::toLower(item_label[0]) == uni_char)
  2318. {
  2319. selectItem(item);
  2320. mNeedsScroll = true;
  2321. cellp->highlightText(0, 1);
  2322. mSearchTimer.reset();
  2323. needs_commit = mCommitOnKeyboardMovement &&
  2324. !mCommitOnSelectionChange;
  2325. handled = true;
  2326. break;
  2327. }
  2328. }
  2329. ++iter;
  2330. if (iter == mItemList.end())
  2331. {
  2332. iter = mItemList.begin();
  2333. }
  2334. }
  2335. if (needs_commit)
  2336. {
  2337. onCommit();
  2338. }
  2339. }
  2340. return handled;
  2341. }
  2342. void LLScrollListCtrl::reportInvalidInput()
  2343. {
  2344. make_ui_sound("UISndBadKeystroke");
  2345. }
  2346. bool LLScrollListCtrl::isRepeatedChars(const LLWString& string) const
  2347. {
  2348. if (string.empty())
  2349. {
  2350. return false;
  2351. }
  2352. llwchar first_char = string[0];
  2353. for (U32 i = 0, count = string.size(); i < count; ++i)
  2354. {
  2355. if (string[i] != first_char)
  2356. {
  2357. return false;
  2358. }
  2359. }
  2360. return true;
  2361. }
  2362. void LLScrollListCtrl::selectItem(LLScrollListItem* itemp,
  2363. bool select_single_item)
  2364. {
  2365. if (!itemp) return;
  2366. if (!itemp->getSelected())
  2367. {
  2368. if (mLastSelected)
  2369. {
  2370. LLScrollListCell* cellp =
  2371. mLastSelected->getColumn(getSearchColumn());
  2372. if (cellp)
  2373. {
  2374. cellp->highlightText(0, 0);
  2375. }
  2376. }
  2377. if (select_single_item)
  2378. {
  2379. deselectAllItems(true);
  2380. }
  2381. itemp->setSelected(true);
  2382. mLastSelected = itemp;
  2383. mSelectionChanged = true;
  2384. }
  2385. }
  2386. void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp)
  2387. {
  2388. if (!itemp) return;
  2389. if (itemp->getSelected())
  2390. {
  2391. if (mLastSelected == itemp)
  2392. {
  2393. mLastSelected = NULL;
  2394. }
  2395. itemp->setSelected(false);
  2396. LLScrollListCell* cellp = itemp->getColumn(getSearchColumn());
  2397. if (cellp)
  2398. {
  2399. cellp->highlightText(0, 0);
  2400. }
  2401. mSelectionChanged = true;
  2402. }
  2403. }
  2404. void LLScrollListCtrl::commitIfChanged()
  2405. {
  2406. if (mSelectionChanged)
  2407. {
  2408. mDirty = true;
  2409. mSelectionChanged = false;
  2410. onCommit();
  2411. }
  2412. }
  2413. struct SameSortColumn
  2414. {
  2415. SameSortColumn(S32 column)
  2416. : mColumn(column)
  2417. {
  2418. }
  2419. LL_INLINE bool operator()(std::pair<S32, bool> sort_column)
  2420. {
  2421. return sort_column.first == mColumn;
  2422. }
  2423. S32 mColumn;
  2424. };
  2425. bool LLScrollListCtrl::setSort(S32 column_idx, bool ascending)
  2426. {
  2427. LLScrollListColumn* sort_column = getColumn(column_idx);
  2428. if (!sort_column) return false;
  2429. sort_column->mSortAscending = ascending;
  2430. sort_column_t new_sort_column(column_idx, ascending);
  2431. if (mSortColumns.empty())
  2432. {
  2433. mSortColumns.push_back(new_sort_column);
  2434. return true;
  2435. }
  2436. else
  2437. {
  2438. // Grab current sort column
  2439. sort_column_t cur_sort_column = mSortColumns.back();
  2440. // Remove any existing sort criterion referencing this column and add
  2441. // the new one
  2442. mSortColumns.erase(remove_if(mSortColumns.begin(),
  2443. mSortColumns.end(),
  2444. SameSortColumn(column_idx)),
  2445. mSortColumns.end());
  2446. mSortColumns.push_back(new_sort_column);
  2447. // Did the sort criteria change ?
  2448. return cur_sort_column != new_sort_column;
  2449. }
  2450. }
  2451. // Called by scrollbar
  2452. //static
  2453. void LLScrollListCtrl::onScrollChange(S32 new_pos, LLScrollbar* scrollbar,
  2454. void* userdata)
  2455. {
  2456. LLScrollListCtrl* self = (LLScrollListCtrl*) userdata;
  2457. self->mScrollLines = new_pos;
  2458. }
  2459. void LLScrollListCtrl::sortByColumn(const std::string& name, bool ascending)
  2460. {
  2461. column_map_t::iterator it = mColumns.find(name);
  2462. if (it != mColumns.end())
  2463. {
  2464. sortByColumnIndex(it->second.mIndex, ascending);
  2465. }
  2466. }
  2467. // First column is column 0
  2468. void LLScrollListCtrl::sortByColumnIndex(U32 column, bool ascending)
  2469. {
  2470. if (setSort(column, ascending))
  2471. {
  2472. sortItems();
  2473. }
  2474. }
  2475. void LLScrollListCtrl::sortItems()
  2476. {
  2477. // Do stable sort to preserve any previous sorts
  2478. std::stable_sort(mItemList.begin(), mItemList.end(),
  2479. SortScrollListItem(mSortColumns));
  2480. setSorted(true);
  2481. }
  2482. // For one-shot sorts; does not save sort column/order.
  2483. void LLScrollListCtrl::sortOnce(S32 column, bool ascending)
  2484. {
  2485. std::vector<std::pair<S32, bool> > sort_column;
  2486. sort_column.emplace_back(column, ascending);
  2487. // Do stable sort to preserve any previous sorts
  2488. std::stable_sort(mItemList.begin(), mItemList.end(),
  2489. SortScrollListItem(sort_column));
  2490. }
  2491. void LLScrollListCtrl::setAllowRefresh(bool allow)
  2492. {
  2493. mAllowRefresh = allow;
  2494. if (allow)
  2495. {
  2496. dirtyColumns();
  2497. }
  2498. }
  2499. void LLScrollListCtrl::dirtyColumns()
  2500. {
  2501. if (!mAllowRefresh) return; // lazy updates
  2502. mColumnsDirty = mColumnWidthsDirty = true;
  2503. // We need to keep mColumnsIndexed up to date just in case someone indexes
  2504. // into it immediately
  2505. mColumnsIndexed.resize(mColumns.size());
  2506. for (column_map_t::iterator it = mColumns.begin(), end = mColumns.end();
  2507. it != end; ++it)
  2508. {
  2509. LLScrollListColumn* column = &it->second;
  2510. mColumnsIndexed[it->second.mIndex] = column;
  2511. }
  2512. }
  2513. S32 LLScrollListCtrl::getScrollPos() const
  2514. {
  2515. return mScrollbar->getDocPos();
  2516. }
  2517. void LLScrollListCtrl::setScrollPos(S32 pos)
  2518. {
  2519. mScrollbar->setDocPos(pos);
  2520. onScrollChange(mScrollbar->getDocPos(), mScrollbar, this);
  2521. }
  2522. void LLScrollListCtrl::scrollToShowSelected()
  2523. {
  2524. // Do not scroll automatically when capturing mouse input as that will
  2525. // change what is currently under the mouse cursor
  2526. if (hasMouseCapture())
  2527. {
  2528. return;
  2529. }
  2530. // If user specifies sort, make sure it is maintained, else we end up
  2531. // showing the wrong item line... HB
  2532. if (!mSorted && !mSortColumns.empty())
  2533. {
  2534. sortItems();
  2535. }
  2536. S32 index = getFirstSelectedIndex();
  2537. if (index < 0)
  2538. {
  2539. return;
  2540. }
  2541. LLScrollListItem* item = mItemList[index];
  2542. if (!item) // Paranoia
  2543. {
  2544. return;
  2545. }
  2546. if (index < mScrollLines)
  2547. {
  2548. // Need to scroll to show item
  2549. setScrollPos(index);
  2550. }
  2551. else if (index >= mScrollLines + mPageLines)
  2552. {
  2553. setScrollPos(index - mPageLines + 1);
  2554. }
  2555. }
  2556. void LLScrollListCtrl::scrollToShowLast()
  2557. {
  2558. // Do not scroll automatically when capturing mouse input as that will
  2559. // change what is currently under the mouse cursor
  2560. if (hasMouseCapture())
  2561. {
  2562. return;
  2563. }
  2564. S32 index = mItemList.size() - 1;
  2565. if (index < 0)
  2566. {
  2567. return;
  2568. }
  2569. if (index < mScrollLines)
  2570. {
  2571. // Need to scroll to show item
  2572. setScrollPos(index);
  2573. }
  2574. else if (index >= mScrollLines + mPageLines)
  2575. {
  2576. setScrollPos(index - mPageLines + 1);
  2577. }
  2578. }
  2579. void LLScrollListCtrl::updateStaticColumnWidth(LLScrollListColumn* col,
  2580. S32 new_width)
  2581. {
  2582. mTotalStaticColumnWidth += llmax(0, new_width) - llmax(0, col->getWidth());
  2583. }
  2584. //virtual
  2585. const std::string& LLScrollListCtrl::getTag() const
  2586. {
  2587. return LL_SCROLL_LIST_CTRL_TAG;
  2588. }
  2589. //virtual
  2590. LLXMLNodePtr LLScrollListCtrl::getXML(bool save_children) const
  2591. {
  2592. LLXMLNodePtr node = LLUICtrl::getXML();
  2593. node->setName(LL_SCROLL_LIST_CTRL_TAG);
  2594. // Attributes
  2595. node->createChild("multi_select",
  2596. true)->setBoolValue(mAllowMultipleSelection);
  2597. node->createChild("draw_border",
  2598. true)->setBoolValue(mBorder != NULL);
  2599. node->createChild("draw_heading",
  2600. true)->setBoolValue(mDisplayColumnHeaders);
  2601. node->createChild("background_visible",
  2602. true)->setBoolValue(mBackgroundVisible);
  2603. node->createChild("draw_stripes", true)->setBoolValue(mDrawStripes);
  2604. node->createChild("column_padding", true)->setIntValue(mColumnPadding);
  2605. addColorXML(node, mBgWriteableColor, "bg_writeable_color",
  2606. "ScrollBgWriteableColor");
  2607. addColorXML(node, mBgReadOnlyColor, "bg_read_only_color",
  2608. "ScrollBgReadOnlyColor");
  2609. addColorXML(node, mBgSelectedColor, "bg_selected_color",
  2610. "ScrollSelectedBGColor");
  2611. addColorXML(node, mBgStripeColor, "bg_stripe_color",
  2612. "ScrollBGStripeColor");
  2613. addColorXML(node, mFgSelectedColor, "fg_selected_color",
  2614. "ScrollSelectedFGColor");
  2615. addColorXML(node, mFgUnselectedColor, "fg_unselected_color",
  2616. "ScrollUnselectedColor");
  2617. addColorXML(node, mFgDisabledColor, "fg_disable_color",
  2618. "ScrollDisabledColor");
  2619. addColorXML(node, mHighlightedColor, "highlighted_color",
  2620. "ScrollHighlightedColor");
  2621. // Contents
  2622. std::vector<const LLScrollListColumn*> sorted_list;
  2623. sorted_list.resize(mColumns.size());
  2624. for (column_map_t::const_iterator it = mColumns.begin(),
  2625. end = mColumns.end();
  2626. it != end; ++it)
  2627. {
  2628. sorted_list[it->second.mIndex] = &it->second;
  2629. }
  2630. for (size_t i = 0, count = sorted_list.size(); i < count; ++i)
  2631. {
  2632. LLXMLNodePtr child_node = node->createChild("column", false);
  2633. const LLScrollListColumn* column = sorted_list[i];
  2634. child_node->createChild("name", true)->setStringValue(column->mName);
  2635. child_node->createChild("label", true)->setStringValue(column->mLabel);
  2636. child_node->createChild("width",
  2637. true)->setIntValue(column->getWidth());
  2638. }
  2639. return node;
  2640. }
  2641. void LLScrollListCtrl::setScrollListParameters(LLXMLNodePtr node)
  2642. {
  2643. // James: this is not a good way to do colors. We need a central "UI style"
  2644. // manager that sets the colors for ALL scroll lists, buttons, etc.
  2645. LLColor4 color;
  2646. if (node->hasAttribute("fg_unselected_color"))
  2647. {
  2648. LLUICtrlFactory::getAttributeColor(node,"fg_unselected_color", color);
  2649. setFgUnselectedColor(color);
  2650. }
  2651. if (node->hasAttribute("fg_selected_color"))
  2652. {
  2653. LLUICtrlFactory::getAttributeColor(node,"fg_selected_color", color);
  2654. setFgSelectedColor(color);
  2655. }
  2656. if (node->hasAttribute("bg_selected_color"))
  2657. {
  2658. LLUICtrlFactory::getAttributeColor(node,"bg_selected_color", color);
  2659. setBgSelectedColor(color);
  2660. }
  2661. if (node->hasAttribute("fg_disable_color"))
  2662. {
  2663. LLUICtrlFactory::getAttributeColor(node,"fg_disable_color", color);
  2664. setFgDisableColor(color);
  2665. }
  2666. if (node->hasAttribute("bg_writeable_color"))
  2667. {
  2668. LLUICtrlFactory::getAttributeColor(node,"bg_writeable_color", color);
  2669. setBgWriteableColor(color);
  2670. }
  2671. if (node->hasAttribute("bg_read_only_color"))
  2672. {
  2673. LLUICtrlFactory::getAttributeColor(node,"bg_read_only_color", color);
  2674. setReadOnlyBgColor(color);
  2675. }
  2676. if (LLUICtrlFactory::getAttributeColor(node,"bg_stripe_color", color))
  2677. {
  2678. setBgStripeColor(color);
  2679. }
  2680. if (LLUICtrlFactory::getAttributeColor(node,"highlighted_color", color))
  2681. {
  2682. setHighlightedColor(color);
  2683. }
  2684. if (node->hasAttribute("background_visible"))
  2685. {
  2686. bool background_visible = false;
  2687. node->getAttributeBool("background_visible", background_visible);
  2688. setBackgroundVisible(background_visible);
  2689. }
  2690. if (node->hasAttribute("draw_stripes"))
  2691. {
  2692. bool draw_stripes = false;
  2693. node->getAttributeBool("draw_stripes", draw_stripes);
  2694. setDrawStripes(draw_stripes);
  2695. }
  2696. if (node->hasAttribute("column_padding"))
  2697. {
  2698. S32 column_padding;
  2699. node->getAttributeS32("column_padding", column_padding);
  2700. setColumnPadding(column_padding);
  2701. }
  2702. }
  2703. //static
  2704. LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView* parent,
  2705. LLUICtrlFactory* factory)
  2706. {
  2707. std::string name = LL_SCROLL_LIST_CTRL_TAG;
  2708. node->getAttributeString("name", name);
  2709. LLRect rect;
  2710. createRect(node, rect, parent, LLRect());
  2711. bool multi_select = false;
  2712. node->getAttributeBool("multi_select", multi_select);
  2713. bool draw_border = true;
  2714. node->getAttributeBool("draw_border", draw_border);
  2715. bool draw_heading = false;
  2716. node->getAttributeBool("draw_heading", draw_heading);
  2717. S32 search_column = 0;
  2718. node->getAttributeS32("search_column", search_column);
  2719. S32 sort_column = -1;
  2720. node->getAttributeS32("sort_column", sort_column);
  2721. bool sort_ascending = true;
  2722. node->getAttributeBool("sort_ascending", sort_ascending);
  2723. LLUICtrlCallback callback = NULL;
  2724. LLScrollListCtrl* scroll_list = new LLScrollListCtrl(name, rect, callback,
  2725. NULL, multi_select,
  2726. draw_border);
  2727. scroll_list->setDisplayHeading(draw_heading);
  2728. if (node->hasAttribute("heading_height"))
  2729. {
  2730. S32 heading_height;
  2731. node->getAttributeS32("heading_height", heading_height);
  2732. scroll_list->setHeadingHeight(heading_height);
  2733. }
  2734. scroll_list->setScrollListParameters(node);
  2735. scroll_list->initFromXML(node, parent);
  2736. scroll_list->setSearchColumn(search_column);
  2737. LLSD columns;
  2738. S32 index = 0;
  2739. LLXMLNodePtr child;
  2740. std::string labelname, columnname, sortname, imagename, tooltip;
  2741. for (child = node->getFirstChild(); child.notNull();
  2742. child = child->getNextSibling())
  2743. {
  2744. if (child->hasName("column"))
  2745. {
  2746. labelname.clear();
  2747. child->getAttributeString("label", labelname);
  2748. columnname.clear();
  2749. child->getAttributeString("name", columnname);
  2750. if (columnname.empty())
  2751. {
  2752. columnname = labelname;
  2753. }
  2754. else if (labelname.empty())
  2755. {
  2756. labelname = columnname;
  2757. }
  2758. sortname = columnname;
  2759. child->getAttributeString("sort", sortname);
  2760. bool sort_ascending = true;
  2761. child->getAttributeBool("sort_ascending", sort_ascending);
  2762. imagename.clear();
  2763. child->getAttributeString("image", imagename);
  2764. bool columndynamicwidth = false;
  2765. child->getAttributeBool("dynamicwidth", columndynamicwidth);
  2766. S32 columnwidth = -1;
  2767. child->getAttributeS32("width", columnwidth);
  2768. tooltip.clear();
  2769. child->getAttributeString("tool_tip", tooltip);
  2770. F32 columnrelwidth = 0.f;
  2771. child->getAttributeF32("relwidth", columnrelwidth);
  2772. LLFontGL::HAlign h_align = LLFontGL::LEFT;
  2773. h_align = LLView::selectFontHAlign(child);
  2774. columns[index]["name"] = columnname;
  2775. columns[index]["sort"] = sortname;
  2776. columns[index]["sort_ascending"] = sort_ascending;
  2777. columns[index]["image"] = imagename;
  2778. columns[index]["label"] = labelname;
  2779. columns[index]["width"] = columnwidth;
  2780. columns[index]["relwidth"] = columnrelwidth;
  2781. columns[index]["dynamicwidth"] = columndynamicwidth;
  2782. columns[index]["halign"] = (S32)h_align;
  2783. columns[index++]["tool_tip"] = tooltip;
  2784. }
  2785. }
  2786. scroll_list->setColumnHeadings(columns);
  2787. if (sort_column >= 0)
  2788. {
  2789. scroll_list->sortByColumnIndex(sort_column, sort_ascending);
  2790. }
  2791. LLUUID id;
  2792. std::string value, font, font_style;
  2793. for (child = node->getFirstChild(); child.notNull();
  2794. child = child->getNextSibling())
  2795. {
  2796. if (child->hasName("row"))
  2797. {
  2798. child->getAttributeUUID("id", id);
  2799. LLSD row;
  2800. row["id"] = id;
  2801. S32 column_idx = 0;
  2802. for (LLXMLNodePtr row_child = child->getFirstChild();
  2803. row_child.notNull(); row_child = row_child->getNextSibling())
  2804. {
  2805. if (row_child->hasName("column"))
  2806. {
  2807. value = row_child->getTextContents();
  2808. columnname.clear();
  2809. row_child->getAttributeString("name", columnname);
  2810. font.clear();
  2811. row_child->getAttributeString("font", font);
  2812. font_style.clear();
  2813. row_child->getAttributeString("font-style", font_style);
  2814. row["columns"][column_idx]["column"] = columnname;
  2815. row["columns"][column_idx]["value"] = value;
  2816. row["columns"][column_idx]["font"] = font;
  2817. row["columns"][column_idx++]["font-style"] = font_style;
  2818. }
  2819. }
  2820. scroll_list->addElement(row);
  2821. }
  2822. }
  2823. std::string contents = node->getTextContents();
  2824. if (!contents.empty())
  2825. {
  2826. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  2827. boost::char_separator<char> sep("\t\n");
  2828. tokenizer tokens(contents, sep);
  2829. tokenizer::iterator token_iter = tokens.begin();
  2830. while (token_iter != tokens.end())
  2831. {
  2832. const std::string& line = *token_iter;
  2833. scroll_list->addSimpleElement(line);
  2834. ++token_iter;
  2835. }
  2836. }
  2837. return scroll_list;
  2838. }
  2839. // LLEditMenuHandler functions
  2840. //virtual
  2841. void LLScrollListCtrl::copy()
  2842. {
  2843. std::string buffer;
  2844. std::vector<LLScrollListItem*> items = getAllSelected();
  2845. for (size_t i = 0, count = items.size(); i < count; ++i)
  2846. {
  2847. LLScrollListItem* itemp = items[i];
  2848. if (itemp)
  2849. {
  2850. buffer += itemp->getContentsCSV() + "\n";
  2851. }
  2852. }
  2853. gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0,
  2854. buffer.length());
  2855. }
  2856. //virtual
  2857. bool LLScrollListCtrl::canCopy() const
  2858. {
  2859. return getFirstSelected() != NULL;
  2860. }
  2861. //virtual
  2862. void LLScrollListCtrl::cut()
  2863. {
  2864. copy();
  2865. doDelete();
  2866. }
  2867. //virtual
  2868. bool LLScrollListCtrl::canCut() const
  2869. {
  2870. return canCopy() && canDoDelete();
  2871. }
  2872. //virtual
  2873. void LLScrollListCtrl::selectAll()
  2874. {
  2875. // Deselects all other items
  2876. for (item_list::iterator iter = mItemList.begin(), end = mItemList.end();
  2877. iter != end; ++iter)
  2878. {
  2879. LLScrollListItem* itemp = *iter;
  2880. if (itemp && itemp->getEnabled())
  2881. {
  2882. selectItem(itemp, false);
  2883. }
  2884. }
  2885. if (mCommitOnSelectionChange)
  2886. {
  2887. commitIfChanged();
  2888. }
  2889. }
  2890. //virtual
  2891. bool LLScrollListCtrl::canSelectAll() const
  2892. {
  2893. return getCanSelect() && mAllowMultipleSelection &&
  2894. !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable);
  2895. }
  2896. //virtual
  2897. void LLScrollListCtrl::deselect()
  2898. {
  2899. deselectAllItems();
  2900. }
  2901. void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos)
  2902. {
  2903. std::string name = column["name"].asString();
  2904. // if no column name provided, just use ordinal as name
  2905. if (name.empty())
  2906. {
  2907. std::ostringstream new_name;
  2908. new_name << mColumnsIndexed.size();
  2909. name = new_name.str();
  2910. }
  2911. if (mColumns.find(name) == mColumns.end())
  2912. {
  2913. // Add column
  2914. mColumns[name] = LLScrollListColumn(column, this);
  2915. LLScrollListColumn* new_column = &mColumns[name];
  2916. new_column->mParentCtrl = this;
  2917. new_column->mIndex = mColumns.size() - 1;
  2918. // Add button
  2919. if (new_column->getWidth() > 0 || new_column->mRelWidth > 0 ||
  2920. new_column->mDynamicWidth)
  2921. {
  2922. if (getNumColumns() > 0)
  2923. {
  2924. mTotalColumnPadding += mColumnPadding;
  2925. }
  2926. if (new_column->mRelWidth >= 0)
  2927. {
  2928. new_column->setWidth(ll_roundp(new_column->mRelWidth *
  2929. (mItemListRect.getWidth() -
  2930. mTotalStaticColumnWidth -
  2931. mTotalColumnPadding)));
  2932. }
  2933. else if (new_column->mDynamicWidth)
  2934. {
  2935. ++mNumDynamicWidthColumns;
  2936. new_column->setWidth((mItemListRect.getWidth() -
  2937. mTotalStaticColumnWidth -
  2938. mTotalColumnPadding) /
  2939. mNumDynamicWidthColumns);
  2940. }
  2941. S32 top = mItemListRect.mTop;
  2942. S32 left = mItemListRect.mLeft;
  2943. for (column_map_t::iterator it = mColumns.begin(),
  2944. end = mColumns.end();
  2945. it != end; ++it)
  2946. {
  2947. if (it->second.mIndex < new_column->mIndex &&
  2948. it->second.getWidth() > 0)
  2949. {
  2950. left += it->second.getWidth() + mColumnPadding;
  2951. }
  2952. }
  2953. std::string button_name = "btn_" + name;
  2954. S32 right = left + new_column->getWidth();
  2955. if (new_column->mIndex != (S32)mColumns.size() - 1)
  2956. {
  2957. right += mColumnPadding;
  2958. }
  2959. LLRect temp_rect = LLRect(left, top + mHeadingHeight, right, top);
  2960. new_column->mHeader = new LLColumnHeader(button_name, temp_rect,
  2961. new_column);
  2962. if (column["image"].asString() != "")
  2963. {
  2964. #if 0
  2965. new_column->mHeader->setScaleImage(false);
  2966. #endif
  2967. new_column->mHeader->setImage(column["image"].asString());
  2968. }
  2969. else
  2970. {
  2971. new_column->mHeader->setLabel(new_column->mLabel);
  2972. }
  2973. new_column->mHeader->setToolTip(column["tool_tip"].asString());
  2974. // RN: although it might be useful to change sort order with the
  2975. // keyboard, mixing tab stops on child items along with the parent
  2976. // item is not supported yet
  2977. new_column->mHeader->setTabStop(false);
  2978. addChild(new_column->mHeader);
  2979. new_column->mHeader->setVisible(mDisplayColumnHeaders);
  2980. sendChildToFront(mScrollbar);
  2981. }
  2982. }
  2983. dirtyColumns();
  2984. }
  2985. //static
  2986. void LLScrollListCtrl::onClickColumn(void* userdata)
  2987. {
  2988. LLScrollListColumn* info = (LLScrollListColumn*)userdata;
  2989. if (!info) return;
  2990. LLScrollListCtrl* parent = info->mParentCtrl;
  2991. if (!parent) return;
  2992. S32 column_index = info->mIndex;
  2993. LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex];
  2994. bool ascending = column->mSortAscending;
  2995. if (column->mSortingColumn != column->mName &&
  2996. parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end())
  2997. {
  2998. LLScrollListColumn& info_redir =
  2999. parent->mColumns[column->mSortingColumn];
  3000. column_index = info_redir.mIndex;
  3001. }
  3002. // If this column is the primary sort key, reverse the direction
  3003. if (!parent->mSortColumns.empty() &&
  3004. parent->mSortColumns.back().first == column_index)
  3005. {
  3006. ascending = !parent->mSortColumns.back().second;
  3007. }
  3008. parent->sortByColumnIndex(column_index, ascending);
  3009. if (parent->mOnSortChangedCallback)
  3010. {
  3011. parent->mOnSortChangedCallback(parent->getCallbackUserData());
  3012. }
  3013. }
  3014. std::string LLScrollListCtrl::getSortColumnName()
  3015. {
  3016. LLScrollListColumn* column = NULL;
  3017. if (!mSortColumns.empty())
  3018. {
  3019. column = mColumnsIndexed[mSortColumns.back().first];
  3020. }
  3021. return column ? column->mName : "";
  3022. }
  3023. void LLScrollListCtrl::clearColumns()
  3024. {
  3025. for (column_map_t::iterator it = mColumns.begin(), end = mColumns.end();
  3026. it != end; ++it)
  3027. {
  3028. LLColumnHeader* header = it->second.mHeader;
  3029. if (header)
  3030. {
  3031. removeChild(header);
  3032. delete header;
  3033. }
  3034. }
  3035. mColumns.clear();
  3036. mSortColumns.clear();
  3037. mTotalStaticColumnWidth = 0;
  3038. mTotalColumnPadding = 0;
  3039. dirtyColumns(); // Clears mColumnsIndexed
  3040. }
  3041. void LLScrollListCtrl::setColumnLabel(const std::string& column,
  3042. const std::string& label)
  3043. {
  3044. column_map_t::iterator it = mColumns.find(column);
  3045. if (it != mColumns.end())
  3046. {
  3047. it->second.mLabel = label;
  3048. if (it->second.mHeader)
  3049. {
  3050. it->second.mHeader->setLabel(label);
  3051. }
  3052. }
  3053. }
  3054. LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index)
  3055. {
  3056. if (index < 0 || index >= (S32)mColumnsIndexed.size())
  3057. {
  3058. return NULL;
  3059. }
  3060. return mColumnsIndexed[index];
  3061. }
  3062. void LLScrollListCtrl::setColumnHeadings(LLSD headings)
  3063. {
  3064. mColumns.clear();
  3065. for (LLSD::array_const_iterator itor = headings.beginArray(),
  3066. end = headings.endArray();
  3067. itor != end; ++itor)
  3068. {
  3069. addColumn(*itor);
  3070. }
  3071. }
  3072. LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value,
  3073. EAddPosition pos,
  3074. void* userdata)
  3075. {
  3076. LLSD id = value["id"];
  3077. LLScrollListItem* new_item = new LLScrollListItem(id, userdata);
  3078. if (value.has("enabled"))
  3079. {
  3080. new_item->setEnabled(value["enabled"].asBoolean());
  3081. }
  3082. new_item->setNumColumns(mColumns.size());
  3083. static const LLFontGL* default_font = LLFontGL::getFontSansSerifSmall();
  3084. // Add any columns we do not already have
  3085. LLSD columns = value["columns"];
  3086. S32 col_index = 0;
  3087. for (LLSD::array_const_iterator it = columns.beginArray(),
  3088. end = columns.endArray();
  3089. it != end; ++it)
  3090. {
  3091. if (it->isUndefined())
  3092. {
  3093. // Skip unused columns in item passed in
  3094. continue;
  3095. }
  3096. std::string column = (*it)["column"].asString();
  3097. LLScrollListColumn* columnp = NULL;
  3098. // Empty columns strings index by ordinal
  3099. if (column.empty())
  3100. {
  3101. std::ostringstream new_name;
  3102. new_name << col_index;
  3103. column = new_name.str();
  3104. }
  3105. column_map_t::iterator column_it = mColumns.find(column);
  3106. if (column_it != mColumns.end())
  3107. {
  3108. columnp = &column_it->second;
  3109. }
  3110. // Create new column on demand
  3111. if (!columnp)
  3112. {
  3113. LLSD new_column;
  3114. new_column["name"] = column;
  3115. new_column["label"] = column;
  3116. // If width supplied for column, use it, otherwise use adaptive
  3117. // width
  3118. if (it->has("width"))
  3119. {
  3120. new_column["width"] = (*it)["width"];
  3121. }
  3122. else
  3123. {
  3124. new_column["dynamicwidth"] = true;
  3125. }
  3126. addColumn(new_column);
  3127. columnp = &mColumns[column];
  3128. new_item->setNumColumns(mColumns.size());
  3129. }
  3130. S32 index = columnp->mIndex;
  3131. S32 width = columnp->getWidth();
  3132. LLFontGL::HAlign font_alignment = columnp->mFontAlignment;
  3133. LLColor4 fcolor = LLColor4::black;
  3134. LLSD value = (*it)["value"];
  3135. std::string fontname = (*it)["font"].asString();
  3136. std::string fontstyle = (*it)["font-style"].asString();
  3137. std::string type = (*it)["type"].asString();
  3138. std::string format = (*it)["format"].asString();
  3139. if (it->has("font-color"))
  3140. {
  3141. LLSD sd_color = (*it)["font-color"];
  3142. fcolor.setValue(sd_color);
  3143. }
  3144. bool has_color = it->has("color");
  3145. LLColor4 color = LLColor4((*it)["color"]);
  3146. bool enabled = !it->has("enabled") || (*it)["enabled"].asBoolean();
  3147. const LLFontGL* font = LLFontGL::getFont(fontname);
  3148. if (!font)
  3149. {
  3150. font = default_font;
  3151. }
  3152. U8 font_style = LLFontGL::getStyleFromString(fontstyle);
  3153. if (type == "icon")
  3154. {
  3155. LLScrollListIcon* cell = new LLScrollListIcon(value, width);
  3156. if (has_color)
  3157. {
  3158. cell->setColor(color);
  3159. }
  3160. new_item->setColumn(index, cell);
  3161. }
  3162. else if (type == "checkbox")
  3163. {
  3164. LLCheckBoxCtrl* ctrl =
  3165. new LLCheckBoxCtrl("check", LLRect(0, width, width, 0), " ");
  3166. ctrl->setEnabled(enabled);
  3167. ctrl->setValue(value);
  3168. LLScrollListCheck* cell = new LLScrollListCheck(ctrl, width);
  3169. if (has_color)
  3170. {
  3171. cell->setColor(color);
  3172. }
  3173. new_item->setColumn(index, cell);
  3174. }
  3175. else if (type == "separator")
  3176. {
  3177. LLScrollListSeparator* cell = new LLScrollListSeparator(width);
  3178. if (has_color)
  3179. {
  3180. cell->setColor(color);
  3181. }
  3182. new_item->setColumn(index, cell);
  3183. }
  3184. else if (type == "date")
  3185. {
  3186. LLScrollListDate* cell = new LLScrollListDate(value.asDate(),
  3187. format, font,
  3188. width, font_style,
  3189. font_alignment);
  3190. if (has_color)
  3191. {
  3192. cell->setColor(color);
  3193. }
  3194. new_item->setColumn(index, cell);
  3195. if (columnp->mHeader && !value.asString().empty())
  3196. {
  3197. columnp->mHeader->setHasResizableElement(true);
  3198. }
  3199. }
  3200. else
  3201. {
  3202. LLScrollListText* cell = new LLScrollListText(value.asString(),
  3203. font, width,
  3204. font_style,
  3205. font_alignment,
  3206. fcolor, true);
  3207. if (has_color)
  3208. {
  3209. cell->setColor(color);
  3210. }
  3211. new_item->setColumn(index, cell);
  3212. if (columnp->mHeader && !value.asString().empty())
  3213. {
  3214. columnp->mHeader->setHasResizableElement(true);
  3215. }
  3216. }
  3217. ++col_index;
  3218. }
  3219. // Add dummy cells for missing columns
  3220. for (column_map_t::iterator column_it = mColumns.begin();
  3221. column_it != mColumns.end(); ++column_it)
  3222. {
  3223. S32 column_idx = column_it->second.mIndex;
  3224. if (!new_item->getColumn(column_idx))
  3225. {
  3226. LLScrollListColumn* column_ptr = &column_it->second;
  3227. new_item->setColumn(column_idx,
  3228. new LLScrollListText(LLStringUtil::null,
  3229. default_font,
  3230. column_ptr->getWidth(),
  3231. LLFontGL::NORMAL));
  3232. }
  3233. }
  3234. addItem(new_item, pos);
  3235. return new_item;
  3236. }
  3237. LLScrollListItem* LLScrollListCtrl::addSimpleElement(const std::string& value,
  3238. EAddPosition pos,
  3239. const LLSD& id)
  3240. {
  3241. LLSD entry_id = id;
  3242. if (id.isUndefined())
  3243. {
  3244. entry_id = value;
  3245. }
  3246. LLScrollListItem* new_item = new LLScrollListItem(entry_id);
  3247. static const LLFontGL* font = LLFontGL::getFontSansSerifSmall();
  3248. new_item->addColumn(value, font, getRect().getWidth());
  3249. addItem(new_item, pos);
  3250. return new_item;
  3251. }
  3252. void LLScrollListCtrl::setValue(const LLSD& value)
  3253. {
  3254. for (LLSD::array_const_iterator it = value.beginArray(),
  3255. end = value.endArray();
  3256. it != end; ++it)
  3257. {
  3258. addElement(*it);
  3259. }
  3260. }
  3261. LLSD LLScrollListCtrl::getValue() const
  3262. {
  3263. LLScrollListItem* item = getFirstSelected();
  3264. return item ? item->getValue() : LLSD();
  3265. }
  3266. bool LLScrollListCtrl::operateOnSelection(EOperation op)
  3267. {
  3268. if (op == OP_DELETE)
  3269. {
  3270. deleteSelectedItems();
  3271. return true;
  3272. }
  3273. else if (op == OP_DESELECT)
  3274. {
  3275. deselectAllItems();
  3276. }
  3277. return false;
  3278. }
  3279. bool LLScrollListCtrl::operateOnAll(EOperation op)
  3280. {
  3281. if (op == OP_DELETE)
  3282. {
  3283. clearRows();
  3284. return true;
  3285. }
  3286. else if (op == OP_DESELECT)
  3287. {
  3288. deselectAllItems();
  3289. }
  3290. else if (op == OP_SELECT)
  3291. {
  3292. selectAll();
  3293. }
  3294. return false;
  3295. }
  3296. //virtual
  3297. void LLScrollListCtrl::setFocus(bool b)
  3298. {
  3299. mSearchString.clear();
  3300. // For tabbing into pristine scroll lists (Finder)
  3301. if (!getFirstSelected())
  3302. {
  3303. selectFirstItem();
  3304. }
  3305. if (b)
  3306. {
  3307. grabMenuHandler();
  3308. }
  3309. else
  3310. {
  3311. releaseMenuHandler();
  3312. }
  3313. LLUICtrl::setFocus(b);
  3314. }
  3315. //virtual
  3316. bool LLScrollListCtrl::isDirty() const
  3317. {
  3318. return mAllowMultipleSelection ? mDirty
  3319. : mOriginalSelection != getFirstSelectedIndex();
  3320. }
  3321. // Clear dirty state
  3322. void LLScrollListCtrl::resetDirty()
  3323. {
  3324. mDirty = false;
  3325. mOriginalSelection = getFirstSelectedIndex();
  3326. }
  3327. //virtual
  3328. void LLScrollListCtrl::onFocusReceived()
  3329. {
  3330. // Forget latent selection changes when getting focus
  3331. mSelectionChanged = false;
  3332. LLUICtrl::onFocusReceived();
  3333. }
  3334. //virtual
  3335. void LLScrollListCtrl::onFocusLost()
  3336. {
  3337. if (hasMouseCapture())
  3338. {
  3339. gFocusMgr.setMouseCapture(NULL);
  3340. }
  3341. LLUICtrl::onFocusLost();
  3342. }
  3343. LLColumnHeader::LLColumnHeader(const std::string& label,
  3344. const LLRect& rect,
  3345. LLScrollListColumn* column,
  3346. const LLFontGL* fontp)
  3347. : LLComboBox(label, rect, label, NULL, NULL),
  3348. mColumn(column),
  3349. mOrigLabel(label),
  3350. mShowSortOptions(false),
  3351. mHasResizableElement(false)
  3352. {
  3353. mListPosition = LLComboBox::ABOVE;
  3354. setCommitCallback(onSelectSort);
  3355. setCallbackUserData(this);
  3356. mButton->setTabStop(false);
  3357. // Require at least two frames between mouse down and mouse up event to
  3358. // capture intentional "hold" not just bad framerate
  3359. mButton->setHeldDownDelay(LLUI::sColumnHeaderDropDownDelay, 2);
  3360. mButton->setHeldDownCallback(onHeldDown);
  3361. mButton->setClickedCallback(onClick);
  3362. mButton->setMouseDownCallback(onMouseDown);
  3363. mButton->setCallbackUserData(this);
  3364. mButton->setToolTip(label);
  3365. // *TODO: Translate
  3366. mAscendingText = "[LOW]...[HIGH](Ascending)";
  3367. mDescendingText = "[HIGH]...[LOW](Descending)";
  3368. mList->reshape(llmax(mList->getRect().getWidth(), 110,
  3369. getRect().getWidth()),
  3370. mList->getRect().getHeight());
  3371. // Resize handles on left and right
  3372. constexpr S32 RESIZE_BAR_THICKNESS = 3;
  3373. mResizeBar = new LLResizeBar("resizebar", this,
  3374. LLRect(getRect().getWidth() -
  3375. RESIZE_BAR_THICKNESS,
  3376. getRect().getHeight(),
  3377. getRect().getWidth(), 0),
  3378. MIN_COLUMN_WIDTH, S32_MAX,
  3379. LLResizeBar::RIGHT);
  3380. addChild(mResizeBar);
  3381. mResizeBar->setEnabled(false);
  3382. }
  3383. void LLColumnHeader::draw()
  3384. {
  3385. static const LLUIImagePtr up_arrow_image =
  3386. LLUI::getUIImage("up_arrow.tga");
  3387. static const LLUIImagePtr down_arrow_image =
  3388. LLUI::getUIImage("down_arrow.tga");
  3389. bool draw_arrow = !mColumn->mLabel.empty() &&
  3390. mColumn->mParentCtrl->isSorted() &&
  3391. mColumn->mParentCtrl->getSortColumnName() ==
  3392. mColumn->mSortingColumn;
  3393. bool is_ascending = mColumn->mParentCtrl->getSortAscending();
  3394. mButton->setImageOverlay(is_ascending ? up_arrow_image : down_arrow_image,
  3395. LLFontGL::RIGHT, draw_arrow ? LLColor4::white
  3396. : LLColor4::transparent);
  3397. mArrowImage = mButton->getImageOverlay();
  3398. #if 0
  3399. bool clip = getRect().mRight > mColumn->mParentCtrl->getItemListRect().getWidth();
  3400. LLGLEnable scissor_test(clip ? GL_SCISSOR_TEST : GL_FALSE);
  3401. LLRect column_header_local_rect(-getRect().mLeft, getRect().getHeight(),
  3402. mColumn->mParentCtrl->getItemListRect().getWidth() -
  3403. getRect().mLeft, 0);
  3404. LLUI::setScissorRegionLocal(column_header_local_rect);
  3405. #endif
  3406. // Draw children
  3407. LLComboBox::draw();
  3408. if (mList->getVisible())
  3409. {
  3410. // Sync sort order with list selection every frame
  3411. mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn,
  3412. getCurrentIndex() == 0);
  3413. }
  3414. }
  3415. bool LLColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask)
  3416. {
  3417. if (canResize() && mResizeBar->getRect().pointInRect(x, y))
  3418. {
  3419. // Reshape column to max content width
  3420. mColumn->mParentCtrl->calcMaxContentWidth();
  3421. LLRect column_rect = getRect();
  3422. column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth;
  3423. userSetShape(column_rect);
  3424. }
  3425. else
  3426. {
  3427. onClick(this);
  3428. }
  3429. return true;
  3430. }
  3431. void LLColumnHeader::setImage(const std::string& image_name)
  3432. {
  3433. if (mButton)
  3434. {
  3435. mButton->setImageSelected(image_name);
  3436. mButton->setImageUnselected(image_name);
  3437. }
  3438. }
  3439. //static
  3440. void LLColumnHeader::onClick(void* user_data)
  3441. {
  3442. LLColumnHeader* headerp = (LLColumnHeader*)user_data;
  3443. if (!headerp) return;
  3444. LLScrollListColumn* column = headerp->mColumn;
  3445. if (!column) return;
  3446. if (headerp->mList->getVisible())
  3447. {
  3448. headerp->hideList();
  3449. }
  3450. LLScrollListCtrl::onClickColumn(column);
  3451. // Propagate new sort order to sort order list
  3452. headerp->mList->selectNthItem(column->mParentCtrl->getSortAscending() ? 0
  3453. : 1);
  3454. }
  3455. //static
  3456. void LLColumnHeader::onMouseDown(void* user_data)
  3457. {
  3458. // For now, do nothing but block the normal showList() behavior
  3459. }
  3460. //static
  3461. void LLColumnHeader::onHeldDown(void* user_data)
  3462. {
  3463. LLColumnHeader* headerp = (LLColumnHeader*)user_data;
  3464. if (headerp)
  3465. {
  3466. headerp->showList();
  3467. }
  3468. }
  3469. void LLColumnHeader::showList()
  3470. {
  3471. if (mShowSortOptions)
  3472. {
  3473. mOrigLabel = mButton->getLabelSelected();
  3474. // Move sort column over to this column and do initial sort
  3475. mColumn->mParentCtrl->sortByColumn(mColumn->mSortingColumn,
  3476. mColumn->mParentCtrl->getSortAscending());
  3477. std::string low_item_text;
  3478. std::string high_item_text;
  3479. LLScrollListItem* itemp = mColumn->mParentCtrl->getFirstData();
  3480. if (itemp)
  3481. {
  3482. LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex);
  3483. if (cell && cell->isText())
  3484. {
  3485. if (mColumn->mParentCtrl->getSortAscending())
  3486. {
  3487. low_item_text = cell->getValue().asString();
  3488. }
  3489. else
  3490. {
  3491. high_item_text = cell->getValue().asString();
  3492. }
  3493. }
  3494. }
  3495. itemp = mColumn->mParentCtrl->getLastData();
  3496. if (itemp)
  3497. {
  3498. LLScrollListCell* cell = itemp->getColumn(mColumn->mIndex);
  3499. if (cell && cell->isText())
  3500. {
  3501. if (mColumn->mParentCtrl->getSortAscending())
  3502. {
  3503. high_item_text = cell->getValue().asString();
  3504. }
  3505. else
  3506. {
  3507. low_item_text = cell->getValue().asString();
  3508. }
  3509. }
  3510. }
  3511. LLStringUtil::truncate(low_item_text, 3);
  3512. LLStringUtil::truncate(high_item_text, 3);
  3513. std::string ascending_string;
  3514. std::string descending_string;
  3515. if (low_item_text.empty() || high_item_text.empty())
  3516. {
  3517. ascending_string = "Ascending";
  3518. descending_string = "Descending";
  3519. }
  3520. else
  3521. {
  3522. mAscendingText.setArg("[LOW]", low_item_text);
  3523. mAscendingText.setArg("[HIGH]", high_item_text);
  3524. mDescendingText.setArg("[LOW]", low_item_text);
  3525. mDescendingText.setArg("[HIGH]", high_item_text);
  3526. ascending_string = mAscendingText.getString();
  3527. descending_string = mDescendingText.getString();
  3528. }
  3529. static const LLFontGL* font = LLFontGL::getFontSansSerifSmall();
  3530. S32 text_width = font->getWidth(ascending_string);
  3531. text_width = llmax(text_width,
  3532. font->getWidth(descending_string)) + 10;
  3533. text_width = llmax(text_width, getRect().getWidth() - 30);
  3534. mList->getColumn(0)->setWidth(text_width);
  3535. ((LLScrollListText*)mList->getFirstData()->getColumn(0))->setText(ascending_string);
  3536. ((LLScrollListText*)mList->getLastData()->getColumn(0))->setText(descending_string);
  3537. mList->reshape(llmax(text_width + 30, 110, getRect().getWidth()),
  3538. mList->getRect().getHeight());
  3539. LLComboBox::showList();
  3540. }
  3541. }
  3542. //static
  3543. void LLColumnHeader::onSelectSort(LLUICtrl* ctrl, void* user_data)
  3544. {
  3545. LLColumnHeader* headerp = (LLColumnHeader*)user_data;
  3546. if (!headerp) return;
  3547. LLScrollListColumn* column = headerp->mColumn;
  3548. if (!column) return;
  3549. LLScrollListCtrl* parent = column->mParentCtrl;
  3550. if (!parent) return;
  3551. if (headerp->getCurrentIndex() == 0)
  3552. {
  3553. // Ascending
  3554. parent->sortByColumn(column->mSortingColumn, true);
  3555. }
  3556. else
  3557. {
  3558. // Descending
  3559. parent->sortByColumn(column->mSortingColumn, false);
  3560. }
  3561. // Restore original column header
  3562. headerp->setLabel(headerp->mOrigLabel);
  3563. }
  3564. LLView* LLColumnHeader::findSnapEdge(S32& new_edge_val,
  3565. const LLCoordGL& mouse_dir,
  3566. ESnapEdge snap_edge, ESnapType snap_type,
  3567. S32 threshold, S32 padding)
  3568. {
  3569. // This logic assumes dragging on right
  3570. llassert(snap_edge == SNAP_RIGHT);
  3571. // Use higher snap threshold for column headers
  3572. threshold = llmin(threshold, 10);
  3573. LLRect snap_rect = getSnapRect();
  3574. mColumn->mParentCtrl->calcMaxContentWidth();
  3575. S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth();
  3576. // X coord growing means column growing, so same signs mean we are going in
  3577. // right direction
  3578. if (abs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0)
  3579. {
  3580. new_edge_val = snap_rect.mRight + snap_delta;
  3581. }
  3582. else
  3583. {
  3584. LLScrollListColumn* next_column =
  3585. mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1);
  3586. while (next_column)
  3587. {
  3588. if (next_column->mHeader)
  3589. {
  3590. snap_delta = next_column->mHeader->getSnapRect().mRight -
  3591. next_column->mMaxContentWidth - snap_rect.mRight;
  3592. if (abs(snap_delta) <= threshold &&
  3593. mouse_dir.mX * snap_delta > 0)
  3594. {
  3595. new_edge_val = snap_rect.mRight + snap_delta;
  3596. }
  3597. break;
  3598. }
  3599. next_column =
  3600. mColumn->mParentCtrl->getColumn(next_column->mIndex + 1);
  3601. }
  3602. }
  3603. return this;
  3604. }
  3605. void LLColumnHeader::userSetShape(const LLRect& new_rect)
  3606. {
  3607. S32 new_width = new_rect.getWidth();
  3608. #if 0
  3609. S32 delta_width = new_width -
  3610. (getRect().getWidth() +
  3611. mColumn->mParentCtrl->getColumnPadding());
  3612. #else
  3613. S32 delta_width = new_width - getRect().getWidth();
  3614. #endif
  3615. if (delta_width != 0)
  3616. {
  3617. S32 remaining_width = -delta_width;
  3618. S32 col;
  3619. for (col = mColumn->mIndex + 1;
  3620. col < mColumn->mParentCtrl->getNumColumns(); ++col)
  3621. {
  3622. LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
  3623. if (!columnp) continue;
  3624. if (columnp->mHeader && columnp->mHeader->canResize())
  3625. {
  3626. // How many pixels in width can this column afford to give up ?
  3627. S32 resize_buffer_amt =
  3628. llmax(0, columnp->getWidth() - MIN_COLUMN_WIDTH);
  3629. // User shrinking column, need to add width to other columns
  3630. if (delta_width < 0)
  3631. {
  3632. if (columnp->getWidth() > 0)
  3633. {
  3634. // Statically sized column, give all remaining width to
  3635. // this column
  3636. columnp->setWidth(columnp->getWidth() +
  3637. remaining_width);
  3638. if (columnp->mRelWidth > 0.f)
  3639. {
  3640. columnp->mRelWidth =
  3641. (F32)columnp->getWidth() /
  3642. (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
  3643. }
  3644. // All padding went to this widget, we are done
  3645. break;
  3646. }
  3647. }
  3648. else
  3649. {
  3650. // User growing column, need to take width from other
  3651. // columns
  3652. remaining_width += resize_buffer_amt;
  3653. if (columnp->getWidth() > 0)
  3654. {
  3655. columnp->setWidth(columnp->getWidth() -
  3656. llmin(columnp->getWidth() -
  3657. MIN_COLUMN_WIDTH,
  3658. delta_width));
  3659. if (columnp->mRelWidth > 0.f)
  3660. {
  3661. columnp->mRelWidth =
  3662. (F32)columnp->getWidth() /
  3663. (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
  3664. }
  3665. }
  3666. if (remaining_width >= 0)
  3667. {
  3668. // Width sucked up from neighboring columns, done
  3669. break;
  3670. }
  3671. }
  3672. }
  3673. }
  3674. // Clamp resize amount to maximum that can be absorbed by other columns
  3675. if (delta_width > 0)
  3676. {
  3677. delta_width += llmin(remaining_width, 0);
  3678. }
  3679. // Propagate constrained delta_width to new width for this column
  3680. new_width = getRect().getWidth() + delta_width -
  3681. mColumn->mParentCtrl->getColumnPadding();
  3682. // Use requested width
  3683. mColumn->setWidth(new_width);
  3684. // Update proportional spacing
  3685. if (mColumn->mRelWidth > 0.f)
  3686. {
  3687. mColumn->mRelWidth =
  3688. (F32)new_width /
  3689. (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
  3690. }
  3691. // Tell scroll list to layout columns again. Do immediate update to get
  3692. // proper feedback to resize handle which needs to know how far the
  3693. // resize actually went.
  3694. mColumn->mParentCtrl->updateColumns(true);
  3695. }
  3696. }
  3697. void LLColumnHeader::setHasResizableElement(bool resizable)
  3698. {
  3699. if (mHasResizableElement != resizable)
  3700. {
  3701. mColumn->mParentCtrl->dirtyColumns();
  3702. mHasResizableElement = resizable;
  3703. }
  3704. }
  3705. void LLColumnHeader::updateResizeBars()
  3706. {
  3707. S32 num_resizable_columns = 0;
  3708. for (S32 col = 0, count = mColumn->mParentCtrl->getNumColumns();
  3709. col < count; ++col)
  3710. {
  3711. LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
  3712. if (!columnp) continue;
  3713. LLColumnHeader* headerp = columnp->mHeader;
  3714. if (headerp && headerp->canResize())
  3715. {
  3716. ++num_resizable_columns;
  3717. }
  3718. }
  3719. S32 num_resizers_enabled = 0;
  3720. // Now enable/disable resize handles on resizable columns if we have at
  3721. // least two
  3722. for (S32 col = 0, count = mColumn->mParentCtrl->getNumColumns();
  3723. col < count; ++col)
  3724. {
  3725. LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
  3726. if (!columnp) continue;
  3727. LLColumnHeader* headerp = columnp->mHeader;
  3728. if (!headerp) continue;
  3729. bool enable = num_resizable_columns >= 2 &&
  3730. num_resizers_enabled < num_resizable_columns - 1 &&
  3731. headerp->canResize();
  3732. headerp->enableResizeBar(enable);
  3733. if (enable)
  3734. {
  3735. ++num_resizers_enabled;
  3736. }
  3737. }
  3738. }
  3739. void LLColumnHeader::enableResizeBar(bool enable)
  3740. {
  3741. mResizeBar->setEnabled(enable);
  3742. }
  3743. bool LLColumnHeader::canResize()
  3744. {
  3745. return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth);
  3746. }
  3747. void LLScrollListColumn::setWidth(S32 width)
  3748. {
  3749. if (!mDynamicWidth && mRelWidth <= 0.f)
  3750. {
  3751. mParentCtrl->updateStaticColumnWidth(this, width);
  3752. }
  3753. mWidth = width;
  3754. }
  3755. // Default constructor
  3756. LLScrollListColumn::LLScrollListColumn()
  3757. : mSortAscending(true),
  3758. mWidth(-1),
  3759. mRelWidth(-1.0),
  3760. mDynamicWidth(false),
  3761. mMaxContentWidth(0),
  3762. mIndex(-1),
  3763. mParentCtrl(NULL),
  3764. mHeader(NULL),
  3765. mFontAlignment(LLFontGL::LEFT)
  3766. {
  3767. }
  3768. LLScrollListColumn::LLScrollListColumn(const LLSD& sd,
  3769. LLScrollListCtrl* parent)
  3770. : mWidth(0),
  3771. mIndex (-1),
  3772. mParentCtrl(parent),
  3773. mHeader(NULL),
  3774. mMaxContentWidth(0),
  3775. mDynamicWidth(false),
  3776. mSortAscending(true),
  3777. mRelWidth(-1.f)
  3778. {
  3779. mName = sd.get("name").asString();
  3780. mSortingColumn = mName;
  3781. if (sd.has("sort"))
  3782. {
  3783. mSortingColumn = sd.get("sort").asString();
  3784. }
  3785. if (sd.has("sort_ascending"))
  3786. {
  3787. mSortAscending = sd.get("sort_ascending").asBoolean();
  3788. }
  3789. mLabel = sd.get("label").asString();
  3790. if (sd.has("relwidth") && (F32)sd.get("relwidth").asReal() > 0)
  3791. {
  3792. mRelWidth = llclamp((F32)sd.get("relwidth").asReal(), 0.f, 1.f);
  3793. }
  3794. else if (sd.has("dynamicwidth") && sd.get("dynamicwidth").asBoolean())
  3795. {
  3796. mDynamicWidth = true;
  3797. mRelWidth = -1;
  3798. }
  3799. else
  3800. {
  3801. setWidth(sd.get("width").asInteger());
  3802. }
  3803. if (sd.has("halign"))
  3804. {
  3805. mFontAlignment =
  3806. (LLFontGL::HAlign)llclamp(sd.get("halign").asInteger(),
  3807. (S32)LLFontGL::LEFT,
  3808. (S32)LLFontGL::HCENTER);
  3809. }
  3810. else
  3811. {
  3812. mFontAlignment = LLFontGL::LEFT;
  3813. }
  3814. }