llmenugl.cpp 121 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014
  1. /**
  2. * @file llmenugl.cpp
  3. * @brief LLMenuItemGL 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. //*****************************************************************************
  33. //
  34. // This file contains the opengl based menu implementation.
  35. //
  36. // NOTES: A menu label is split into 4 columns. The left column, the
  37. // label colum, the accelerator column, and the right column. The left
  38. // column is used for displaying boolean values for toggle and check
  39. // controls. The right column is used for submenus.
  40. //
  41. //*****************************************************************************
  42. #include "linden_common.h"
  43. #include "boost/tokenizer.hpp"
  44. #include "llmenugl.h"
  45. #include "llcriticaldamp.h"
  46. #include "llrender.h"
  47. #include "llstl.h" // For DeletePointer()
  48. #include "lltrans.h"
  49. #include "lluictrlfactory.h"
  50. #include "llwindow.h"
  51. #include "llvector2.h"
  52. using namespace LLOldEvents;
  53. const std::string LL_PIE_MENU_TAG = "pie_menu";
  54. static const std::string LL_MENU_ITEM_TAG = "menu_item";
  55. static const std::string LL_MENU_GL_TAG = "menu";
  56. static const std::string LL_MENU_BAR_GL_TAG = "menu_bar";
  57. static const std::string LL_MENU_ITEM_CALL_GL_TAG = "menu_item_call";
  58. static const std::string LL_MENU_ITEM_CHECK_GL_TAG = "menu_item_check";
  59. static const std::string LL_MENU_ITEM_SEPARATOR_GL_TAG = "menu_item_separator";
  60. static const std::string LL_MENU_ITEM_TEAR_OFF_GL_TAG = "tearoff_menu";
  61. //static
  62. LLMenuHolderGL* LLMenuGL::sMenuContainer = NULL;
  63. //============================================================================
  64. // Local function declarations, constants, enums, and typedefs
  65. //============================================================================
  66. const std::string SEPARATOR_NAME("separator");
  67. const std::string TEAROFF_SEPARATOR_LABEL("~~~~~~~~~~~");
  68. const std::string SEPARATOR_LABEL("-----------");
  69. const std::string VERTICAL_SEPARATOR_LABEL("|");
  70. constexpr S32 LABEL_BOTTOM_PAD_PIXELS = 2;
  71. constexpr U32 LEFT_PAD_PIXELS = 3;
  72. constexpr U32 LEFT_WIDTH_PIXELS = 15;
  73. constexpr U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
  74. constexpr U32 RIGHT_PAD_PIXELS = 2;
  75. constexpr U32 RIGHT_WIDTH_PIXELS = 15;
  76. constexpr U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
  77. constexpr U32 ACCEL_PAD_PIXELS = 10;
  78. constexpr U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS +
  79. RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
  80. constexpr U32 BRIEF_PAD_PIXELS = 2;
  81. constexpr U32 SEPARATOR_HEIGHT_PIXELS = 8;
  82. constexpr S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
  83. constexpr S32 MENU_ITEM_PADDING = 4;
  84. static const std::string BOOLEAN_TRUE_PREFIX("X");
  85. static const std::string BRANCH_SUFFIX(">");
  86. static const std::string ARROW_UP ("^^^^^^^");
  87. static const std::string ARROW_DOWN("vvvvvvv");
  88. constexpr F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
  89. LLColor4 LLMenuItemGL::sEnabledColor(0.0f, 0.0f, 0.0f, 1.0f);
  90. LLColor4 LLMenuItemGL::sDisabledColor(0.5f, 0.5f, 0.5f, 1.0f);
  91. LLColor4 LLMenuItemGL::sHighlightBackground(0.0f, 0.0f, 0.7f, 1.0f);
  92. LLColor4 LLMenuItemGL::sHighlightForeground(1.0f, 1.0f, 1.0f, 1.0f);
  93. LLColor4 LLMenuGL::sDefaultBackgroundColor(0.25f, 0.25f, 0.25f, 0.75f);
  94. bool LLMenuGL::sKeyboardMode = false;
  95. LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle;
  96. LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
  97. //LLColor4 LLMenuGL::sBackgroundColor(0.8f, 0.8f, 0.0f, 1.0f);
  98. constexpr S32 PIE_CENTER_SIZE = 20; // pixels, radius of center hole
  99. // Scale factor for pie menu when mouse is initially down
  100. constexpr F32 PIE_SCALE_FACTOR = 1.7f;
  101. // Time of transition between unbounded and bounded display of pie menu
  102. constexpr F32 PIE_SHRINK_TIME = 0.2f;
  103. constexpr F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
  104. //============================================================================
  105. // Class LLMenuItemGL
  106. //============================================================================
  107. // Default constructor
  108. LLMenuItemGL::LLMenuItemGL(const std::string& name, const std::string& label,
  109. KEY key, MASK mask)
  110. : LLView(name, true),
  111. mJumpKey(KEY_NONE),
  112. mAcceleratorKey(key),
  113. mAcceleratorMask(mask),
  114. mAllowKeyRepeat(false),
  115. mHighlight(false),
  116. mGotHover(false),
  117. mBriefItem(false),
  118. mFont(LLFontGL::getFontSansSerif()),
  119. mStyle(LLFontGL::NORMAL),
  120. mDrawTextDisabled(false)
  121. {
  122. setLabel(label);
  123. }
  124. //virtual
  125. const std::string& LLMenuItemGL::getTag() const
  126. {
  127. return LL_MENU_ITEM_TAG;
  128. }
  129. //virtual
  130. LLXMLNodePtr LLMenuItemGL::getXML(bool save_children) const
  131. {
  132. LLXMLNodePtr node = LLView::getXML();
  133. node->setName(LL_MENU_ITEM_TAG);
  134. node->createChild("type", true)->setStringValue(getType());
  135. node->createChild("label", true)->setStringValue(mLabel);
  136. if (mAcceleratorKey != KEY_NONE)
  137. {
  138. std::stringstream out;
  139. if (mAcceleratorMask & MASK_CONTROL)
  140. {
  141. out << "control|";
  142. }
  143. if (mAcceleratorMask & MASK_ALT)
  144. {
  145. out << "alt|";
  146. }
  147. if (mAcceleratorMask & MASK_SHIFT)
  148. {
  149. out << "shift|";
  150. }
  151. out << LLKeyboard::stringFromKey(mAcceleratorKey);
  152. node->createChild("shortcut", true)->setStringValue(out.str());
  153. #ifdef LL_DARWIN
  154. // Write in special tag if this key is really a ctrl combination on the
  155. // Mac
  156. if (mAcceleratorMask & MASK_MAC_CONTROL)
  157. {
  158. node->createChild("useMacCtrl", true)->setBoolValue(true);
  159. }
  160. #endif // LL_DARWIN
  161. }
  162. return node;
  163. }
  164. bool LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
  165. {
  166. if (getEnabled() && gKeyboardp &&
  167. (!gKeyboardp->getKeyRepeated(key) || mAllowKeyRepeat) &&
  168. key == mAcceleratorKey && mask == (mAcceleratorMask & MASK_NORMALKEYS))
  169. {
  170. doIt();
  171. return true;
  172. }
  173. return false;
  174. }
  175. bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
  176. {
  177. setHover(true);
  178. gWindowp->setCursor(UI_CURSOR_ARROW);
  179. return true;
  180. }
  181. // This function checks to see if the accelerator key is already in use;
  182. // if not, it will be added to the list
  183. bool LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*>* listp)
  184. {
  185. if (mAcceleratorKey != KEY_NONE)
  186. {
  187. LLKeyBinding* accelerator = NULL;
  188. std::list<LLKeyBinding*>::iterator list_it;
  189. for (list_it = listp->begin(); list_it != listp->end(); ++list_it)
  190. {
  191. accelerator = *list_it;
  192. if (accelerator->mKey == mAcceleratorKey &&
  193. accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS))
  194. {
  195. // *NOTE: get calling code to throw up warning or route
  196. // warning messages back to app-provided output
  197. // std::string warning;
  198. // warning.append("Duplicate key binding <");
  199. // appendAcceleratorString(warning);
  200. // warning.append("> for menu items:\n ");
  201. // warning.append(accelerator->mName);
  202. // warning.append("\n ");
  203. // warning.append(mLabel);
  204. // LLAlertDialog::modalAlert(warning);
  205. return false;
  206. }
  207. }
  208. if (!accelerator)
  209. {
  210. accelerator = new LLKeyBinding;
  211. if (accelerator)
  212. {
  213. accelerator->mKey = mAcceleratorKey;
  214. accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS);
  215. #if 0
  216. accelerator->mName = mLabel;
  217. #endif
  218. }
  219. listp->push_back(accelerator);
  220. }
  221. }
  222. return true;
  223. }
  224. // This method appends the character string representation of the current
  225. // accelerator key and mask to the provided string.
  226. void LLMenuItemGL::appendAcceleratorString(std::string& st) const
  227. {
  228. // break early if this is a silly thing to do.
  229. if (KEY_NONE == mAcceleratorKey)
  230. {
  231. return;
  232. }
  233. // Append any masks
  234. #ifdef LL_DARWIN
  235. // Standard Mac names for modifier keys in menu equivalents
  236. // We could use the symbol characters, but they only exist in certain fonts.
  237. if (mAcceleratorMask & MASK_CONTROL)
  238. {
  239. if (mAcceleratorMask & MASK_MAC_CONTROL)
  240. {
  241. static std::string symbol = LLTrans::getUIString("accel-mac-control");
  242. st.append(symbol);
  243. }
  244. else
  245. {
  246. // Symbol would be "\xE2\x8C\x98"
  247. static std::string symbol = LLTrans::getUIString("accel-mac-command");
  248. st.append(symbol);
  249. }
  250. }
  251. if (mAcceleratorMask & MASK_ALT)
  252. {
  253. // Symbol would be "\xE2\x8C\xA5"
  254. static std::string symbol = LLTrans::getUIString("accel-mac-option");
  255. st.append(symbol);
  256. }
  257. if (mAcceleratorMask & MASK_SHIFT)
  258. {
  259. // Symbol would be "\xE2\x8C\xA7"
  260. static std::string symbol = LLTrans::getUIString("accel-mac-shift");
  261. st.append(symbol);
  262. }
  263. #else
  264. if (mAcceleratorMask & MASK_CONTROL)
  265. {
  266. static std::string symbol = LLTrans::getUIString("accel-control");
  267. st.append(symbol);
  268. }
  269. if (mAcceleratorMask & MASK_ALT)
  270. {
  271. static std::string symbol = LLTrans::getUIString("accel-alt");
  272. st.append(symbol);
  273. }
  274. if (mAcceleratorMask & MASK_SHIFT)
  275. {
  276. static std::string symbol = LLTrans::getUIString("accel-shift");
  277. st.append(symbol);
  278. }
  279. #endif
  280. std::string keystr = LLKeyboard::stringFromKey(mAcceleratorKey);
  281. if ((mAcceleratorMask & MASK_NORMALKEYS) &&
  282. (keystr[0] == '-' || keystr[0] == '='))
  283. {
  284. st.append(" ");
  285. }
  286. st.append(keystr);
  287. }
  288. void LLMenuItemGL::setJumpKey(KEY key)
  289. {
  290. mJumpKey = LLStringOps::toUpper((char)key);
  291. }
  292. //virtual
  293. U32 LLMenuItemGL::getNominalHeight() const
  294. {
  295. return ll_roundp(mFont->getLineHeight()) + MENU_ITEM_PADDING;
  296. }
  297. // Get the parent menu for this item
  298. LLMenuGL* LLMenuItemGL::getMenu()
  299. {
  300. return (LLMenuGL*)getParent();
  301. }
  302. // getNominalWidth() - returns the normal width of this control in pixels:
  303. // this is used for calculating the widest item, as well as for horizontal
  304. // arrangement.
  305. U32 LLMenuItemGL::getNominalWidth() const
  306. {
  307. U32 width;
  308. if (mBriefItem)
  309. {
  310. width = BRIEF_PAD_PIXELS;
  311. }
  312. else
  313. {
  314. width = PLAIN_PAD_PIXELS;
  315. }
  316. if (KEY_NONE != mAcceleratorKey)
  317. {
  318. width += ACCEL_PAD_PIXELS;
  319. std::string temp;
  320. appendAcceleratorString(temp);
  321. width += mFont->getWidth(temp);
  322. }
  323. width += mFont->getWidth(mLabel.getWString().c_str());
  324. return width;
  325. }
  326. // Called to rebuild the draw label
  327. void LLMenuItemGL::buildDrawLabel()
  328. {
  329. mDrawAccelLabel.clear();
  330. std::string st = mDrawAccelLabel.getString();
  331. appendAcceleratorString(st);
  332. mDrawAccelLabel = st;
  333. }
  334. void LLMenuItemGL::doIt()
  335. {
  336. // Close all open menus by default if parent menu is actually visible (and
  337. // we are not triggering menu item via accelerator)
  338. LLMenuGL* menup = getMenu();
  339. if (!menup)
  340. {
  341. llwarns << "NULL menu. Aborted." << llendl;
  342. return;
  343. }
  344. if (!menup->getTornOff() && menup->getVisible() &&
  345. LLMenuGL::sMenuContainer)
  346. {
  347. LLMenuGL::sMenuContainer->hideMenus();
  348. }
  349. }
  350. // Set the hover status (called by its menu)
  351. void LLMenuItemGL::setHighlight(bool highlight)
  352. {
  353. LLMenuGL* menup = getMenu();
  354. if (highlight && menup)
  355. {
  356. menup->clearHoverItem();
  357. }
  358. mHighlight = highlight;
  359. }
  360. bool LLMenuItemGL::handleKeyHere(KEY key, MASK mask)
  361. {
  362. LLMenuGL* menup = getMenu();
  363. if (!menup)
  364. {
  365. llwarns << "NULL menu. Aborted." << llendl;
  366. return false;
  367. }
  368. if (getHighlight() && menup->isOpen())
  369. {
  370. if (key == KEY_UP)
  371. {
  372. // Switch to keyboard navigation mode
  373. LLMenuGL::setKeyboardMode(true);
  374. menup->highlightPrevItem(this);
  375. return true;
  376. }
  377. if (key == KEY_DOWN)
  378. {
  379. // Switch to keyboard navigation mode
  380. LLMenuGL::setKeyboardMode(true);
  381. menup->highlightNextItem(this);
  382. return true;
  383. }
  384. if (key == KEY_RETURN && mask == MASK_NONE)
  385. {
  386. // switch to keyboard navigation mode
  387. LLMenuGL::setKeyboardMode(true);
  388. doIt();
  389. return true;
  390. }
  391. }
  392. return false;
  393. }
  394. bool LLMenuItemGL::handleMouseUp(S32 x, S32 y, MASK)
  395. {
  396. // Switch to mouse navigation mode
  397. LLMenuGL::setKeyboardMode(false);
  398. doIt();
  399. make_ui_sound("UISndClickRelease");
  400. return true;
  401. }
  402. bool LLMenuItemGL::handleMouseDown(S32 x, S32 y, MASK)
  403. {
  404. // Switch to mouse navigation mode
  405. LLMenuGL::setKeyboardMode(false);
  406. setHighlight(true);
  407. return true;
  408. }
  409. void LLMenuItemGL::draw()
  410. {
  411. // *HACK: Brief items do not highlight. Pie menu takes care of it. JC
  412. // Let disabled items be highlighted, just don't draw them as such.
  413. if (getEnabled() && getHighlight() && !mBriefItem)
  414. {
  415. gGL.color4fv(sHighlightBackground.mV);
  416. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0);
  417. }
  418. LLColor4 color;
  419. U8 font_style = mStyle;
  420. if (getEnabled() && !mDrawTextDisabled)
  421. {
  422. font_style |= LLFontGL::DROP_SHADOW_SOFT;
  423. }
  424. if (getEnabled() && getHighlight())
  425. {
  426. color = sHighlightForeground;
  427. }
  428. else if (getEnabled() && !mDrawTextDisabled)
  429. {
  430. color = sEnabledColor;
  431. }
  432. else
  433. {
  434. color = sDisabledColor;
  435. }
  436. // Draw the text on top.
  437. if (mBriefItem)
  438. {
  439. mFont->render(mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
  440. LLFontGL::LEFT, LLFontGL::BOTTOM, font_style);
  441. }
  442. else
  443. {
  444. if (!mDrawBoolLabel.empty())
  445. {
  446. mFont->render(mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS,
  447. (F32)MENU_ITEM_PADDING * 0.5f + 1.f, color,
  448. LLFontGL::LEFT, LLFontGL::BOTTOM, font_style,
  449. S32_MAX, S32_MAX, NULL, false);
  450. }
  451. mFont->render(mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS,
  452. (F32)MENU_ITEM_PADDING * 0.5f + 1.f, color,
  453. LLFontGL::LEFT, LLFontGL::BOTTOM, font_style,
  454. S32_MAX, S32_MAX, NULL, false);
  455. if (!mDrawAccelLabel.empty())
  456. {
  457. mFont->render(mDrawAccelLabel.getWString(), 0,
  458. (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS,
  459. (F32)MENU_ITEM_PADDING * 0.5f + 1.f, color,
  460. LLFontGL::RIGHT, LLFontGL::BOTTOM, font_style,
  461. S32_MAX, S32_MAX, NULL, false);
  462. }
  463. if (!mDrawBranchLabel.empty())
  464. {
  465. mFont->render(mDrawBranchLabel.getWString(), 0,
  466. (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS,
  467. (F32)MENU_ITEM_PADDING * 0.5f + 1.f, color,
  468. LLFontGL::RIGHT, LLFontGL::BOTTOM, font_style,
  469. S32_MAX, S32_MAX, NULL, false);
  470. }
  471. }
  472. // Underline "jump" key only when keyboard navigation has been initiated
  473. LLMenuGL* menup = getMenu();
  474. if (menup && menup->jumpKeysActive() && LLMenuGL::getKeyboardMode())
  475. {
  476. std::string upper_case_label = mLabel.getString();
  477. LLStringUtil::toUpper(upper_case_label);
  478. std::string::size_type offset = upper_case_label.find(mJumpKey);
  479. if (offset != std::string::npos)
  480. {
  481. S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0,
  482. offset);
  483. S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0,
  484. offset + 1);
  485. gl_line_2d(x_begin, MENU_ITEM_PADDING / 2 + 1,
  486. x_end, MENU_ITEM_PADDING / 2 + 1);
  487. }
  488. }
  489. // Clear got hover every frame
  490. setHover(false);
  491. }
  492. bool LLMenuItemGL::setLabelArg(const std::string& key, const std::string& text)
  493. {
  494. mLabel.setArg(key, text);
  495. return true;
  496. }
  497. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  498. // Class LLMenuItemSeparatorGL
  499. //
  500. // This class represents a separator.
  501. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  502. class LLMenuItemSeparatorGL : public LLMenuItemGL
  503. {
  504. public:
  505. LLMenuItemSeparatorGL(const std::string& name = SEPARATOR_NAME);
  506. const std::string& getTag() const override;
  507. LLXMLNodePtr getXML(bool save_children = true) const override;
  508. LL_INLINE std::string getType() const override { return "separator"; }
  509. // Does the primary funcationality of the menu item.
  510. LL_INLINE void doIt() override {}
  511. void draw() override;
  512. bool handleMouseDown(S32 x, S32 y, MASK mask) override;
  513. bool handleMouseUp(S32 x, S32 y, MASK mask) override;
  514. bool handleHover(S32 x, S32 y, MASK mask) override;
  515. LL_INLINE U32 getNominalHeight() const override { return SEPARATOR_HEIGHT_PIXELS; }
  516. };
  517. LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const std::string& name)
  518. : LLMenuItemGL(name, SEPARATOR_LABEL)
  519. {
  520. }
  521. //virtual
  522. const std::string& LLMenuItemSeparatorGL::getTag() const
  523. {
  524. return LL_MENU_ITEM_SEPARATOR_GL_TAG;
  525. }
  526. LLXMLNodePtr LLMenuItemSeparatorGL::getXML(bool save_children) const
  527. {
  528. LLXMLNodePtr node = LLMenuItemGL::getXML();
  529. node->setName(LL_MENU_ITEM_SEPARATOR_GL_TAG);
  530. return node;
  531. }
  532. void LLMenuItemSeparatorGL::draw()
  533. {
  534. gGL.color4fv(getDisabledColor().mV);
  535. const S32 y = getRect().getHeight() / 2;
  536. constexpr S32 PAD = 6;
  537. gl_line_2d(PAD, y, getRect().getWidth() - PAD, y);
  538. }
  539. bool LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
  540. {
  541. LLMenuGL* menup = getMenu();
  542. if (!menup)
  543. {
  544. llwarns << "NULL menu. Aborted." << llendl;
  545. return false;
  546. }
  547. if (y > getRect().getHeight() / 2)
  548. {
  549. return menup->handleMouseDown(x + getRect().mLeft,
  550. getRect().mTop + 1, mask);
  551. }
  552. return menup->handleMouseDown(x + getRect().mLeft,
  553. getRect().mBottom - 1, mask);
  554. }
  555. bool LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask)
  556. {
  557. LLMenuGL* menup = getMenu();
  558. if (!menup)
  559. {
  560. llwarns << "NULL menu. Aborted." << llendl;
  561. return false;
  562. }
  563. if (y > getRect().getHeight() / 2)
  564. {
  565. return menup->handleMouseUp(x + getRect().mLeft,
  566. getRect().mTop + 1, mask);
  567. }
  568. return menup->handleMouseUp(x + getRect().mLeft,
  569. getRect().mBottom - 1, mask);
  570. }
  571. bool LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask)
  572. {
  573. LLMenuGL* menup = getMenu();
  574. if (menup)
  575. {
  576. if (y > getRect().getHeight() / 2)
  577. {
  578. menup->highlightPrevItem(this, false);
  579. }
  580. else
  581. {
  582. menup->highlightNextItem(this, false);
  583. }
  584. }
  585. return false;
  586. }
  587. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  588. // Class LLMenuItemVerticalSeparatorGL
  589. //
  590. // This class represents a vertical separator.
  591. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  592. class LLMenuItemVerticalSeparatorGL : public LLMenuItemSeparatorGL
  593. {
  594. public:
  595. LLMenuItemVerticalSeparatorGL();
  596. virtual bool handleMouseDown(S32 x, S32 y, MASK mask) { return false; }
  597. };
  598. LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL()
  599. {
  600. setLabel(VERTICAL_SEPARATOR_LABEL);
  601. }
  602. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  603. // Class LLMenuItemTearOffGL
  604. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  605. LLMenuItemTearOffGL::LLMenuItemTearOffGL(LLHandle<LLFloater> parent_floater_handle)
  606. : LLMenuItemGL("tear off", TEAROFF_SEPARATOR_LABEL),
  607. mParentHandle(parent_floater_handle)
  608. {
  609. }
  610. //virtual
  611. const std::string& LLMenuItemTearOffGL::getTag() const
  612. {
  613. return LL_MENU_ITEM_TEAR_OFF_GL_TAG;
  614. }
  615. LLXMLNodePtr LLMenuItemTearOffGL::getXML(bool save_children) const
  616. {
  617. LLXMLNodePtr node = LLMenuItemGL::getXML();
  618. node->setName(LL_MENU_ITEM_TEAR_OFF_GL_TAG);
  619. return node;
  620. }
  621. void LLMenuItemTearOffGL::doIt()
  622. {
  623. LLMenuGL* menup = getMenu();
  624. if (!menup)
  625. {
  626. llwarns << "NULL menu. Aborted." << llendl;
  627. return;
  628. }
  629. if (menup->getTornOff())
  630. {
  631. LLTearOffMenu* torn_off_menu =
  632. dynamic_cast<LLTearOffMenu*>(menup->getParent());
  633. if (torn_off_menu)
  634. {
  635. torn_off_menu->close();
  636. }
  637. }
  638. else
  639. {
  640. // Transfer keyboard focus and highlight to first real item in list
  641. if (getHighlight())
  642. {
  643. menup->highlightNextItem(this);
  644. }
  645. menup->arrange();
  646. LLFloater* parent_floater = mParentHandle.get();
  647. LLFloater* tear_off_menu = LLTearOffMenu::create(menup);
  648. if (tear_off_menu)
  649. {
  650. if (parent_floater)
  651. {
  652. parent_floater->addDependentFloater(tear_off_menu, false);
  653. }
  654. // Give focus to torn off menu because it will have been taken
  655. // away when parent menu closes
  656. tear_off_menu->setFocus(true);
  657. }
  658. }
  659. LLMenuItemGL::doIt();
  660. }
  661. void LLMenuItemTearOffGL::draw()
  662. {
  663. // Disabled items can be highlighted, but shouldn't render as such
  664. if (getEnabled() && getHighlight() && !isBriefItem())
  665. {
  666. gGL.color4fv(getHighlightBGColor().mV);
  667. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0);
  668. }
  669. if (getEnabled())
  670. {
  671. gGL.color4fv(getEnabledColor().mV);
  672. }
  673. else
  674. {
  675. gGL.color4fv(getDisabledColor().mV);
  676. }
  677. const S32 y = getRect().getHeight() / 3;
  678. constexpr S32 PAD = 6;
  679. gl_line_2d(PAD, y, getRect().getWidth() - PAD, y);
  680. gl_line_2d(PAD, y * 2, getRect().getWidth() - PAD, y * 2);
  681. }
  682. U32 LLMenuItemTearOffGL::getNominalHeight() const
  683. {
  684. return TEAROFF_SEPARATOR_HEIGHT_PIXELS;
  685. }
  686. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  687. // Class LLMenuItemBlankGL
  688. //
  689. // This class represents a blank, non-functioning item.
  690. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  691. class LLMenuItemBlankGL : public LLMenuItemGL
  692. {
  693. public:
  694. LLMenuItemBlankGL()
  695. : LLMenuItemGL(LLStringUtil::null, LLStringUtil::null)
  696. {
  697. setEnabled(false);
  698. }
  699. LL_INLINE virtual void doIt() {}
  700. LL_INLINE virtual void draw() {}
  701. };
  702. //============================================================================
  703. // Class LLMenuItemCallGL
  704. //============================================================================
  705. LLMenuItemCallGL::LLMenuItemCallGL(const std::string& name,
  706. const std::string& label,
  707. menu_callback clicked_cb,
  708. enabled_callback enabled_cb,
  709. void* user_data, KEY key, MASK mask,
  710. bool enabled,
  711. on_disabled_callback on_disabled_cb)
  712. : LLMenuItemGL(name, label, key, mask),
  713. mCallback(clicked_cb),
  714. mEnabledCallback(enabled_cb),
  715. mLabelCallback(NULL),
  716. mUserData(user_data),
  717. mOnDisabledCallback(on_disabled_cb)
  718. {
  719. if (!enabled) setEnabled(false);
  720. }
  721. LLMenuItemCallGL::LLMenuItemCallGL(const std::string& name,
  722. menu_callback clicked_cb,
  723. enabled_callback enabled_cb,
  724. void* user_data, KEY key, MASK mask,
  725. bool enabled,
  726. on_disabled_callback on_disabled_cb)
  727. : LLMenuItemGL(name, name, key, mask),
  728. mCallback(clicked_cb),
  729. mEnabledCallback(enabled_cb),
  730. mLabelCallback(NULL),
  731. mUserData(user_data),
  732. mOnDisabledCallback(on_disabled_cb)
  733. {
  734. if (!enabled) setEnabled(false);
  735. }
  736. LLMenuItemCallGL::LLMenuItemCallGL(const std::string& name,
  737. const std::string& label,
  738. menu_callback clicked_cb,
  739. enabled_callback enabled_cb,
  740. label_callback label_cb,
  741. void* user_data, KEY key, MASK mask,
  742. bool enabled,
  743. on_disabled_callback on_disabled_cb)
  744. : LLMenuItemGL(name, label, key, mask),
  745. mCallback(clicked_cb),
  746. mEnabledCallback(enabled_cb),
  747. mLabelCallback(label_cb),
  748. mUserData(user_data),
  749. mOnDisabledCallback(on_disabled_cb)
  750. {
  751. if (!enabled) setEnabled(false);
  752. }
  753. LLMenuItemCallGL::LLMenuItemCallGL(const std::string& name,
  754. menu_callback clicked_cb,
  755. enabled_callback enabled_cb,
  756. label_callback label_cb,
  757. void* user_data, KEY key, MASK mask,
  758. bool enabled,
  759. on_disabled_callback on_disabled_cb)
  760. : LLMenuItemGL(name, name, key, mask),
  761. mCallback(clicked_cb),
  762. mEnabledCallback(enabled_cb),
  763. mLabelCallback(label_cb),
  764. mUserData(user_data),
  765. mOnDisabledCallback(on_disabled_cb)
  766. {
  767. if (!enabled) setEnabled(false);
  768. }
  769. void LLMenuItemCallGL::setEnabledControl(const std::string& enabled_control,
  770. LLView* context)
  771. {
  772. // Register new listener
  773. if (!enabled_control.empty())
  774. {
  775. LLControlVariable* control = context->findControl(enabled_control);
  776. if (!control)
  777. {
  778. context->addBoolControl(enabled_control, getEnabled());
  779. control = context->findControl(enabled_control);
  780. llassert_always(control);
  781. }
  782. control->getSignal()->connect(boost::bind(&LLView::controlListener,
  783. _2, getHandle(), "enabled"));
  784. setEnabled(control->getValue());
  785. }
  786. }
  787. void LLMenuItemCallGL::setVisibleControl(const std::string& visible_control,
  788. LLView* context)
  789. {
  790. // Register new listener
  791. if (!visible_control.empty())
  792. {
  793. LLControlVariable* control = context->findControl(visible_control);
  794. if (!control)
  795. {
  796. context->addBoolControl(visible_control, getVisible());
  797. control = context->findControl(visible_control);
  798. llassert_always(control);
  799. }
  800. control->getSignal()->connect(boost::bind(&LLView::controlListener,
  801. _2, getHandle(), "visible"));
  802. setVisible(control->getValue());
  803. }
  804. }
  805. //virtual
  806. const std::string& LLMenuItemCallGL::getTag() const
  807. {
  808. return LL_MENU_ITEM_CALL_GL_TAG;
  809. }
  810. //virtual
  811. LLXMLNodePtr LLMenuItemCallGL::getXML(bool save_children) const
  812. {
  813. LLXMLNodePtr node = LLMenuItemGL::getXML();
  814. node->setName(LL_MENU_ITEM_CALL_GL_TAG);
  815. std::vector<LLListenerEntry> listeners = mDispatcher->getListeners();
  816. for (std::vector<LLListenerEntry>::iterator itor = listeners.begin(),
  817. end = listeners.end();
  818. itor != end; ++itor)
  819. {
  820. std::string listener_name =
  821. findEventListener((LLSimpleListener*)itor->listener);
  822. if (!listener_name.empty())
  823. {
  824. // *FIXME: It is not always on_click. It could be on_check,
  825. // on_enable or on_visible, but there is no way to get that from
  826. // the data that is stored.
  827. LLXMLNodePtr child_node = node->createChild("on_click", false);
  828. child_node->createChild("function",
  829. true)->setStringValue(listener_name);
  830. child_node->createChild("filter",
  831. true)->setStringValue(itor->filter.asString());
  832. child_node->createChild("userdata",
  833. true)->setStringValue(itor->userdata.asString());
  834. }
  835. }
  836. return node;
  837. }
  838. // Calls the provided callback
  839. void LLMenuItemCallGL::doIt()
  840. {
  841. LLMenuGL* menup = getMenu();
  842. if (!menup)
  843. {
  844. llwarns << "NULL menu. Aborted." << llendl;
  845. return;
  846. }
  847. // RN: menu item can be deleted in callback, so beware
  848. menup->setItemLastSelected(this);
  849. if (mCallback)
  850. {
  851. mCallback(mUserData);
  852. }
  853. LLPointer<LLEvent> fired_event = new LLEvent(this);
  854. fireEvent(fired_event, "on_click");
  855. LLMenuItemGL::doIt();
  856. }
  857. void LLMenuItemCallGL::buildDrawLabel()
  858. {
  859. LLPointer<LLEvent> fired_event = new LLEvent(this);
  860. fireEvent(fired_event, "on_build");
  861. if (mEnabledCallback)
  862. {
  863. setEnabled(mEnabledCallback(mUserData));
  864. }
  865. if (mLabelCallback)
  866. {
  867. std::string label;
  868. mLabelCallback(label, mUserData);
  869. mLabel = label;
  870. }
  871. LLMenuItemGL::buildDrawLabel();
  872. }
  873. bool LLMenuItemCallGL::handleAcceleratorKey(KEY key, MASK mask)
  874. {
  875. if (gKeyboardp &&
  876. (!gKeyboardp->getKeyRepeated(key) || getAllowKeyRepeat()) &&
  877. key == mAcceleratorKey && mask == (mAcceleratorMask & MASK_NORMALKEYS))
  878. {
  879. LLPointer<LLEvent> fired_event = new LLEvent(this);
  880. fireEvent(fired_event, "on_build");
  881. if (mEnabledCallback)
  882. {
  883. setEnabled(mEnabledCallback(mUserData));
  884. }
  885. if (!getEnabled())
  886. {
  887. if (mOnDisabledCallback)
  888. {
  889. mOnDisabledCallback(mUserData);
  890. }
  891. }
  892. }
  893. return LLMenuItemGL::handleAcceleratorKey(key, mask);
  894. }
  895. //============================================================================
  896. // Class LLMenuItemCheckGL
  897. //============================================================================
  898. LLMenuItemCheckGL::LLMenuItemCheckGL(const std::string& name,
  899. const std::string& label,
  900. menu_callback clicked_cb,
  901. enabled_callback enabled_cb,
  902. check_callback check_cb,
  903. void* user_data, KEY key, MASK mask)
  904. : LLMenuItemCallGL(name, label, clicked_cb, enabled_cb, user_data, key,
  905. mask),
  906. mCheckCallback(check_cb),
  907. mChecked(false)
  908. {
  909. }
  910. LLMenuItemCheckGL::LLMenuItemCheckGL(const std::string& name,
  911. menu_callback clicked_cb,
  912. enabled_callback enabled_cb,
  913. check_callback check_cb,
  914. void* user_data, KEY key, MASK mask)
  915. : LLMenuItemCallGL(name, name, clicked_cb, enabled_cb, user_data, key, mask),
  916. mCheckCallback(check_cb),
  917. mChecked(false)
  918. {
  919. }
  920. LLMenuItemCheckGL::LLMenuItemCheckGL(const std::string& name,
  921. const std::string& label,
  922. menu_callback clicked_cb,
  923. enabled_callback enabled_cb,
  924. const char* control_name,
  925. LLView* context, void* user_data,
  926. KEY key, MASK mask)
  927. : LLMenuItemCallGL(name, label, clicked_cb, enabled_cb, user_data, key,
  928. mask),
  929. mCheckCallback(NULL)
  930. {
  931. setControlName(control_name, context);
  932. }
  933. //virtual
  934. void LLMenuItemCheckGL::setValue(const LLSD& value)
  935. {
  936. mChecked = value.asBoolean();
  937. if (mChecked)
  938. {
  939. mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
  940. }
  941. else
  942. {
  943. mDrawBoolLabel.clear();
  944. }
  945. }
  946. void LLMenuItemCheckGL::setCheckedControl(std::string checked_control,
  947. LLView* context)
  948. {
  949. // Register new listener
  950. if (!checked_control.empty())
  951. {
  952. LLControlVariable* control = context->findControl(checked_control);
  953. if (!control)
  954. {
  955. context->addBoolControl(checked_control, mChecked);
  956. control = context->findControl(checked_control);
  957. llassert_always(control);
  958. }
  959. control->getSignal()->connect(boost::bind(&LLView::controlListener,
  960. _2, getHandle(), "value"));
  961. mChecked = control->getValue();
  962. }
  963. }
  964. //virtual
  965. const std::string& LLMenuItemCheckGL::getTag() const
  966. {
  967. return LL_MENU_ITEM_CHECK_GL_TAG;
  968. }
  969. //virtual
  970. LLXMLNodePtr LLMenuItemCheckGL::getXML(bool save_children) const
  971. {
  972. LLXMLNodePtr node = LLMenuItemCallGL::getXML();
  973. node->setName(LL_MENU_ITEM_CHECK_GL_TAG);
  974. return node;
  975. }
  976. // Called to rebuild the draw label
  977. void LLMenuItemCheckGL::buildDrawLabel()
  978. {
  979. if (mChecked || (mCheckCallback && mCheckCallback(getUserData())))
  980. {
  981. mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
  982. }
  983. else
  984. {
  985. mDrawBoolLabel.clear();
  986. }
  987. LLMenuItemCallGL::buildDrawLabel();
  988. }
  989. //============================================================================
  990. // Class LLMenuItemToggleGL
  991. //============================================================================
  992. LLMenuItemToggleGL::LLMenuItemToggleGL(const std::string& name,
  993. const std::string& label,
  994. bool* toggle, KEY key, MASK mask)
  995. : LLMenuItemGL(name, label, key, mask),
  996. mToggle(toggle)
  997. {
  998. }
  999. LLMenuItemToggleGL::LLMenuItemToggleGL(const std::string& name, bool* toggle,
  1000. KEY key, MASK mask)
  1001. : LLMenuItemGL(name, name, key, mask),
  1002. mToggle(toggle)
  1003. {
  1004. }
  1005. // Called to rebuild the draw label
  1006. void LLMenuItemToggleGL::buildDrawLabel()
  1007. {
  1008. if (*mToggle)
  1009. {
  1010. mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
  1011. }
  1012. else
  1013. {
  1014. mDrawBoolLabel.clear();
  1015. }
  1016. mDrawAccelLabel.clear();
  1017. std::string st = mDrawAccelLabel;
  1018. appendAcceleratorString(st);
  1019. mDrawAccelLabel = st;
  1020. }
  1021. // Does the primary funcationality of the menu item.
  1022. void LLMenuItemToggleGL::doIt()
  1023. {
  1024. LLMenuGL* menup = getMenu();
  1025. if (!menup)
  1026. {
  1027. llwarns << "NULL menu. Aborted." << llendl;
  1028. return;
  1029. }
  1030. menup->setItemLastSelected(this);
  1031. *mToggle = !(*mToggle);
  1032. buildDrawLabel();
  1033. LLMenuItemGL::doIt();
  1034. }
  1035. LLMenuItemBranchGL::LLMenuItemBranchGL(const std::string& name,
  1036. const std::string& label,
  1037. LLHandle<LLView> branch,
  1038. KEY key, MASK mask)
  1039. : LLMenuItemGL(name, label, key, mask),
  1040. mBranch(branch)
  1041. {
  1042. if (!dynamic_cast<LLMenuGL*>(branch.get()))
  1043. {
  1044. llerrs << "Non-menu handle passed as branch reference." << llendl;
  1045. }
  1046. if (getBranch())
  1047. {
  1048. getBranch()->setVisible(false);
  1049. getBranch()->setParentMenuItem(this);
  1050. }
  1051. }
  1052. LLMenuItemBranchGL::~LLMenuItemBranchGL()
  1053. {
  1054. deleteViewByHandle(mBranch);
  1055. }
  1056. //virtual
  1057. LLView* LLMenuItemBranchGL::getChildView(const char* name, bool recurse,
  1058. bool create_if_missing) const
  1059. {
  1060. // richard: this is redundant with parent, remove
  1061. if (getBranch())
  1062. {
  1063. if (getBranch()->getName() == name)
  1064. {
  1065. return getBranch();
  1066. }
  1067. // Always recurse on branches
  1068. LLView* child = getBranch()->getChildView(name, recurse, false);
  1069. if (child)
  1070. {
  1071. return child;
  1072. }
  1073. }
  1074. return LLView::getChildView(name, recurse, create_if_missing);
  1075. }
  1076. //virtual
  1077. bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)
  1078. {
  1079. // switch to mouse navigation mode
  1080. LLMenuGL::setKeyboardMode(false);
  1081. doIt();
  1082. make_ui_sound("UISndClickRelease");
  1083. return true;
  1084. }
  1085. bool LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
  1086. {
  1087. if (getBranch())
  1088. {
  1089. return getBranch()->handleAcceleratorKey(key, mask);
  1090. }
  1091. return false;
  1092. }
  1093. //virtual
  1094. const std::string& LLMenuItemBranchGL::getTag() const
  1095. {
  1096. if (getBranch())
  1097. {
  1098. return getBranch()->getTag();
  1099. }
  1100. return LLMenuItemGL::getTag();
  1101. }
  1102. //virtual
  1103. LLXMLNodePtr LLMenuItemBranchGL::getXML(bool save_children) const
  1104. {
  1105. if (getBranch())
  1106. {
  1107. return getBranch()->getXML();
  1108. }
  1109. return LLMenuItemGL::getXML();
  1110. }
  1111. // This method checks to see if the accelerator key is already in use; if not,
  1112. // it will be added to the list
  1113. bool LLMenuItemBranchGL::addToAcceleratorList(std::list<LLKeyBinding*>* listp)
  1114. {
  1115. if (getBranch())
  1116. {
  1117. U32 item_count = getBranch()->getItemCount();
  1118. LLMenuItemGL* item;
  1119. while (item_count--)
  1120. {
  1121. if ((item = getBranch()->getItem(item_count)))
  1122. {
  1123. return item->addToAcceleratorList(listp);
  1124. }
  1125. }
  1126. }
  1127. return false;
  1128. }
  1129. // Called to rebuild the draw label
  1130. void LLMenuItemBranchGL::buildDrawLabel()
  1131. {
  1132. mDrawAccelLabel.clear();
  1133. std::string st = mDrawAccelLabel;
  1134. appendAcceleratorString(st);
  1135. mDrawAccelLabel = st;
  1136. mDrawBranchLabel = BRANCH_SUFFIX;
  1137. }
  1138. // Does the primary functionality of the menu item.
  1139. void LLMenuItemBranchGL::doIt()
  1140. {
  1141. openMenu();
  1142. // keyboard navigation automatically propagates highlight to sub-menu
  1143. // to facilitate fast menu control via jump keys
  1144. if (getBranch() && LLMenuGL::getKeyboardMode() &&
  1145. !getBranch()->getHighlightedItem())
  1146. {
  1147. getBranch()->highlightNextItem(NULL);
  1148. }
  1149. }
  1150. bool LLMenuItemBranchGL::handleKey(KEY key, MASK mask, bool called_from_parent)
  1151. {
  1152. bool handled = false;
  1153. if (called_from_parent && getBranch())
  1154. {
  1155. handled = getBranch()->handleKey(key, mask, called_from_parent);
  1156. }
  1157. if (!handled)
  1158. {
  1159. handled = LLMenuItemGL::handleKey(key, mask, called_from_parent);
  1160. }
  1161. return handled;
  1162. }
  1163. bool LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char,
  1164. bool called_from_parent)
  1165. {
  1166. bool handled = false;
  1167. if (called_from_parent && getBranch())
  1168. {
  1169. handled = getBranch()->handleUnicodeChar(uni_char, true);
  1170. }
  1171. if (!handled)
  1172. {
  1173. handled = LLMenuItemGL::handleUnicodeChar(uni_char,
  1174. called_from_parent);
  1175. }
  1176. return handled;
  1177. }
  1178. void LLMenuItemBranchGL::setHighlight(bool highlight)
  1179. {
  1180. if (highlight == getHighlight()) return;
  1181. LLMenuGL* menup = getMenu();
  1182. LLMenuGL* branchp = getBranch();
  1183. if (!menup || !branchp)
  1184. {
  1185. return;
  1186. }
  1187. bool torn_off = branchp->getTornOff();
  1188. // Note: do not auto open torn off sub-menus (need to explicitly active
  1189. // menu item to give them focus)
  1190. bool auto_open = !torn_off && getEnabled() && !branchp->getVisible();
  1191. // Torn off menus do not open sub menus on hover unless they have focus
  1192. if (auto_open && menup->getTornOff())
  1193. {
  1194. LLView* mviewp = menup->getParent();
  1195. if (mviewp)
  1196. {
  1197. LLFloater* mparentp = mviewp->asFloater();
  1198. if (mparentp && !mparentp->hasFocus())
  1199. {
  1200. auto_open = false;
  1201. }
  1202. }
  1203. }
  1204. LLMenuItemGL::setHighlight(highlight);
  1205. if (highlight)
  1206. {
  1207. if (auto_open)
  1208. {
  1209. openMenu();
  1210. }
  1211. }
  1212. else if (torn_off)
  1213. {
  1214. LLView* pviewp = branchp->getParent();
  1215. if (pviewp)
  1216. {
  1217. LLFloater* parentp = pviewp->asFloater();
  1218. if (parentp)
  1219. {
  1220. parentp->setFocus(false);
  1221. }
  1222. }
  1223. branchp->clearHoverItem();
  1224. }
  1225. else
  1226. {
  1227. branchp->setVisible(false);
  1228. }
  1229. }
  1230. void LLMenuItemBranchGL::draw()
  1231. {
  1232. LLMenuItemGL::draw();
  1233. LLMenuGL* branch = getBranch();
  1234. if (branch && branch->getVisible() && !branch->getTornOff())
  1235. {
  1236. setHighlight(true);
  1237. }
  1238. }
  1239. void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
  1240. {
  1241. LLMenuGL* branchp = getBranch();
  1242. if (branchp && !branchp->getParent())
  1243. {
  1244. // Make the branch menu a sibling of my parent menu
  1245. branchp->updateParent(parentp);
  1246. }
  1247. }
  1248. void LLMenuItemBranchGL::onVisibilityChange(bool new_visibility)
  1249. {
  1250. LLMenuGL* branch = getBranch();
  1251. if (!new_visibility && branch && !branch->getTornOff())
  1252. {
  1253. branch->setVisible(false);
  1254. }
  1255. LLMenuItemGL::onVisibilityChange(new_visibility);
  1256. }
  1257. bool LLMenuItemBranchGL::handleKeyHere(KEY key, MASK mask)
  1258. {
  1259. LLMenuGL* menup = getMenu();
  1260. LLMenuGL* branchp = getBranch();
  1261. if (branchp && menup)
  1262. {
  1263. if (branchp->getVisible() && menup->getVisible() && key == KEY_LEFT)
  1264. {
  1265. // Switch to keyboard navigation mode
  1266. LLMenuGL::setKeyboardMode(true);
  1267. bool handled = branchp->clearHoverItem();
  1268. if (branchp->getTornOff())
  1269. {
  1270. LLView* pviewp = branchp->getParent();
  1271. if (pviewp)
  1272. {
  1273. LLFloater* parentp = pviewp->asFloater();
  1274. if (parentp)
  1275. {
  1276. parentp->setFocus(false);
  1277. }
  1278. }
  1279. }
  1280. if (handled && menup->getTornOff())
  1281. {
  1282. LLView* mviewp = menup->getParent();
  1283. if (mviewp)
  1284. {
  1285. LLFloater* mparentp = mviewp->asFloater();
  1286. if (mparentp)
  1287. {
  1288. mparentp->setFocus(true);
  1289. }
  1290. }
  1291. }
  1292. return handled;
  1293. }
  1294. if (getHighlight() && menup->isOpen() && key == KEY_RIGHT &&
  1295. !branchp->getHighlightedItem())
  1296. {
  1297. // Switch to keyboard navigation mode
  1298. LLMenuGL::setKeyboardMode(true);
  1299. LLMenuItemGL* itemp = branchp->highlightNextItem(NULL);
  1300. if (itemp)
  1301. {
  1302. return true;
  1303. }
  1304. }
  1305. }
  1306. return LLMenuItemGL::handleKeyHere(key, mask);
  1307. }
  1308. void LLMenuItemBranchGL::openMenu()
  1309. {
  1310. LLMenuGL* branch = getBranch();
  1311. if (!branch) return;
  1312. if (branch->getTornOff())
  1313. {
  1314. LLView* pviewp = branch->getParent();
  1315. if (pviewp)
  1316. {
  1317. LLFloater* parentp = pviewp->asFloater();
  1318. if (parentp)
  1319. {
  1320. gFloaterViewp->bringToFront(parentp);
  1321. // This might not be necessary, as torn off branches do not get
  1322. // focus and hence no highligth
  1323. branch->highlightNextItem(NULL);
  1324. }
  1325. }
  1326. return;
  1327. }
  1328. if (branch->getVisible() || !LLMenuGL::sMenuContainer)
  1329. {
  1330. return;
  1331. }
  1332. // Get valid rectangle for menus
  1333. const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
  1334. branch->arrange();
  1335. LLRect rect = branch->getRect();
  1336. // Calculate root-view relative position for branch menu
  1337. S32 left = getRect().mRight;
  1338. S32 top = getRect().mTop - getRect().mBottom;
  1339. LLView* parentp = branch->getParent();
  1340. localPointToOtherView(left, top, &left, &top, parentp);
  1341. rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight());
  1342. if (branch->getCanTearOff())
  1343. {
  1344. rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
  1345. }
  1346. branch->setRect(rect);
  1347. S32 x = 0;
  1348. S32 y = 0;
  1349. branch->localPointToOtherView(0, 0, &x, &y, parentp);
  1350. S32 delta_x = 0;
  1351. S32 delta_y = 0;
  1352. if (y < menu_region_rect.mBottom)
  1353. {
  1354. delta_y = menu_region_rect.mBottom - y;
  1355. }
  1356. S32 menu_region_width = menu_region_rect.getWidth();
  1357. if (x - menu_region_rect.mLeft > menu_region_width - rect.getWidth())
  1358. {
  1359. // Move sub-menu over to left side
  1360. delta_x = llmax(-x, -rect.getWidth() - getRect().getWidth());
  1361. }
  1362. branch->translate(delta_x, delta_y);
  1363. branch->setVisible(true);
  1364. if (parentp)
  1365. {
  1366. parentp->sendChildToFront(branch);
  1367. }
  1368. }
  1369. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1370. // Class LLMenuItemBranchDownGL
  1371. //
  1372. // The LLMenuItemBranchDownGL represents a menu item that has a
  1373. // sub-menu. This is used to make menu bar menus.
  1374. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1375. class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
  1376. {
  1377. protected:
  1378. public:
  1379. LLMenuItemBranchDownGL(const std::string& name, const std::string& label,
  1380. LLHandle<LLView> branch, KEY key = KEY_NONE,
  1381. MASK mask = MASK_NONE);
  1382. virtual std::string getType() const { return "menu"; }
  1383. // Returns the normal width of this control in pixels - this is
  1384. // used for calculating the widest item, as well as for horizontal
  1385. // arrangement.
  1386. virtual U32 getNominalWidth() const;
  1387. // Called to rebuild the draw label
  1388. virtual void buildDrawLabel();
  1389. // Handles opening, positioning, and arranging the menu branch associated
  1390. // with this item
  1391. virtual void openMenu();
  1392. // Sets the hover status (called by its menu) and if the object is active.
  1393. // This is used for behavior transfer.
  1394. virtual void setHighlight(bool highlight);
  1395. virtual bool isActive() const;
  1396. // LLView functionality
  1397. virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
  1398. virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
  1399. virtual void draw();
  1400. virtual bool handleKeyHere(KEY key, MASK mask);
  1401. virtual bool handleAcceleratorKey(KEY key, MASK mask);
  1402. };
  1403. LLMenuItemBranchDownGL::LLMenuItemBranchDownGL(const std::string& name,
  1404. const std::string& label,
  1405. LLHandle<LLView> branch,
  1406. KEY key, MASK mask)
  1407. : LLMenuItemBranchGL(name, label, branch, key, mask)
  1408. {
  1409. }
  1410. // Returns the normal width of this control in pixels: this is used for
  1411. // calculating the widest item, as well as for horizontal arrangement.
  1412. U32 LLMenuItemBranchDownGL::getNominalWidth() const
  1413. {
  1414. U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS;
  1415. width += getFont()->getWidth(mLabel.getWString().c_str());
  1416. return width;
  1417. }
  1418. // Called to rebuild the draw label
  1419. void LLMenuItemBranchDownGL::buildDrawLabel()
  1420. {
  1421. mDrawAccelLabel.clear();
  1422. std::string st = mDrawAccelLabel;
  1423. appendAcceleratorString(st);
  1424. mDrawAccelLabel = st;
  1425. }
  1426. void LLMenuItemBranchDownGL::openMenu()
  1427. {
  1428. LLMenuGL* branch = getBranch();
  1429. if (!branch) return;
  1430. if (branch->getVisible() && !branch->getTornOff())
  1431. {
  1432. branch->setVisible(false);
  1433. }
  1434. else if (branch->getTornOff())
  1435. {
  1436. LLView* pviewp = branch->getParent();
  1437. if (pviewp)
  1438. {
  1439. LLFloater* parentp = pviewp->asFloater();
  1440. if (parentp)
  1441. {
  1442. gFloaterViewp->bringToFront(parentp);
  1443. }
  1444. }
  1445. }
  1446. else
  1447. {
  1448. // We are showing the drop-down menu, so patch up its labels/rects
  1449. branch->arrange();
  1450. LLRect rect = branch->getRect();
  1451. S32 left = 0;
  1452. S32 top = getRect().mBottom;
  1453. LLView* parentp = branch->getParent();
  1454. localPointToOtherView(left, top, &left, &top, parentp);
  1455. rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight());
  1456. branch->setRect(rect);
  1457. S32 x = 0;
  1458. S32 y = 0;
  1459. branch->localPointToScreen(0, 0, &x, &y);
  1460. S32 delta_x = 0;
  1461. LLCoordScreen window_size;
  1462. gWindowp->getSize(&window_size);
  1463. S32 window_width = window_size.mX;
  1464. if (x > window_width - rect.getWidth())
  1465. {
  1466. delta_x = (window_width - rect.getWidth()) - x;
  1467. }
  1468. branch->translate(delta_x, 0);
  1469. setHighlight(true);
  1470. branch->setVisible(true);
  1471. if (parentp)
  1472. {
  1473. parentp->sendChildToFront(branch);
  1474. }
  1475. }
  1476. }
  1477. // Sets the hover status (called by its menu)
  1478. void LLMenuItemBranchDownGL::setHighlight(bool highlight)
  1479. {
  1480. if (highlight == getHighlight()) return;
  1481. LLMenuGL* branch = getBranch();
  1482. if (!branch) return;
  1483. // NOTE: purposely calling all the way to the base to bypass auto-open.
  1484. LLMenuItemGL::setHighlight(highlight);
  1485. if (highlight)
  1486. {
  1487. return;
  1488. }
  1489. if (branch->getTornOff())
  1490. {
  1491. LLView* pviewp = branch->getParent();
  1492. if (pviewp)
  1493. {
  1494. LLFloater* parentp = pviewp->asFloater();
  1495. if (parentp)
  1496. {
  1497. parentp->setFocus(false);
  1498. }
  1499. }
  1500. branch->clearHoverItem();
  1501. }
  1502. else
  1503. {
  1504. branch->setVisible(false);
  1505. }
  1506. }
  1507. bool LLMenuItemBranchDownGL::isActive() const
  1508. {
  1509. // For top level menus, being open is sufficient to be considered active,
  1510. // because clicking on them with the mouse will open them, without moving
  1511. // keyboard focus to them
  1512. return isOpen();
  1513. }
  1514. bool LLMenuItemBranchDownGL::handleMouseDown(S32 x, S32 y, MASK mask)
  1515. {
  1516. // Switch to mouse control mode
  1517. LLMenuGL::setKeyboardMode(false);
  1518. doIt();
  1519. make_ui_sound("UISndClick");
  1520. return true;
  1521. }
  1522. bool LLMenuItemBranchDownGL::handleMouseUp(S32 x, S32 y, MASK mask)
  1523. {
  1524. return true;
  1525. }
  1526. bool LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
  1527. {
  1528. bool branch_visible = getBranch()->getVisible();
  1529. bool handled = getBranch()->handleAcceleratorKey(key, mask);
  1530. if (handled && !branch_visible && getVisible())
  1531. {
  1532. // Flash this menu entry because we triggered an invisible menu item
  1533. LLMenuHolderGL::setActivatedItem(this);
  1534. }
  1535. return handled;
  1536. }
  1537. bool LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask)
  1538. {
  1539. LLMenuGL* menup = getMenu();
  1540. LLMenuGL* branchp = getBranch();
  1541. if (!branchp || !menup)
  1542. {
  1543. return false;
  1544. }
  1545. // Do not do keyboard navigation of top-level menus unless in keyboard
  1546. // mode, or menu expanded
  1547. if (getHighlight() && menup->getVisible() &&
  1548. (isActive() || LLMenuGL::getKeyboardMode()))
  1549. {
  1550. if (key == KEY_LEFT)
  1551. {
  1552. // switch to keyboard navigation mode
  1553. LLMenuGL::setKeyboardMode(true);
  1554. LLMenuItemGL* itemp = menup->highlightPrevItem(this);
  1555. // Open new menu only if previous menu was open
  1556. if (itemp && itemp->getEnabled() && branchp->getVisible())
  1557. {
  1558. itemp->doIt();
  1559. }
  1560. return true;
  1561. }
  1562. if (key == KEY_RIGHT)
  1563. {
  1564. // Switch to keyboard navigation mode
  1565. LLMenuGL::setKeyboardMode(true);
  1566. LLMenuItemGL* itemp = menup->highlightNextItem(this);
  1567. // Open new menu only if previous menu was open
  1568. if (itemp && itemp->getEnabled() && branchp->getVisible())
  1569. {
  1570. itemp->doIt();
  1571. }
  1572. return true;
  1573. }
  1574. if (key == KEY_DOWN)
  1575. {
  1576. // Switch to keyboard navigation mode
  1577. LLMenuGL::setKeyboardMode(true);
  1578. if (!isActive())
  1579. {
  1580. doIt();
  1581. }
  1582. branchp->highlightNextItem(NULL);
  1583. return true;
  1584. }
  1585. if (key == KEY_UP)
  1586. {
  1587. // Switch to keyboard navigation mode
  1588. LLMenuGL::setKeyboardMode(true);
  1589. if (!isActive())
  1590. {
  1591. doIt();
  1592. }
  1593. branchp->highlightPrevItem(NULL);
  1594. return true;
  1595. }
  1596. }
  1597. return false;
  1598. }
  1599. void LLMenuItemBranchDownGL::draw()
  1600. {
  1601. // *FIXME: try removing this
  1602. if (getBranch()->getVisible() && !getBranch()->getTornOff())
  1603. {
  1604. setHighlight(true);
  1605. }
  1606. if (getHighlight())
  1607. {
  1608. gGL.color4fv(getHighlightBGColor().mV);
  1609. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0);
  1610. }
  1611. U8 font_style = getFontStyle();
  1612. if (getEnabled() && !getDrawTextDisabled())
  1613. {
  1614. font_style |= LLFontGL::DROP_SHADOW_SOFT;
  1615. }
  1616. LLColor4 color;
  1617. if (getHighlight())
  1618. {
  1619. color = getHighlightFGColor();
  1620. }
  1621. else if (getEnabled())
  1622. {
  1623. color = getEnabledColor();
  1624. }
  1625. else
  1626. {
  1627. color = getDisabledColor();
  1628. }
  1629. getFont()->render(mLabel.getWString(), 0, (F32)getRect().getWidth() * 0.5f,
  1630. (F32)LABEL_BOTTOM_PAD_PIXELS, color, LLFontGL::HCENTER,
  1631. LLFontGL::BOTTOM, font_style);
  1632. // Underline navigation key only when keyboard navigation has been
  1633. // initiated
  1634. LLMenuGL* menup = getMenu();
  1635. if (menup && menup->jumpKeysActive() && LLMenuGL::getKeyboardMode())
  1636. {
  1637. std::string upper_case_label = mLabel.getString();
  1638. LLStringUtil::toUpper(upper_case_label);
  1639. std::string::size_type offset = upper_case_label.find(getJumpKey());
  1640. if (offset != std::string::npos)
  1641. {
  1642. S32 x_offset = ll_round((F32)getRect().getWidth() * 0.5f -
  1643. getFont()->getWidthF32(mLabel.getString(),
  1644. 0, S32_MAX) * 0.5f);
  1645. S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset);
  1646. S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1);
  1647. gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end,
  1648. LABEL_BOTTOM_PAD_PIXELS);
  1649. }
  1650. }
  1651. // Reset every frame so that we only show highlight when we get hover
  1652. // events on that frame
  1653. setHover(false);
  1654. }
  1655. //============================================================================
  1656. // Class LLMenuGL
  1657. //============================================================================
  1658. static LLRegisterWidget<LLMenuGL> r08(LL_MENU_GL_TAG);
  1659. // Default constructor
  1660. LLMenuGL::LLMenuGL(const std::string& name, const std::string& label,
  1661. LLHandle<LLFloater> parent_floater_handle)
  1662. : LLUICtrl(name, LLRect(), false, NULL, NULL),
  1663. mBackgroundColor(sDefaultBackgroundColor),
  1664. mBgVisible(true),
  1665. mParentMenuItem(NULL),
  1666. mLabel(label),
  1667. mDropShadowed(true),
  1668. mHorizontalLayout(false),
  1669. mKeepFixedSize(false),
  1670. mLastMouseX(0),
  1671. mLastMouseY(0),
  1672. mMouseVelX(0),
  1673. mMouseVelY(0),
  1674. mTornOff(false),
  1675. mTearOffItem(NULL),
  1676. mSpilloverBranch(NULL),
  1677. mSpilloverMenu(NULL),
  1678. mParentFloaterHandle(parent_floater_handle),
  1679. mJumpKey(KEY_NONE)
  1680. {
  1681. mFadeTimer.stop();
  1682. setCanTearOff(true, parent_floater_handle);
  1683. setTabStop(false);
  1684. }
  1685. LLMenuGL::LLMenuGL(const std::string& label,
  1686. LLHandle<LLFloater> parent_floater_handle)
  1687. : LLUICtrl(label, LLRect(), false, NULL, NULL),
  1688. mBackgroundColor(sDefaultBackgroundColor),
  1689. mBgVisible(true),
  1690. mParentMenuItem(NULL),
  1691. mLabel(label),
  1692. mDropShadowed(true),
  1693. mHorizontalLayout(false),
  1694. mKeepFixedSize(false),
  1695. mLastMouseX(0),
  1696. mLastMouseY(0),
  1697. mMouseVelX(0),
  1698. mMouseVelY(0),
  1699. mTornOff(false),
  1700. mTearOffItem(NULL),
  1701. mSpilloverBranch(NULL),
  1702. mSpilloverMenu(NULL),
  1703. mParentFloaterHandle(parent_floater_handle),
  1704. mJumpKey(KEY_NONE)
  1705. {
  1706. mFadeTimer.stop();
  1707. setCanTearOff(true, parent_floater_handle);
  1708. setTabStop(false);
  1709. }
  1710. LLMenuGL::~LLMenuGL()
  1711. {
  1712. // Delete the branch, as it might not be in view hierarchy leave the menu,
  1713. // because it is always in view hierarchy
  1714. delete mSpilloverBranch;
  1715. mJumpKeys.clear();
  1716. }
  1717. void LLMenuGL::setCanTearOff(bool tear_off,
  1718. LLHandle<LLFloater> parent_floater_handle)
  1719. {
  1720. if (tear_off && mTearOffItem == NULL)
  1721. {
  1722. mTearOffItem = new LLMenuItemTearOffGL(parent_floater_handle);
  1723. mItems.insert(mItems.begin(), mTearOffItem);
  1724. addChildAtEnd(mTearOffItem);
  1725. arrange();
  1726. }
  1727. else if (!tear_off && mTearOffItem != NULL)
  1728. {
  1729. mItems.remove(mTearOffItem);
  1730. removeChild(mTearOffItem);
  1731. delete mTearOffItem;
  1732. mTearOffItem = NULL;
  1733. arrange();
  1734. }
  1735. }
  1736. //virtual
  1737. const std::string& LLMenuGL::getTag() const
  1738. {
  1739. return LL_MENU_GL_TAG;
  1740. }
  1741. //virtual
  1742. LLXMLNodePtr LLMenuGL::getXML(bool save_children) const
  1743. {
  1744. LLXMLNodePtr node = LLView::getXML();
  1745. node->setName(LL_MENU_GL_TAG);
  1746. // Attributes
  1747. node->createChild("opaque", true)->setBoolValue(mBgVisible);
  1748. node->createChild("drop_shadow", true)->setBoolValue(mDropShadowed);
  1749. node->createChild("tear_off", true)->setBoolValue(mTearOffItem != NULL);
  1750. if (mBgVisible)
  1751. {
  1752. // TomY TODO: this should save out the color control name
  1753. node->createChild("color", true)->setFloatValue(4, mBackgroundColor.mV);
  1754. }
  1755. // Contents
  1756. for (item_list_t::const_iterator it = mItems.begin(),
  1757. end = mItems.end();
  1758. it != end; ++it)
  1759. {
  1760. LLMenuItemGL* item = *it;
  1761. LLXMLNodePtr child_node = item->getXML();
  1762. node->addChild(child_node);
  1763. }
  1764. return node;
  1765. }
  1766. void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView* parent,
  1767. LLUICtrlFactory* factory)
  1768. {
  1769. if (child->hasName(LL_MENU_GL_TAG))
  1770. {
  1771. // SUBMENU
  1772. LLMenuGL* submenu = (LLMenuGL*)LLMenuGL::fromXML(child, parent,
  1773. factory);
  1774. appendMenu(submenu);
  1775. if (sMenuContainer)
  1776. {
  1777. submenu->updateParent(sMenuContainer);
  1778. }
  1779. else
  1780. {
  1781. submenu->updateParent(parent);
  1782. }
  1783. }
  1784. else if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) ||
  1785. child->hasName(LL_MENU_ITEM_CHECK_GL_TAG) ||
  1786. child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG))
  1787. {
  1788. LLMenuItemGL* item = NULL;
  1789. std::string type;
  1790. std::string item_name;
  1791. std::string source_label;
  1792. std::string item_label;
  1793. KEY jump_key = KEY_NONE;
  1794. child->getAttributeString("type", type);
  1795. child->getAttributeString("name", item_name);
  1796. child->getAttributeString("label", source_label);
  1797. // Parse jump key out of label
  1798. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  1799. boost::char_separator<char> sep("_");
  1800. tokenizer tokens(source_label, sep);
  1801. S32 token_count = 0;
  1802. for (tokenizer::iterator token_iter = tokens.begin(),
  1803. end = tokens.end();
  1804. token_iter != end; ++token_iter)
  1805. {
  1806. item_label += *token_iter;
  1807. if (token_count > 0)
  1808. {
  1809. jump_key = (*token_iter).c_str()[0];
  1810. }
  1811. ++token_count;
  1812. }
  1813. if (child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG))
  1814. {
  1815. appendSeparator(item_name);
  1816. }
  1817. else
  1818. {
  1819. // ITEM
  1820. if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) ||
  1821. child->hasName(LL_MENU_ITEM_CHECK_GL_TAG))
  1822. {
  1823. MASK mask = 0;
  1824. #ifdef LL_DARWIN
  1825. // See if this Mac accelerator should really use the ctrl key
  1826. // and not get mapped to cmd
  1827. bool useMacCtrl = false;
  1828. child->getAttributeBool("useMacCtrl", useMacCtrl);
  1829. #endif // LL_DARWIN
  1830. std::string shortcut;
  1831. child->getAttributeString("shortcut", shortcut);
  1832. if (shortcut.find("control") != shortcut.npos)
  1833. {
  1834. #ifdef LL_DARWIN
  1835. if (useMacCtrl)
  1836. {
  1837. mask |= MASK_MAC_CONTROL;
  1838. }
  1839. #endif // LL_DARWIN
  1840. mask |= MASK_CONTROL;
  1841. }
  1842. if (shortcut.find("alt") != shortcut.npos)
  1843. {
  1844. mask |= MASK_ALT;
  1845. }
  1846. if (shortcut.find("shift") != shortcut.npos)
  1847. {
  1848. mask |= MASK_SHIFT;
  1849. }
  1850. size_t pipe_pos = shortcut.rfind("|");
  1851. std::string key_str = shortcut.substr(pipe_pos + 1);
  1852. KEY key = KEY_NONE;
  1853. LLKeyboard::keyFromString(key_str.c_str(), &key);
  1854. LLMenuItemCallGL* new_item;
  1855. LLXMLNodePtr call_child;
  1856. if (child->hasName(LL_MENU_ITEM_CHECK_GL_TAG))
  1857. {
  1858. std::string control_name;
  1859. child->getAttributeString("control_name", control_name);
  1860. new_item = new LLMenuItemCheckGL(item_name, item_label, 0,
  1861. 0, control_name.c_str(),
  1862. parent, 0, key, mask);
  1863. for (call_child = child->getFirstChild();
  1864. call_child.notNull();
  1865. call_child = call_child->getNextSibling())
  1866. {
  1867. if (call_child->hasName("on_check"))
  1868. {
  1869. std::string callback_name;
  1870. std::string control_name;
  1871. if (call_child->hasAttribute("function"))
  1872. {
  1873. call_child->getAttributeString("function",
  1874. callback_name);
  1875. control_name = callback_name;
  1876. std::string callback_data = item_name;
  1877. if (call_child->hasAttribute("userdata"))
  1878. {
  1879. call_child->getAttributeString("userdata",
  1880. callback_data);
  1881. if (!callback_data.empty())
  1882. {
  1883. control_name = llformat("%s(%s)",
  1884. callback_name.c_str(),
  1885. callback_data.c_str());
  1886. }
  1887. }
  1888. LLSD userdata;
  1889. userdata["control"] = control_name;
  1890. userdata["data"] = callback_data;
  1891. LLSimpleListener* callback;
  1892. callback = parent->getListenerByName(callback_name);
  1893. if (!callback)
  1894. {
  1895. LL_DEBUGS("MenuGL") << "Ignoring \"on_check\" \""
  1896. << item_name
  1897. << "\" because \""
  1898. << callback_name
  1899. << "\" is not registered"
  1900. << LL_ENDL;
  1901. continue;
  1902. }
  1903. new_item->addListener(callback, "on_build",
  1904. userdata);
  1905. }
  1906. else if (call_child->hasAttribute("control"))
  1907. {
  1908. call_child->getAttributeString("control",
  1909. control_name);
  1910. }
  1911. else
  1912. {
  1913. continue;
  1914. }
  1915. LLControlVariable* control = parent->findControl(control_name);
  1916. if (!control)
  1917. {
  1918. parent->addBoolControl(control_name, false);
  1919. }
  1920. ((LLMenuItemCheckGL*)new_item)->setCheckedControl(control_name,
  1921. parent);
  1922. }
  1923. }
  1924. }
  1925. else
  1926. {
  1927. new_item = new LLMenuItemCallGL(item_name, item_label, 0,
  1928. 0, 0, 0, key, mask);
  1929. }
  1930. for (call_child = child->getFirstChild();
  1931. call_child.notNull();
  1932. call_child = call_child->getNextSibling())
  1933. {
  1934. if (call_child->hasName("on_click"))
  1935. {
  1936. std::string callback_name;
  1937. call_child->getAttributeString("function",
  1938. callback_name);
  1939. std::string callback_data = item_name;
  1940. if (call_child->hasAttribute("userdata"))
  1941. {
  1942. call_child->getAttributeString("userdata",
  1943. callback_data);
  1944. }
  1945. LLSimpleListener* cb =
  1946. parent->getListenerByName(callback_name);
  1947. if (!cb)
  1948. {
  1949. LL_DEBUGS("MenuGL") << "Ignoring \"on_click\" \""
  1950. << item_name << "\" because \""
  1951. << callback_name
  1952. << "\" is not registered"
  1953. << LL_ENDL;
  1954. continue;
  1955. }
  1956. new_item->addListener(cb, "on_click", callback_data);
  1957. }
  1958. if (call_child->hasName("on_enable"))
  1959. {
  1960. std::string callback_name;
  1961. std::string control_name;
  1962. if (call_child->hasAttribute("function"))
  1963. {
  1964. call_child->getAttributeString("function",
  1965. callback_name);
  1966. control_name = callback_name;
  1967. std::string callback_data;
  1968. if (call_child->hasAttribute("userdata"))
  1969. {
  1970. call_child->getAttributeString("userdata",
  1971. callback_data);
  1972. if (!callback_data.empty())
  1973. {
  1974. control_name = llformat("%s(%s)",
  1975. callback_name.c_str(),
  1976. callback_data.c_str());
  1977. }
  1978. }
  1979. LLSD userdata;
  1980. userdata["control"] = control_name;
  1981. userdata["data"] = callback_data;
  1982. LLSimpleListener* cb =
  1983. parent->getListenerByName(callback_name);
  1984. if (!cb)
  1985. {
  1986. LL_DEBUGS("MenuGL") << "Ignoring \"on_enable\" \""
  1987. << item_name << "\" because \""
  1988. << callback_name
  1989. << "\" is not registered"
  1990. << LL_ENDL;
  1991. continue;
  1992. }
  1993. new_item->addListener(cb, "on_build", userdata);
  1994. }
  1995. else if (call_child->hasAttribute("control"))
  1996. {
  1997. call_child->getAttributeString("control",
  1998. control_name);
  1999. }
  2000. else
  2001. {
  2002. continue;
  2003. }
  2004. new_item->setEnabledControl(control_name, parent);
  2005. }
  2006. if (call_child->hasName("on_visible"))
  2007. {
  2008. std::string callback_name;
  2009. std::string control_name;
  2010. if (call_child->hasAttribute("function"))
  2011. {
  2012. call_child->getAttributeString("function",
  2013. callback_name);
  2014. control_name = callback_name;
  2015. std::string callback_data;
  2016. if (call_child->hasAttribute("userdata"))
  2017. {
  2018. call_child->getAttributeString("userdata",
  2019. callback_data);
  2020. if (!callback_data.empty())
  2021. {
  2022. control_name = llformat("%s(%s)",
  2023. callback_name.c_str(),
  2024. callback_data.c_str());
  2025. }
  2026. }
  2027. LLSD userdata;
  2028. userdata["control"] = control_name;
  2029. userdata["data"] = callback_data;
  2030. LLSimpleListener* cb =
  2031. parent->getListenerByName(callback_name);
  2032. if (!cb)
  2033. {
  2034. LL_DEBUGS("MenuGL") << "Ignoring \"on_visible\" \""
  2035. << item_name
  2036. << "\" because \""
  2037. << callback_name
  2038. << "\" is not registered"
  2039. << LL_ENDL;
  2040. continue;
  2041. }
  2042. new_item->addListener(cb, "on_build", userdata);
  2043. }
  2044. else if (call_child->hasAttribute("control"))
  2045. {
  2046. call_child->getAttributeString("control",
  2047. control_name);
  2048. }
  2049. else
  2050. {
  2051. continue;
  2052. }
  2053. new_item->setVisibleControl(control_name, parent);
  2054. }
  2055. }
  2056. item = new_item;
  2057. item->setLabel(item_label);
  2058. if (jump_key != KEY_NONE)
  2059. {
  2060. item->setJumpKey(jump_key);
  2061. }
  2062. }
  2063. if (item)
  2064. {
  2065. append(item);
  2066. }
  2067. }
  2068. }
  2069. }
  2070. // Are we the childmost active menu and hence our jump keys should be enabled ?
  2071. // Or are we a free-standing torn-off menu (which uses jump keys too)
  2072. bool LLMenuGL::jumpKeysActive()
  2073. {
  2074. LLMenuItemGL* highlighted_item = getHighlightedItem();
  2075. if (!getVisible() || !getEnabled())
  2076. {
  2077. return false;
  2078. }
  2079. if (getTornOff())
  2080. {
  2081. // Activation of jump keys on torn off menus controlled by keyboard
  2082. // focus
  2083. LLView* pviewp = getParent();
  2084. if (!pviewp)
  2085. {
  2086. return false;
  2087. }
  2088. LLFloater* parentp = pviewp->asFloater();
  2089. return parentp && parentp->hasFocus();
  2090. }
  2091. // Are we the terminal active menu ?
  2092. // Yes, if parent menu item deems us to be active (just being visible is
  2093. // sufficient for top-level menus) and we do not have a highlighted menu
  2094. // item pointing to an active sub-menu.
  2095. // I have a parent that is active...
  2096. return (!getParentMenuItem() || getParentMenuItem()->isActive()) &&
  2097. //... but no child that is active
  2098. (!highlighted_item || !highlighted_item->isActive());
  2099. }
  2100. bool LLMenuGL::isOpen()
  2101. {
  2102. if (getTornOff())
  2103. {
  2104. LLMenuItemGL* itemp = getHighlightedItem();
  2105. // If we have an open sub-menu, then we are considered part of the open
  2106. // menu chain even if we don't have focus
  2107. if (itemp && itemp->isOpen())
  2108. {
  2109. return true;
  2110. }
  2111. // Otherwise we are only active if we have keyboard focus
  2112. LLView* pviewp = getParent();
  2113. if (!pviewp)
  2114. {
  2115. return false;
  2116. }
  2117. LLFloater* parentp = pviewp->asFloater();
  2118. return parentp && parentp->hasFocus();
  2119. }
  2120. // Normally, menus are hidden as soon as the user focuses on another menu,
  2121. // so just use the visibility criterion
  2122. return getVisible();
  2123. }
  2124. //static
  2125. LLView* LLMenuGL::fromXML(LLXMLNodePtr node, LLView* parent,
  2126. LLUICtrlFactory* factory)
  2127. {
  2128. std::string name = LL_MENU_GL_TAG;
  2129. node->getAttributeString("name", name);
  2130. std::string label = name;
  2131. node->getAttributeString("label", label);
  2132. // parse jump key out of label
  2133. std::string new_menu_label;
  2134. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  2135. boost::char_separator<char> sep("_");
  2136. tokenizer tokens(label, sep);
  2137. KEY jump_key = KEY_NONE;
  2138. S32 token_count = 0;
  2139. for (tokenizer::iterator token_iter = tokens.begin(), end = tokens.end();
  2140. token_iter != end; ++token_iter)
  2141. {
  2142. new_menu_label += *token_iter;
  2143. if (token_count > 0)
  2144. {
  2145. jump_key = (*token_iter).c_str()[0];
  2146. }
  2147. ++token_count;
  2148. }
  2149. bool opaque = false;
  2150. node->getAttributeBool("opaque", opaque);
  2151. LLMenuGL* menu = new LLMenuGL(name, new_menu_label);
  2152. menu->setJumpKey(jump_key);
  2153. bool tear_off = false;
  2154. node->getAttributeBool("tear_off", tear_off);
  2155. menu->setCanTearOff(tear_off);
  2156. if (node->hasAttribute("drop_shadow"))
  2157. {
  2158. bool drop_shadow = false;
  2159. node->getAttributeBool("drop_shadow", drop_shadow);
  2160. menu->setDropShadowed(drop_shadow);
  2161. }
  2162. menu->setBackgroundVisible(opaque);
  2163. LLColor4 color(0, 0, 0, 1);
  2164. if (opaque && LLUICtrlFactory::getAttributeColor(node, "color", color))
  2165. {
  2166. menu->setBackgroundColor(color);
  2167. }
  2168. bool create_jump_keys = false;
  2169. node->getAttributeBool("create_jump_keys", create_jump_keys);
  2170. LLXMLNodePtr child;
  2171. for (child = node->getFirstChild(); child.notNull();
  2172. child = child->getNextSibling())
  2173. {
  2174. menu->parseChildXML(child, parent, factory);
  2175. }
  2176. if (create_jump_keys)
  2177. {
  2178. menu->createJumpKeys();
  2179. }
  2180. return menu;
  2181. }
  2182. //virtual
  2183. void LLMenuGL::deleteAllChildren()
  2184. {
  2185. mItems.clear();
  2186. LLUICtrl::deleteAllChildren();
  2187. }
  2188. // Rearranges the child rects so they fit the shape of the menu.
  2189. //virtual
  2190. void LLMenuGL::arrange()
  2191. {
  2192. // Calculate the height & width, and set our rect based on that information
  2193. const LLRect& initial_rect = getRect();
  2194. U32 width = 0, height = MENU_ITEM_PADDING;
  2195. cleanupSpilloverBranch();
  2196. if (mItems.size())
  2197. {
  2198. const LLRect menu_region_rect =
  2199. sMenuContainer ? sMenuContainer->getMenuRect()
  2200. : LLRect(0, S32_MAX, S32_MAX, 0);
  2201. // Torn off menus are not constrained to the size of the screen
  2202. U32 max_width = getTornOff() ? U32_MAX : menu_region_rect.getWidth();
  2203. U32 max_height = getTornOff() ? U32_MAX : menu_region_rect.getHeight();
  2204. // *FIX: create the item first and then ask for its dimensions ?
  2205. static const S32 spillover_item_width =
  2206. PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth("More");
  2207. static const S32 spillover_item_height =
  2208. MENU_ITEM_PADDING +
  2209. ll_roundp(LLFontGL::getFontSansSerif()->getLineHeight());
  2210. item_list_t::iterator item_iter;
  2211. item_list_t::iterator items_begin = mItems.begin();
  2212. item_list_t::iterator items_end = mItems.end();
  2213. if (mHorizontalLayout)
  2214. {
  2215. for (item_iter = items_begin; item_iter != items_end; ++item_iter)
  2216. {
  2217. if ((*item_iter)->getVisible())
  2218. {
  2219. if (!getTornOff() &&
  2220. // Do not spillover the first item !
  2221. item_iter != items_begin &&
  2222. width + (*item_iter)->getNominalWidth() >
  2223. max_width - spillover_item_width)
  2224. {
  2225. // No room for any more items
  2226. createSpilloverBranch();
  2227. item_list_t::iterator spillover_iter;
  2228. for (spillover_iter = item_iter;
  2229. spillover_iter != items_end; ++spillover_iter)
  2230. {
  2231. LLMenuItemGL* itemp = (*spillover_iter);
  2232. removeChild(itemp);
  2233. // *NOTE: Mani Favor addChild() in merge with
  2234. // skinning:
  2235. mSpilloverMenu->appendNoArrange(itemp);
  2236. }
  2237. // *NOTE: Mani Remove following two lines in merge with
  2238. // skinning/viewer2.0 branch
  2239. mSpilloverMenu->arrange();
  2240. mSpilloverMenu->updateParent(sMenuContainer);
  2241. mItems.erase(item_iter, items_end);
  2242. mItems.push_back(mSpilloverBranch);
  2243. addChild(mSpilloverBranch);
  2244. height = llmax(height,
  2245. mSpilloverBranch->getNominalHeight());
  2246. width += mSpilloverBranch->getNominalWidth();
  2247. break;
  2248. }
  2249. else
  2250. {
  2251. // Track our rect
  2252. height = llmax(height,
  2253. (*item_iter)->getNominalHeight());
  2254. width += (*item_iter)->getNominalWidth();
  2255. }
  2256. }
  2257. }
  2258. }
  2259. else
  2260. {
  2261. for (item_iter = items_begin; item_iter != items_end; ++item_iter)
  2262. {
  2263. if ((*item_iter)->getVisible())
  2264. {
  2265. if (!getTornOff() &&
  2266. // Do not spillover the first item!
  2267. item_iter != items_begin &&
  2268. height + (*item_iter)->getNominalHeight() >
  2269. max_height - spillover_item_height)
  2270. {
  2271. // No room for any more items
  2272. createSpilloverBranch();
  2273. item_list_t::iterator spillover_iter;
  2274. for (spillover_iter= item_iter;
  2275. spillover_iter != items_end; ++spillover_iter)
  2276. {
  2277. LLMenuItemGL* itemp = (*spillover_iter);
  2278. removeChild(itemp);
  2279. // *NOTE:Mani Favor addChild() in merge with
  2280. // skinning:
  2281. mSpilloverMenu->appendNoArrange(itemp);
  2282. }
  2283. // *NOTE: Mani Remove following two lines in merge with
  2284. // skinning/viewer2.0 branch
  2285. mSpilloverMenu->arrange();
  2286. mSpilloverMenu->updateParent(sMenuContainer);
  2287. mItems.erase(item_iter, items_end);
  2288. mItems.push_back(mSpilloverBranch);
  2289. addChild(mSpilloverBranch);
  2290. height += mSpilloverBranch->getNominalHeight();
  2291. width = llmax(width, mSpilloverBranch->getNominalWidth());
  2292. break;
  2293. }
  2294. else
  2295. {
  2296. // Track our rect
  2297. height += (*item_iter)->getNominalHeight();
  2298. width = llmax(width, (*item_iter)->getNominalWidth());
  2299. }
  2300. }
  2301. }
  2302. }
  2303. setRect(LLRect(getRect().mLeft, getRect().mBottom + height,
  2304. getRect().mLeft + width, getRect().mBottom));
  2305. S32 cur_height = (S32)llmin(max_height, height);
  2306. S32 cur_width = 0;
  2307. items_begin = mItems.begin();
  2308. items_end = mItems.end();
  2309. for (item_iter = items_begin; item_iter != items_end; ++item_iter)
  2310. {
  2311. if ((*item_iter)->getVisible())
  2312. {
  2313. // setup item rect to hold label
  2314. LLRect rect;
  2315. if (mHorizontalLayout)
  2316. {
  2317. rect.setLeftTopAndSize(cur_width, height,
  2318. (*item_iter)->getNominalWidth(),
  2319. height);
  2320. cur_width += (*item_iter)->getNominalWidth();
  2321. }
  2322. else
  2323. {
  2324. rect.setLeftTopAndSize(0, cur_height, width,
  2325. (*item_iter)->getNominalHeight());
  2326. cur_height -= (*item_iter)->getNominalHeight();
  2327. }
  2328. (*item_iter)->setRect(rect);
  2329. (*item_iter)->buildDrawLabel();
  2330. }
  2331. }
  2332. }
  2333. if (mKeepFixedSize)
  2334. {
  2335. reshape(initial_rect.getWidth(), initial_rect.getHeight());
  2336. }
  2337. }
  2338. void LLMenuGL::createSpilloverBranch()
  2339. {
  2340. if (!mSpilloverBranch)
  2341. {
  2342. // Should be NULL but delete anyway
  2343. delete mSpilloverMenu;
  2344. // Technically, you cannot tear off spillover menus, but we are passing
  2345. // the handle along just to be safe
  2346. mSpilloverMenu = new LLMenuGL("More", "More", mParentFloaterHandle);
  2347. mSpilloverMenu->updateParent(sMenuContainer);
  2348. // Inherit colors
  2349. mSpilloverMenu->setBackgroundColor(mBackgroundColor);
  2350. mSpilloverMenu->setCanTearOff(false);
  2351. mSpilloverBranch = new LLMenuItemBranchGL("More", "More",
  2352. mSpilloverMenu->getHandle());
  2353. mSpilloverBranch->setFontStyle(LLFontGL::ITALIC);
  2354. }
  2355. }
  2356. void LLMenuGL::cleanupSpilloverBranch()
  2357. {
  2358. if (mSpilloverBranch && mSpilloverBranch->getParent() == this)
  2359. {
  2360. // Head-recursion to propagate items back up to root menu
  2361. mSpilloverMenu->cleanupSpilloverBranch();
  2362. removeChild(mSpilloverBranch);
  2363. item_list_t::iterator found_iter;
  2364. found_iter = std::find(mItems.begin(), mItems.end(), mSpilloverBranch);
  2365. if (found_iter != mItems.end())
  2366. {
  2367. mItems.erase(found_iter);
  2368. }
  2369. // Pop off spillover items
  2370. while (mSpilloverMenu->getItemCount())
  2371. {
  2372. LLMenuItemGL* itemp = mSpilloverMenu->getItem(0);
  2373. mSpilloverMenu->removeChild(itemp);
  2374. mSpilloverMenu->mItems.erase(mSpilloverMenu->mItems.begin());
  2375. // put them at the end of our own list
  2376. mItems.push_back(itemp);
  2377. addChild(itemp);
  2378. }
  2379. // Delete the branch, and since the branch will delete the menu,
  2380. // set the menu* to null.
  2381. delete mSpilloverBranch;
  2382. mSpilloverBranch = NULL;
  2383. mSpilloverMenu = NULL;
  2384. }
  2385. }
  2386. void LLMenuGL::createJumpKeys()
  2387. {
  2388. mJumpKeys.clear();
  2389. std::set<std::string> unique_words;
  2390. std::set<std::string> shared_words;
  2391. item_list_t::iterator item_it;
  2392. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  2393. boost::char_separator<char> sep(" ");
  2394. for (item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
  2395. {
  2396. std::string uppercase_label = (*item_it)->getLabel();
  2397. LLStringUtil::toUpper(uppercase_label);
  2398. tokenizer tokens(uppercase_label, sep);
  2399. tokenizer::iterator token_iter;
  2400. for (token_iter = tokens.begin(); token_iter != tokens.end();
  2401. ++token_iter)
  2402. {
  2403. if (unique_words.find(*token_iter) != unique_words.end())
  2404. {
  2405. // This word exists in more than one menu instance
  2406. shared_words.insert(*token_iter);
  2407. }
  2408. else
  2409. {
  2410. // We have a new word, keep track of it
  2411. unique_words.insert(*token_iter);
  2412. }
  2413. }
  2414. }
  2415. // Pre-assign specified jump keys
  2416. for (item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
  2417. {
  2418. KEY jump_key = (*item_it)->getJumpKey();
  2419. if (jump_key != KEY_NONE)
  2420. {
  2421. if (mJumpKeys.find(jump_key) == mJumpKeys.end())
  2422. {
  2423. mJumpKeys.emplace(jump_key, *item_it);
  2424. }
  2425. else
  2426. {
  2427. // this key is already spoken for, so we need to reassign it
  2428. // below
  2429. (*item_it)->setJumpKey(KEY_NONE);
  2430. }
  2431. }
  2432. }
  2433. for (item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
  2434. {
  2435. // Skip over items that already have assigned jump keys
  2436. if ((*item_it)->getJumpKey() != KEY_NONE)
  2437. {
  2438. continue;
  2439. }
  2440. std::string uppercase_label = (*item_it)->getLabel();
  2441. LLStringUtil::toUpper(uppercase_label);
  2442. tokenizer tokens(uppercase_label, sep);
  2443. tokenizer::iterator token_iter;
  2444. bool found_key = false;
  2445. for (token_iter = tokens.begin(); token_iter != tokens.end();
  2446. ++token_iter)
  2447. {
  2448. std::string uppercase_word = *token_iter;
  2449. // This word is not shared with other menu entries...
  2450. if (shared_words.find(*token_iter) == shared_words.end())
  2451. {
  2452. for (S32 i = 0; i < (S32)uppercase_word.size(); ++i)
  2453. {
  2454. char jump_key = uppercase_word[i];
  2455. if (LLStringOps::isDigit(jump_key) ||
  2456. (LLStringOps::isUpper(jump_key) &&
  2457. mJumpKeys.find(jump_key) == mJumpKeys.end()))
  2458. {
  2459. mJumpKeys[jump_key] = *item_it;
  2460. (*item_it)->setJumpKey(jump_key);
  2461. found_key = true;
  2462. break;
  2463. }
  2464. }
  2465. }
  2466. if (found_key)
  2467. {
  2468. break;
  2469. }
  2470. }
  2471. }
  2472. }
  2473. // Removes all items on the menu
  2474. void LLMenuGL::empty()
  2475. {
  2476. cleanupSpilloverBranch();
  2477. mItems.clear();
  2478. deleteAllChildren();
  2479. }
  2480. // Adjusts rectangle of the menu
  2481. void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom)
  2482. {
  2483. setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom));
  2484. arrange();
  2485. }
  2486. bool LLMenuGL::handleJumpKey(KEY key)
  2487. {
  2488. // must perform case-insensitive comparison, so just switch to uppercase
  2489. // input key
  2490. key = toupper(key);
  2491. navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
  2492. if (found_it != mJumpKeys.end() && found_it->second->getEnabled())
  2493. {
  2494. // switch to keyboard navigation mode
  2495. LLMenuGL::setKeyboardMode(true);
  2496. // force highlight to close old menus and open and sub-menus
  2497. #if 0
  2498. clearHoverItem();
  2499. #endif
  2500. found_it->second->setHighlight(true);
  2501. found_it->second->doIt();
  2502. }
  2503. // If we are navigating the menus, we need to eat the keystroke so rest of
  2504. // UI does not handle it
  2505. return true;
  2506. }
  2507. // Adds the menu item to this menu.
  2508. bool LLMenuGL::append(LLMenuItemGL* item)
  2509. {
  2510. mItems.push_back(item);
  2511. addChild(item);
  2512. arrange();
  2513. return true;
  2514. }
  2515. // *NOTE: should be removed when merging to skinning/viewer2.0 - Mani
  2516. // It is added as a fix to a viewer 1.23 bug that has already been addressed
  2517. // by skinning work.
  2518. bool LLMenuGL::appendNoArrange(LLMenuItemGL* item)
  2519. {
  2520. mItems.push_back(item);
  2521. addChild(item);
  2522. return true;
  2523. }
  2524. // Adds a separator to this menu
  2525. bool LLMenuGL::appendSeparator(const std::string& separator_name)
  2526. {
  2527. LLMenuItemGL* separator;
  2528. if (separator_name.empty())
  2529. {
  2530. separator = new LLMenuItemSeparatorGL("separator");
  2531. }
  2532. else
  2533. {
  2534. separator = new LLMenuItemSeparatorGL(separator_name);
  2535. }
  2536. return append(separator);
  2537. }
  2538. // Removes a menu item from this menu.
  2539. bool LLMenuGL::remove(LLMenuItemGL* item)
  2540. {
  2541. if (mSpilloverMenu)
  2542. {
  2543. cleanupSpilloverBranch();
  2544. }
  2545. item_list_t::iterator found_iter = std::find(mItems.begin(), mItems.end(),
  2546. item);
  2547. if (found_iter != mItems.end())
  2548. {
  2549. mItems.erase(found_iter);
  2550. }
  2551. removeChild(item);
  2552. if (sMenuContainer)
  2553. {
  2554. // We keep it around in case someone is pointing at it. The caller can
  2555. // delete it if it is safe. Note that getMenu() will still not work
  2556. // since its parent is not a menu.
  2557. sMenuContainer->addChild(item);
  2558. }
  2559. arrange();
  2560. return true;
  2561. }
  2562. // Adds a menu: this will create a cascading menu
  2563. bool LLMenuGL::appendMenu(LLMenuGL* menu)
  2564. {
  2565. if (menu == this)
  2566. {
  2567. llerrs << "** Attempt to attach menu to itself. This is certainly "
  2568. << "a logic error." << llendl;
  2569. }
  2570. LLMenuItemBranchGL* branch = new LLMenuItemBranchGL(menu->getName(),
  2571. menu->getLabel(),
  2572. menu->getHandle());
  2573. branch->setJumpKey(menu->getJumpKey());
  2574. bool success = append(branch);
  2575. // Inherit colors
  2576. menu->setBackgroundColor(mBackgroundColor);
  2577. return success;
  2578. }
  2579. void LLMenuGL::setEnabledSubMenus(bool enable)
  2580. {
  2581. setEnabled(enable);
  2582. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  2583. it != end; ++it)
  2584. {
  2585. (*it)->setEnabledSubMenus(enable);
  2586. }
  2587. }
  2588. // Pass the label and the enable flag for a menu item. true will make sure it
  2589. // is enabled, false will disable it.
  2590. void LLMenuGL::setItemEnabled(const std::string& name, bool enable)
  2591. {
  2592. LLMenuItemGL* item = getItem(name);
  2593. if (item)
  2594. {
  2595. item->setEnabled(enable);
  2596. item->setEnabledSubMenus(enable);
  2597. }
  2598. }
  2599. void LLMenuGL::setItemVisible(const std::string& name, bool visible)
  2600. {
  2601. LLMenuItemGL* item = getItem(name);
  2602. if (item)
  2603. {
  2604. item->setVisible(visible);
  2605. }
  2606. }
  2607. void LLMenuGL::setItemLastSelected(LLMenuItemGL* item)
  2608. {
  2609. if (getVisible())
  2610. {
  2611. LLMenuHolderGL::setActivatedItem(item);
  2612. }
  2613. // Fix the checkmarks
  2614. item->buildDrawLabel();
  2615. }
  2616. void LLMenuGL::setItemLabel(const std::string& name, const std::string& label)
  2617. {
  2618. LLMenuItemGL* item = getItem(name);
  2619. if (item)
  2620. {
  2621. item->setLabel(label);
  2622. }
  2623. }
  2624. U32 LLMenuGL::getItemCount()
  2625. {
  2626. return mItems.size();
  2627. }
  2628. LLMenuItemGL* LLMenuGL::getItem(S32 number)
  2629. {
  2630. if (number >= 0 && number < (S32)mItems.size())
  2631. {
  2632. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  2633. it != end; ++it)
  2634. {
  2635. if (number == 0)
  2636. {
  2637. return *it;
  2638. }
  2639. --number;
  2640. }
  2641. }
  2642. return NULL;
  2643. }
  2644. LLMenuItemGL* LLMenuGL::getItem(const std::string& name)
  2645. {
  2646. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  2647. it != end; ++it)
  2648. {
  2649. if ((*it)->getName() == name)
  2650. {
  2651. return *it;
  2652. }
  2653. }
  2654. return NULL;
  2655. }
  2656. LLMenuItemGL* LLMenuGL::getHighlightedItem()
  2657. {
  2658. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  2659. it != end; ++it)
  2660. {
  2661. if ((*it)->getHighlight())
  2662. {
  2663. return *it;
  2664. }
  2665. }
  2666. return NULL;
  2667. }
  2668. LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item,
  2669. bool skip_disabled)
  2670. {
  2671. // Highlighting first item on a torn off menu is the same as giving focus
  2672. // to it
  2673. if (!cur_item && getTornOff())
  2674. {
  2675. LLView* pviewp = getParent();
  2676. if (pviewp)
  2677. {
  2678. LLFloater* parentp = pviewp->asFloater();
  2679. if (parentp)
  2680. {
  2681. parentp->setFocus(true);
  2682. }
  2683. }
  2684. }
  2685. item_list_t::iterator cur_item_iter;
  2686. item_list_t::iterator items_begin = mItems.begin();
  2687. item_list_t::iterator items_end = mItems.end();
  2688. for (cur_item_iter = items_begin; cur_item_iter != items_end;
  2689. ++cur_item_iter)
  2690. {
  2691. if (*cur_item_iter == cur_item)
  2692. {
  2693. break;
  2694. }
  2695. }
  2696. item_list_t::iterator next_item_iter;
  2697. if (cur_item_iter == items_end)
  2698. {
  2699. next_item_iter = items_begin;
  2700. }
  2701. else
  2702. {
  2703. next_item_iter = cur_item_iter;
  2704. ++next_item_iter;
  2705. if (next_item_iter == items_end)
  2706. {
  2707. next_item_iter = items_begin;
  2708. }
  2709. }
  2710. // When first highlighting a menu, skip over tear off menu item
  2711. if (mTearOffItem && !cur_item)
  2712. {
  2713. // We know the first item is the tear off menu item
  2714. cur_item_iter = items_begin;
  2715. ++next_item_iter;
  2716. if (next_item_iter == items_end)
  2717. {
  2718. next_item_iter = items_begin;
  2719. }
  2720. }
  2721. while (true)
  2722. {
  2723. // Skip separators and disabled/invisible items
  2724. if ((*next_item_iter)->getEnabled() &&
  2725. (*next_item_iter)->getVisible() &&
  2726. (*next_item_iter)->getType() != SEPARATOR_NAME)
  2727. {
  2728. if (cur_item)
  2729. {
  2730. cur_item->setHighlight(false);
  2731. }
  2732. (*next_item_iter)->setHighlight(true);
  2733. return *next_item_iter;
  2734. }
  2735. if (!skip_disabled || next_item_iter == cur_item_iter)
  2736. {
  2737. break;
  2738. }
  2739. ++next_item_iter;
  2740. if (next_item_iter == items_end)
  2741. {
  2742. if (cur_item_iter == items_end)
  2743. {
  2744. break;
  2745. }
  2746. next_item_iter = items_begin;
  2747. }
  2748. }
  2749. return NULL;
  2750. }
  2751. LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item,
  2752. bool skip_disabled)
  2753. {
  2754. // Highlighting first item on a torn off menu is the same as giving focus
  2755. // to it
  2756. if (!cur_item && getTornOff())
  2757. {
  2758. LLView* pviewp = getParent();
  2759. if (pviewp)
  2760. {
  2761. LLFloater* parentp = pviewp->asFloater();
  2762. if (parentp)
  2763. {
  2764. parentp->setFocus(true);
  2765. }
  2766. }
  2767. }
  2768. item_list_t::reverse_iterator cur_item_iter;
  2769. item_list_t::reverse_iterator items_rbegin = mItems.rbegin();
  2770. item_list_t::reverse_iterator items_rend = mItems.rend();
  2771. for (cur_item_iter = items_rbegin; cur_item_iter != items_rend;
  2772. ++cur_item_iter)
  2773. {
  2774. if (*cur_item_iter == cur_item)
  2775. {
  2776. break;
  2777. }
  2778. }
  2779. item_list_t::reverse_iterator prev_item_iter;
  2780. if (cur_item_iter == items_rend)
  2781. {
  2782. prev_item_iter = items_rbegin;
  2783. }
  2784. else
  2785. {
  2786. prev_item_iter = cur_item_iter;
  2787. ++prev_item_iter;
  2788. if (prev_item_iter == items_rend)
  2789. {
  2790. prev_item_iter = items_rbegin;
  2791. }
  2792. }
  2793. while (true)
  2794. {
  2795. // Skip separators and disabled/invisible items
  2796. if ((*prev_item_iter)->getEnabled() &&
  2797. (*prev_item_iter)->getVisible() &&
  2798. (*prev_item_iter)->getType() != SEPARATOR_NAME)
  2799. {
  2800. (*prev_item_iter)->setHighlight(true);
  2801. return *prev_item_iter;
  2802. }
  2803. if (!skip_disabled || prev_item_iter == cur_item_iter)
  2804. {
  2805. break;
  2806. }
  2807. ++prev_item_iter;
  2808. if (prev_item_iter == items_rend)
  2809. {
  2810. if (cur_item_iter == items_rend)
  2811. {
  2812. break;
  2813. }
  2814. prev_item_iter = items_rbegin;
  2815. }
  2816. }
  2817. return NULL;
  2818. }
  2819. void LLMenuGL::buildDrawLabels()
  2820. {
  2821. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  2822. it != end; ++it)
  2823. {
  2824. (*it)->buildDrawLabel();
  2825. }
  2826. }
  2827. void LLMenuGL::updateParent(LLView* parentp)
  2828. {
  2829. if (!parentp) return;
  2830. if (getParent())
  2831. {
  2832. getParent()->removeChild(this);
  2833. }
  2834. parentp->addChild(this);
  2835. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  2836. it != end; ++it)
  2837. {
  2838. (*it)->updateBranchParent(parentp);
  2839. }
  2840. }
  2841. bool LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)
  2842. {
  2843. // Do not handle if not enabled
  2844. if (!getEnabled())
  2845. {
  2846. return false;
  2847. }
  2848. // Pass down even if not visible
  2849. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  2850. it != end; ++it)
  2851. {
  2852. LLMenuItemGL* itemp = *it;
  2853. if (itemp->handleAcceleratorKey(key, mask))
  2854. {
  2855. return true;
  2856. }
  2857. }
  2858. return false;
  2859. }
  2860. bool LLMenuGL::handleUnicodeCharHere(llwchar uni_char)
  2861. {
  2862. if (jumpKeysActive())
  2863. {
  2864. return handleJumpKey((KEY)uni_char);
  2865. }
  2866. return false;
  2867. }
  2868. bool LLMenuGL::handleHover(S32 x, S32 y, MASK mask)
  2869. {
  2870. // Leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU
  2871. bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
  2872. S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
  2873. S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
  2874. LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y);
  2875. mouse_dir.normalize();
  2876. LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY);
  2877. mouse_avg_dir.normalize();
  2878. F32 interp = 0.5f * llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f);
  2879. mMouseVelX = ll_round(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp));
  2880. mMouseVelY = ll_round(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp));
  2881. mLastMouseX = x;
  2882. mLastMouseY = y;
  2883. // Do not change menu focus unless mouse is moving or alt key is not held
  2884. // down
  2885. if ((abs(mMouseVelX) > 0 || abs(mMouseVelY) > 0) &&
  2886. (!mHasSelection || mMouseVelX < 0 ||
  2887. //(mouse_delta_x == 0 && mouse_delta_y == 0) ||
  2888. fabsf((F32)mMouseVelY) / fabsf((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU))
  2889. {
  2890. child_list_const_iter_t child_it;
  2891. child_list_const_iter_t child_begin = getChildList()->begin();
  2892. child_list_const_iter_t child_end = getChildList()->end();
  2893. for (child_it = child_begin; child_it != child_end; ++child_it)
  2894. {
  2895. LLView* viewp = *child_it;
  2896. S32 local_x = x - viewp->getRect().mLeft;
  2897. S32 local_y = y - viewp->getRect().mBottom;
  2898. if (!viewp->pointInView(local_x, local_y) &&
  2899. ((LLMenuItemGL*)viewp)->getHighlight())
  2900. {
  2901. // moving mouse always highlights new item
  2902. if (mouse_delta_x != 0 || mouse_delta_y != 0)
  2903. {
  2904. ((LLMenuItemGL*)viewp)->setHighlight(false);
  2905. }
  2906. }
  2907. }
  2908. for (child_it = child_begin; child_it != child_end; ++child_it)
  2909. {
  2910. LLView* viewp = *child_it;
  2911. S32 local_x = x - viewp->getRect().mLeft;
  2912. S32 local_y = y - viewp->getRect().mBottom;
  2913. // RN: always call handleHover to track mGotHover status but only
  2914. // set highlight when mouse is moving
  2915. if (viewp->getVisible() &&
  2916. // RN: allow disabled items to be highlighted to preserve
  2917. // "active" menus when/ moving mouse through them
  2918. //viewp->getEnabled() &&
  2919. viewp->pointInView(local_x, local_y) &&
  2920. viewp->handleHover(local_x, local_y, mask))
  2921. {
  2922. // moving mouse always highlights new item
  2923. if (mouse_delta_x != 0 || mouse_delta_y != 0)
  2924. {
  2925. ((LLMenuItemGL*)viewp)->setHighlight(true);
  2926. LLMenuGL::setKeyboardMode(false);
  2927. }
  2928. mHasSelection = true;
  2929. }
  2930. }
  2931. }
  2932. gWindowp->setCursor(UI_CURSOR_ARROW);
  2933. return true;
  2934. }
  2935. void LLMenuGL::draw()
  2936. {
  2937. if (mDropShadowed && !mTornOff)
  2938. {
  2939. gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
  2940. LLUI::sColorDropShadow, LLUI::sDropShadowFloater);
  2941. }
  2942. if (mBgVisible)
  2943. {
  2944. gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0,
  2945. mBackgroundColor);
  2946. }
  2947. LLView::draw();
  2948. }
  2949. void LLMenuGL::drawBackground(LLMenuItemGL* itemp, LLColor4& color)
  2950. {
  2951. gGL.color4fv(color.mV);
  2952. LLRect item_rect = itemp->getRect();
  2953. gl_rect_2d(0, item_rect.getHeight(), item_rect.getWidth(), 0);
  2954. }
  2955. void LLMenuGL::setVisible(bool visible)
  2956. {
  2957. if (visible != getVisible())
  2958. {
  2959. if (!visible)
  2960. {
  2961. mFadeTimer.start();
  2962. clearHoverItem();
  2963. // Reset last known mouse coordinates so we don't spoof a mouse
  2964. // move next time we're opened
  2965. mLastMouseX = 0;
  2966. mLastMouseY = 0;
  2967. }
  2968. else
  2969. {
  2970. mHasSelection = false;
  2971. mFadeTimer.stop();
  2972. }
  2973. LLView::setVisible(visible);
  2974. }
  2975. }
  2976. LLMenuGL* LLMenuGL::getChildMenuByName(const char* name, bool recurse) const
  2977. {
  2978. LLView* view = getChildView(name, recurse, false);
  2979. if (view)
  2980. {
  2981. LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view);
  2982. if (branch)
  2983. {
  2984. return branch->getBranch();
  2985. }
  2986. LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
  2987. if (menup)
  2988. {
  2989. return menup;
  2990. }
  2991. }
  2992. llwarns << "Child Menu " << name << " not found in menu " << getName()
  2993. << llendl;
  2994. return NULL;
  2995. }
  2996. bool LLMenuGL::clearHoverItem()
  2997. {
  2998. for (child_list_const_iter_t child_it = getChildList()->begin(),
  2999. end = getChildList()->end();
  3000. child_it != end; ++child_it)
  3001. {
  3002. LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it;
  3003. if (itemp->getHighlight())
  3004. {
  3005. itemp->setHighlight(false);
  3006. return true;
  3007. }
  3008. }
  3009. return false;
  3010. }
  3011. void hide_top_view(LLView* view)
  3012. {
  3013. if (view) view->setVisible(false);
  3014. }
  3015. //static
  3016. void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
  3017. {
  3018. if (!sMenuContainer) return;
  3019. const LLRect menu_region_rect = sMenuContainer->getMenuRect();
  3020. constexpr S32 HPAD = 2;
  3021. LLRect rect = menu->getRect();
  3022. //LLView* cur_view = spawning_view;
  3023. S32 left = x + HPAD;
  3024. S32 top = y;
  3025. spawning_view->localPointToOtherView(left, top, &left, &top,
  3026. menu->getParent());
  3027. rect.setLeftTopAndSize(left, top,
  3028. rect.getWidth(), rect.getHeight());
  3029. //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight());
  3030. menu->setRect(rect);
  3031. S32 bottom;
  3032. left = rect.mLeft;
  3033. bottom = rect.mBottom;
  3034. #if 0
  3035. menu->getParent()->localPointToScreen(rect.mLeft, rect.mBottom,
  3036. &left, &bottom);
  3037. #endif
  3038. S32 delta_x = 0;
  3039. S32 delta_y = 0;
  3040. if (bottom < menu_region_rect.mBottom)
  3041. {
  3042. // At this point, we need to move the context menu to the
  3043. // other side of the mouse.
  3044. //delta_y = menu_region_rect.mBottom - bottom;
  3045. delta_y = (rect.getHeight() + 2 * HPAD);
  3046. }
  3047. if (left > menu_region_rect.mRight - rect.getWidth())
  3048. {
  3049. // At this point, we need to move the context menu to the
  3050. // other side of the mouse.
  3051. //delta_x = (window_width - rect.getWidth()) - x;
  3052. delta_x = -rect.getWidth() - 2 * HPAD;
  3053. }
  3054. menu->translate(delta_x, delta_y);
  3055. menu->setVisible(true);
  3056. LLView* parent = menu->getParent();
  3057. if (parent)
  3058. {
  3059. parent->sendChildToFront(menu);
  3060. }
  3061. }
  3062. //-----------------------------------------------------------------------------
  3063. // class LLPieMenuBranch
  3064. // A branch to another pie menu
  3065. //-----------------------------------------------------------------------------
  3066. class LLPieMenuBranch : public LLMenuItemGL
  3067. {
  3068. public:
  3069. LLPieMenuBranch(const std::string& name, const std::string& label,
  3070. LLPieMenu* branch);
  3071. const std::string& getTag() const override;
  3072. LLXMLNodePtr getXML(bool save_children = true) const override;
  3073. // Called to rebuild the draw label
  3074. void buildDrawLabel() override;
  3075. // Does the primary funcationality of the menu item.
  3076. void doIt() override;
  3077. LL_INLINE LLPieMenu* getBranch() { return mBranch; }
  3078. protected:
  3079. LLPieMenu* mBranch;
  3080. };
  3081. LLPieMenuBranch::LLPieMenuBranch(const std::string& name,
  3082. const std::string& label,
  3083. LLPieMenu* branch)
  3084. : LLMenuItemGL(name, label, KEY_NONE, MASK_NONE),
  3085. mBranch(branch)
  3086. {
  3087. mBranch->hide(false);
  3088. mBranch->setParentMenuItem(this);
  3089. }
  3090. //virtual
  3091. const std::string& LLPieMenuBranch::getTag() const
  3092. {
  3093. if (mBranch)
  3094. {
  3095. return mBranch->getTag();
  3096. }
  3097. return LLMenuItemGL::getTag();
  3098. }
  3099. //virtual
  3100. LLXMLNodePtr LLPieMenuBranch::getXML(bool save_children) const
  3101. {
  3102. if (mBranch)
  3103. {
  3104. return mBranch->getXML();
  3105. }
  3106. return LLMenuItemGL::getXML();
  3107. }
  3108. // called to rebuild the draw label
  3109. void LLPieMenuBranch::buildDrawLabel()
  3110. {
  3111. {
  3112. // default enablement is this -- if any of the subitems are
  3113. // enabled, this item is enabled. JC
  3114. U32 sub_count = mBranch->getItemCount();
  3115. U32 i;
  3116. bool any_enabled = false;
  3117. for (i = 0; i < sub_count; ++i)
  3118. {
  3119. LLMenuItemGL* item = mBranch->getItem(i);
  3120. item->buildDrawLabel();
  3121. if (item->getEnabled() && !item->getDrawTextDisabled())
  3122. {
  3123. any_enabled = true;
  3124. break;
  3125. }
  3126. }
  3127. setDrawTextDisabled(!any_enabled);
  3128. setEnabled(true);
  3129. }
  3130. mDrawAccelLabel.clear();
  3131. std::string st = mDrawAccelLabel;
  3132. appendAcceleratorString(st);
  3133. mDrawAccelLabel = st;
  3134. // No special branch suffix
  3135. mDrawBranchLabel.clear();
  3136. }
  3137. // Does the primary funcationality of the menu item.
  3138. void LLPieMenuBranch::doIt()
  3139. {
  3140. LLPieMenu* parentp = (LLPieMenu*)getParent();
  3141. if (!parentp)
  3142. {
  3143. llwarns << "NULL parent. Aborted." << llendl;
  3144. return;
  3145. }
  3146. LLRect rect = parentp->getRect();
  3147. S32 center_x;
  3148. S32 center_y;
  3149. parentp->localPointToScreen(rect.getWidth() / 2, rect.getHeight() / 2,
  3150. &center_x, &center_y);
  3151. parentp->hide(false);
  3152. mBranch->show(center_x, center_y, false);
  3153. }
  3154. //-----------------------------------------------------------------------------
  3155. // class LLPieMenu
  3156. // A circular menu of items, icons, etc.
  3157. //-----------------------------------------------------------------------------
  3158. LLPieMenu::LLPieMenu(const std::string& name, const std::string& label)
  3159. : LLMenuGL(name, label),
  3160. mFirstMouseDown(false),
  3161. mUseInfiniteRadius(false),
  3162. mHoverItem(NULL),
  3163. mHoverThisFrame(false),
  3164. mHoveredAnyItem(false),
  3165. mOuterRingAlpha(1.f),
  3166. mCurRadius(0.f),
  3167. mRightMouseDown(false)
  3168. {
  3169. LLMenuGL::setVisible(false);
  3170. setCanTearOff(false);
  3171. }
  3172. LLPieMenu::LLPieMenu(const std::string& name)
  3173. : LLMenuGL(name, name),
  3174. mFirstMouseDown(false),
  3175. mUseInfiniteRadius(false),
  3176. mHoverItem(NULL),
  3177. mHoverThisFrame(false),
  3178. mHoveredAnyItem(false),
  3179. mOuterRingAlpha(1.f),
  3180. mCurRadius(0.f),
  3181. mRightMouseDown(false)
  3182. {
  3183. LLMenuGL::setVisible(false);
  3184. setCanTearOff(false);
  3185. }
  3186. //virtual
  3187. const std::string& LLPieMenu::getTag() const
  3188. {
  3189. return LL_PIE_MENU_TAG;
  3190. }
  3191. //virtual
  3192. LLXMLNodePtr LLPieMenu::getXML(bool save_children) const
  3193. {
  3194. LLXMLNodePtr node = LLMenuGL::getXML();
  3195. node->setName(LL_PIE_MENU_TAG);
  3196. return node;
  3197. }
  3198. void LLPieMenu::initXML(LLXMLNodePtr node, LLView* context, LLUICtrlFactory* factory)
  3199. {
  3200. LLXMLNodePtr child;
  3201. for (child = node->getFirstChild(); child.notNull();
  3202. child = child->getNextSibling())
  3203. {
  3204. if (child->hasName(LL_PIE_MENU_TAG))
  3205. {
  3206. // SUBMENU
  3207. std::string name(LL_MENU_GL_TAG);
  3208. child->getAttributeString("name", name);
  3209. std::string label(name);
  3210. child->getAttributeString("label", label);
  3211. LLPieMenu* submenu = new LLPieMenu(name, label);
  3212. appendPieMenu(submenu);
  3213. submenu->initXML(child, context, factory);
  3214. }
  3215. else
  3216. {
  3217. parseChildXML(child, context, factory);
  3218. }
  3219. }
  3220. }
  3221. //virtual
  3222. void LLPieMenu::setVisible(bool visible)
  3223. {
  3224. if (!visible)
  3225. {
  3226. hide(false);
  3227. }
  3228. }
  3229. bool LLPieMenu::handleHover(S32 x, S32 y, MASK mask)
  3230. {
  3231. // This is mostly copied from the llview class, but it continues the hover
  3232. // handle code after a hover handler has been found.
  3233. bool handled = false;
  3234. #if 0 // If we got a hover event, we've already moved the cursor for any menu
  3235. // shifts, so subsequent mouseup messages will be in the correct
  3236. // position. No need to correct them.
  3237. mShiftHoriz = 0;
  3238. mShiftVert = 0;
  3239. #endif
  3240. // Release mouse capture after short period of visibility if we are using a
  3241. // finite boundary so that right click outside of boundary will trigger new
  3242. // pie menu
  3243. if (hasMouseCapture() && !mRightMouseDown &&
  3244. mShrinkBorderTimer.getStarted() &&
  3245. mShrinkBorderTimer.getElapsedTimeF32() >= PIE_SHRINK_TIME)
  3246. {
  3247. gFocusMgr.setMouseCapture(NULL);
  3248. mUseInfiniteRadius = false;
  3249. }
  3250. LLMenuItemGL* item = pieItemFromXY(x, y);
  3251. if (item && item->getEnabled())
  3252. {
  3253. gWindowp->setCursor(UI_CURSOR_ARROW);
  3254. LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL;
  3255. handled = true;
  3256. if (item != mHoverItem)
  3257. {
  3258. if (mHoverItem)
  3259. {
  3260. mHoverItem->setHighlight(false);
  3261. }
  3262. mHoverItem = item;
  3263. mHoverItem->setHighlight(true);
  3264. #if 0 // Useless... They are all the same sound anyway !
  3265. switch (pieItemIndexFromXY(x, y))
  3266. {
  3267. case 0:
  3268. make_ui_sound("UISndPieMenuSliceHighlight0");
  3269. break;
  3270. case 1:
  3271. make_ui_sound("UISndPieMenuSliceHighlight1");
  3272. break;
  3273. case 2:
  3274. make_ui_sound("UISndPieMenuSliceHighlight2");
  3275. break;
  3276. case 3:
  3277. make_ui_sound("UISndPieMenuSliceHighlight3");
  3278. break;
  3279. case 4:
  3280. make_ui_sound("UISndPieMenuSliceHighlight4");
  3281. break;
  3282. case 5:
  3283. make_ui_sound("UISndPieMenuSliceHighlight5");
  3284. break;
  3285. case 6:
  3286. make_ui_sound("UISndPieMenuSliceHighlight6");
  3287. break;
  3288. case 7:
  3289. make_ui_sound("UISndPieMenuSliceHighlight7");
  3290. break;
  3291. default:
  3292. make_ui_sound("UISndPieMenuSliceHighlight0");
  3293. break;
  3294. }
  3295. #else
  3296. make_ui_sound("UISndPieMenuSliceHighlight");
  3297. #endif
  3298. }
  3299. mHoveredAnyItem = true;
  3300. }
  3301. else
  3302. {
  3303. // Clear out our selection
  3304. if (mHoverItem)
  3305. {
  3306. mHoverItem->setHighlight(false);
  3307. mHoverItem = NULL;
  3308. }
  3309. }
  3310. if (!handled && pointInView(x, y))
  3311. {
  3312. gWindowp->setCursor(UI_CURSOR_ARROW);
  3313. LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL;
  3314. handled = true;
  3315. }
  3316. mHoverThisFrame = true;
  3317. return handled;
  3318. }
  3319. bool LLPieMenu::handleMouseDown(S32 x, S32 y, MASK mask)
  3320. {
  3321. bool handled = false;
  3322. // The click was somewhere within our rectangle
  3323. LLMenuItemGL* item = pieItemFromXY(x, y);
  3324. if (item)
  3325. {
  3326. // Lie to the item about where the click happened to make sure it is
  3327. // within its rectangle
  3328. handled = item->handleMouseDown(0, 0, mask);
  3329. }
  3330. else if (!mRightMouseDown)
  3331. {
  3332. // Call hidemenus to make sure transient selections get cleared
  3333. ((LLMenuHolderGL*)getParent())->hideMenus();
  3334. }
  3335. // Always handle mouse down as mouse up will close open menus
  3336. return handled;
  3337. }
  3338. bool LLPieMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
  3339. {
  3340. bool handled = false;
  3341. mRightMouseDown = true;
  3342. // The click was somewhere within our rectangle
  3343. LLMenuItemGL* item = pieItemFromXY(x, y);
  3344. S32 delta_x = x - getLocalRect().getCenterX() /*+ mShiftHoriz*/;
  3345. S32 delta_y = y - getLocalRect().getCenterY() /*+ mShiftVert*/;
  3346. bool clicked_in_pie = mUseInfiniteRadius ||
  3347. delta_x * delta_x + delta_y * delta_y <
  3348. mCurRadius * mCurRadius;
  3349. // Grab mouse if right clicking anywhere within pie (even deadzone in
  3350. // middle), to detect drag outside of pie
  3351. if (clicked_in_pie)
  3352. {
  3353. // Capture mouse cursor as if on initial menu show
  3354. gFocusMgr.setMouseCapture(this);
  3355. mShrinkBorderTimer.stop();
  3356. mUseInfiniteRadius = true;
  3357. handled = true;
  3358. }
  3359. // Lie to the item about where the click happened to make sure it is within
  3360. // its rectangle
  3361. if (item && item->handleMouseDown(0, 0, mask))
  3362. {
  3363. handled = true;
  3364. }
  3365. return handled;
  3366. }
  3367. bool LLPieMenu::handleRightMouseUp(S32 x, S32 y, MASK mask)
  3368. {
  3369. // Release mouse capture when right mouse button released, and we're past
  3370. // the shrink time
  3371. if (mShrinkBorderTimer.getStarted() &&
  3372. mShrinkBorderTimer.getElapsedTimeF32() > PIE_SHRINK_TIME)
  3373. {
  3374. mUseInfiniteRadius = false;
  3375. gFocusMgr.setMouseCapture(NULL);
  3376. }
  3377. S32 delta_x = x /*+ mShiftHoriz*/ - getLocalRect().getCenterX();
  3378. S32 delta_y = y /*+ mShiftVert*/ - getLocalRect().getCenterY();
  3379. if (!mHoveredAnyItem && !mFirstMouseDown &&
  3380. delta_x * delta_x + delta_y * delta_y < PIE_CENTER_SIZE * PIE_CENTER_SIZE)
  3381. {
  3382. // User released right mouse button in middle of pie, interpret this as
  3383. // closing the menu
  3384. if (sMenuContainer)
  3385. {
  3386. sMenuContainer->hideMenus();
  3387. }
  3388. return true;
  3389. }
  3390. bool result = handleMouseUp(x, y, mask);
  3391. mRightMouseDown = false;
  3392. mHoveredAnyItem = false;
  3393. return result;
  3394. }
  3395. bool LLPieMenu::handleMouseUp(S32 x, S32 y, MASK mask)
  3396. {
  3397. bool handled = false;
  3398. // The click was somewhere within our rectangle
  3399. LLMenuItemGL* item = pieItemFromXY(x, y);
  3400. if (item)
  3401. {
  3402. // Lie to the item about where the click happened to make sure it is
  3403. // within the item's rectangle
  3404. if (item->getEnabled())
  3405. {
  3406. handled = item->handleMouseUp(0, 0, mask);
  3407. hide(true);
  3408. }
  3409. }
  3410. else if (!mRightMouseDown)
  3411. {
  3412. // Call hidemenus to make sure transient selections get cleared
  3413. ((LLMenuHolderGL*)getParent())->hideMenus();
  3414. }
  3415. if (handled)
  3416. {
  3417. make_ui_sound("UISndClickRelease");
  3418. }
  3419. if (!handled && !mUseInfiniteRadius && sMenuContainer)
  3420. {
  3421. // Call hidemenus to make sure transient selections get cleared
  3422. sMenuContainer->hideMenus();
  3423. }
  3424. if (mFirstMouseDown)
  3425. {
  3426. make_ui_sound("UISndPieMenuAppear");
  3427. mFirstMouseDown = false;
  3428. }
  3429. // *FIXME: is this necessary ?
  3430. if (!mShrinkBorderTimer.getStarted())
  3431. {
  3432. mShrinkBorderTimer.start();
  3433. }
  3434. return handled;
  3435. }
  3436. //virtual
  3437. void LLPieMenu::draw()
  3438. {
  3439. // Clear hover if mouse moved away
  3440. if (!mHoverThisFrame && mHoverItem)
  3441. {
  3442. mHoverItem->setHighlight(false);
  3443. mHoverItem = NULL;
  3444. }
  3445. // correct for non-square pixels
  3446. F32 center_x = (F32)getRect().getWidth() * 0.5f;
  3447. F32 center_y = (F32)getRect().getHeight() * 0.5f;
  3448. S32 steps = 100;
  3449. mCurRadius = PIE_SCALE_FACTOR * llmax(center_x, center_y);
  3450. mOuterRingAlpha = mUseInfiniteRadius ? 0.f : 1.f;
  3451. if (mShrinkBorderTimer.getStarted())
  3452. {
  3453. mOuterRingAlpha = clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(),
  3454. 0.f, PIE_SHRINK_TIME, 0.f, 1.f);
  3455. mCurRadius *= clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(),
  3456. 0.f, PIE_SHRINK_TIME,
  3457. 1.f, 1.f / PIE_SCALE_FACTOR);
  3458. }
  3459. gGL.pushUIMatrix();
  3460. gGL.translateUI(center_x, center_y, 0.f);
  3461. {
  3462. // Main body
  3463. LLColor4 outer_color = LLUI::sPieMenuBgColor;
  3464. outer_color.mV[VALPHA] *= mOuterRingAlpha;
  3465. gl_washer_2d(mCurRadius, (F32)PIE_CENTER_SIZE, steps,
  3466. LLUI::sPieMenuBgColor, outer_color);
  3467. // Selected wedge
  3468. S32 i = 0;
  3469. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  3470. it != end; ++it)
  3471. {
  3472. if ((*it)->getHighlight())
  3473. {
  3474. F32 arc_size = F_PI * 0.25f;
  3475. F32 start_radians = ((F32)i - 0.5f) * arc_size;
  3476. F32 end_radians = start_radians + arc_size;
  3477. LLColor4 outer_color = LLUI::sPieMenuSelectedColor;
  3478. outer_color.mV[VALPHA] *= mOuterRingAlpha;
  3479. gl_washer_segment_2d(mCurRadius, (F32)PIE_CENTER_SIZE,
  3480. start_radians, end_radians, steps / 8,
  3481. LLUI::sPieMenuSelectedColor, outer_color);
  3482. }
  3483. ++i;
  3484. }
  3485. LLUI::setLineWidth(LLUI::sPieMenuLineWidth);
  3486. // Inner lines
  3487. outer_color = LLUI::sPieMenuLineColor;
  3488. outer_color.mV[VALPHA] *= mOuterRingAlpha;
  3489. gl_washer_spokes_2d(mCurRadius, (F32)PIE_CENTER_SIZE, 8,
  3490. LLUI::sPieMenuLineColor, outer_color);
  3491. // Inner circle
  3492. gGL.color4fv(LLUI::sPieMenuLineColor.mV);
  3493. gl_circle_2d(0, 0, (F32)PIE_CENTER_SIZE, steps, false);
  3494. // Outer circle
  3495. gGL.color4fv(outer_color.mV);
  3496. gl_circle_2d(0, 0, mCurRadius, steps, false);
  3497. LLUI::setLineWidth(1.0f);
  3498. }
  3499. gGL.popUIMatrix();
  3500. mHoverThisFrame = false;
  3501. LLView::draw();
  3502. }
  3503. void LLPieMenu::drawBackground(LLMenuItemGL* itemp, LLColor4& color)
  3504. {
  3505. F32 center_x = (F32)getRect().getWidth() * 0.5f;
  3506. F32 center_y = (F32)getRect().getHeight() * 0.5f;
  3507. S32 steps = 100;
  3508. gGL.color4fv(color.mV);
  3509. gGL.pushUIMatrix();
  3510. {
  3511. gGL.translateUI(center_x - itemp->getRect().mLeft,
  3512. center_y - itemp->getRect().mBottom, 0.f);
  3513. S32 i = 0;
  3514. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  3515. it != end; ++it)
  3516. {
  3517. if (*it == itemp)
  3518. {
  3519. F32 arc_size = F_PI * 0.25f;
  3520. F32 start_radians = i * arc_size - arc_size * 0.5f;
  3521. F32 end_radians = start_radians + arc_size;
  3522. LLColor4 outer_color = color;
  3523. outer_color.mV[VALPHA] *= mOuterRingAlpha;
  3524. gl_washer_segment_2d(mCurRadius, (F32)PIE_CENTER_SIZE,
  3525. start_radians, end_radians, steps / 8,
  3526. color, outer_color);
  3527. }
  3528. ++i;
  3529. }
  3530. }
  3531. gGL.popUIMatrix();
  3532. }
  3533. //virtual
  3534. bool LLPieMenu::append(LLMenuItemGL* item)
  3535. {
  3536. item->setBriefItem(true);
  3537. item->setFont(LLFontGL::getFontSansSerifSmall());
  3538. return LLMenuGL::append(item);
  3539. }
  3540. //virtual
  3541. bool LLPieMenu::appendSeparator(const std::string&)
  3542. {
  3543. LLMenuItemGL* separator = new LLMenuItemBlankGL();
  3544. separator->setFont(LLFontGL::getFontSansSerifSmall());
  3545. return append(separator);
  3546. }
  3547. bool LLPieMenu::appendPieMenu(LLPieMenu* menu)
  3548. {
  3549. if (menu == this)
  3550. {
  3551. llerrs << "Cannot attach a pie menu to itself !" << llendl;
  3552. }
  3553. LLPieMenuBranch* item = new LLPieMenuBranch(menu->getName(),
  3554. menu->getLabel(), menu);
  3555. getParent()->addChild(item->getBranch());
  3556. item->setFont(LLFontGL::getFontSansSerifSmall());
  3557. return append(item);
  3558. }
  3559. //virtual
  3560. void LLPieMenu::arrange()
  3561. {
  3562. constexpr S32 rect_height = 190;
  3563. constexpr S32 rect_width = 190;
  3564. // All divide by 6
  3565. constexpr S32 CARD_X = 60;
  3566. constexpr S32 DIAG_X = 48;
  3567. constexpr S32 CARD_Y = 76;
  3568. constexpr S32 DIAG_Y = 42;
  3569. static const S32 ITEM_CENTER_X[] =
  3570. {
  3571. CARD_X, DIAG_X, 0, -DIAG_X,
  3572. -CARD_X, -DIAG_X, 0, DIAG_X
  3573. };
  3574. static const S32 ITEM_CENTER_Y[] =
  3575. {
  3576. 0, DIAG_Y, CARD_Y, DIAG_Y,
  3577. 0, -DIAG_Y, -CARD_Y, -DIAG_Y
  3578. };
  3579. // *TODO: Compute actual bounding rect for menu
  3580. LLRect rect;
  3581. // *HACK: casting away const. Should use setRect or some helper function
  3582. // instead.
  3583. const_cast<LLRect&>(getRect()).setOriginAndSize(getRect().mLeft,
  3584. getRect().mBottom,
  3585. rect_width, rect_height);
  3586. S32 font_height = 0;
  3587. if (mItems.size())
  3588. {
  3589. font_height = (*mItems.begin())->getNominalHeight();
  3590. }
  3591. // Place items around a circle, with item 0 at positive X, rotating
  3592. // counter-clockwise
  3593. S32 item_width = 0;
  3594. S32 i = 0;
  3595. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  3596. it != end; ++it)
  3597. {
  3598. LLMenuItemGL* item = *it;
  3599. item_width = item->getNominalWidth();
  3600. // Put in the right place around a circle centered at 0,0
  3601. rect.setCenterAndSize(ITEM_CENTER_X[i], ITEM_CENTER_Y[i],
  3602. item_width, font_height);
  3603. // Correct for the actual rectangle size
  3604. rect.translate(rect_width / 2, rect_height / 2);
  3605. item->setRect(rect);
  3606. // Make sure enablement is correct
  3607. item->buildDrawLabel();
  3608. ++i;
  3609. }
  3610. }
  3611. LLMenuItemGL* LLPieMenu::pieItemFromXY(S32 x, S32 y)
  3612. {
  3613. #if 0
  3614. // We might have shifted this menu on draw. If so, we need to shift over
  3615. // mouseup events until we get a hover event.
  3616. x += mShiftHoriz;
  3617. y += mShiftVert;
  3618. #endif
  3619. // An arc of the pie menu is 45 degrees
  3620. constexpr F32 ARC_DEG = 45.f;
  3621. S32 delta_x = x - getRect().getWidth() / 2;
  3622. S32 delta_y = y - getRect().getHeight() / 2;
  3623. // circle safe zone in the center
  3624. S32 dist_squared = delta_x * delta_x + delta_y * delta_y;
  3625. if (dist_squared < PIE_CENTER_SIZE * PIE_CENTER_SIZE)
  3626. {
  3627. return NULL;
  3628. }
  3629. // Infinite radius is only used with right clicks
  3630. S32 radius = llmax(getRect().getWidth() / 2, getRect().getHeight() / 2);
  3631. if (!(mUseInfiniteRadius && mRightMouseDown) &&
  3632. dist_squared > radius * radius)
  3633. {
  3634. return NULL;
  3635. }
  3636. F32 angle = RAD_TO_DEG * atan2f((F32)delta_y, (F32)delta_x);
  3637. // Rotate marks CCW so that east = [0, ARC_DEG) instead of
  3638. // [-ARC_DEG/2, ARC_DEG/2)
  3639. angle += ARC_DEG * 0.5f;
  3640. // Make sure we are only using positive angles
  3641. if (angle < 0.f) angle += 360.f;
  3642. S32 which = S32(angle / ARC_DEG);
  3643. if (which >= 0 && which < (S32)mItems.size())
  3644. {
  3645. for (item_list_t::iterator it = mItems.begin(), end = mItems.end();
  3646. it != end; ++it)
  3647. {
  3648. if (which == 0)
  3649. {
  3650. return *it;
  3651. }
  3652. --which;
  3653. }
  3654. }
  3655. return NULL;
  3656. }
  3657. S32 LLPieMenu::pieItemIndexFromXY(S32 x, S32 y)
  3658. {
  3659. // An arc of the pie menu is 45 degrees
  3660. constexpr F32 ARC_DEG = 45.f;
  3661. // Correct for non-square pixels
  3662. S32 delta_x = x - getRect().getWidth() / 2;
  3663. S32 delta_y = y - getRect().getHeight() / 2;
  3664. // Circle safe zone in the center
  3665. if (delta_x * delta_x + delta_y * delta_y <
  3666. PIE_CENTER_SIZE * PIE_CENTER_SIZE)
  3667. {
  3668. return -1;
  3669. }
  3670. F32 angle = RAD_TO_DEG * atan2f((F32)delta_y, (F32)delta_x);
  3671. // Rotate marks CCW so that east = [0, ARC_DEG) instead of
  3672. // [-ARC_DEG/2, ARC_DEG/2)
  3673. angle += ARC_DEG * 0.5f;
  3674. // Make sure we are only using positive angles
  3675. if (angle < 0.f)
  3676. {
  3677. angle += 360.f;
  3678. }
  3679. S32 which = S32(angle / ARC_DEG);
  3680. return which;
  3681. }
  3682. void LLPieMenu::show(S32 x, S32 y, bool mouse_down)
  3683. {
  3684. if (!sMenuContainer) return;
  3685. S32 width = getRect().getWidth();
  3686. S32 height = getRect().getHeight();
  3687. const LLRect menu_region_rect = sMenuContainer->getMenuRect();
  3688. LLView* parent_view = getParent();
  3689. S32 local_x, local_y;
  3690. parent_view->screenPointToLocal(x, y, &local_x, &local_y);
  3691. // *HACK: casting away const. Should use setRect or some helper function
  3692. // instead.
  3693. const_cast<LLRect&>(getRect()).setCenterAndSize(local_x, local_y,
  3694. width, height);
  3695. arrange();
  3696. bool moved = false;
  3697. // Adjust the pie rectangle to keep it on screen
  3698. if (getRect().mLeft < menu_region_rect.mLeft)
  3699. {
  3700. // *HACK: casting away const. Should use setRect or some helper
  3701. // function instead.
  3702. const_cast<LLRect&>(getRect()).translate(menu_region_rect.mLeft -
  3703. getRect().mLeft, 0);
  3704. moved = true;
  3705. }
  3706. if (getRect().mRight > menu_region_rect.mRight)
  3707. {
  3708. // *HACK: casting away const. Should use setRect or some helper
  3709. // function instead.
  3710. const_cast<LLRect&>(getRect()).translate(menu_region_rect.mRight -
  3711. getRect().mRight, 0);
  3712. moved = true;
  3713. }
  3714. if (getRect().mBottom < menu_region_rect.mBottom)
  3715. {
  3716. // *HACK: casting away const. Should use setRect or some helper
  3717. // function instead.
  3718. const_cast<LLRect&>(getRect()).translate(0, menu_region_rect.mBottom -
  3719. getRect().mBottom);
  3720. moved = true;
  3721. }
  3722. if (getRect().mTop > menu_region_rect.mTop)
  3723. {
  3724. // *HACK: casting away const. Should use setRect or some helper
  3725. // function instead.
  3726. const_cast<LLRect&>(getRect()).translate(0, menu_region_rect.mTop -
  3727. getRect().mTop);
  3728. moved = true;
  3729. }
  3730. // If we had to relocate the pie menu, put the cursor in the center of its
  3731. // rectangle
  3732. if (moved)
  3733. {
  3734. LLCoordGL center;
  3735. center.mX = (getRect().mLeft + getRect().mRight) / 2;
  3736. center.mY = (getRect().mTop + getRect().mBottom) / 2;
  3737. LLUI::setCursorPositionLocal(getParent(), center.mX, center.mY);
  3738. }
  3739. // *FIX: what happens when mouse buttons reversed?
  3740. mRightMouseDown = mouse_down;
  3741. mFirstMouseDown = mouse_down;
  3742. mUseInfiniteRadius = true;
  3743. mHoveredAnyItem = false;
  3744. if (!mFirstMouseDown)
  3745. {
  3746. make_ui_sound("UISndPieMenuAppear");
  3747. }
  3748. LLView::setVisible(true);
  3749. // We want all mouse events in case user does quick right click again off
  3750. // of pie menu rectangle, to support gestural menu traversal
  3751. gFocusMgr.setMouseCapture(this);
  3752. if (mouse_down)
  3753. {
  3754. mShrinkBorderTimer.stop();
  3755. }
  3756. else
  3757. {
  3758. mShrinkBorderTimer.start();
  3759. }
  3760. }
  3761. void LLPieMenu::hide(bool item_selected)
  3762. {
  3763. if (!getVisible()) return;
  3764. if (mHoverItem)
  3765. {
  3766. mHoverItem->setHighlight(false);
  3767. mHoverItem = NULL;
  3768. }
  3769. make_ui_sound("UISndPieMenuHide");
  3770. mFirstMouseDown = false;
  3771. mRightMouseDown = false;
  3772. mUseInfiniteRadius = false;
  3773. mHoveredAnyItem = false;
  3774. LLView::setVisible(false);
  3775. gFocusMgr.setMouseCapture(NULL);
  3776. }
  3777. //============================================================================
  3778. // Class LLMenuBarGL
  3779. //============================================================================
  3780. static LLRegisterWidget<LLMenuBarGL> r09(LL_MENU_BAR_GL_TAG);
  3781. // Default constructor
  3782. LLMenuBarGL::LLMenuBarGL(const std::string& name)
  3783. : LLMenuGL(name, name)
  3784. {
  3785. mHorizontalLayout = true;
  3786. setCanTearOff(false);
  3787. mKeepFixedSize = true;
  3788. mAltKeyTrigger = false;
  3789. }
  3790. LLMenuBarGL::~LLMenuBarGL()
  3791. {
  3792. std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer());
  3793. mAccelerators.clear();
  3794. }
  3795. //virtual
  3796. const std::string& LLMenuBarGL::getTag() const
  3797. {
  3798. return LL_MENU_BAR_GL_TAG;
  3799. }
  3800. //virtual
  3801. LLXMLNodePtr LLMenuBarGL::getXML(bool save_children) const
  3802. {
  3803. // Sorty of hacky: reparent items to this and then back at the end of the
  3804. // export
  3805. LLView* orig_parent = NULL;
  3806. item_list_t::const_iterator it;
  3807. for (it = mItems.begin(); it != mItems.end(); ++it)
  3808. {
  3809. LLMenuItemGL* child = *it;
  3810. LLMenuItemBranchGL* branch = (LLMenuItemBranchGL*)child;
  3811. LLMenuGL* menu = branch->getBranch();
  3812. orig_parent = menu->getParent();
  3813. menu->updateParent((LLView*)this);
  3814. }
  3815. LLXMLNodePtr node = LLMenuGL::getXML();
  3816. node->setName(LL_MENU_BAR_GL_TAG);
  3817. for (it = mItems.begin(); it != mItems.end(); ++it)
  3818. {
  3819. LLMenuItemGL* child = *it;
  3820. LLMenuItemBranchGL* branch = (LLMenuItemBranchGL*)child;
  3821. LLMenuGL* menu = branch->getBranch();
  3822. menu->updateParent(orig_parent);
  3823. }
  3824. return node;
  3825. }
  3826. LLView* LLMenuBarGL::fromXML(LLXMLNodePtr node, LLView* parent,
  3827. LLUICtrlFactory* factory)
  3828. {
  3829. std::string name = LL_MENU_BAR_GL_TAG;
  3830. node->getAttributeString("name", name);
  3831. bool opaque = false;
  3832. node->getAttributeBool("opaque", opaque);
  3833. LLMenuBarGL* menubar = new LLMenuBarGL(name);
  3834. LLHandle<LLFloater> parent_handle;
  3835. LLFloater* floaterp = parent->asFloater();
  3836. if (floaterp)
  3837. {
  3838. parent_handle = floaterp->getHandle();
  3839. }
  3840. // We need to have the rect early so that it is around when building the
  3841. // menu items
  3842. LLRect view_rect;
  3843. createRect(node, view_rect, parent, menubar->getRequiredRect());
  3844. menubar->setRect(view_rect);
  3845. if (node->hasAttribute("drop_shadow"))
  3846. {
  3847. bool drop_shadow = false;
  3848. node->getAttributeBool("drop_shadow", drop_shadow);
  3849. menubar->setDropShadowed(drop_shadow);
  3850. }
  3851. menubar->setBackgroundVisible(opaque);
  3852. LLColor4 color(0, 0, 0, 0);
  3853. if (opaque && LLUICtrlFactory::getAttributeColor(node,"color", color))
  3854. {
  3855. menubar->setBackgroundColor(color);
  3856. }
  3857. LLXMLNodePtr child;
  3858. for (child = node->getFirstChild(); child.notNull();
  3859. child = child->getNextSibling())
  3860. {
  3861. if (child->hasName("menu"))
  3862. {
  3863. LLMenuGL* menu =
  3864. (LLMenuGL*)LLMenuGL::fromXML(child, parent, factory);
  3865. // Because of lazy initialization, have to disable tear off
  3866. // functionality and then re-enable with proper parent handle
  3867. if (menu->getCanTearOff())
  3868. {
  3869. menu->setCanTearOff(false);
  3870. menu->setCanTearOff(true, parent_handle);
  3871. }
  3872. menubar->appendMenu(menu);
  3873. if (sMenuContainer)
  3874. {
  3875. menu->updateParent(sMenuContainer);
  3876. }
  3877. else
  3878. {
  3879. menu->updateParent(parent);
  3880. }
  3881. }
  3882. }
  3883. menubar->initFromXML(node, parent);
  3884. bool create_jump_keys = false;
  3885. node->getAttributeBool("create_jump_keys", create_jump_keys);
  3886. if (create_jump_keys)
  3887. {
  3888. menubar->createJumpKeys();
  3889. }
  3890. return menubar;
  3891. }
  3892. bool LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)
  3893. {
  3894. bool has_higlight = getHighlightedItem() != NULL;
  3895. if (has_higlight && mask == MASK_NONE)
  3896. {
  3897. // Unmodified key accelerators are ignored when navigating menu (but
  3898. // are used as jump keys so will still work when appropriate menu is
  3899. // up)
  3900. return false;
  3901. }
  3902. bool result = LLMenuGL::handleAcceleratorKey(key, mask);
  3903. if (result && mask & MASK_ALT)
  3904. {
  3905. // ALT key used to trigger hotkey, do not use as shortcut to open menu
  3906. mAltKeyTrigger = false;
  3907. }
  3908. #if 1
  3909. if (result && has_higlight && sMenuContainer &&
  3910. sMenuContainer->hasVisibleMenu())
  3911. {
  3912. // Close menus originating from other menu bars
  3913. sMenuContainer->hideMenus();
  3914. }
  3915. #endif
  3916. return result;
  3917. }
  3918. bool LLMenuBarGL::handleKeyHere(KEY key, MASK mask)
  3919. {
  3920. if (key == KEY_ALT && gKeyboardp && !gKeyboardp->getKeyRepeated(key) &&
  3921. LLUI::sUseAltKeyForMenus)
  3922. {
  3923. mAltKeyTrigger = true;
  3924. }
  3925. else // if any key other than ALT hit, clear out waiting for Alt key mode
  3926. {
  3927. mAltKeyTrigger = false;
  3928. }
  3929. if (key == KEY_ESCAPE && mask == MASK_NONE)
  3930. {
  3931. LLMenuGL::setKeyboardMode(false);
  3932. // If any menus are visible, this will return true, stopping further
  3933. // processing of ESCAPE key
  3934. return sMenuContainer && sMenuContainer->hideMenus();
  3935. }
  3936. // Before processing any other key, check to see if ALT key has triggered
  3937. // menu access
  3938. checkMenuTrigger();
  3939. return LLMenuGL::handleKeyHere(key, mask);
  3940. }
  3941. bool LLMenuBarGL::handleJumpKey(KEY key)
  3942. {
  3943. // Perform case-insensitive comparison
  3944. key = toupper(key);
  3945. navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
  3946. if (found_it != mJumpKeys.end() && found_it->second->getEnabled())
  3947. {
  3948. // Switch to keyboard navigation mode
  3949. LLMenuGL::setKeyboardMode(true);
  3950. found_it->second->setHighlight(true);
  3951. found_it->second->doIt();
  3952. }
  3953. return true;
  3954. }
  3955. bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask)
  3956. {
  3957. // Clicks on menu bar closes existing menus from other contexts but leave
  3958. // own menu open so that we get toggle behavior
  3959. if ((!getHighlightedItem() || !getHighlightedItem()->isActive()) &&
  3960. sMenuContainer)
  3961. {
  3962. sMenuContainer->hideMenus();
  3963. }
  3964. return LLMenuGL::handleMouseDown(x, y, mask);
  3965. }
  3966. bool LLMenuBarGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
  3967. {
  3968. // Clicks on menu bar closes existing menus from other contexts but leave
  3969. // own menu open so that we get toggle behavior
  3970. if ((!getHighlightedItem() || !getHighlightedItem()->isActive()) &&
  3971. sMenuContainer)
  3972. {
  3973. sMenuContainer->hideMenus();
  3974. }
  3975. return LLMenuGL::handleMouseDown(x, y, mask);
  3976. }
  3977. void LLMenuBarGL::draw()
  3978. {
  3979. LLMenuItemGL* itemp = getHighlightedItem();
  3980. // If we are in mouse-control mode and the mouse cursor is not hovering
  3981. // over the current highlighted menu item and it is not open, then remove
  3982. // the highlight. This is done via a polling mechanism here, as we do not
  3983. // receive notifications when the mouse cursor moves off of us
  3984. if (itemp && !itemp->isOpen() && !itemp->getHover() &&
  3985. !LLMenuGL::getKeyboardMode())
  3986. {
  3987. clearHoverItem();
  3988. }
  3989. checkMenuTrigger();
  3990. LLMenuGL::draw();
  3991. }
  3992. void LLMenuBarGL::checkMenuTrigger()
  3993. {
  3994. // Has the ALT key been pressed and subsequently released ?
  3995. if (mAltKeyTrigger && gKeyboardp && !gKeyboardp->getKeyDown(KEY_ALT))
  3996. {
  3997. // If alt key was released quickly, treat it as a menu access key
  3998. // otherwise it was probably an Alt-zoom or similar action
  3999. if (gKeyboardp->getKeyElapsedFrameCount(KEY_ALT) < 2 ||
  4000. gKeyboardp->getKeyElapsedTime(KEY_ALT) <= LLUI::sMenuAccessKeyTime)
  4001. {
  4002. if (getHighlightedItem())
  4003. {
  4004. clearHoverItem();
  4005. }
  4006. else if (sMenuContainer)
  4007. {
  4008. // Close menus originating from other menu bars
  4009. sMenuContainer->hideMenus();
  4010. highlightNextItem(NULL);
  4011. LLMenuGL::setKeyboardMode(true);
  4012. }
  4013. }
  4014. mAltKeyTrigger = false;
  4015. }
  4016. }
  4017. bool LLMenuBarGL::jumpKeysActive()
  4018. {
  4019. // Require user to be in keyboard navigation mode to activate key triggers
  4020. // as menu bars are always visible and it is easy to leave the mouse cursor
  4021. // over them
  4022. return LLMenuGL::getKeyboardMode() && getHighlightedItem() &&
  4023. LLMenuGL::jumpKeysActive();
  4024. }
  4025. // Rearranges the child rects so they fit the shape of the menu bar.
  4026. void LLMenuBarGL::arrange()
  4027. {
  4028. U32 pos = 0;
  4029. LLRect rect(0, getRect().getHeight(), 0, 0);
  4030. for (item_list_t::const_iterator it = mItems.begin(), end = mItems.end();
  4031. it != end; ++it)
  4032. {
  4033. LLMenuItemGL* item = *it;
  4034. if (item->getVisible())
  4035. {
  4036. rect.mLeft = pos;
  4037. pos += item->getNominalWidth();
  4038. rect.mRight = pos;
  4039. item->setRect(rect);
  4040. item->buildDrawLabel();
  4041. }
  4042. }
  4043. reshape(rect.mRight, rect.getHeight());
  4044. }
  4045. S32 LLMenuBarGL::getRightmostMenuEdge()
  4046. {
  4047. // Find the last visible menu
  4048. for (item_list_t::reverse_iterator rit = mItems.rbegin(),
  4049. rend = mItems.rend();
  4050. rit != rend; ++rit)
  4051. {
  4052. if ((*rit)->getVisible())
  4053. {
  4054. return (*rit)->getRect().mRight;
  4055. }
  4056. }
  4057. return 0;
  4058. }
  4059. // Adds a vertical separator to this menu
  4060. bool LLMenuBarGL::appendSeparator(const std::string& separator_name)
  4061. {
  4062. LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL();
  4063. return append(separator);
  4064. }
  4065. // Adds a menu: this will create a drop down menu.
  4066. bool LLMenuBarGL::appendMenu(LLMenuGL* menu)
  4067. {
  4068. if (menu == this)
  4069. {
  4070. llerrs << "** Attempt to attach menu to itself. This is certainly "
  4071. << "a logic error." << llendl;
  4072. }
  4073. LLMenuItemBranchGL* branch = new LLMenuItemBranchDownGL(menu->getName(),
  4074. menu->getLabel(),
  4075. menu->getHandle());
  4076. bool success = branch->addToAcceleratorList(&mAccelerators);
  4077. success &= append(branch);
  4078. branch->setJumpKey(branch->getJumpKey());
  4079. return success;
  4080. }
  4081. bool LLMenuBarGL::handleHover(S32 x, S32 y, MASK mask)
  4082. {
  4083. bool handled = false;
  4084. LLView* active_menu = NULL;
  4085. bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
  4086. S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
  4087. S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
  4088. mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2);
  4089. mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2);
  4090. mLastMouseX = x;
  4091. mLastMouseY = y;
  4092. // If nothing currently selected or mouse has moved since last call, pick
  4093. // menu item via mouse otherwise let keyboard control it
  4094. if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() ||
  4095. abs(mMouseVelX) > 0 || abs(mMouseVelY) > 0)
  4096. {
  4097. // Find current active menu
  4098. for (child_list_const_iter_t child_it = getChildList()->begin();
  4099. child_it != getChildList()->end(); ++child_it)
  4100. {
  4101. LLView* viewp = *child_it;
  4102. if (((LLMenuItemGL*)viewp)->isOpen())
  4103. {
  4104. active_menu = viewp;
  4105. }
  4106. }
  4107. // Check for new active menu
  4108. for (child_list_const_iter_t child_it = getChildList()->begin();
  4109. child_it != getChildList()->end(); ++child_it)
  4110. {
  4111. LLView* viewp = *child_it;
  4112. S32 local_x = x - viewp->getRect().mLeft;
  4113. S32 local_y = y - viewp->getRect().mBottom;
  4114. if (viewp->getVisible() && viewp->getEnabled() &&
  4115. viewp->pointInView(local_x, local_y) &&
  4116. viewp->handleHover(local_x, local_y, mask))
  4117. {
  4118. ((LLMenuItemGL*)viewp)->setHighlight(true);
  4119. handled = true;
  4120. if (active_menu && active_menu != viewp)
  4121. {
  4122. ((LLMenuItemGL*)viewp)->doIt();
  4123. }
  4124. LLMenuGL::setKeyboardMode(false);
  4125. }
  4126. }
  4127. if (handled)
  4128. {
  4129. // Set hover false on inactive menus
  4130. for (child_list_const_iter_t child_it = getChildList()->begin();
  4131. child_it != getChildList()->end(); ++child_it)
  4132. {
  4133. LLView* viewp = *child_it;
  4134. S32 local_x = x - viewp->getRect().mLeft;
  4135. S32 local_y = y - viewp->getRect().mBottom;
  4136. if (!viewp->pointInView(local_x, local_y) &&
  4137. ((LLMenuItemGL*)viewp)->getHighlight())
  4138. {
  4139. ((LLMenuItemGL*)viewp)->setHighlight(false);
  4140. }
  4141. }
  4142. }
  4143. }
  4144. gWindowp->setCursor(UI_CURSOR_ARROW);
  4145. return true;
  4146. }
  4147. //============================================================================
  4148. // Class LLMenuHolderGL
  4149. //============================================================================
  4150. LLMenuHolderGL::LLMenuHolderGL()
  4151. : LLPanel("Menu Holder")
  4152. {
  4153. setMouseOpaque(false);
  4154. sItemActivationTimer.stop();
  4155. mCanHide = true;
  4156. }
  4157. LLMenuHolderGL::LLMenuHolderGL(const std::string& name, const LLRect& rect,
  4158. bool mouse_opaque, U32 follows)
  4159. : LLPanel(name, rect, false)
  4160. {
  4161. setMouseOpaque(mouse_opaque);
  4162. sItemActivationTimer.stop();
  4163. mCanHide = true;
  4164. }
  4165. void LLMenuHolderGL::draw()
  4166. {
  4167. LLView::draw();
  4168. // Now draw last selected item as overlay
  4169. LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get();
  4170. if (selecteditem && sItemActivationTimer.getStarted() &&
  4171. sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
  4172. {
  4173. // Make sure toggle items, for example, show the proper state when
  4174. // fading out
  4175. selecteditem->buildDrawLabel();
  4176. LLRect item_rect;
  4177. selecteditem->localRectToOtherView(selecteditem->getLocalRect(),
  4178. &item_rect, this);
  4179. F32 interpolant = sItemActivationTimer.getElapsedTimeF32() /
  4180. ACTIVATE_HIGHLIGHT_TIME;
  4181. F32 alpha = lerp(LLMenuItemGL::getHighlightBGColor().mV[VALPHA],
  4182. 0.f, interpolant);
  4183. LLColor4 bg_color(LLMenuItemGL::getHighlightBGColor().mV[VRED],
  4184. LLMenuItemGL::getHighlightBGColor().mV[VGREEN],
  4185. LLMenuItemGL::getHighlightBGColor().mV[VBLUE],
  4186. alpha);
  4187. LLUI::pushMatrix();
  4188. LLMenuGL* menup = selecteditem->getMenu();
  4189. if (menup)
  4190. {
  4191. LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom, 0.f);
  4192. menup->drawBackground(selecteditem, bg_color);
  4193. selecteditem->draw();
  4194. }
  4195. LLUI::popMatrix();
  4196. }
  4197. }
  4198. bool LLMenuHolderGL::handleMouseDown(S32 x, S32 y, MASK mask)
  4199. {
  4200. bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
  4201. if (!handled)
  4202. {
  4203. // Clicked off of menu, hide them all
  4204. hideMenus();
  4205. }
  4206. return handled;
  4207. }
  4208. bool LLMenuHolderGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
  4209. {
  4210. bool handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL;
  4211. if (!handled)
  4212. {
  4213. // Clicked off of menu, hide them all
  4214. hideMenus();
  4215. }
  4216. return handled;
  4217. }
  4218. void LLMenuHolderGL::reshape(S32 width, S32 height, bool called_from_parent)
  4219. {
  4220. if (width != getRect().getWidth() || height != getRect().getHeight())
  4221. {
  4222. hideMenus();
  4223. }
  4224. LLView::reshape(width, height, called_from_parent);
  4225. }
  4226. bool LLMenuHolderGL::hasVisibleMenu() const
  4227. {
  4228. for (child_list_const_iter_t child_it = getChildList()->begin();
  4229. child_it != getChildList()->end(); ++child_it)
  4230. {
  4231. LLView* viewp = *child_it;
  4232. if (viewp->getVisible() && dynamic_cast<LLMenuBarGL*>(viewp) == NULL)
  4233. {
  4234. return true;
  4235. }
  4236. }
  4237. return false;
  4238. }
  4239. bool LLMenuHolderGL::hideMenus()
  4240. {
  4241. if (!mCanHide)
  4242. {
  4243. return false;
  4244. }
  4245. bool menu_visible = hasVisibleMenu();
  4246. if (menu_visible)
  4247. {
  4248. LLMenuGL::setKeyboardMode(false);
  4249. // Clicked off of menu, hide them all
  4250. for (child_list_const_iter_t child_it = getChildList()->begin();
  4251. child_it != getChildList()->end(); ++child_it)
  4252. {
  4253. LLView* viewp = *child_it;
  4254. // Clicks off of menu do not hide menu bar
  4255. if (viewp && viewp->getVisible() &&
  4256. !dynamic_cast<LLMenuBarGL*>(viewp))
  4257. {
  4258. viewp->setVisible(false);
  4259. }
  4260. }
  4261. }
  4262. #if 0
  4263. if (gFocusMgr.childHasKeyboardFocus(this))
  4264. {
  4265. gFocusMgr.setKeyboardFocus(NULL);
  4266. }
  4267. #endif
  4268. return menu_visible;
  4269. }
  4270. void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
  4271. {
  4272. sItemLastSelectedHandle = item->getHandle();
  4273. sItemActivationTimer.start();
  4274. }
  4275. //============================================================================
  4276. // Class LLTearOffMenu
  4277. //============================================================================
  4278. LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup)
  4279. : LLFloater(menup->getName(), LLRect(0, 100, 100, 0), menup->getLabel(),
  4280. false, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, false, false)
  4281. {
  4282. // Flag menu as being torn off
  4283. menup->setTornOff(true);
  4284. // Update menu layout as torn off menu (no spillover menus)
  4285. menup->arrange();
  4286. LLRect rect;
  4287. menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(),
  4288. menup->getRect().getWidth() + 3, 0),
  4289. &rect, gFloaterViewp);
  4290. // Make sure this floater is big enough for menu
  4291. mTargetHeight = (F32)(rect.getHeight() + LLFLOATER_HEADER_SIZE + 5);
  4292. reshape(rect.getWidth(), rect.getHeight());
  4293. setRect(rect);
  4294. // Attach menu to floater
  4295. menup->setFollowsAll();
  4296. mOldParent = menup->getParent();
  4297. addChild(menup);
  4298. menup->setVisible(true);
  4299. menup->translate(-menup->getRect().mLeft + 1,
  4300. -menup->getRect().mBottom + 1);
  4301. menup->setDropShadowed(false);
  4302. mMenu = menup;
  4303. // Highlight first item (tear off item will be disabled)
  4304. mMenu->highlightNextItem(NULL);
  4305. }
  4306. void LLTearOffMenu::draw()
  4307. {
  4308. mMenu->setBackgroundVisible(isBackgroundOpaque());
  4309. mMenu->arrange();
  4310. if (getRect().getHeight() != mTargetHeight)
  4311. {
  4312. // Animate towards target height
  4313. reshape(getRect().getWidth(),
  4314. llceil(lerp((F32)getRect().getHeight(), mTargetHeight,
  4315. LLCriticalDamp::getInterpolant(0.05f))));
  4316. }
  4317. else
  4318. {
  4319. // When in stasis, remain big enough to hold menu contents
  4320. mTargetHeight = (F32)(mMenu->getRect().getHeight() +
  4321. LLFLOATER_HEADER_SIZE + 4);
  4322. reshape(mMenu->getRect().getWidth() + 3,
  4323. mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 5);
  4324. }
  4325. LLFloater::draw();
  4326. }
  4327. void LLTearOffMenu::onFocusReceived()
  4328. {
  4329. // If nothing is highlighted, just highlight first item
  4330. if (!mMenu->getHighlightedItem())
  4331. {
  4332. mMenu->highlightNextItem(NULL);
  4333. }
  4334. // Parent menu items get highlights so navigation logic keeps working
  4335. LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem();
  4336. while (parent_menu_item)
  4337. {
  4338. LLMenuGL* menup = parent_menu_item->getMenu();
  4339. if (!menup || !menup->getVisible())
  4340. {
  4341. break;
  4342. }
  4343. parent_menu_item->setHighlight(true);
  4344. parent_menu_item = menup->getParentMenuItem();
  4345. }
  4346. LLFloater::onFocusReceived();
  4347. }
  4348. void LLTearOffMenu::onFocusLost()
  4349. {
  4350. // Remove highlight from parent item and our own menu
  4351. mMenu->clearHoverItem();
  4352. LLFloater::onFocusLost();
  4353. }
  4354. bool LLTearOffMenu::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
  4355. {
  4356. // Pass keystrokes down to menu
  4357. return mMenu->handleUnicodeChar(uni_char, true);
  4358. }
  4359. bool LLTearOffMenu::handleKeyHere(KEY key, MASK mask)
  4360. {
  4361. if (!mMenu->getHighlightedItem())
  4362. {
  4363. if (key == KEY_UP)
  4364. {
  4365. mMenu->highlightPrevItem(NULL);
  4366. return true;
  4367. }
  4368. else if (key == KEY_DOWN)
  4369. {
  4370. mMenu->highlightNextItem(NULL);
  4371. return true;
  4372. }
  4373. }
  4374. // Pass keystrokes down to menu
  4375. return mMenu->handleKey(key, mask, true);
  4376. }
  4377. void LLTearOffMenu::translate(S32 x, S32 y)
  4378. {
  4379. if (x != 0 && y != 0)
  4380. {
  4381. // Hide open sub-menus by clearing current hover item
  4382. mMenu->clearHoverItem();
  4383. }
  4384. LLFloater::translate(x, y);
  4385. }
  4386. //static
  4387. LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)
  4388. {
  4389. LLTearOffMenu* tearoffp = new LLTearOffMenu(menup);
  4390. // Keep onscreen
  4391. gFloaterViewp->adjustToFitScreen(tearoffp);
  4392. tearoffp->open();
  4393. return tearoffp;
  4394. }
  4395. void LLTearOffMenu::onClose(bool app_quitting)
  4396. {
  4397. removeChild(mMenu);
  4398. mOldParent->addChild(mMenu);
  4399. mMenu->clearHoverItem();
  4400. mMenu->setFollowsNone();
  4401. mMenu->setBackgroundVisible(true);
  4402. mMenu->setVisible(false);
  4403. mMenu->setTornOff(false);
  4404. mMenu->setDropShadowed(true);
  4405. destroy();
  4406. }