TB2Item.pas 228 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129
  1. { MP }
  2. unit TB2Item;
  3. {
  4. Toolbar2000
  5. Copyright (C) 1998-2005 by Jordan Russell
  6. All rights reserved.
  7. The contents of this file are subject to the "Toolbar2000 License"; you may
  8. not use or distribute this file except in compliance with the
  9. "Toolbar2000 License". A copy of the "Toolbar2000 License" may be found in
  10. TB2k-LICENSE.txt or at:
  11. http://www.jrsoftware.org/files/tb2k/TB2k-LICENSE.txt
  12. Alternatively, the contents of this file may be used under the terms of the
  13. GNU General Public License (the "GPL"), in which case the provisions of the
  14. GPL are applicable instead of those in the "Toolbar2000 License". A copy of
  15. the GPL may be found in GPL-LICENSE.txt or at:
  16. http://www.jrsoftware.org/files/tb2k/GPL-LICENSE.txt
  17. If you wish to allow use of your version of this file only under the terms of
  18. the GPL and not to allow others to use your version of this file under the
  19. "Toolbar2000 License", indicate your decision by deleting the provisions
  20. above and replace them with the notice and other provisions required by the
  21. GPL. If you do not delete the provisions above, a recipient may use your
  22. version of this file under either the "Toolbar2000 License" or the GPL.
  23. $jrsoftware: tb2k/Source/TB2Item.pas,v 1.277 2005/06/23 21:55:44 jr Exp $
  24. }
  25. interface
  26. {$I TB2Ver.inc}
  27. {x$DEFINE TB2K_NO_ANIMATION}
  28. { Enabling the above define disables all menu animation. For debugging
  29. purpose only. }
  30. {x$DEFINE TB2K_USE_STRICT_O2K_MENU_STYLE}
  31. { Enabling the above define forces it to use clBtnFace for the menu color
  32. instead of clMenu, and disables the use of flat menu borders on Windows
  33. XP with themes enabled. }
  34. uses
  35. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  36. StdCtrls, CommCtrl, Menus, ActnList, ImgList, TB2Anim, UITypes;
  37. const
  38. WM_TB2K_POPUPSHOWING = WM_USER + 554;
  39. { Parameter in LParam of WM_TB2K_POPUPSHOWING }
  40. TPS_ANIMSTART = 1; // animation query: if Result <> 0, do not animate!
  41. TPS_ANIMFINISHED = 2; // only fired when animation thread is done
  42. TPS_NOANIM = 3; // fired when animation is done, or if showing with no animation
  43. type
  44. TTBCustomItem = class;
  45. TTBCustomItemClass = class of TTBCustomItem;
  46. TTBCustomItemActionLink = class;
  47. TTBCustomItemActionLinkClass = class of TTBCustomItemActionLink;
  48. TTBItemViewer = class;
  49. TTBItemViewerClass = class of TTBItemViewer;
  50. TTBPopupWindow = class;
  51. TTBPopupWindowClass = class of TTBPopupWindow;
  52. TTBView = class;
  53. TTBDoneAction = (tbdaNone, tbdaCancel, tbdaClickItem, tbdaOpenSystemMenu,
  54. tbdaHelpContext { MP }, tbdaHelpKeyword { /MP });
  55. PTBDoneActionData = ^TTBDoneActionData;
  56. TTBDoneActionData = record
  57. DoneAction: TTBDoneAction;
  58. case TTBDoneAction of
  59. tbdaClickItem: (ClickItem: TTBCustomItem; Sound: Boolean);
  60. tbdaOpenSystemMenu: (Wnd: HWND; Key: Cardinal);
  61. tbdaHelpContext: (ContextID: Integer);
  62. { MP }
  63. tbdaHelpKeyword: (HelpKeyword: String[100]);
  64. end;
  65. TTBInsertItemProc = procedure(AParent: TComponent; AItem: TTBCustomItem) of object;
  66. TTBItemChangedAction = (tbicInserted, tbicDeleting, tbicSubitemsChanged,
  67. tbicSubitemsBeginUpdate, tbicSubitemsEndUpdate, tbicInvalidate,
  68. tbicInvalidateAndResize, tbicRecreateItemViewers, tbicNameChanged,
  69. tbicSubMenuImagesChanged);
  70. TTBItemChangedProc = procedure(Sender: TTBCustomItem; Relayed: Boolean;
  71. Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem) of object;
  72. TTBItemData = record
  73. Item: TTBCustomItem;
  74. end;
  75. PTBItemDataArray = ^TTBItemDataArray;
  76. TTBItemDataArray = array[0..$7FFFFFFF div SizeOf(TTBItemData)-1] of TTBItemData;
  77. TTBItemDisplayMode = (nbdmDefault, nbdmTextOnly, nbdmTextOnlyInMenus, nbdmImageAndText);
  78. TTBItemOption = (tboDefault, tboDropdownArrow, tboImageAboveCaption,
  79. tboLongHintInMenuOnly, tboNoAutoHint, tboNoRotation, tboSameWidth,
  80. tboShowHint, tboToolbarStyle, tboToolbarSize);
  81. TTBItemOptions = set of TTBItemOption;
  82. TTBItemStyle = set of (tbisSubmenu, tbisSelectable, tbisSeparator,
  83. tbisEmbeddedGroup, tbisClicksTransparent, tbisCombo, tbisNoAutoOpen,
  84. tbisSubitemsEditable, tbisNoLineBreak, tbisRightAlign, tbisDontSelectFirst,
  85. tbisRedrawOnSelChange, tbisRedrawOnMouseOverChange, tbisStretch);
  86. TTBPopupAlignment = (tbpaLeft, tbpaRight, tbpaCenter);
  87. TTBPopupEvent = procedure(Sender: TTBCustomItem; FromLink: Boolean) of object;
  88. TTBSelectEvent = procedure(Sender: TTBCustomItem; Viewer: TTBItemViewer;
  89. Selecting: Boolean) of object;
  90. ETBItemError = class(Exception);
  91. TTBImageChangeLink = class(TChangeLink)
  92. private
  93. FLastWidth, FLastHeight: Integer;
  94. end;
  95. TTBPopupPositionRec = record
  96. PositionAsSubmenu: Boolean;
  97. Alignment: TTBPopupAlignment;
  98. Opposite: Boolean;
  99. MonitorRect: TRect;
  100. ParentItemRect: TRect;
  101. NCSizeX: Integer;
  102. NCSizeY: Integer;
  103. X, Y, W, H: Integer;
  104. AnimDir: TTBAnimationDirection;
  105. PlaySound: Boolean;
  106. end;
  107. TTBCustomItem = class(TComponent)
  108. private
  109. FActionLink: TTBCustomItemActionLink;
  110. FAutoCheck: Boolean;
  111. FCaption: String;
  112. FChecked: Boolean;
  113. FDisplayMode: TTBItemDisplayMode;
  114. FEnabled: Boolean;
  115. FEffectiveOptions: TTBItemOptions;
  116. FGroupIndex: Integer;
  117. FHelpContext: THelpContext;
  118. { MP }
  119. FHelpKeyword: String;
  120. FHint: String;
  121. FImageIndex: TImageIndex;
  122. FImages: TCustomImageList;
  123. FImagesChangeLink: TTBImageChangeLink;
  124. FItems: PTBItemDataArray;
  125. FItemCount: Integer;
  126. FItemStyle: TTBItemStyle;
  127. FLinkParents: TList;
  128. FMaskOptions: TTBItemOptions;
  129. FOptions: TTBItemOptions;
  130. FInheritOptions: Boolean;
  131. FNotifyList: TList;
  132. FOnClick: TNotifyEvent;
  133. FOnPopup: TTBPopupEvent;
  134. FOnSelect: TTBSelectEvent;
  135. FParent: TTBCustomItem;
  136. FParentComponent: TComponent;
  137. FRadioItem: Boolean;
  138. FShortCut: TShortCut;
  139. FSubMenuImages: TCustomImageList;
  140. FSubMenuImagesChangeLink: TTBImageChangeLink;
  141. FLinkSubitems: TTBCustomItem;
  142. FVisible: Boolean;
  143. procedure DoActionChange(Sender: TObject);
  144. function ChangeImages(var AImages: TCustomImageList;
  145. const Value: TCustomImageList; var AChangeLink: TTBImageChangeLink): Boolean;
  146. class procedure ClickWndProc(var Message: TMessage);
  147. function FindItemWithShortCut(AShortCut: TShortCut;
  148. var ATopmostParent: TTBCustomItem): TTBCustomItem;
  149. function FixOptions(const AOptions: TTBItemOptions): TTBItemOptions;
  150. function GetAction: TBasicAction;
  151. function GetItem(Index: Integer): TTBCustomItem;
  152. procedure ImageListChangeHandler(Sender: TObject);
  153. procedure InternalNotify(Ancestor: TTBCustomItem; NestingLevel: Integer;
  154. Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem);
  155. function IsAutoCheckStored: Boolean;
  156. function IsCaptionStored: Boolean;
  157. function IsCheckedStored: Boolean;
  158. function IsEnabledStored: Boolean;
  159. function IsHelpContextStored: Boolean;
  160. function IsHintStored: Boolean;
  161. function IsImageIndexStored: Boolean;
  162. function IsOnClickStored: Boolean;
  163. function IsShortCutStored: Boolean;
  164. function IsVisibleStored: Boolean;
  165. procedure Notify(Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem);
  166. procedure RefreshOptions;
  167. procedure SetAction(Value: TBasicAction);
  168. procedure SetCaption(Value: String);
  169. procedure SetChecked(Value: Boolean);
  170. procedure SetDisplayMode(Value: TTBItemDisplayMode);
  171. procedure SetEnabled(Value: Boolean);
  172. procedure SetGroupIndex(Value: Integer);
  173. procedure SetImageIndex(Value: TImageIndex);
  174. procedure SetImages(Value: TCustomImageList);
  175. procedure SetInheritOptions(Value: Boolean);
  176. procedure SetLinkSubitems(Value: TTBCustomItem);
  177. procedure SetMaskOptions(Value: TTBItemOptions);
  178. procedure SetOptions(Value: TTBItemOptions);
  179. procedure SetRadioItem(Value: Boolean);
  180. procedure SetSubMenuImages(Value: TCustomImageList);
  181. procedure SetVisible(Value: Boolean);
  182. procedure SubMenuImagesChanged;
  183. procedure TurnSiblingsOff;
  184. protected
  185. procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); dynamic;
  186. procedure Change(NeedResize: Boolean); virtual;
  187. function CreatePopup(const ParentView: TTBView; const ParentViewer: TTBItemViewer;
  188. const PositionAsSubmenu, SelectFirstItem, Customizing: Boolean;
  189. const APopupPoint: TPoint; const Alignment: TTBPopupAlignment): TTBPopupWindow; virtual;
  190. procedure DoPopup(Sender: TTBCustomItem; FromLink: Boolean); virtual;
  191. procedure EnabledChanged; virtual;
  192. function GetActionLinkClass: TTBCustomItemActionLinkClass; dynamic;
  193. function GetChevronParentView: TTBView; virtual;
  194. procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  195. function GetItemViewerClass(AView: TTBView): TTBItemViewerClass; virtual;
  196. procedure GetPopupPosition(ParentView: TTBView;
  197. PopupWindow: TTBPopupWindow; var PopupPositionRec: TTBPopupPositionRec); virtual;
  198. function GetPopupWindowClass: TTBPopupWindowClass; virtual;
  199. procedure IndexError;
  200. procedure Loaded; override;
  201. function NeedToRecreateViewer(AViewer: TTBItemViewer): Boolean; virtual;
  202. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  203. function OpenPopup(const SelectFirstItem, TrackRightButton: Boolean;
  204. const PopupPoint: TPoint; const Alignment: TTBPopupAlignment;
  205. const ReturnClickedItemOnly: Boolean; PositionAsSubmenu: Boolean): TTBCustomItem;
  206. procedure RecreateItemViewers;
  207. procedure SetChildOrder(Child: TComponent; Order: Integer); override;
  208. procedure SetName(const NewName: TComponentName); override;
  209. procedure SetParentComponent(Value: TComponent); override;
  210. property ActionLink: TTBCustomItemActionLink read FActionLink write FActionLink;
  211. property ItemStyle: TTBItemStyle read FItemStyle write FItemStyle;
  212. public
  213. constructor Create(AOwner: TComponent); override;
  214. destructor Destroy; override;
  215. function HasParent: Boolean; override;
  216. function GetParentComponent: TComponent; override;
  217. procedure Add(AItem: TTBCustomItem);
  218. procedure Clear;
  219. procedure Click; virtual;
  220. function ContainsItem(AItem: TTBCustomItem): Boolean;
  221. procedure Delete(Index: Integer);
  222. function GetShortCutText: String;
  223. function IndexOf(AItem: TTBCustomItem): Integer;
  224. procedure InitiateAction; virtual;
  225. procedure Insert(NewIndex: Integer; AItem: TTBCustomItem);
  226. function IsShortCut(var Message: TWMKey): Boolean;
  227. procedure Move(CurIndex, NewIndex: Integer);
  228. function Popup(X, Y: Integer; TrackRightButton: Boolean;
  229. Alignment: TTBPopupAlignment = tbpaLeft;
  230. ReturnClickedItemOnly: Boolean = False;
  231. PositionAsSubmenu: Boolean = False): TTBCustomItem;
  232. procedure PostClick;
  233. procedure RegisterNotification(ANotify: TTBItemChangedProc);
  234. procedure Remove(Item: TTBCustomItem);
  235. procedure UnregisterNotification(ANotify: TTBItemChangedProc);
  236. procedure ViewBeginUpdate;
  237. procedure ViewEndUpdate;
  238. procedure ChangeScale(M, D: Integer); virtual;
  239. property Action: TBasicAction read GetAction write SetAction;
  240. property AutoCheck: Boolean read FAutoCheck write FAutoCheck stored IsAutoCheckStored default False;
  241. property Caption: String read FCaption write SetCaption stored IsCaptionStored;
  242. property Count: Integer read FItemCount;
  243. property Checked: Boolean read FChecked write SetChecked stored IsCheckedStored default False;
  244. property DisplayMode: TTBItemDisplayMode read FDisplayMode write SetDisplayMode default nbdmDefault;
  245. property EffectiveOptions: TTBItemOptions read FEffectiveOptions;
  246. property Enabled: Boolean read FEnabled write SetEnabled stored IsEnabledStored default True;
  247. property GroupIndex: Integer read FGroupIndex write SetGroupIndex default 0;
  248. property HelpContext: THelpContext read FHelpContext write FHelpContext stored IsHelpContextStored default 0;
  249. { MP }
  250. property HelpKeyword: String read FHelpKeyword write FHelpKeyword stored IsHelpContextStored;
  251. property Hint: String read FHint write FHint stored IsHintStored;
  252. property ImageIndex: TImageIndex read FImageIndex write SetImageIndex stored IsImageIndexStored default -1;
  253. property Images: TCustomImageList read FImages write SetImages;
  254. property InheritOptions: Boolean read FInheritOptions write SetInheritOptions default True;
  255. property Items[Index: Integer]: TTBCustomItem read GetItem; default;
  256. property LinkSubitems: TTBCustomItem read FLinkSubitems write SetLinkSubitems;
  257. property MaskOptions: TTBItemOptions read FMaskOptions write SetMaskOptions default [];
  258. property Options: TTBItemOptions read FOptions write SetOptions default [];
  259. property Parent: TTBCustomItem read FParent;
  260. property ParentComponent: TComponent read FParentComponent write FParentComponent;
  261. property RadioItem: Boolean read FRadioItem write SetRadioItem default False;
  262. property ShortCut: TShortCut read FShortCut write FShortCut stored IsShortCutStored default 0;
  263. property SubMenuImages: TCustomImageList read FSubMenuImages write SetSubMenuImages;
  264. property Visible: Boolean read FVisible write SetVisible stored IsVisibleStored default True;
  265. property OnClick: TNotifyEvent read FOnClick write FOnClick stored IsOnClickStored;
  266. property OnPopup: TTBPopupEvent read FOnPopup write FOnPopup;
  267. property OnSelect: TTBSelectEvent read FOnSelect write FOnSelect;
  268. end;
  269. TTBCustomItemActionLink = class(TActionLink)
  270. protected
  271. FClient: TTBCustomItem;
  272. procedure AssignClient(AClient: TObject); override;
  273. function IsAutoCheckLinked: Boolean; virtual;
  274. function IsCaptionLinked: Boolean; override;
  275. function IsCheckedLinked: Boolean; override;
  276. function IsEnabledLinked: Boolean; override;
  277. function IsHelpContextLinked: Boolean; override;
  278. { MP }
  279. function IsHelpLinked: Boolean; override;
  280. function IsHintLinked: Boolean; override;
  281. function IsImageIndexLinked: Boolean; override;
  282. function IsShortCutLinked: Boolean; override;
  283. function IsVisibleLinked: Boolean; override;
  284. function IsOnExecuteLinked: Boolean; override;
  285. procedure SetAutoCheck(Value: Boolean); override;
  286. procedure SetCaption(const Value: String); override;
  287. procedure SetChecked(Value: Boolean); override;
  288. procedure SetEnabled(Value: Boolean); override;
  289. procedure SetHelpContext(Value: THelpContext); override;
  290. { MP }
  291. procedure SetHelpKeyword(const Value: string); override;
  292. procedure SetHint(const Value: String); override;
  293. procedure SetImageIndex(Value: Integer); override;
  294. procedure SetShortCut(Value: TShortCut); override;
  295. procedure SetVisible(Value: Boolean); override;
  296. procedure SetOnExecute(Value: TNotifyEvent); override;
  297. end;
  298. TTBBaseAccObject = class(TInterfacedObject, IDispatch)
  299. public
  300. procedure ClientIsDestroying; virtual; abstract;
  301. { IDispatch }
  302. function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
  303. function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
  304. function GetIDsOfNames(const IID: TGUID; Names: Pointer;
  305. NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
  306. function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
  307. Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
  308. end;
  309. TTBItemViewer = class
  310. private
  311. FBoundsRect: TRect;
  312. FClipped: Boolean;
  313. FGroupLevel: Integer;
  314. FItem: TTBCustomItem;
  315. FOffEdge: Boolean;
  316. FShow: Boolean;
  317. FView: TTBView;
  318. procedure AccSelect(const AExecute: Boolean);
  319. function GetIndex: Integer;
  320. protected
  321. FAccObjectInstance: TTBBaseAccObject;
  322. procedure CalcSize(const Canvas: TCanvas; var AWidth, AHeight: Integer);
  323. virtual;
  324. function CaptionShown: Boolean; dynamic;
  325. function DoExecute: Boolean; virtual;
  326. procedure DrawItemCaption(const Canvas: TCanvas; ARect: TRect;
  327. const ACaption: String; ADrawDisabledShadow: Boolean; AFormat: UINT); virtual;
  328. procedure Entering(OldSelected: TTBItemViewer); virtual;
  329. function GetAccRole: Integer; virtual;
  330. function GetAccValue(var Value: WideString): Boolean; virtual;
  331. function GetCaptionText: String; virtual;
  332. procedure GetCursor(const Pt: TPoint; var ACursor: HCURSOR); virtual;
  333. function GetImageList: TCustomImageList;
  334. function ImageShown: Boolean;
  335. function IsRotated: Boolean;
  336. function IsToolbarSize: Boolean; virtual;
  337. function IsPtInButtonPart(X, Y: Integer): Boolean; virtual;
  338. procedure KeyDown(var Key: Word; Shift: TShiftState); virtual;
  339. procedure Leaving; virtual;
  340. procedure LosingCapture; virtual;
  341. procedure MouseDown(Shift: TShiftState; X, Y: Integer;
  342. var MouseDownOnMenu: Boolean); virtual;
  343. procedure MouseMove(X, Y: Integer); virtual;
  344. procedure MouseUp(X, Y: Integer; MouseWasDownOnMenu: Boolean); virtual;
  345. procedure MouseWheel(WheelDelta: Integer; X, Y: Integer); virtual;
  346. procedure Paint(const Canvas: TCanvas; const ClientAreaRect: TRect;
  347. IsSelected, IsPushed, UseDisabledShadow: Boolean); virtual;
  348. procedure PostAccSelect(const AExecute: Boolean);
  349. function UsesSameWidth: Boolean; virtual;
  350. public
  351. State: set of (tbisInvalidated, tbisLineSep);
  352. property BoundsRect: TRect read FBoundsRect;
  353. property Clipped: Boolean read FClipped;
  354. property Index: Integer read GetIndex;
  355. property Item: TTBCustomItem read FItem;
  356. property OffEdge: Boolean read FOffEdge;
  357. property Show: Boolean read FShow;
  358. property View: TTBView read FView;
  359. constructor Create(AView: TTBView; AItem: TTBCustomItem; AGroupLevel: Integer); virtual;
  360. destructor Destroy; override;
  361. procedure Execute(AGivePriority: Boolean);
  362. function GetAccObject: IDispatch;
  363. function GetHintText: String;
  364. function IsAccessible: Boolean;
  365. function IsToolbarStyle: Boolean; virtual;
  366. function ScreenToClient(const P: TPoint): TPoint;
  367. end;
  368. PTBItemViewerArray = ^TTBItemViewerArray;
  369. TTBItemViewerArray = array[0..$7FFFFFFF div SizeOf(TTBItemViewer)-1] of TTBItemViewer;
  370. TTBViewOrientation = (tbvoHorizontal, tbvoVertical, tbvoFloating);
  371. TTBEnterToolbarLoopOptions = set of (tbetMouseDown, tbetExecuteSelected,
  372. tbetFromMSAA);
  373. TTBViewState = set of (vsModal, vsMouseInWindow, vsDrawInOrder, vsOppositePopup,
  374. vsIgnoreFirstMouseUp, vsShowAccels, vsDropDownMenus, vsNoAnimation);
  375. TTBViewStyle = set of (vsMenuBar, vsUseHiddenAccels, vsAlwaysShowHints);
  376. TTBViewTimerID = (tiOpen, tiClose, tiScrollUp, tiScrollDown);
  377. TTBViewClass = class of TTBView;
  378. TTBView = class(TComponent)
  379. private
  380. FActiveTimers: set of TTBViewTimerID;
  381. FBackgroundColor: TColor;
  382. FBaseSize: TPoint;
  383. FCapture: Boolean;
  384. FCaptureWnd: HWND;
  385. FChevronOffset: Integer;
  386. FChevronParentView: TTBView;
  387. FChevronSize: Integer;
  388. FCurParentItem: TTBCustomItem;
  389. FCustomizing: Boolean;
  390. FDoneActionData: TTBDoneActionData;
  391. FInternalViewersAtEnd: Integer;
  392. FInternalViewersAtFront: Integer;
  393. FIsPopup: Boolean;
  394. FIsToolbar: Boolean;
  395. FMaxHeight: Integer;
  396. FMonitorRect: TRect;
  397. FMouseOverSelected: Boolean;
  398. FNewViewersGetHighestPriority: Boolean;
  399. FOpenViewer: TTBItemViewer;
  400. FOpenViewerView: TTBView;
  401. FOpenViewerWindow: TTBPopupWindow;
  402. FParentView: TTBView;
  403. FParentItem: TTBCustomItem;
  404. FPriorityList: TList;
  405. FOrientation: TTBViewOrientation;
  406. FScrollOffset: Integer;
  407. FSelected: TTBItemViewer;
  408. FSelectedViaMouse: Boolean;
  409. FShowDownArrow: Boolean;
  410. FShowUpArrow: Boolean;
  411. FState: TTBViewState;
  412. FStyle: TTBViewStyle;
  413. FUpdating: Integer;
  414. FUsePriorityList: Boolean;
  415. FValidated: Boolean;
  416. FViewerCount: Integer;
  417. FViewers: PTBItemViewerArray;
  418. FWindow: TWinControl;
  419. FWrapOffset: Integer;
  420. procedure DeletingViewer(Viewer: TTBItemViewer);
  421. procedure DrawItem(Viewer: TTBItemViewer; DrawTo: TCanvas; Offscreen: Boolean);
  422. procedure FreeViewers;
  423. procedure ImagesChanged;
  424. function InsertItemViewers(const NewIndex: Integer;
  425. const AItem: TTBCustomItem; const AGroupLevel: Integer;
  426. const AddToPriorityList, TopOfPriorityList: Boolean): Integer;
  427. procedure ItemNotification(Ancestor: TTBCustomItem; Relayed: Boolean;
  428. Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem);
  429. procedure LinkNotification(Ancestor: TTBCustomItem; Relayed: Boolean;
  430. Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem);
  431. procedure RecreateItemViewer(const I: Integer);
  432. procedure Scroll(ADown: Boolean);
  433. procedure SetCustomizing(Value: Boolean);
  434. procedure SetSelected(Value: TTBItemViewer);
  435. procedure SetUsePriorityList(Value: Boolean);
  436. procedure StartTimer(const ATimer: TTBViewTimerID; const Interval: Integer);
  437. procedure StopAllTimers;
  438. procedure StopTimer(const ATimer: TTBViewTimerID);
  439. procedure UpdateCurParentItem;
  440. protected
  441. FAccObjectInstance: TTBBaseAccObject;
  442. procedure AutoSize(AWidth, AHeight: Integer); virtual;
  443. function CalculatePositions(const CanMoveControls: Boolean;
  444. const AOrientation: TTBViewOrientation;
  445. AWrapOffset, AChevronOffset, AChevronSize: Integer;
  446. var ABaseSize, TotalSize: TPoint;
  447. var AWrappedLines: Integer): Boolean;
  448. procedure DoUpdatePositions(var ASize: TPoint); virtual;
  449. function GetChevronItem: TTBCustomItem; virtual;
  450. procedure GetMargins(AOrientation: TTBViewOrientation; var Margins: TRect);
  451. virtual;
  452. function GetMDIButtonsItem: TTBCustomItem; virtual;
  453. function GetMDISystemMenuItem: TTBCustomItem; virtual;
  454. function GetParentToolbarView: TTBView;
  455. function GetRootView: TTBView;
  456. function HandleWMGetObject(var Message: TMessage): Boolean;
  457. procedure InitiateActions;
  458. procedure KeyDown(var Key: Word; Shift: TShiftState); virtual;
  459. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  460. procedure SetAccelsVisibility(AShowAccels: Boolean);
  461. procedure SetState(AState: TTBViewState);
  462. property DoneActionData: TTBDoneActionData read FDoneActionData write FDoneActionData;
  463. property ShowDownArrow: Boolean read FShowDownArrow; {vb+}
  464. property ShowUpArrow: Boolean read FShowUpArrow; {vb+}
  465. public
  466. constructor CreateView(AOwner: TComponent; AParentView: TTBView;
  467. AParentItem: TTBCustomItem; AWindow: TWinControl;
  468. AIsToolbar, ACustomizing, AUsePriorityList: Boolean); virtual;
  469. destructor Destroy; override;
  470. procedure BeginUpdate;
  471. procedure CancelCapture;
  472. procedure CancelChildPopups;
  473. procedure CancelMode;
  474. procedure CloseChildPopups;
  475. function ContainsView(AView: TTBView): Boolean;
  476. procedure DrawSubitems(ACanvas: TCanvas);
  477. procedure EndModal;
  478. procedure EndModalWithClick(AViewer: TTBItemViewer);
  479. { MP }
  480. procedure EndModalWithHelp(AContextID: Integer); overload;
  481. procedure EndModalWithHelp(HelpKeyword: string); overload;
  482. { /MP }
  483. procedure EndModalWithSystemMenu(AWnd: HWND; AKey: Cardinal);
  484. procedure EndUpdate;
  485. procedure EnterToolbarLoop(Options: TTBEnterToolbarLoopOptions); virtual;
  486. procedure ExecuteSelected(AGivePriority: Boolean);
  487. function Find(Item: TTBCustomItem): TTBItemViewer;
  488. function FirstSelectable: TTBItemViewer;
  489. function GetAccObject: IDispatch;
  490. function GetCaptureWnd: HWND;
  491. function GetFont: TFont; virtual;
  492. procedure GetOffEdgeControlList(const List: TList);
  493. procedure GivePriority(AViewer: TTBItemViewer);
  494. function HighestPriorityViewer: TTBItemViewer;
  495. procedure Invalidate(AViewer: TTBItemViewer);
  496. procedure InvalidatePositions; virtual;
  497. function IndexOf(AViewer: TTBItemViewer): Integer;
  498. function IsModalEnding: Boolean;
  499. function NextSelectable(CurViewer: TTBItemViewer; GoForward: Boolean): TTBItemViewer;
  500. function NextSelectableWithAccel(CurViewer: TTBItemViewer; Key: Char;
  501. RequirePrimaryAccel: Boolean; var IsOnlyItemWithAccel: Boolean): TTBItemViewer;
  502. procedure NotifyFocusEvent;
  503. function OpenChildPopup(const SelectFirstItem: Boolean): Boolean;
  504. procedure RecreateAllViewers;
  505. procedure ScrollSelectedIntoView;
  506. procedure Select(Value: TTBItemViewer; ViaMouse: Boolean);
  507. procedure SetCapture;
  508. procedure TryValidatePositions;
  509. procedure UpdateSelection(const P: PPoint; const AllowNewSelection: Boolean);
  510. function UpdatePositions: TPoint;
  511. procedure ValidatePositions;
  512. function ViewerFromPoint(const P: TPoint): TTBItemViewer;
  513. function GetMonitor: TMonitor; virtual;
  514. property BackgroundColor: TColor read FBackgroundColor write FBackgroundColor;
  515. property BaseSize: TPoint read FBaseSize;
  516. property Capture: Boolean read FCapture;
  517. property ChevronOffset: Integer read FChevronOffset write FChevronOffset;
  518. property ChevronSize: Integer read FChevronSize write FChevronSize;
  519. property Customizing: Boolean read FCustomizing write SetCustomizing;
  520. property IsPopup: Boolean read FIsPopup;
  521. property IsToolbar: Boolean read FIsToolbar;
  522. property MouseOverSelected: Boolean read FMouseOverSelected;
  523. property NewViewersGetHighestPriority: Boolean read FNewViewersGetHighestPriority
  524. write FNewViewersGetHighestPriority;
  525. property ParentView: TTBView read FParentView;
  526. property ParentItem: TTBCustomItem read FParentItem;
  527. property OpenViewer: TTBItemViewer read FOpenViewer;
  528. property OpenViewerView: TTBView read FOpenViewerView;
  529. property Orientation: TTBViewOrientation read FOrientation write FOrientation;
  530. property Selected: TTBItemViewer read FSelected write SetSelected;
  531. property SelectedViaMouse: Boolean read FSelectedViaMouse;
  532. property State: TTBViewState read FState;
  533. property Style: TTBViewStyle read FStyle write FStyle;
  534. property UsePriorityList: Boolean read FUsePriorityList write SetUsePriorityList;
  535. property Viewers: PTBItemViewerArray read FViewers;
  536. property ViewerCount: Integer read FViewerCount;
  537. property Window: TWinControl read FWindow;
  538. property WrapOffset: Integer read FWrapOffset write FWrapOffset;
  539. end;
  540. TTBRootItemClass = class of TTBRootItem;
  541. TTBRootItem = class(TTBCustomItem);
  542. { same as TTBCustomItem, except there's a property editor for it }
  543. TTBItem = class(TTBCustomItem)
  544. published
  545. property Action;
  546. property AutoCheck;
  547. property Caption;
  548. property Checked;
  549. property DisplayMode;
  550. property Enabled;
  551. property GroupIndex;
  552. property HelpContext;
  553. { MP }
  554. property HelpKeyword;
  555. property Hint;
  556. property ImageIndex;
  557. property Images;
  558. property InheritOptions;
  559. property MaskOptions;
  560. property Options;
  561. property RadioItem;
  562. property ShortCut;
  563. property Visible;
  564. property OnClick;
  565. property OnSelect;
  566. end;
  567. TTBGroupItem = class(TTBCustomItem)
  568. public
  569. constructor Create(AOwner: TComponent); override;
  570. published
  571. property InheritOptions;
  572. property LinkSubitems;
  573. property MaskOptions;
  574. property Options;
  575. end;
  576. TTBSubmenuItem = class(TTBCustomItem)
  577. private
  578. function GetDropdownCombo: Boolean;
  579. procedure SetDropdownCombo(Value: Boolean);
  580. public
  581. constructor Create(AOwner: TComponent); override;
  582. published
  583. property Action;
  584. property AutoCheck;
  585. property Caption;
  586. property Checked;
  587. //property DisplayAsToolbar;
  588. property DisplayMode;
  589. property DropdownCombo: Boolean read GetDropdownCombo write SetDropdownCombo default False;
  590. property Enabled;
  591. property GroupIndex;
  592. property HelpContext;
  593. { MP }
  594. property HelpKeyword;
  595. property Hint;
  596. property ImageIndex;
  597. property Images;
  598. property InheritOptions;
  599. property LinkSubitems;
  600. property MaskOptions;
  601. property Options;
  602. property RadioItem;
  603. property ShortCut;
  604. property SubMenuImages;
  605. property Visible;
  606. property OnClick;
  607. property OnPopup;
  608. property OnSelect;
  609. end;
  610. TTBSeparatorItem = class(TTBCustomItem)
  611. private
  612. FBlank: Boolean;
  613. procedure SetBlank(Value: Boolean);
  614. protected
  615. function GetItemViewerClass(AView: TTBView): TTBItemViewerClass; override;
  616. public
  617. constructor Create(AOwner: TComponent); override;
  618. published
  619. property Blank: Boolean read FBlank write SetBlank default False;
  620. property Hint;
  621. property Visible;
  622. end;
  623. TTBSeparatorItemViewer = class(TTBItemViewer)
  624. protected
  625. procedure CalcSize(const Canvas: TCanvas;
  626. var AWidth, AHeight: Integer); override;
  627. procedure Paint(const Canvas: TCanvas; const ClientAreaRect: TRect;
  628. IsSelected, IsPushed, UseDisabledShadow: Boolean); override;
  629. function UsesSameWidth: Boolean; override;
  630. end;
  631. TTBControlItem = class(TTBCustomItem)
  632. private
  633. FControl: TControl;
  634. FDontFreeControl: Boolean;
  635. procedure SetControl(Value: TControl);
  636. protected
  637. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  638. public
  639. constructor Create(AOwner: TComponent); override;
  640. constructor CreateControlItem(AOwner: TComponent; AControl: TControl);
  641. destructor Destroy; override;
  642. property DontFreeControl: Boolean read FDontFreeControl write FDontFreeControl;
  643. published
  644. property Control: TControl read FControl write SetControl;
  645. end;
  646. TTBPopupView = class(TTBView)
  647. protected
  648. procedure AutoSize(AWidth, AHeight: Integer); override;
  649. public
  650. function GetMonitor: TMonitor; override;
  651. function GetFont: TFont; override;
  652. end;
  653. ITBPopupWindow = interface
  654. ['{E45CBE74-1ECF-44CB-B064-6D45B1924708}']
  655. end;
  656. TTBPopupWindow = class(TCustomControl, ITBPopupWindow)
  657. private
  658. FAccelsVisibilitySet: Boolean;
  659. FAnimationDirection: TTBAnimationDirection;
  660. FView: TTBView;
  661. procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
  662. procedure CMHintShowPause(var Message: TMessage); message CM_HINTSHOWPAUSE;
  663. procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED;
  664. procedure WMClose(var Message: TWMClose); message WM_CLOSE;
  665. procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
  666. procedure WMGetObject(var Message: TMessage); message WM_GETOBJECT;
  667. procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
  668. procedure WMNCPaint(var Message: TMessage); message WM_NCPAINT;
  669. procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  670. procedure WMPrint(var Message: TMessage); message WM_PRINT;
  671. procedure WMPrintClient(var Message: TMessage); message WM_PRINTCLIENT;
  672. procedure WMTB2kStepAnimation(var Message: TMessage); message WM_TB2K_STEPANIMATION;
  673. procedure WMTB2kAnimationEnded (var Message: TMessage); message WM_TB2K_ANIMATIONENDED;
  674. protected
  675. procedure CreateParams(var Params: TCreateParams); override;
  676. procedure CreateWnd; override;
  677. procedure DestroyWindowHandle; override;
  678. function GetNCSize: TPoint; dynamic;
  679. function GetViewClass: TTBViewClass; dynamic;
  680. procedure Paint; override;
  681. procedure PaintScrollArrows; virtual;
  682. property AnimationDirection: TTBAnimationDirection read FAnimationDirection;
  683. {MP}
  684. procedure Cancel; dynamic;
  685. public
  686. constructor CreatePopupWindow(AOwner: TComponent; const AParentView: TTBView;
  687. const AItem: TTBCustomItem; const ACustomizing: Boolean; const PopupPoint: TPoint); virtual;
  688. destructor Destroy; override;
  689. procedure BeforeDestruction; override;
  690. property View: TTBView read FView;
  691. end;
  692. ITBItems = interface
  693. ['{A5C0D7CC-3EC4-4090-A0F8-3D03271877EA}']
  694. function GetItems: TTBCustomItem;
  695. end;
  696. TTBItemContainer = class(TComponent, ITBItems)
  697. private
  698. FItem: TTBRootItem;
  699. function GetImages: TCustomImageList;
  700. function GetItems: TTBCustomItem;
  701. procedure SetImages(Value: TCustomImageList);
  702. protected
  703. procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  704. public
  705. constructor Create(AOwner: TComponent); override;
  706. destructor Destroy; override;
  707. property Items: TTBRootItem read FItem;
  708. published
  709. property Images: TCustomImageList read GetImages write SetImages;
  710. end;
  711. TTBPopupMenu = class(TPopupMenu, ITBItems)
  712. private
  713. FItem: TTBRootItem;
  714. //procedure SetItems(Value: TTBCustomItem);
  715. function GetImages: TCustomImageList;
  716. function GetItems: TTBCustomItem;
  717. function GetLinkSubitems: TTBCustomItem;
  718. function GetOptions: TTBItemOptions;
  719. procedure RootItemClick(Sender: TObject);
  720. procedure SetImages(Value: TCustomImageList);
  721. procedure SetLinkSubitems(Value: TTBCustomItem);
  722. procedure SetOptions(Value: TTBItemOptions);
  723. protected
  724. function GetRootItemClass: TTBRootItemClass; dynamic;
  725. procedure SetChildOrder(Child: TComponent; Order: Integer); override;
  726. public
  727. constructor Create(AOwner: TComponent); override;
  728. destructor Destroy; override;
  729. function IsShortCut(var Message: TWMKey): Boolean; override;
  730. procedure Popup(X, Y: Integer); override;
  731. function PopupEx(X, Y: Integer; ReturnClickedItemOnly: Boolean = False): TTBCustomItem;
  732. procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  733. published
  734. property Images: TCustomImageList read GetImages write SetImages;
  735. property Items: TTBRootItem read FItem;
  736. property LinkSubitems: TTBCustomItem read GetLinkSubitems write SetLinkSubitems;
  737. property Options: TTBItemOptions read GetOptions write SetOptions default [];
  738. end;
  739. TTBCustomImageList = class(TImageList)
  740. private
  741. FCheckedImages: TCustomImageList;
  742. FCheckedImagesChangeLink: TChangeLink;
  743. FDisabledImages: TCustomImageList;
  744. FDisabledImagesChangeLink: TChangeLink;
  745. FHotImages: TCustomImageList;
  746. FHotImagesChangeLink: TChangeLink;
  747. FImagesBitmap: TBitmap;
  748. FImagesBitmapMaskColor: TColor;
  749. procedure ChangeImages(var AImageList: TCustomImageList;
  750. Value: TCustomImageList; AChangeLink: TChangeLink);
  751. procedure ImageListChanged(Sender: TObject);
  752. procedure ImagesBitmapChanged(Sender: TObject);
  753. procedure SetCheckedImages(Value: TCustomImageList);
  754. procedure SetDisabledImages(Value: TCustomImageList);
  755. procedure SetHotImages(Value: TCustomImageList);
  756. procedure SetImagesBitmap(Value: TBitmap);
  757. procedure SetImagesBitmapMaskColor(Value: TColor);
  758. protected
  759. procedure DefineProperties(Filer: TFiler); override;
  760. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  761. property CheckedImages: TCustomImageList read FCheckedImages write SetCheckedImages;
  762. property DisabledImages: TCustomImageList read FDisabledImages write SetDisabledImages;
  763. property HotImages: TCustomImageList read FHotImages write SetHotImages;
  764. property ImagesBitmap: TBitmap read FImagesBitmap write SetImagesBitmap;
  765. property ImagesBitmapMaskColor: TColor read FImagesBitmapMaskColor
  766. write SetImagesBitmapMaskColor default clFuchsia;
  767. public
  768. constructor Create(AOwner: TComponent); override;
  769. destructor Destroy; override;
  770. procedure DrawState(Canvas: TCanvas; X, Y, Index: Integer;
  771. Enabled, Selected, Checked: Boolean); virtual;
  772. end;
  773. TTBImageList = class(TTBCustomImageList)
  774. published
  775. property CheckedImages;
  776. property DisabledImages;
  777. property HotImages;
  778. property ImagesBitmap;
  779. property ImagesBitmapMaskColor;
  780. end;
  781. const
  782. {$IFNDEF TB2K_USE_STRICT_O2K_MENU_STYLE}
  783. tbMenuBkColor = clMenu;
  784. tbMenuTextColor = clMenuText;
  785. {$ELSE}
  786. tbMenuBkColor = clBtnFace;
  787. tbMenuTextColor = clBtnText;
  788. {$ENDIF}
  789. tbMenuVerticalMargin = 4;
  790. tbMenuImageTextSpace = 1;
  791. tbMenuLeftTextMargin = 2;
  792. tbMenuRightTextMargin = 3;
  793. tbMenuSeparatorOffset = 12;
  794. tbMenuScrollArrowHeight = 19;
  795. tbDropdownArrowWidth = 8;
  796. tbDropdownArrowMargin = 3;
  797. tbDropdownComboArrowWidth = 11;
  798. tbDropdownComboMargin = 2;
  799. tbLineSpacing = 6;
  800. tbLineSepOffset = 1;
  801. tbDockedLineSepOffset = 4;
  802. WM_TB2K_CLICKITEM = WM_USER + $100;
  803. procedure TBInitToolbarSystemFont;
  804. function GetToolbarFont(PixelsPerInch: Integer): TFont; overload;
  805. function GetToolbarFont(Control: TControl): TFont; overload;
  806. type
  807. TTBModalHandler = class
  808. private
  809. FCreatedWnd: Boolean;
  810. FInited: Boolean;
  811. FWnd: HWND;
  812. FRootPopup: TTBPopupWindow;
  813. procedure WndProc(var Msg: TMessage);
  814. public
  815. constructor Create(AExistingWnd: HWND);
  816. destructor Destroy; override;
  817. procedure Loop(const RootView: TTBView; const AMouseDown, AExecuteSelected,
  818. AFromMSAA, TrackRightButton: Boolean);
  819. property RootPopup: TTBPopupWindow read FRootPopup write FRootPopup;
  820. property Wnd: HWND read FWnd;
  821. class procedure DoLockForegroundWindow(LockCode: Cardinal);
  822. class procedure LockForegroundWindow;
  823. class procedure UnlockForegroundWindow;
  824. end;
  825. function ProcessDoneAction(const DoneActionData: TTBDoneActionData;
  826. const ReturnClickedItemOnly: Boolean): TTBCustomItem;
  827. implementation
  828. uses
  829. MMSYSTEM, TB2Consts, TB2Common, IMM, TB2Acc, Winapi.oleacc, Types, PasTools, Generics.Collections;
  830. var
  831. LastPos: TPoint;
  832. threadvar
  833. ClickWndRefCount: Integer;
  834. ClickWnd: HWND;
  835. ClickList: TList;
  836. type
  837. PItemChangedNotificationData = ^TItemChangedNotificationData;
  838. TItemChangedNotificationData = record
  839. Proc: TTBItemChangedProc;
  840. RefCount: Integer;
  841. end;
  842. TComponentAccess = class(TComponent);
  843. TControlAccess = class(TControl);
  844. const
  845. ViewTimerBaseID = 9000;
  846. MaxGroupLevel = 10;
  847. { Misc. }
  848. procedure DestroyClickWnd;
  849. begin
  850. if ClickWnd <> 0 then begin
  851. Classes.DeallocateHWnd(ClickWnd);
  852. ClickWnd := 0;
  853. end;
  854. FreeAndNil(ClickList);
  855. end;
  856. procedure ReferenceClickWnd;
  857. begin
  858. Inc(ClickWndRefCount);
  859. end;
  860. procedure ReleaseClickWnd;
  861. begin
  862. Dec(ClickWndRefCount);
  863. if ClickWndRefCount = 0 then
  864. DestroyClickWnd;
  865. end;
  866. procedure QueueClick(const AItem: TObject; const AArg: Integer);
  867. { Adds an item to ClickList and posts a message to handle it. AItem must be
  868. either a TTBCustomItem or TTBItemViewer. }
  869. var
  870. I: Integer;
  871. begin
  872. if ClickWnd = 0 then
  873. ClickWnd := Classes.AllocateHWnd(TTBCustomItem.ClickWndProc);
  874. if ClickList = nil then
  875. ClickList := TList.Create;
  876. { Add a new item to ClickList or replace an empty one }
  877. I := ClickList.IndexOf(nil);
  878. if I = -1 then
  879. I := ClickList.Add(AItem)
  880. else
  881. ClickList[I] := AItem;
  882. PostMessage(ClickWnd, WM_TB2K_CLICKITEM, AArg, I);
  883. end;
  884. procedure RemoveFromClickList(const AItem: TObject);
  885. { Any class that potentially calls QueueClick needs to call RemoveFromClickList
  886. before an instance is destroyed to ensure that any references to the
  887. instance still in ClickList are removed. }
  888. var
  889. I: Integer;
  890. begin
  891. if Assigned(ClickList) and Assigned(AItem) then
  892. for I := 0 to ClickList.Count-1 do
  893. if ClickList[I] = AItem then
  894. ClickList[I] := Pointer(1);
  895. { ^ The special value of Pointer(1) is assigned to the item instead of
  896. of nil because we want the index to stay reserved until the
  897. WM_TB2K_CLICKITEM message for the index is processed. We don't want
  898. the WM_TB2K_CLICKITEM message that's still in the queue to later
  899. refer to a different item; this would result in queued clicks being
  900. processed in the wrong order in a case like this:
  901. A.PostClick; B.PostClick; A.Free; C.PostClick;
  902. C's click would end up being processed before A's, because C would
  903. get A's index. }
  904. end;
  905. function ProcessDoneAction(const DoneActionData: TTBDoneActionData;
  906. const ReturnClickedItemOnly: Boolean): TTBCustomItem;
  907. begin
  908. Result := nil;
  909. case DoneActionData.DoneAction of
  910. tbdaNone: ;
  911. tbdaClickItem: begin
  912. if DoneActionData.Sound and NeedToPlaySound('MenuCommand') then
  913. PlaySoundA('MenuCommand', 0, SND_ALIAS or SND_ASYNC or SND_NODEFAULT or SND_NOSTOP);
  914. Result := DoneActionData.ClickItem;
  915. if not ReturnClickedItemOnly then
  916. Result.PostClick;
  917. end;
  918. tbdaOpenSystemMenu: begin
  919. SendMessage(DoneActionData.Wnd, WM_SYSCOMMAND, SC_KEYMENU, DoneActionData.Key);
  920. end;
  921. tbdaHelpContext: begin
  922. { Based on code in TPopupList.WndProc: }
  923. if Assigned(Screen.ActiveForm) and
  924. (biHelp in Screen.ActiveForm.BorderIcons) then
  925. Application.HelpCommand(HELP_CONTEXTPOPUP, DoneActionData.ContextID)
  926. else
  927. Application.HelpContext(DoneActionData.ContextID);
  928. end;
  929. { MP }
  930. tbdaHelpKeyword: begin
  931. Application.HelpKeyword(string(DoneActionData.HelpKeyword));
  932. end;
  933. { /MP }
  934. end;
  935. end;
  936. { TTBItemDataArray routines }
  937. procedure InsertIntoItemArray(var AItems: PTBItemDataArray;
  938. var AItemCount: Integer; NewIndex: Integer; AItem: TTBCustomItem);
  939. begin
  940. ReallocMem(AItems, (AItemCount+1) * SizeOf(AItems[0]));
  941. if NewIndex < AItemCount then
  942. System.Move(AItems[NewIndex], AItems[NewIndex+1],
  943. (AItemCount-NewIndex) * SizeOf(AItems[0]));
  944. AItems[NewIndex].Item := AItem;
  945. Inc(AItemCount);
  946. end;
  947. procedure DeleteFromItemArray(var AItems: PTBItemDataArray;
  948. var AItemCount: Integer; Index: Integer);
  949. begin
  950. Dec(AItemCount);
  951. if Index < AItemCount then
  952. System.Move(AItems[Index+1], AItems[Index],
  953. (AItemCount-Index) * SizeOf(AItems[0]));
  954. ReallocMem(AItems, AItemCount * SizeOf(AItems[0]));
  955. end;
  956. procedure InsertIntoViewerArray(var AItems: PTBItemViewerArray;
  957. var AItemCount: Integer; NewIndex: Integer; AItem: TTBItemViewer);
  958. begin
  959. ReallocMem(AItems, (AItemCount+1) * SizeOf(AItems[0]));
  960. if NewIndex < AItemCount then
  961. System.Move(AItems[NewIndex], AItems[NewIndex+1],
  962. (AItemCount-NewIndex) * SizeOf(AItems[0]));
  963. AItems[NewIndex] := AItem;
  964. Inc(AItemCount);
  965. end;
  966. procedure DeleteFromViewerArray(var AItems: PTBItemViewerArray;
  967. var AItemCount: Integer; Index: Integer);
  968. begin
  969. Dec(AItemCount);
  970. if Index < AItemCount then
  971. System.Move(AItems[Index+1], AItems[Index],
  972. (AItemCount-Index) * SizeOf(AItems[0]));
  973. ReallocMem(AItems, AItemCount * SizeOf(AItems[0]));
  974. end;
  975. { TTBCustomItemActionLink }
  976. procedure TTBCustomItemActionLink.AssignClient(AClient: TObject);
  977. begin
  978. FClient := AClient as TTBCustomItem;
  979. end;
  980. function TTBCustomItemActionLink.IsAutoCheckLinked: Boolean;
  981. begin
  982. Result := (FClient.AutoCheck = (Action as TCustomAction).AutoCheck);
  983. end;
  984. function TTBCustomItemActionLink.IsCaptionLinked: Boolean;
  985. begin
  986. Result := inherited IsCaptionLinked and
  987. (FClient.Caption = (Action as TCustomAction).Caption);
  988. end;
  989. function TTBCustomItemActionLink.IsCheckedLinked: Boolean;
  990. begin
  991. Result := inherited IsCheckedLinked and
  992. (FClient.Checked = (Action as TCustomAction).Checked);
  993. end;
  994. function TTBCustomItemActionLink.IsEnabledLinked: Boolean;
  995. begin
  996. Result := inherited IsEnabledLinked and
  997. (FClient.Enabled = (Action as TCustomAction).Enabled);
  998. end;
  999. function TTBCustomItemActionLink.IsHelpContextLinked: Boolean;
  1000. begin
  1001. Result := inherited IsHelpContextLinked and
  1002. (FClient.HelpContext = (Action as TCustomAction).HelpContext);
  1003. end;
  1004. { MP }
  1005. function TTBCustomItemActionLink.IsHelpLinked: Boolean;
  1006. begin
  1007. Result := inherited IsHelpLinked and
  1008. (FClient.HelpContext = (Action as TCustomAction).HelpContext) and
  1009. (FClient.HelpKeyword = (Action as TCustomAction).HelpKeyword){ and
  1010. (FClient.HelpType = (Action as TCustomAction).HelpType);} // TODO
  1011. end;
  1012. { /MP }
  1013. function TTBCustomItemActionLink.IsHintLinked: Boolean;
  1014. begin
  1015. Result := inherited IsHintLinked and
  1016. (FClient.Hint = (Action as TCustomAction).Hint);
  1017. end;
  1018. function TTBCustomItemActionLink.IsImageIndexLinked: Boolean;
  1019. begin
  1020. Result := inherited IsImageIndexLinked and
  1021. (FClient.ImageIndex = (Action as TCustomAction).ImageIndex);
  1022. end;
  1023. function TTBCustomItemActionLink.IsShortCutLinked: Boolean;
  1024. begin
  1025. Result := inherited IsShortCutLinked and
  1026. (FClient.ShortCut = (Action as TCustomAction).ShortCut);
  1027. end;
  1028. function TTBCustomItemActionLink.IsVisibleLinked: Boolean;
  1029. begin
  1030. Result := inherited IsVisibleLinked and
  1031. (FClient.Visible = (Action as TCustomAction).Visible);
  1032. end;
  1033. function TTBCustomItemActionLink.IsOnExecuteLinked: Boolean;
  1034. begin
  1035. Result := inherited IsOnExecuteLinked and
  1036. MethodsEqual(TMethod(FClient.OnClick), TMethod(Action.OnExecute));
  1037. end;
  1038. procedure TTBCustomItemActionLink.SetAutoCheck(Value: Boolean);
  1039. begin
  1040. if IsAutoCheckLinked then FClient.AutoCheck := Value;
  1041. end;
  1042. procedure TTBCustomItemActionLink.SetCaption(const Value: string);
  1043. begin
  1044. if IsCaptionLinked then FClient.Caption := Value;
  1045. end;
  1046. procedure TTBCustomItemActionLink.SetChecked(Value: Boolean);
  1047. begin
  1048. if IsCheckedLinked then FClient.Checked := Value;
  1049. end;
  1050. procedure TTBCustomItemActionLink.SetEnabled(Value: Boolean);
  1051. begin
  1052. if IsEnabledLinked then FClient.Enabled := Value;
  1053. end;
  1054. procedure TTBCustomItemActionLink.SetHelpContext(Value: THelpContext);
  1055. begin
  1056. if { MP } IsHelpLinked { /MP } then FClient.HelpContext := Value;
  1057. end;
  1058. { MP }
  1059. procedure TTBCustomItemActionLink.SetHelpKeyword(const Value: String);
  1060. begin
  1061. if IsHelpLinked then FClient.HelpKeyword := Value;
  1062. end;
  1063. { /MP }
  1064. procedure TTBCustomItemActionLink.SetHint(const Value: string);
  1065. begin
  1066. if IsHintLinked then FClient.Hint := Value;
  1067. end;
  1068. procedure TTBCustomItemActionLink.SetImageIndex(Value: Integer);
  1069. begin
  1070. if IsImageIndexLinked then FClient.ImageIndex := Value;
  1071. end;
  1072. procedure TTBCustomItemActionLink.SetShortCut(Value: TShortCut);
  1073. begin
  1074. if IsShortCutLinked then FClient.ShortCut := Value;
  1075. end;
  1076. procedure TTBCustomItemActionLink.SetVisible(Value: Boolean);
  1077. begin
  1078. if IsVisibleLinked then FClient.Visible := Value;
  1079. end;
  1080. procedure TTBCustomItemActionLink.SetOnExecute(Value: TNotifyEvent);
  1081. begin
  1082. if IsOnExecuteLinked then FClient.OnClick := Value;
  1083. end;
  1084. { TTBCustomItem }
  1085. {}function ItemContainingItems(const AItem: TTBCustomItem): TTBCustomItem;
  1086. begin
  1087. if Assigned(AItem) and Assigned(AItem.FLinkSubitems) then
  1088. Result := AItem.FLinkSubitems
  1089. else
  1090. Result := AItem;
  1091. end;
  1092. constructor TTBCustomItem.Create(AOwner: TComponent);
  1093. begin
  1094. inherited;
  1095. FEnabled := True;
  1096. FImageIndex := -1;
  1097. FInheritOptions := True;
  1098. FItemStyle := [tbisSelectable, tbisRedrawOnSelChange, tbisRedrawOnMouseOverChange];
  1099. FVisible := True;
  1100. ReferenceClickWnd;
  1101. end;
  1102. destructor TTBCustomItem.Destroy;
  1103. var
  1104. I: Integer;
  1105. begin
  1106. Destroying;
  1107. RemoveFromClickList(Self);
  1108. { Changed in 0.33. Moved FParent.Remove call *after* the child items are
  1109. deleted. }
  1110. for I := Count-1 downto 0 do
  1111. Items[I].Free;
  1112. if Assigned(FParent) then
  1113. FParent.Remove(Self);
  1114. FreeMem(FItems);
  1115. FActionLink.Free;
  1116. FActionLink := nil;
  1117. FreeAndNil(FSubMenuImagesChangeLink);
  1118. FreeAndNil(FImagesChangeLink);
  1119. inherited;
  1120. if Assigned(FNotifyList) then begin
  1121. for I := FNotifyList.Count-1 downto 0 do
  1122. Dispose(PItemChangedNotificationData(FNotifyList[I]));
  1123. FNotifyList.Free;
  1124. end;
  1125. FLinkParents.Free;
  1126. ReleaseClickWnd;
  1127. end;
  1128. function TTBCustomItem.IsAutoCheckStored: Boolean;
  1129. begin
  1130. Result := (ActionLink = nil) or not FActionLink.IsAutoCheckLinked;
  1131. end;
  1132. function TTBCustomItem.IsCaptionStored: Boolean;
  1133. begin
  1134. Result := (ActionLink = nil) or not FActionLink.IsCaptionLinked;
  1135. end;
  1136. function TTBCustomItem.IsCheckedStored: Boolean;
  1137. begin
  1138. Result := (ActionLink = nil) or not FActionLink.IsCheckedLinked;
  1139. end;
  1140. function TTBCustomItem.IsEnabledStored: Boolean;
  1141. begin
  1142. Result := (ActionLink = nil) or not FActionLink.IsEnabledLinked;
  1143. end;
  1144. function TTBCustomItem.IsHintStored: Boolean;
  1145. begin
  1146. Result := (ActionLink = nil) or not FActionLink.IsHintLinked;
  1147. end;
  1148. function TTBCustomItem.IsHelpContextStored: Boolean;
  1149. begin
  1150. { MP }
  1151. Result := (ActionLink = nil) or not FActionLink.IsHelpLinked;
  1152. end;
  1153. function TTBCustomItem.IsImageIndexStored: Boolean;
  1154. begin
  1155. Result := (ActionLink = nil) or not FActionLink.IsImageIndexLinked;
  1156. end;
  1157. function TTBCustomItem.IsShortCutStored: Boolean;
  1158. begin
  1159. Result := (ActionLink = nil) or not FActionLink.IsShortCutLinked;
  1160. end;
  1161. function TTBCustomItem.IsVisibleStored: Boolean;
  1162. begin
  1163. Result := (ActionLink = nil) or not FActionLink.IsVisibleLinked;
  1164. end;
  1165. function TTBCustomItem.IsOnClickStored: Boolean;
  1166. begin
  1167. Result := (ActionLink = nil) or not FActionLink.IsOnExecuteLinked;
  1168. end;
  1169. function TTBCustomItem.GetAction: TBasicAction;
  1170. begin
  1171. if FActionLink <> nil then
  1172. Result := FActionLink.Action
  1173. else
  1174. Result := nil;
  1175. end;
  1176. function TTBCustomItem.GetActionLinkClass: TTBCustomItemActionLinkClass;
  1177. begin
  1178. Result := TTBCustomItemActionLink;
  1179. end;
  1180. procedure TTBCustomItem.DoActionChange(Sender: TObject);
  1181. begin
  1182. if Sender = Action then ActionChange(Sender, False);
  1183. end;
  1184. procedure TTBCustomItem.ActionChange(Sender: TObject; CheckDefaults: Boolean);
  1185. begin
  1186. if Action is TCustomAction then
  1187. with TCustomAction(Sender) do
  1188. begin
  1189. if not CheckDefaults or (Self.AutoCheck = False) then
  1190. Self.AutoCheck := AutoCheck;
  1191. if not CheckDefaults or (Self.Caption = '') then
  1192. Self.Caption := Caption;
  1193. if not CheckDefaults or (Self.Checked = False) then
  1194. Self.Checked := Checked;
  1195. if not CheckDefaults or (Self.Enabled = True) then
  1196. Self.Enabled := Enabled;
  1197. if not CheckDefaults or (Self.HelpContext = 0) then
  1198. Self.HelpContext := HelpContext;
  1199. { MP }
  1200. if not CheckDefaults or (Self.HelpKeyword = '') then
  1201. Self.HelpKeyword := HelpKeyword;
  1202. { /MP }
  1203. if not CheckDefaults or (Self.Hint = '') then
  1204. Self.Hint := Hint;
  1205. if not CheckDefaults or (Self.ImageIndex = -1) then
  1206. Self.ImageIndex := ImageIndex;
  1207. if not CheckDefaults or (Self.ShortCut = scNone) then
  1208. Self.ShortCut := ShortCut;
  1209. if not CheckDefaults or (Self.Visible = True) then
  1210. Self.Visible := Visible;
  1211. if not CheckDefaults or not Assigned(Self.OnClick) then
  1212. Self.OnClick := OnExecute;
  1213. end;
  1214. end;
  1215. procedure TTBCustomItem.SetAction(Value: TBasicAction);
  1216. begin
  1217. if Value = nil then begin
  1218. FActionLink.Free;
  1219. FActionLink := nil;
  1220. end
  1221. else begin
  1222. if FActionLink = nil then
  1223. FActionLink := GetActionLinkClass.Create(Self);
  1224. FActionLink.Action := Value;
  1225. FActionLink.OnChange := DoActionChange;
  1226. { Note: Delphi's Controls.pas and Menus.pas merely check for
  1227. "csLoading in Value.ComponentState" here. But that doesn't help when
  1228. the Action property references an action on another form / data module
  1229. that has already finished loading. So we check two things:
  1230. 1. csLoading in Value.ComponentState
  1231. 2. csLoading in ComponentState
  1232. In the typical case where the item and action list reside on the same
  1233. form, #1 and #2 are both true.
  1234. Only #1 is true when Action references an action on another form / data
  1235. module that is created *after* the item (e.g. if Form1.TBItem1.Action =
  1236. Form2.Action1, and Form1 is created before Form2).
  1237. Only #2 is true when Action references an action on another form / data
  1238. module that is created *before* the item (e.g. if Form2.TBItem1.Action =
  1239. Form1.Action1, and Form1 is created before Form2). }
  1240. ActionChange(Value, (csLoading in Value.ComponentState) or
  1241. (csLoading in ComponentState));
  1242. Value.FreeNotification(Self);
  1243. end;
  1244. end;
  1245. procedure TTBCustomItem.InitiateAction;
  1246. begin
  1247. if FActionLink <> nil then FActionLink.Update;
  1248. end;
  1249. procedure TTBCustomItem.Loaded;
  1250. begin
  1251. inherited;
  1252. if Action <> nil then ActionChange(Action, True);
  1253. end;
  1254. procedure TTBCustomItem.GetChildren(Proc: TGetChildProc; Root: TComponent);
  1255. var
  1256. I: Integer;
  1257. begin
  1258. for I := 0 to FItemCount-1 do
  1259. Proc(FItems[I].Item);
  1260. end;
  1261. procedure TTBCustomItem.SetChildOrder(Child: TComponent; Order: Integer);
  1262. var
  1263. I: Integer;
  1264. begin
  1265. I := IndexOf(Child as TTBCustomItem);
  1266. if I <> -1 then
  1267. Move(I, Order);
  1268. end;
  1269. function TTBCustomItem.HasParent: Boolean;
  1270. begin
  1271. Result := True;
  1272. end;
  1273. function TTBCustomItem.GetParentComponent: TComponent;
  1274. begin
  1275. if (FParent <> nil) and (FParent.FParentComponent <> nil) then
  1276. Result := FParent.FParentComponent
  1277. else
  1278. Result := FParent;
  1279. end;
  1280. procedure TTBCustomItem.SetName(const NewName: TComponentName);
  1281. begin
  1282. if Name <> NewName then begin
  1283. inherited;
  1284. if Assigned(FParent) then
  1285. FParent.Notify(tbicNameChanged, -1, Self);
  1286. end;
  1287. end;
  1288. procedure TTBCustomItem.SetParentComponent(Value: TComponent);
  1289. var
  1290. Intf: ITBItems;
  1291. begin
  1292. if FParent <> nil then FParent.Remove(Self);
  1293. if Value <> nil then begin
  1294. if Value is TTBCustomItem then
  1295. TTBCustomItem(Value).Add(Self)
  1296. else if Value.GetInterface(ITBItems, Intf) then
  1297. Intf.GetItems.Add(Self);
  1298. end;
  1299. end;
  1300. procedure TTBCustomItem.Notification(AComponent: TComponent;
  1301. Operation: TOperation);
  1302. begin
  1303. inherited;
  1304. if Operation = opRemove then begin
  1305. RemoveFromList(FLinkParents, AComponent);
  1306. if AComponent = Action then Action := nil;
  1307. if AComponent = Images then Images := nil;
  1308. if AComponent = SubMenuImages then SubMenuImages := nil;
  1309. if AComponent = LinkSubitems then LinkSubitems := nil;
  1310. end;
  1311. end;
  1312. procedure TTBCustomItem.IndexError;
  1313. begin
  1314. raise ETBItemError.Create(STBToolbarIndexOutOfBounds);
  1315. end;
  1316. class procedure TTBCustomItem.ClickWndProc(var Message: TMessage);
  1317. var
  1318. List: TList;
  1319. I: Integer;
  1320. Item: TObject;
  1321. begin
  1322. if Message.Msg = WM_TB2K_CLICKITEM then begin
  1323. List := ClickList; { optimization... }
  1324. if Assigned(List) then begin
  1325. I := Message.LParam;
  1326. if (I >= 0) and (I < List.Count) then begin
  1327. Item := List[I];
  1328. List[I] := nil;
  1329. if Item = Pointer(1) then { is it a destroyed item? }
  1330. Item := nil;
  1331. end
  1332. else
  1333. Item := nil;
  1334. { Remove trailing nil items from ClickList. This is not *necessary*, but
  1335. it will make RemoveFromClickList faster if we clean out items that
  1336. aren't used, and may never be used again. }
  1337. for I := List.Count-1 downto 0 do begin
  1338. if List[I] = nil then
  1339. List.Delete(I)
  1340. else
  1341. Break;
  1342. end;
  1343. if Assigned(Item) then begin
  1344. try
  1345. if Item is TTBCustomItem then
  1346. TTBCustomItem(Item).Click
  1347. else if Item is TTBItemViewer then
  1348. TTBItemViewer(Item).AccSelect(Message.WParam <> 0);
  1349. except
  1350. Application.HandleException(Item);
  1351. end;
  1352. end;
  1353. end;
  1354. end
  1355. else
  1356. with Message do
  1357. Result := DefWindowProc(ClickWnd, Msg, wParam, lParam);
  1358. end;
  1359. procedure TTBCustomItem.PostClick;
  1360. { Posts a message to the message queue that causes the item's Click handler to
  1361. be executed when control is returned to the message loop.
  1362. This should be called instead of Click when a WM_SYSCOMMAND message is
  1363. (possibly) currently being handled, because TApplication.WndProc's
  1364. CM_APPSYSCOMMAND handler disables the VCL's processing of focus messages
  1365. until the Perform(WM_SYSCOMMAND, ...) call returns. (An OnClick handler which
  1366. calls TForm.ShowModal needs focus messages to be enabled or else the form
  1367. will be shown with no initial focus.) }
  1368. begin
  1369. QueueClick(Self, 0);
  1370. end;
  1371. procedure TTBCustomItem.Click;
  1372. begin
  1373. if Enabled then begin
  1374. { Following code based on D6's TMenuItem.Click }
  1375. if (not Assigned(ActionLink) and AutoCheck) or
  1376. (Assigned(ActionLink) and not ActionLink.IsAutoCheckLinked and AutoCheck) then
  1377. Checked := not Checked;
  1378. { Following code based on D4's TControl.Click }
  1379. { Call OnClick if assigned and not equal to associated action's OnExecute.
  1380. If associated action's OnExecute assigned then call it, otherwise, call
  1381. OnClick. }
  1382. if Assigned(FOnClick) and (Action <> nil) and
  1383. not MethodsEqual(TMethod(FOnClick), TMethod(Action.OnExecute)) then
  1384. FOnClick(Self)
  1385. else
  1386. if not(csDesigning in ComponentState) and (ActionLink <> nil) then
  1387. ActionLink.Execute(Self)
  1388. else
  1389. if Assigned(FOnClick) then
  1390. FOnClick(Self);
  1391. end;
  1392. end;
  1393. function TTBCustomItem.GetItem(Index: Integer): TTBCustomItem;
  1394. begin
  1395. if (Index < 0) or (Index >= FItemCount) then IndexError;
  1396. Result := FItems[Index].Item;
  1397. end;
  1398. procedure TTBCustomItem.Add(AItem: TTBCustomItem);
  1399. begin
  1400. Insert(Count, AItem);
  1401. end;
  1402. procedure TTBCustomItem.InternalNotify(Ancestor: TTBCustomItem;
  1403. NestingLevel: Integer; Action: TTBItemChangedAction; Index: Integer;
  1404. Item: TTBCustomItem);
  1405. { Note: Ancestor is Item's parent, or in the case of a group item relayed
  1406. notification, it can also be a group item which *links* to Item's parent
  1407. (i.e. ItemContainingItems(Ancestor) = Item.Parent). }
  1408. procedure RelayToParentOf(const AItem: TTBCustomItem);
  1409. begin
  1410. if NestingLevel > MaxGroupLevel then
  1411. Exit;
  1412. if (tbisEmbeddedGroup in AItem.ItemStyle) and Assigned(AItem.Parent) then begin
  1413. if Ancestor = Self then
  1414. AItem.Parent.InternalNotify(AItem, NestingLevel + 1, Action, Index, Item)
  1415. else
  1416. { Don't alter Ancestor on subsequent relays; only on the first. }
  1417. AItem.Parent.InternalNotify(Ancestor, NestingLevel + 1, Action, Index, Item);
  1418. end;
  1419. end;
  1420. var
  1421. I: Integer;
  1422. P: TTBCustomItem;
  1423. SaveProc: TTBItemChangedProc;
  1424. begin
  1425. { If Self is a group item, relay the notification to the parent }
  1426. RelayToParentOf(Self);
  1427. { If any group items are linked to Self, relay the notification to
  1428. those items' parents }
  1429. if Assigned(FLinkParents) then
  1430. for I := 0 to FLinkParents.Count-1 do begin
  1431. P := FLinkParents[I];
  1432. if P <> Parent then
  1433. RelayToParentOf(P);
  1434. end;
  1435. if Assigned(FNotifyList) then begin
  1436. I := 0;
  1437. while I < FNotifyList.Count do begin
  1438. with PItemChangedNotificationData(FNotifyList[I])^ do begin
  1439. SaveProc := Proc;
  1440. Proc(Ancestor, Ancestor <> Self, Action, Index, Item);
  1441. end;
  1442. { Is I now out of bounds? }
  1443. if I >= FNotifyList.Count then
  1444. Break;
  1445. { Only proceed to the next index if the list didn't change }
  1446. if MethodsEqual(TMethod(PItemChangedNotificationData(FNotifyList[I])^.Proc),
  1447. TMethod(SaveProc)) then
  1448. Inc(I);
  1449. end;
  1450. end;
  1451. end;
  1452. procedure TTBCustomItem.Notify(Action: TTBItemChangedAction; Index: Integer;
  1453. Item: TTBCustomItem);
  1454. begin
  1455. InternalNotify(Self, 0, Action, Index, Item);
  1456. end;
  1457. procedure TTBCustomItem.ViewBeginUpdate;
  1458. begin
  1459. Notify(tbicSubitemsBeginUpdate, -1, nil);
  1460. end;
  1461. procedure TTBCustomItem.ViewEndUpdate;
  1462. begin
  1463. Notify(tbicSubitemsEndUpdate, -1, nil);
  1464. end;
  1465. procedure TTBCustomItem.Insert(NewIndex: Integer; AItem: TTBCustomItem);
  1466. begin
  1467. if Assigned(AItem.FParent) then
  1468. raise ETBItemError.Create(STBToolbarItemReinserted);
  1469. if (NewIndex < 0) or (NewIndex > FItemCount) then IndexError;
  1470. InsertIntoItemArray(FItems, FItemCount, NewIndex, AItem);
  1471. AItem.FParent := Self;
  1472. ViewBeginUpdate;
  1473. try
  1474. Notify(tbicInserted, NewIndex, AItem);
  1475. AItem.RefreshOptions;
  1476. finally
  1477. ViewEndUpdate;
  1478. end;
  1479. end;
  1480. procedure TTBCustomItem.Delete(Index: Integer);
  1481. begin
  1482. if (Index < 0) or (Index >= FItemCount) then IndexError;
  1483. Notify(tbicDeleting, Index, FItems[Index].Item);
  1484. FItems[Index].Item.FParent := nil;
  1485. DeleteFromItemArray(FItems, FItemCount, Index);
  1486. end;
  1487. function TTBCustomItem.IndexOf(AItem: TTBCustomItem): Integer;
  1488. var
  1489. I: Integer;
  1490. begin
  1491. for I := 0 to FItemCount-1 do
  1492. if FItems[I].Item = AItem then begin
  1493. Result := I;
  1494. Exit;
  1495. end;
  1496. Result := -1;
  1497. end;
  1498. procedure TTBCustomItem.Remove(Item: TTBCustomItem);
  1499. var
  1500. I: Integer;
  1501. begin
  1502. I := IndexOf(Item);
  1503. //if I = -1 then raise ETBItemError.Create(STBToolbarItemNotFound);
  1504. if I <> -1 then
  1505. Delete(I);
  1506. end;
  1507. procedure TTBCustomItem.Clear;
  1508. var
  1509. I: Integer;
  1510. begin
  1511. for I := Count-1 downto 0 do
  1512. Items[I].Free;
  1513. end;
  1514. procedure TTBCustomItem.Move(CurIndex, NewIndex: Integer);
  1515. var
  1516. Item: TTBCustomItem;
  1517. begin
  1518. if CurIndex <> NewIndex then begin
  1519. if (NewIndex < 0) or (NewIndex >= FItemCount) then IndexError;
  1520. Item := Items[CurIndex];
  1521. ViewBeginUpdate;
  1522. try
  1523. Delete(CurIndex);
  1524. Insert(NewIndex, Item);
  1525. finally
  1526. ViewEndUpdate;
  1527. end;
  1528. end;
  1529. end;
  1530. function TTBCustomItem.ContainsItem(AItem: TTBCustomItem): Boolean;
  1531. begin
  1532. while Assigned(AItem) and (AItem <> Self) do
  1533. AItem := AItem.Parent;
  1534. Result := Assigned(AItem);
  1535. end;
  1536. procedure TTBCustomItem.RegisterNotification(ANotify: TTBItemChangedProc);
  1537. var
  1538. I: Integer;
  1539. Data: PItemChangedNotificationData;
  1540. begin
  1541. if FNotifyList = nil then FNotifyList := TList.Create;
  1542. for I := 0 to FNotifyList.Count-1 do
  1543. with PItemChangedNotificationData(FNotifyList[I])^ do
  1544. if MethodsEqual(TMethod(ANotify), TMethod(Proc)) then begin
  1545. Inc(RefCount);
  1546. Exit;
  1547. end;
  1548. FNotifyList.Expand;
  1549. New(Data);
  1550. Data.Proc := ANotify;
  1551. Data.RefCount := 1;
  1552. FNotifyList.Add(Data);
  1553. end;
  1554. procedure TTBCustomItem.UnregisterNotification(ANotify: TTBItemChangedProc);
  1555. var
  1556. I: Integer;
  1557. Data: PItemChangedNotificationData;
  1558. begin
  1559. if Assigned(FNotifyList) then
  1560. for I := 0 to FNotifyList.Count-1 do begin
  1561. Data := FNotifyList[I];
  1562. if MethodsEqual(TMethod(Data.Proc), TMethod(ANotify)) then begin
  1563. Dec(Data.RefCount);
  1564. if Data.RefCount = 0 then begin
  1565. FNotifyList.Delete(I);
  1566. Dispose(Data);
  1567. if FNotifyList.Count = 0 then begin
  1568. FNotifyList.Free;
  1569. FNotifyList := nil;
  1570. end;
  1571. end;
  1572. Break;
  1573. end;
  1574. end;
  1575. end;
  1576. function TTBCustomItem.GetPopupWindowClass: TTBPopupWindowClass;
  1577. begin
  1578. Result := TTBPopupWindow;
  1579. end;
  1580. procedure TTBCustomItem.DoPopup(Sender: TTBCustomItem; FromLink: Boolean);
  1581. begin
  1582. if Assigned(FOnPopup) then
  1583. FOnPopup(Sender, FromLink);
  1584. if not(tbisCombo in ItemStyle) then
  1585. Click;
  1586. end;
  1587. var
  1588. PlayedSound: Boolean = False;
  1589. procedure TTBCustomItem.GetPopupPosition(ParentView: TTBView;
  1590. PopupWindow: TTBPopupWindow; var PopupPositionRec: TTBPopupPositionRec);
  1591. var
  1592. X2, Y2: Integer;
  1593. RepeatCalcX: Boolean;
  1594. function CountObscured(X, Y, W, H: Integer): Integer;
  1595. var
  1596. I: Integer;
  1597. P: TPoint;
  1598. V: TTBItemViewer;
  1599. begin
  1600. Result := 0;
  1601. if ParentView = nil then
  1602. Exit;
  1603. P := ParentView.FWindow.ClientToScreen(Point(0, 0));
  1604. Dec(X, P.X);
  1605. Dec(Y, P.Y);
  1606. Inc(W, X);
  1607. Inc(H, Y);
  1608. for I := 0 to ParentView.FViewerCount-1 do begin
  1609. V := ParentView.FViewers[I];
  1610. if V.Show and (V.BoundsRect.Left >= X) and (V.BoundsRect.Right <= W) and
  1611. (V.BoundsRect.Top >= Y) and (V.BoundsRect.Bottom <= H) then
  1612. Inc(Result);
  1613. end;
  1614. end;
  1615. begin
  1616. with PopupPositionRec do
  1617. begin
  1618. { Adjust the Y position of the popup window }
  1619. { If the window is going off the bottom of the monitor, try placing it
  1620. above the parent item }
  1621. if (Y + H > MonitorRect.Bottom) and
  1622. ((ParentView = nil) or (ParentView.FOrientation <> tbvoVertical)) then begin
  1623. if not PositionAsSubmenu then
  1624. Y2 := ParentItemRect.Top
  1625. else
  1626. Y2 := ParentItemRect.Bottom + NCSizeY;
  1627. Dec(Y2, H);
  1628. { Only place it above the parent item if it isn't going to go off the
  1629. top of the monitor }
  1630. if Y2 >= MonitorRect.Top then
  1631. Y := Y2;
  1632. end;
  1633. { If it's still going off the bottom (which can be possible if a menu bar
  1634. was off the screen to begin with), clip it to the bottom of the monitor }
  1635. if Y + H > MonitorRect.Bottom then
  1636. Y := MonitorRect.Bottom - H;
  1637. if Y < MonitorRect.Top then
  1638. Y := MonitorRect.Top;
  1639. { Other adjustments to the position of the popup window }
  1640. if not PositionAsSubmenu then begin
  1641. if (ParentView = nil) and (Alignment = tbpaRight) and (X < MonitorRect.Left) then
  1642. Inc(X, W);
  1643. if X + W > MonitorRect.Right then begin
  1644. if Assigned(ParentView) or (Alignment <> tbpaLeft) then
  1645. X := MonitorRect.Right;
  1646. Dec(X, W);
  1647. end;
  1648. if X < MonitorRect.Left then
  1649. X := MonitorRect.Left;
  1650. if (ParentView = nil) or (ParentView.FOrientation <> tbvoVertical) then begin
  1651. Y2 := ParentItemRect.Top - H;
  1652. if Y2 >= MonitorRect.Top then begin
  1653. { Would the popup window obscure less items if it popped out to the
  1654. top instead? }
  1655. if (CountObscured(X, Y2, W, H) < CountObscured(X, Y, W, H)) or
  1656. ((Y < ParentItemRect.Bottom) and (Y + H > ParentItemRect.Top) and
  1657. (X < ParentItemRect.Right) and (X + W > ParentItemRect.Left)) then
  1658. Y := Y2;
  1659. end;
  1660. { Make sure a tall popup window doesn't overlap the parent item }
  1661. if (Y < ParentItemRect.Bottom) and (Y + H > ParentItemRect.Top) and
  1662. (X < ParentItemRect.Right) and (X + W > ParentItemRect.Left) then begin
  1663. if ParentItemRect.Right + W <= MonitorRect.Right then
  1664. X := ParentItemRect.Right
  1665. else
  1666. X := ParentItemRect.Left - W;
  1667. if X < MonitorRect.Top then
  1668. X := MonitorRect.Top;
  1669. end;
  1670. end
  1671. else begin
  1672. X2 := ParentItemRect.Right;
  1673. if X2 + W <= MonitorRect.Right then begin
  1674. { Would the popup window obscure less items if it popped out to the
  1675. right instead? }
  1676. if (CountObscured(X2, Y, W, H) < CountObscured(X, Y, W, H)) or
  1677. ((Y < ParentItemRect.Bottom) and (Y + H > ParentItemRect.Top) and
  1678. (X < ParentItemRect.Right) and (X + W > ParentItemRect.Left)) then
  1679. X := X2;
  1680. end;
  1681. { Make sure a wide popup window doesn't overlap the parent item }
  1682. if (Y < ParentItemRect.Bottom) and (Y + H > ParentItemRect.Top) and
  1683. (X < ParentItemRect.Right) and (X + W > ParentItemRect.Left) then begin
  1684. if ParentItemRect.Bottom + H <= MonitorRect.Bottom then
  1685. Y := ParentItemRect.Bottom
  1686. else
  1687. Y := ParentItemRect.Top - H;
  1688. if Y < MonitorRect.Top then
  1689. Y := MonitorRect.Top;
  1690. end;
  1691. end;
  1692. end
  1693. else begin
  1694. { Make nested submenus go from left to right on the screen. Each it
  1695. runs out of space on the screen, switch directions }
  1696. repeat
  1697. RepeatCalcX := False;
  1698. X2 := X;
  1699. if Opposite or (X2 + W > MonitorRect.Right) then begin
  1700. if Assigned(ParentView) then
  1701. X2 := ParentItemRect.Left + NCSizeX;
  1702. Dec(X2, W);
  1703. if not Opposite then
  1704. Include(PopupWindow.View.FState, vsOppositePopup)
  1705. else begin
  1706. if X2 < MonitorRect.Left then begin
  1707. Opposite := False;
  1708. RepeatCalcX := True;
  1709. end
  1710. else
  1711. Include(PopupWindow.View.FState, vsOppositePopup);
  1712. end;
  1713. end;
  1714. until not RepeatCalcX;
  1715. X := X2;
  1716. if X < MonitorRect.Left then
  1717. X := MonitorRect.Left;
  1718. end;
  1719. { Determine animation direction }
  1720. AnimDir := [];
  1721. if not PositionAsSubmenu then begin
  1722. if Y >= ParentItemRect.Bottom then
  1723. Include(AnimDir, tbadDown)
  1724. else if Y + H <= ParentItemRect.Top then
  1725. Include(AnimDir, tbadUp);
  1726. if X >= ParentItemRect.Right then
  1727. Include(AnimDir, tbadRight)
  1728. else if X + W <= ParentItemRect.Left then
  1729. Include(AnimDir, tbadLeft);
  1730. end
  1731. else begin
  1732. if X + W div 2 >= ParentItemRect.Left + (ParentItemRect.Right - ParentItemRect.Left) div 2 then
  1733. Include(AnimDir, tbadRight)
  1734. else
  1735. Include(AnimDir, tbadLeft);
  1736. end;
  1737. end;
  1738. end;
  1739. function TTBCustomItem.CreatePopup(const ParentView: TTBView;
  1740. const ParentViewer: TTBItemViewer; const PositionAsSubmenu, SelectFirstItem,
  1741. Customizing: Boolean; const APopupPoint: TPoint;
  1742. const Alignment: TTBPopupAlignment): TTBPopupWindow;
  1743. var
  1744. EventItem, ParentItem: TTBCustomItem;
  1745. Opposite: Boolean;
  1746. ChevronParentView: TTBView;
  1747. X, Y, W, H: Integer;
  1748. P: TPoint;
  1749. ParentItemRect: TRect;
  1750. MonitorRect: TRect;
  1751. PopupRec: TTBPopupPositionRec;
  1752. NCSize: TPoint;
  1753. begin
  1754. EventItem := ItemContainingItems(Self);
  1755. if EventItem <> Self then
  1756. EventItem.DoPopup(Self, True);
  1757. DoPopup(Self, False);
  1758. ChevronParentView := GetChevronParentView;
  1759. if ChevronParentView = nil then
  1760. ParentItem := Self
  1761. else
  1762. ParentItem := ChevronParentView.FParentItem;
  1763. Opposite := Assigned(ParentView) and (vsOppositePopup in ParentView.FState);
  1764. Result := GetPopupWindowClass.CreatePopupWindow(nil, ParentView, ParentItem,
  1765. Customizing, APopupPoint);
  1766. try
  1767. if Assigned(ChevronParentView) then begin
  1768. ChevronParentView.FreeNotification(Result.View);
  1769. Result.View.FChevronParentView := ChevronParentView;
  1770. Result.View.FIsToolbar := True;
  1771. Result.View.Style := Result.View.Style +
  1772. (ChevronParentView.Style * [vsAlwaysShowHints]);
  1773. Result.Color := clBtnFace;
  1774. end;
  1775. { Calculate ParentItemRect, and MonitorRect (the rectangle of the monitor
  1776. that the popup window will be confined to) }
  1777. if Assigned(ParentView) then begin
  1778. ParentView.ValidatePositions;
  1779. ParentItemRect := ParentViewer.BoundsRect;
  1780. P := ParentView.FWindow.ClientToScreen(Point(0, 0));
  1781. OffsetRect(ParentItemRect, P.X, P.Y);
  1782. if not IsRectEmpty(ParentView.FMonitorRect) then
  1783. MonitorRect := ParentView.FMonitorRect
  1784. else
  1785. { MP (display menu on correct monitor) }
  1786. MonitorRect := GetRectOfMonitorContainingRect(ParentItemRect, True);
  1787. {MonitorRect := GetRectOfMonitorContainingPoint(APopupPoint, False);} {vb-}
  1788. { MP }
  1789. {MonitorRect := GetRectOfMonitorContainingPoint(APopupPoint, True);} {vb+}
  1790. end
  1791. else begin
  1792. ParentItemRect.TopLeft := APopupPoint;
  1793. ParentItemRect.BottomRight := APopupPoint;
  1794. {MonitorRect := GetRectOfMonitorContainingPoint(APopupPoint, False);} {vb-}
  1795. MonitorRect := GetRectOfMonitorContainingPoint(APopupPoint, True); {vb+}
  1796. end;
  1797. Result.View.FMonitorRect := MonitorRect;
  1798. { Initialize item positions and size of the popup window }
  1799. NCSize := Result.GetNCSize;
  1800. if ChevronParentView = nil then
  1801. Result.View.FMaxHeight := (MonitorRect.Bottom - MonitorRect.Top) -
  1802. (NCSize.Y * 2)
  1803. else
  1804. Result.View.WrapOffset := (MonitorRect.Right - MonitorRect.Left) -
  1805. (NCSize.X * 2);
  1806. if SelectFirstItem then
  1807. Result.View.Selected := Result.View.FirstSelectable;
  1808. Result.View.UpdatePositions;
  1809. W := Result.Width;
  1810. H := Result.Height;
  1811. { Calculate initial X,Y position of the popup window }
  1812. if Assigned(ParentView) then begin
  1813. if not PositionAsSubmenu then begin
  1814. if ChevronParentView = nil then begin
  1815. if (ParentView = nil) or (ParentView.FOrientation <> tbvoVertical) then begin
  1816. if GetSystemMetrics(SM_MENUDROPALIGNMENT) = 0 then
  1817. X := ParentItemRect.Left
  1818. else
  1819. X := ParentItemRect.Right - W;
  1820. Y := ParentItemRect.Bottom;
  1821. end
  1822. else begin
  1823. X := ParentItemRect.Left - W;
  1824. Y := ParentItemRect.Top;
  1825. end;
  1826. end
  1827. else begin
  1828. if ChevronParentView.FOrientation <> tbvoVertical then begin
  1829. X := ParentItemRect.Right - W;
  1830. Y := ParentItemRect.Bottom;
  1831. end
  1832. else begin
  1833. X := ParentItemRect.Left - W;
  1834. Y := ParentItemRect.Top;
  1835. end;
  1836. end;
  1837. end
  1838. else begin
  1839. X := ParentItemRect.Right - NCSize.X;
  1840. Y := ParentItemRect.Top - NCSize.Y;
  1841. end;
  1842. end
  1843. else begin
  1844. X := APopupPoint.X;
  1845. Y := APopupPoint.Y;
  1846. case Alignment of
  1847. tbpaRight: Dec(X, W);
  1848. tbpaCenter: Dec(X, W div 2);
  1849. end;
  1850. end;
  1851. PopupRec.PositionAsSubmenu := PositionAsSubmenu;
  1852. PopupRec.Alignment := Alignment;
  1853. PopupRec.Opposite := Opposite;
  1854. PopupRec.MonitorRect := MonitorRect;
  1855. PopupRec.ParentItemRect := ParentItemRect;
  1856. PopupRec.NCSizeX := NCSize.X;
  1857. PopupRec.NCSizeY := NCSize.Y;
  1858. PopupRec.X := X;
  1859. PopupRec.Y := Y;
  1860. PopupRec.W := W;
  1861. PopupRec.H := H;
  1862. PopupRec.AnimDir := [];
  1863. PopupRec.PlaySound := True;
  1864. GetPopupPosition(ParentView, Result, PopupRec);
  1865. X := PopupRec.X;
  1866. Y := PopupRec.Y;
  1867. W := PopupRec.W;
  1868. H := PopupRec.H;
  1869. Result.FAnimationDirection := PopupRec.AnimDir;
  1870. Result.SetBounds(X, Y, W, H);
  1871. if Assigned(ParentView) then begin
  1872. Result.FreeNotification(ParentView);
  1873. ParentView.FOpenViewerWindow := Result;
  1874. ParentView.FOpenViewerView := Result.View;
  1875. ParentView.FOpenViewer := ParentViewer;
  1876. if ParentView.FIsToolbar then begin
  1877. Include(ParentView.FState, vsDropDownMenus);
  1878. ParentView.Invalidate(ParentViewer);
  1879. ParentView.FWindow.Update;
  1880. end;
  1881. end;
  1882. Include(Result.View.FState, vsDrawInOrder);
  1883. if not PopupRec.PlaySound or not NeedToPlaySound('MenuPopup') then begin
  1884. { Don't call PlaySound if we don't have to }
  1885. Result.Visible := True;
  1886. end
  1887. else begin
  1888. if not PlayedSound then begin
  1889. { Work around Windows 2000 "bug" where there's a 1/3 second delay upon the
  1890. first call to PlaySound (or sndPlaySound) by painting the window
  1891. completely first. This way the delay isn't very noticable. }
  1892. PlayedSound := True;
  1893. Result.Visible := True;
  1894. Result.Update;
  1895. PlaySoundA('MenuPopup', 0, SND_ALIAS or SND_ASYNC or SND_NODEFAULT or SND_NOSTOP);
  1896. end
  1897. else begin
  1898. PlaySoundA('MenuPopup', 0, SND_ALIAS or SND_ASYNC or SND_NODEFAULT or SND_NOSTOP);
  1899. Result.Visible := True;
  1900. end;
  1901. end;
  1902. NotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, Result.View.FWindow.Handle,
  1903. OBJID_CLIENT, CHILDID_SELF);
  1904. { Call NotifyFocusEvent now that the window is visible }
  1905. if Assigned(Result.View.Selected) then
  1906. Result.View.NotifyFocusEvent;
  1907. except
  1908. Result.Free;
  1909. raise;
  1910. end;
  1911. end;
  1912. function TTBCustomItem.OpenPopup(const SelectFirstItem, TrackRightButton: Boolean;
  1913. const PopupPoint: TPoint; const Alignment: TTBPopupAlignment;
  1914. const ReturnClickedItemOnly: Boolean; PositionAsSubmenu: Boolean): TTBCustomItem;
  1915. var
  1916. ModalHandler: TTBModalHandler;
  1917. Popup: TTBPopupWindow;
  1918. DoneActionData: TTBDoneActionData;
  1919. begin
  1920. ModalHandler := TTBModalHandler.Create(0);
  1921. try
  1922. Popup := CreatePopup(nil, nil, PositionAsSubmenu, SelectFirstItem, False, PopupPoint,
  1923. Alignment);
  1924. try
  1925. Include(Popup.View.FState, vsIgnoreFirstMouseUp);
  1926. ModalHandler.RootPopup := Popup;
  1927. ModalHandler.Loop(Popup.View, False, False, False, TrackRightButton);
  1928. DoneActionData := Popup.View.FDoneActionData;
  1929. finally
  1930. ModalHandler.RootPopup := nil;
  1931. { Remove vsModal state from the root view before any TTBView.Destroy
  1932. methods get called, so that NotifyFocusEvent becomes a no-op }
  1933. Exclude(Popup.View.FState, vsModal);
  1934. Popup.Free;
  1935. end;
  1936. finally
  1937. ModalHandler.Free;
  1938. end;
  1939. Result := ProcessDoneAction(DoneActionData, ReturnClickedItemOnly);
  1940. end;
  1941. function TTBCustomItem.Popup(X, Y: Integer; TrackRightButton: Boolean;
  1942. Alignment: TTBPopupAlignment = tbpaLeft;
  1943. ReturnClickedItemOnly: Boolean = False;
  1944. PositionAsSubmenu: Boolean = False): TTBCustomItem;
  1945. var
  1946. P: TPoint;
  1947. begin
  1948. P.X := X;
  1949. P.Y := Y;
  1950. Result := OpenPopup(False, TrackRightButton, P, Alignment,
  1951. ReturnClickedItemOnly, PositionAsSubmenu);
  1952. end;
  1953. function TTBCustomItem.FindItemWithShortCut(AShortCut: TShortCut;
  1954. var ATopmostParent: TTBCustomItem): TTBCustomItem;
  1955. function DoItem(AParentItem: TTBCustomItem; LinkDepth: Integer): TTBCustomItem;
  1956. var
  1957. I: Integer;
  1958. NewParentItem, Item: TTBCustomItem;
  1959. begin
  1960. Result := nil;
  1961. NewParentItem := AParentItem;
  1962. if Assigned(NewParentItem.LinkSubitems) then begin
  1963. NewParentItem := NewParentItem.LinkSubitems;
  1964. Inc(LinkDepth);
  1965. if LinkDepth > 25 then
  1966. Exit; { prevent infinite link recursion }
  1967. end;
  1968. for I := 0 to NewParentItem.Count-1 do begin
  1969. Item := NewParentItem.Items[I];
  1970. if Item.ShortCut = AShortCut then begin
  1971. Result := Item;
  1972. Exit;
  1973. end;
  1974. Result := DoItem(Item, LinkDepth);
  1975. if Assigned(Result) then begin
  1976. ATopmostParent := Item;
  1977. Exit;
  1978. end;
  1979. end;
  1980. end;
  1981. begin
  1982. ATopmostParent := nil;
  1983. Result := DoItem(Self, 0);
  1984. end;
  1985. function TTBCustomItem.IsShortCut(var Message: TWMKey): Boolean;
  1986. var
  1987. ShortCut: TShortCut;
  1988. ShiftState: TShiftState;
  1989. ShortCutItem, TopmostItem, Item, EventItem: TTBCustomItem;
  1990. I: Integer;
  1991. label 1;
  1992. begin
  1993. Result := False;
  1994. ShiftState := KeyDataToShiftState(Message.KeyData);
  1995. ShortCut := Menus.ShortCut(Message.CharCode, ShiftState);
  1996. 1:ShortCutItem := FindItemWithShortCut(ShortCut, TopmostItem);
  1997. if Assigned(ShortCutItem) then begin
  1998. { Send OnPopup/OnClick events to ShortCutItem's parents so that they can
  1999. update the Enabled state of ShortCutItem if needed }
  2000. Item := Self;
  2001. repeat
  2002. if not Item.Enabled then
  2003. Exit;
  2004. EventItem := ItemContainingItems(Item);
  2005. if not(csDesigning in ComponentState) then begin
  2006. for I := 0 to EventItem.Count-1 do
  2007. EventItem.Items[I].InitiateAction;
  2008. end;
  2009. if not(tbisEmbeddedGroup in Item.ItemStyle) then begin
  2010. if EventItem <> Item then begin
  2011. try
  2012. EventItem.DoPopup(Item, True);
  2013. except
  2014. Application.HandleException(Self);
  2015. end;
  2016. end;
  2017. try
  2018. Item.DoPopup(Item, False);
  2019. except
  2020. Application.HandleException(Self);
  2021. end;
  2022. end;
  2023. ShortCutItem := Item.FindItemWithShortCut(ShortCut, TopmostItem);
  2024. if ShortCutItem = nil then
  2025. { Can no longer find the shortcut inside TopmostItem. Start over
  2026. because the shortcut might have moved. }
  2027. goto 1;
  2028. Item := TopmostItem;
  2029. until Item = nil;
  2030. if ShortCutItem.Enabled then begin
  2031. try
  2032. ShortCutItem.Click;
  2033. except
  2034. Application.HandleException(Self);
  2035. end;
  2036. Result := True;
  2037. end;
  2038. end;
  2039. end;
  2040. function TTBCustomItem.GetChevronParentView: TTBView;
  2041. begin
  2042. Result := nil;
  2043. end;
  2044. function TTBCustomItem.GetItemViewerClass(AView: TTBView): TTBItemViewerClass;
  2045. begin
  2046. Result := TTBItemViewer;
  2047. end;
  2048. function TTBCustomItem.NeedToRecreateViewer(AViewer: TTBItemViewer): Boolean;
  2049. begin
  2050. Result := False;
  2051. end;
  2052. function TTBCustomItem.GetShortCutText: String;
  2053. var
  2054. P: Integer;
  2055. begin
  2056. P := Pos(#9, Caption);
  2057. if P = 0 then begin
  2058. if ShortCut <> 0 then
  2059. Result := ShortCutToText(ShortCut)
  2060. else
  2061. Result := '';
  2062. end
  2063. else
  2064. Result := Copy(Caption, P+1, Maxint);
  2065. end;
  2066. procedure TTBCustomItem.Change(NeedResize: Boolean);
  2067. const
  2068. ItemChangedActions: array[Boolean] of TTBItemChangedAction =
  2069. (tbicInvalidate, tbicInvalidateAndResize);
  2070. begin
  2071. if Assigned(FParent) then
  2072. FParent.Notify(ItemChangedActions[NeedResize], -1, Self);
  2073. end;
  2074. procedure TTBCustomItem.RecreateItemViewers;
  2075. begin
  2076. if Assigned(FParent) then
  2077. FParent.Notify(tbicRecreateItemViewers, -1, Self);
  2078. end;
  2079. procedure TTBCustomItem.ImageListChangeHandler(Sender: TObject);
  2080. var
  2081. Resize: Boolean;
  2082. begin
  2083. if Sender = FSubMenuImages then begin
  2084. FSubMenuImagesChangeLink.FLastWidth := FSubMenuImages.Width;
  2085. FSubMenuImagesChangeLink.FLastHeight := FSubMenuImages.Height;
  2086. SubMenuImagesChanged;
  2087. end
  2088. else begin
  2089. { Sender is FImages }
  2090. Resize := False;
  2091. if (FImagesChangeLink.FLastWidth <> FImages.Width) or
  2092. (FImagesChangeLink.FLastHeight <> FImages.Height) then begin
  2093. FImagesChangeLink.FLastWidth := FImages.Width;
  2094. FImagesChangeLink.FLastHeight := FImages.Height;
  2095. Resize := True;
  2096. end;
  2097. Change(Resize);
  2098. end;
  2099. end;
  2100. procedure TTBCustomItem.SubMenuImagesChanged;
  2101. begin
  2102. Notify(tbicSubMenuImagesChanged, -1, nil);
  2103. end;
  2104. procedure TTBCustomItem.TurnSiblingsOff;
  2105. var
  2106. I: Integer;
  2107. Item: TTBCustomItem;
  2108. begin
  2109. if (GroupIndex <> 0) and Assigned(FParent) then begin
  2110. for I := 0 to FParent.Count-1 do begin
  2111. Item := FParent[I];
  2112. if (Item <> Self) and (Item.GroupIndex = GroupIndex) then
  2113. Item.Checked := False;
  2114. end;
  2115. end;
  2116. end;
  2117. procedure TTBCustomItem.SetCaption(Value: String);
  2118. begin
  2119. if FCaption <> Value then begin
  2120. FCaption := Value;
  2121. Change(True);
  2122. end;
  2123. end;
  2124. procedure TTBCustomItem.SetChecked(Value: Boolean);
  2125. begin
  2126. if FChecked <> Value then begin
  2127. FChecked := Value;
  2128. Change(False);
  2129. if Value then
  2130. TurnSiblingsOff;
  2131. end;
  2132. end;
  2133. procedure TTBCustomItem.SetDisplayMode(Value: TTBItemDisplayMode);
  2134. begin
  2135. if FDisplayMode <> Value then begin
  2136. FDisplayMode := Value;
  2137. Change(True);
  2138. end;
  2139. end;
  2140. procedure TTBCustomItem.EnabledChanged;
  2141. begin
  2142. Change(False);
  2143. end;
  2144. procedure TTBCustomItem.SetEnabled(Value: Boolean);
  2145. begin
  2146. if FEnabled <> Value then begin
  2147. FEnabled := Value;
  2148. EnabledChanged;
  2149. end;
  2150. end;
  2151. procedure TTBCustomItem.SetGroupIndex(Value: Integer);
  2152. begin
  2153. if FGroupIndex <> Value then begin
  2154. FGroupIndex := Value;
  2155. if Checked then
  2156. TurnSiblingsOff;
  2157. end;
  2158. end;
  2159. procedure TTBCustomItem.SetImageIndex(Value: TImageIndex);
  2160. var
  2161. HadNoImage: Boolean;
  2162. begin
  2163. if FImageIndex <> Value then begin
  2164. HadNoImage := FImageIndex = -1;
  2165. FImageIndex := Value;
  2166. Change(HadNoImage xor (Value = -1));
  2167. end;
  2168. end;
  2169. function TTBCustomItem.ChangeImages(var AImages: TCustomImageList;
  2170. const Value: TCustomImageList; var AChangeLink: TTBImageChangeLink): Boolean;
  2171. { Returns True if image list was resized }
  2172. var
  2173. LastWidth, LastHeight: Integer;
  2174. begin
  2175. Result := False;
  2176. LastWidth := -1;
  2177. LastHeight := -1;
  2178. if Assigned(AImages) then begin
  2179. LastWidth := AImages.Width;
  2180. LastHeight := AImages.Height;
  2181. AImages.UnregisterChanges(AChangeLink);
  2182. if Value = nil then begin
  2183. AChangeLink.Free;
  2184. AChangeLink := nil;
  2185. Result := True;
  2186. end;
  2187. end;
  2188. AImages := Value;
  2189. if Assigned(Value) then begin
  2190. Result := (Value.Width <> LastWidth) or (Value.Height <> LastHeight);
  2191. if AChangeLink = nil then begin
  2192. AChangeLink := TTBImageChangeLink.Create;
  2193. AChangeLink.FLastWidth := Value.Width;
  2194. AChangeLink.FLastHeight := Value.Height;
  2195. AChangeLink.OnChange := ImageListChangeHandler;
  2196. end;
  2197. Value.RegisterChanges(AChangeLink);
  2198. Value.FreeNotification(Self);
  2199. end;
  2200. end;
  2201. procedure TTBCustomItem.SetImages(Value: TCustomImageList);
  2202. begin
  2203. if FImages <> Value then
  2204. Change(ChangeImages(FImages, Value, FImagesChangeLink));
  2205. end;
  2206. procedure TTBCustomItem.SetSubMenuImages(Value: TCustomImageList);
  2207. begin
  2208. if FSubMenuImages <> Value then begin
  2209. ChangeImages(FSubMenuImages, Value, FSubMenuImagesChangeLink);
  2210. SubMenuImagesChanged;
  2211. end;
  2212. end;
  2213. procedure TTBCustomItem.SetInheritOptions(Value: Boolean);
  2214. begin
  2215. if FInheritOptions <> Value then begin
  2216. FInheritOptions := Value;
  2217. RefreshOptions;
  2218. end;
  2219. end;
  2220. procedure TTBCustomItem.SetLinkSubitems(Value: TTBCustomItem);
  2221. begin
  2222. if Value = Self then
  2223. Value := nil;
  2224. if FLinkSubitems <> Value then begin
  2225. if Assigned(FLinkSubitems) then
  2226. RemoveFromList(FLinkSubitems.FLinkParents, Self);
  2227. FLinkSubitems := Value;
  2228. if Assigned(Value) then begin
  2229. Value.FreeNotification(Self);
  2230. AddToList(Value.FLinkParents, Self);
  2231. end;
  2232. Notify(tbicSubitemsChanged, -1, nil);
  2233. end;
  2234. end;
  2235. function TTBCustomItem.FixOptions(const AOptions: TTBItemOptions): TTBItemOptions;
  2236. begin
  2237. Result := AOptions;
  2238. if not(tboToolbarStyle in Result) then
  2239. Exclude(Result, tboToolbarSize);
  2240. end;
  2241. procedure TTBCustomItem.RefreshOptions;
  2242. const
  2243. NonInheritedOptions = [tboDefault];
  2244. ChangeOptions = [tboDefault, tboDropdownArrow, tboImageAboveCaption,
  2245. tboNoRotation, tboSameWidth, tboToolbarStyle, tboToolbarSize];
  2246. var
  2247. OldOptions, NewOptions: TTBItemOptions;
  2248. I: Integer;
  2249. Item: TTBCustomItem;
  2250. begin
  2251. OldOptions := FEffectiveOptions;
  2252. if FInheritOptions and Assigned(FParent) then
  2253. NewOptions := FParent.FEffectiveOptions - NonInheritedOptions
  2254. else
  2255. NewOptions := [];
  2256. NewOptions := FixOptions(NewOptions - FMaskOptions + FOptions);
  2257. if FEffectiveOptions <> NewOptions then begin
  2258. FEffectiveOptions := NewOptions;
  2259. if (OldOptions * ChangeOptions) <> (NewOptions * ChangeOptions) then
  2260. Change(True);
  2261. for I := 0 to FItemCount-1 do begin
  2262. Item := FItems[I].Item;
  2263. if Item.FInheritOptions then
  2264. Item.RefreshOptions;
  2265. end;
  2266. end;
  2267. end;
  2268. procedure TTBCustomItem.SetMaskOptions(Value: TTBItemOptions);
  2269. begin
  2270. if FMaskOptions <> Value then begin
  2271. FMaskOptions := Value;
  2272. RefreshOptions;
  2273. end;
  2274. end;
  2275. procedure TTBCustomItem.SetOptions(Value: TTBItemOptions);
  2276. begin
  2277. Value := FixOptions(Value);
  2278. if FOptions <> Value then begin
  2279. FOptions := Value;
  2280. RefreshOptions;
  2281. end;
  2282. end;
  2283. procedure TTBCustomItem.SetRadioItem(Value: Boolean);
  2284. begin
  2285. if FRadioItem <> Value then begin
  2286. FRadioItem := Value;
  2287. Change(False);
  2288. end;
  2289. end;
  2290. procedure TTBCustomItem.SetVisible(Value: Boolean);
  2291. begin
  2292. if FVisible <> Value then begin
  2293. FVisible := Value;
  2294. Change(True);
  2295. end;
  2296. end;
  2297. procedure TTBCustomItem.ChangeScale(M, D: Integer);
  2298. var
  2299. I: Integer;
  2300. begin
  2301. for I := 0 to Count - 1 do
  2302. begin
  2303. Items[I].ChangeScale(M, D);
  2304. end;
  2305. end;
  2306. { TTBGroupItem }
  2307. constructor TTBGroupItem.Create(AOwner: TComponent);
  2308. begin
  2309. inherited;
  2310. ItemStyle := ItemStyle + [tbisEmbeddedGroup, tbisSubitemsEditable];
  2311. end;
  2312. { TTBSubmenuItem }
  2313. constructor TTBSubmenuItem.Create(AOwner: TComponent);
  2314. begin
  2315. inherited;
  2316. ItemStyle := ItemStyle + [tbisSubMenu, tbisSubitemsEditable];
  2317. end;
  2318. function TTBSubmenuItem.GetDropdownCombo: Boolean;
  2319. begin
  2320. Result := tbisCombo in ItemStyle;
  2321. end;
  2322. procedure TTBSubmenuItem.SetDropdownCombo(Value: Boolean);
  2323. begin
  2324. if (tbisCombo in ItemStyle) <> Value then begin
  2325. if Value then
  2326. ItemStyle := ItemStyle + [tbisCombo]
  2327. else
  2328. ItemStyle := ItemStyle - [tbisCombo];
  2329. Change(True);
  2330. end;
  2331. end;
  2332. { TTBSeparatorItem }
  2333. constructor TTBSeparatorItem.Create(AOwner: TComponent);
  2334. begin
  2335. inherited;
  2336. ItemStyle := ItemStyle - [tbisSelectable, tbisRedrawOnSelChange,
  2337. tbisRedrawOnMouseOverChange] + [tbisSeparator, tbisClicksTransparent];
  2338. end;
  2339. function TTBSeparatorItem.GetItemViewerClass(AView: TTBView): TTBItemViewerClass;
  2340. begin
  2341. Result := TTBSeparatorItemViewer;
  2342. end;
  2343. procedure TTBSeparatorItem.SetBlank(Value: Boolean);
  2344. begin
  2345. if FBlank <> Value then begin
  2346. FBlank := Value;
  2347. Change(False);
  2348. end;
  2349. end;
  2350. { TTBSeparatorItemViewer }
  2351. procedure TTBSeparatorItemViewer.CalcSize(const Canvas: TCanvas;
  2352. var AWidth, AHeight: Integer);
  2353. begin
  2354. if not IsToolbarStyle then
  2355. Inc(AHeight, DivRoundUp(GetTextHeight(Canvas.Handle) * 2, 3))
  2356. else begin
  2357. AWidth := 6;
  2358. AHeight := 6;
  2359. end;
  2360. end;
  2361. procedure TTBSeparatorItemViewer.Paint(const Canvas: TCanvas;
  2362. const ClientAreaRect: TRect; IsSelected, IsPushed, UseDisabledShadow: Boolean);
  2363. var
  2364. DC: HDC;
  2365. R: TRect;
  2366. ToolbarStyle, Horiz, LineSep: Boolean;
  2367. begin
  2368. DC := Canvas.Handle;
  2369. if TTBSeparatorItem(Item).FBlank then
  2370. Exit;
  2371. R := ClientAreaRect;
  2372. ToolbarStyle := IsToolbarStyle;
  2373. Horiz := not ToolbarStyle or (View.FOrientation = tbvoVertical);
  2374. LineSep := tbisLineSep in State;
  2375. if LineSep then
  2376. Horiz := not Horiz;
  2377. if Horiz then begin
  2378. R.Top := R.Bottom div 2 - 1;
  2379. if not ToolbarStyle then
  2380. InflateRect(R, -tbMenuSeparatorOffset, 0)
  2381. else if LineSep then begin
  2382. if View.FOrientation = tbvoFloating then
  2383. InflateRect(R, -tbLineSepOffset, 0)
  2384. else
  2385. InflateRect(R, -tbDockedLineSepOffset, 0);
  2386. end;
  2387. DrawEdge(DC, R, EDGE_ETCHED, BF_TOP);
  2388. end
  2389. else begin
  2390. R.Left := R.Right div 2 - 1;
  2391. if LineSep then
  2392. InflateRect(R, 0, -tbDockedLineSepOffset);
  2393. DrawEdge(DC, R, EDGE_ETCHED, BF_LEFT);
  2394. end;
  2395. end;
  2396. function TTBSeparatorItemViewer.UsesSameWidth: Boolean;
  2397. begin
  2398. Result := False;
  2399. end;
  2400. { TTBControlItem }
  2401. constructor TTBControlItem.Create(AOwner: TComponent);
  2402. begin
  2403. inherited;
  2404. ItemStyle := ItemStyle - [tbisSelectable] + [tbisClicksTransparent];
  2405. end;
  2406. constructor TTBControlItem.CreateControlItem(AOwner: TComponent;
  2407. AControl: TControl);
  2408. begin
  2409. FControl := AControl;
  2410. AControl.FreeNotification(Self);
  2411. Create(AOwner);
  2412. end;
  2413. destructor TTBControlItem.Destroy;
  2414. begin
  2415. inherited;
  2416. { Free the associated control *after* the item is completely destroyed }
  2417. if not FDontFreeControl and Assigned(FControl) and
  2418. not(csAncestor in FControl.ComponentState) then
  2419. FControl.Free;
  2420. end;
  2421. procedure TTBControlItem.Notification(AComponent: TComponent;
  2422. Operation: TOperation);
  2423. begin
  2424. inherited;
  2425. if (Operation = opRemove) and (AComponent = FControl) then
  2426. Control := nil;
  2427. end;
  2428. procedure TTBControlItem.SetControl(Value: TControl);
  2429. begin
  2430. if FControl <> Value then begin
  2431. FControl := Value;
  2432. if Assigned(Value) then
  2433. Value.FreeNotification(Self);
  2434. Change(True);
  2435. end;
  2436. end;
  2437. { TTBItemViewer }
  2438. constructor TTBItemViewer.Create(AView: TTBView; AItem: TTBCustomItem;
  2439. AGroupLevel: Integer);
  2440. begin
  2441. FItem := AItem;
  2442. FView := AView;
  2443. FGroupLevel := AGroupLevel;
  2444. ReferenceClickWnd;
  2445. end;
  2446. destructor TTBItemViewer.Destroy;
  2447. begin
  2448. RemoveFromClickList(Self);
  2449. if Assigned(FAccObjectInstance) then begin
  2450. FAccObjectInstance.ClientIsDestroying;
  2451. FAccObjectInstance := nil;
  2452. end;
  2453. inherited;
  2454. ReleaseClickWnd;
  2455. end;
  2456. function TTBItemViewer.GetAccObject: IDispatch;
  2457. begin
  2458. if FAccObjectInstance = nil then begin
  2459. FAccObjectInstance := TTBItemViewerAccObject.Create(Self);
  2460. end;
  2461. Result := FAccObjectInstance;
  2462. end;
  2463. procedure TTBItemViewer.AccSelect(const AExecute: Boolean);
  2464. { Called by ClickWndProc when an item of type TTBItemViewer is in ClickList }
  2465. var
  2466. Obj: IDispatch;
  2467. begin
  2468. { Ensure FAccObjectInstance is created by calling GetAccObject }
  2469. Obj := GetAccObject;
  2470. if Assigned(Obj) then
  2471. (FAccObjectInstance as TTBItemViewerAccObject).HandleAccSelect(AExecute);
  2472. end;
  2473. procedure TTBItemViewer.PostAccSelect(const AExecute: Boolean);
  2474. { Internally called by TTBItemViewerAccObject. Don't call directly. }
  2475. begin
  2476. QueueClick(Self, Ord(AExecute));
  2477. end;
  2478. function TTBItemViewer.IsAccessible: Boolean;
  2479. { Returns True if MSAA clients should know about the viewer, specifically
  2480. if it's either shown, off-edge, or clipped (in other words, not completely
  2481. invisible/inaccessible). }
  2482. begin
  2483. { Note: Can't simply check Item.Visible because the chevron item's Visible
  2484. property is always True }
  2485. Result := Show or OffEdge or Clipped;
  2486. end;
  2487. function TTBItemViewer.GetCaptionText: String;
  2488. var
  2489. P: Integer;
  2490. begin
  2491. Result := Item.Caption;
  2492. P := Pos(#9, Result);
  2493. if P <> 0 then
  2494. SetLength(Result, P-1);
  2495. { MP }
  2496. if IsToolbarStyle and not (vsMenuBar in View.Style) then
  2497. Result := StripAccelChars(StripTrailingPunctuation(Result), True);
  2498. end;
  2499. function TTBItemViewer.GetHintText: String;
  2500. var
  2501. P: Integer;
  2502. LongHint: string;
  2503. HintStyleCaption: string;
  2504. begin
  2505. if Pos('|', Item.Hint) > 0 then
  2506. begin
  2507. Result := GetShortHint(Item.Hint);
  2508. LongHint := GetLongHint(Item.Hint);
  2509. end
  2510. else
  2511. begin
  2512. LongHint := Item.Hint;
  2513. end;
  2514. HintStyleCaption := StripAccelChars(StripTrailingPunctuation(GetCaptionText));
  2515. { If there is no short hint, use the caption for the hint. Like Office,
  2516. strip any trailing colon or ellipsis. }
  2517. if (Result = '') and (not(tboNoAutoHint in Item.EffectiveOptions) or (LongHint <> '')) and
  2518. (not(tbisSubmenu in Item.ItemStyle) or (tbisCombo in Item.ItemStyle) or
  2519. not CaptionShown) then
  2520. Result := HintStyleCaption;
  2521. { Call associated action's OnHint event handler to post-process the hint }
  2522. if Assigned(Item.ActionLink) and
  2523. (Item.ActionLink.Action is TCustomAction) then begin
  2524. if not TCustomAction(Item.ActionLink.Action).DoHint(Result) then
  2525. Result := '';
  2526. { Note: TControlActionLink.DoShowHint actually misinterprets the result
  2527. of DoHint, but we get it right... }
  2528. end;
  2529. if Result = '' then
  2530. Result := LongHint;
  2531. // "Select all" and "Select All" are still the same
  2532. if SameText(LongHint, Result) then
  2533. LongHint := '';
  2534. if CaptionShown and (LongHint = '') and SameText(Result, HintStyleCaption) then
  2535. Result := '';
  2536. { Add shortcut text }
  2537. if (Result <> '') and Application.HintShortCuts then
  2538. begin
  2539. { Custom shortcut }
  2540. P := Pos(#9, Item.Caption);
  2541. if (P <> 0) and (P < Length(Item.Caption)) then
  2542. Result := Format('%s (%s)', [Result, Copy(Item.Caption, P+ 1, MaxInt)])
  2543. else
  2544. if (Item.ShortCut <> scNone) then
  2545. Result := Format('%s (%s)', [Result, ShortCutToText(Item.ShortCut)]);
  2546. end;
  2547. if LongHint <> '' then
  2548. Result := Result + '|' + GetLongHint(LongHint);
  2549. end;
  2550. function TTBItemViewer.CaptionShown: Boolean;
  2551. begin
  2552. Result := (GetCaptionText <> '') and (not IsToolbarSize or
  2553. (Item.ImageIndex < 0) or (Item.DisplayMode in [nbdmTextOnly, nbdmImageAndText])) or
  2554. (tboImageAboveCaption in Item.EffectiveOptions);
  2555. end;
  2556. function TTBItemViewer.ImageShown: Boolean;
  2557. begin
  2558. {}{should also return false if Images=nil (use UsedImageList?)}
  2559. ImageShown := (Item.ImageIndex >= 0) and
  2560. ((Item.DisplayMode in [nbdmDefault, nbdmImageAndText]) or
  2561. (IsToolbarStyle and (Item.DisplayMode = nbdmTextOnlyInMenus)));
  2562. end;
  2563. function TTBItemViewer.GetImageList: TCustomImageList;
  2564. var
  2565. V: TTBView;
  2566. begin
  2567. Result := Item.Images;
  2568. if Assigned(Result) then
  2569. Exit;
  2570. V := View;
  2571. repeat
  2572. if Assigned(V.FCurParentItem) then begin
  2573. Result := V.FCurParentItem.SubMenuImages;
  2574. if Assigned(Result) then
  2575. Break;
  2576. end;
  2577. if Assigned(V.FParentItem) then begin
  2578. Result := V.FParentItem.SubMenuImages;
  2579. if Assigned(Result) then
  2580. Break;
  2581. end;
  2582. V := V.FParentView;
  2583. until V = nil;
  2584. end;
  2585. function TTBItemViewer.IsRotated: Boolean;
  2586. { Returns True if the caption should be drawn with rotated (vertical) text,
  2587. underneath the image }
  2588. begin
  2589. Result := (View.Orientation = tbvoVertical) and
  2590. not (tboNoRotation in Item.EffectiveOptions) and
  2591. not (tboImageAboveCaption in Item.EffectiveOptions);
  2592. end;
  2593. procedure TTBItemViewer.CalcSize(const Canvas: TCanvas;
  2594. var AWidth, AHeight: Integer);
  2595. var
  2596. ToolbarStyle: Boolean;
  2597. DC: HDC;
  2598. TextMetrics: TTextMetric;
  2599. H, LeftMargin: Integer;
  2600. ImgList: TCustomImageList;
  2601. S: String;
  2602. RotatedFont, SaveFont: HFONT;
  2603. begin
  2604. ToolbarStyle := IsToolbarStyle;
  2605. DC := Canvas.Handle;
  2606. ImgList := GetImageList;
  2607. if ToolbarStyle then begin
  2608. AWidth := 6;
  2609. AHeight := 6;
  2610. end
  2611. else begin
  2612. AWidth := 0;
  2613. AHeight := 0;
  2614. end;
  2615. if not ToolbarStyle or CaptionShown then begin
  2616. if not IsRotated then begin
  2617. GetTextMetrics(DC, TextMetrics);
  2618. Inc(AHeight, TextMetrics.tmHeight);
  2619. Inc(AWidth, GetTextWidth(DC, GetCaptionText, True));
  2620. if ToolbarStyle then
  2621. Inc(AWidth, 6);
  2622. end
  2623. else begin
  2624. { Vertical text isn't always the same size as horizontal text, so we have
  2625. to select the rotated font into the DC to get an accurate size }
  2626. RotatedFont := CreateRotatedFont(DC);
  2627. SaveFont := SelectObject(DC, RotatedFont);
  2628. GetTextMetrics(DC, TextMetrics);
  2629. Inc(AWidth, TextMetrics.tmHeight);
  2630. Inc(AHeight, GetTextWidth(DC, GetCaptionText, True));
  2631. if ToolbarStyle then
  2632. Inc(AHeight, 6);
  2633. SelectObject(DC, SaveFont);
  2634. DeleteObject(RotatedFont);
  2635. end;
  2636. end;
  2637. if ToolbarStyle and ImageShown and Assigned(ImgList) then begin
  2638. if not IsRotated and not(tboImageAboveCaption in Item.EffectiveOptions) then begin
  2639. Inc(AWidth, ImgList.Width + 1);
  2640. if AHeight < ImgList.Height + 6 then
  2641. AHeight := ImgList.Height + 6;
  2642. end
  2643. else begin
  2644. Inc(AHeight, ImgList.Height);
  2645. if AWidth < ImgList.Width + 7 then
  2646. AWidth := ImgList.Width + 7;
  2647. end;
  2648. end;
  2649. if ToolbarStyle and (tbisSubmenu in Item.ItemStyle) then begin
  2650. if tbisCombo in Item.ItemStyle then
  2651. Inc(AWidth, tbDropdownComboArrowWidth)
  2652. else
  2653. if tboDropdownArrow in Item.EffectiveOptions then begin
  2654. if View.Orientation <> tbvoVertical then
  2655. Inc(AWidth, tbDropdownArrowWidth)
  2656. else
  2657. Inc(AHeight, tbDropdownArrowWidth);
  2658. end;
  2659. end;
  2660. if not ToolbarStyle then begin
  2661. Inc(AHeight, TextMetrics.tmExternalLeading + tbMenuVerticalMargin);
  2662. if Assigned(ImgList) then begin
  2663. H := ImgList.Height + 3;
  2664. if H > AHeight then
  2665. AHeight := H;
  2666. LeftMargin := MulDiv(ImgList.Width + 3, AHeight, H);
  2667. end
  2668. else
  2669. LeftMargin := AHeight;
  2670. Inc(AWidth, LeftMargin + tbMenuImageTextSpace + tbMenuLeftTextMargin +
  2671. tbMenuRightTextMargin);
  2672. S := Item.GetShortCutText;
  2673. if S <> '' then
  2674. Inc(AWidth, (AHeight - 6) + GetTextWidth(DC, S, True));
  2675. Inc(AWidth, AHeight);
  2676. end;
  2677. end;
  2678. procedure TTBItemViewer.DrawItemCaption(const Canvas: TCanvas; ARect: TRect;
  2679. const ACaption: String; ADrawDisabledShadow: Boolean; AFormat: UINT);
  2680. var
  2681. DC: HDC;
  2682. procedure Draw;
  2683. begin
  2684. if not IsRotated then
  2685. DrawText(DC, PChar(ACaption), Length(ACaption), ARect, AFormat)
  2686. else
  2687. DrawRotatedText(DC, ACaption, ARect, AFormat);
  2688. end;
  2689. var
  2690. ShadowColor, HighlightColor, SaveTextColor: DWORD;
  2691. begin
  2692. DC := Canvas.Handle;
  2693. if not ADrawDisabledShadow then
  2694. Draw
  2695. else begin
  2696. ShadowColor := GetSysColor(COLOR_BTNSHADOW);
  2697. HighlightColor := GetSysColor(COLOR_BTNHIGHLIGHT);
  2698. OffsetRect(ARect, 1, 1);
  2699. SaveTextColor := SetTextColor(DC, HighlightColor);
  2700. Draw;
  2701. OffsetRect(ARect, -1, -1);
  2702. SetTextColor(DC, ShadowColor);
  2703. Draw;
  2704. SetTextColor(DC, SaveTextColor);
  2705. end;
  2706. end;
  2707. procedure TTBItemViewer.Paint(const Canvas: TCanvas;
  2708. const ClientAreaRect: TRect; IsSelected, IsPushed, UseDisabledShadow: Boolean);
  2709. var
  2710. ShowEnabled, HasArrow: Boolean;
  2711. MenuCheckWidth, MenuCheckHeight: Integer;
  2712. function GetDrawTextFlags: UINT;
  2713. begin
  2714. Result := 0;
  2715. if not AreKeyboardCuesEnabled and (vsUseHiddenAccels in View.FStyle) and
  2716. not(vsShowAccels in View.FState) then
  2717. Result := DT_HIDEPREFIX;
  2718. end;
  2719. procedure DrawSubmenuArrow;
  2720. var
  2721. BR: TRect;
  2722. Bmp: TBitmap;
  2723. procedure DrawWithColor(AColor: TColor);
  2724. const
  2725. ROP_DSPDxax = $00E20746;
  2726. var
  2727. DC: HDC;
  2728. SaveTextColor, SaveBkColor: TColorRef;
  2729. begin
  2730. Canvas.Brush.Color := AColor;
  2731. DC := Canvas.Handle;
  2732. SaveTextColor := SetTextColor(DC, clWhite);
  2733. SaveBkColor := SetBkColor(DC, clBlack);
  2734. BitBlt(DC, BR.Left, BR.Top, MenuCheckWidth, MenuCheckHeight,
  2735. Bmp.Canvas.Handle, 0, 0, ROP_DSPDxax);
  2736. SetBkColor(DC, SaveBkColor);
  2737. SetTextColor(DC, SaveTextColor);
  2738. Canvas.Brush.Style := bsClear;
  2739. end;
  2740. begin
  2741. Bmp := TBitmap.Create;
  2742. try
  2743. Bmp.Monochrome := True;
  2744. Bmp.Width := MenuCheckWidth;
  2745. Bmp.Height := MenuCheckHeight;
  2746. BR := Rect(0, 0, MenuCheckWidth, MenuCheckHeight);
  2747. DrawFrameControl(Bmp.Canvas.Handle, BR, DFC_MENU, DFCS_MENUARROW);
  2748. OffsetRect(BR, ClientAreaRect.Right - MenuCheckWidth,
  2749. ClientAreaRect.Top + ((ClientAreaRect.Bottom - ClientAreaRect.Top) - MenuCheckHeight) div 2);
  2750. if not UseDisabledShadow then begin
  2751. if ShowEnabled and (tbisCombo in Item.ItemStyle) and IsSelected then begin
  2752. OffsetRect(BR, 1, 1);
  2753. DrawWithColor(clBtnText);
  2754. end
  2755. else
  2756. DrawWithColor(Canvas.Font.Color);
  2757. end
  2758. else begin
  2759. OffsetRect(BR, 1, 1);
  2760. DrawWithColor(clBtnHighlight);
  2761. OffsetRect(BR, -1, -1);
  2762. DrawWithColor(clBtnShadow);
  2763. end;
  2764. finally
  2765. Bmp.Free;
  2766. end;
  2767. end;
  2768. procedure DrawDropdownArrow(R: TRect; Rotated: Boolean);
  2769. procedure DrawWithColor(AColor: TColor);
  2770. var
  2771. X, Y: Integer;
  2772. P: array[0..2] of TPoint;
  2773. begin
  2774. X := (R.Left + R.Right) div 2;
  2775. Y := (R.Top + R.Bottom) div 2;
  2776. if not Rotated then begin
  2777. Dec(Y);
  2778. P[0].X := X-2;
  2779. P[0].Y := Y;
  2780. P[1].X := X+2;
  2781. P[1].Y := Y;
  2782. P[2].X := X;
  2783. P[2].Y := Y+2;
  2784. end
  2785. else begin
  2786. Dec(X);
  2787. P[0].X := X;
  2788. P[0].Y := Y+2;
  2789. P[1].X := X;
  2790. P[1].Y := Y-2;
  2791. P[2].X := X-2;
  2792. P[2].Y := Y;
  2793. end;
  2794. Canvas.Pen.Color := AColor;
  2795. Canvas.Brush.Color := AColor;
  2796. Canvas.Polygon(P);
  2797. end;
  2798. begin
  2799. if not UseDisabledShadow then
  2800. DrawWithColor(Canvas.Font.Color)
  2801. else begin
  2802. OffsetRect(R, 1, 1);
  2803. DrawWithColor(clBtnHighlight);
  2804. OffsetRect(R, -1, -1);
  2805. DrawWithColor(clBtnShadow);
  2806. end;
  2807. end;
  2808. function GetDitherBitmap: TBitmap;
  2809. begin
  2810. Result := AllocPatternBitmap(clBtnFace, clBtnHighlight);
  2811. Result.HandleType := bmDDB; { needed for Win95, or else brush is solid white }
  2812. end;
  2813. const
  2814. EdgeStyles: array[Boolean] of UINT = (BDR_RAISEDINNER, BDR_SUNKENOUTER);
  2815. CheckMarkPoints: array[0..11] of TPoint = (
  2816. { Black }
  2817. (X: -2; Y: -2), (X: 0; Y: 0), (X: 4; Y: -4),
  2818. (X: 4; Y: -3), (X: 0; Y: 1), (X: -2; Y: -1),
  2819. (X: -2; Y: -2),
  2820. { White }
  2821. (X: -3; Y: -2), (X: -3; Y: -1), (X: 0; Y: 2),
  2822. (X: 5; Y: -3), (X: 5; Y: -5));
  2823. var
  2824. ToolbarStyle, ImageIsShown: Boolean;
  2825. R, RC, RD: TRect;
  2826. S: String;
  2827. ImgList: TCustomImageList;
  2828. I, X, Y: Integer;
  2829. Points: array[0..11] of TPoint;
  2830. DrawTextFlags: UINT;
  2831. LeftMargin: Integer;
  2832. TextMetrics: TTextMetric;
  2833. begin
  2834. ToolbarStyle := IsToolbarStyle;
  2835. ShowEnabled := Item.Enabled or View.Customizing;
  2836. HasArrow := (tbisSubmenu in Item.ItemStyle) and
  2837. ((tbisCombo in Item.ItemStyle) or (tboDropdownArrow in Item.EffectiveOptions));
  2838. MenuCheckWidth := GetSystemMetricsForControl(View.FWindow, SM_CXMENUCHECK);
  2839. MenuCheckHeight := GetSystemMetricsForControl(View.FWindow, SM_CYMENUCHECK);
  2840. ImgList := GetImageList;
  2841. ImageIsShown := ImageShown and Assigned(ImgList);
  2842. LeftMargin := 0;
  2843. if not ToolbarStyle then begin
  2844. if Assigned(ImgList) then
  2845. LeftMargin := MulDiv(ImgList.Width + 3, ClientAreaRect.Bottom, ImgList.Height + 3)
  2846. else
  2847. LeftMargin := ClientAreaRect.Bottom;
  2848. end;
  2849. { Border }
  2850. RC := ClientAreaRect;
  2851. if ToolbarStyle then begin
  2852. if HasArrow then begin
  2853. if tbisCombo in Item.ItemStyle then begin
  2854. Dec(RC.Right, tbDropdownComboMargin);
  2855. RD := RC;
  2856. Dec(RC.Right, tbDropdownComboArrowWidth - tbDropdownComboMargin);
  2857. RD.Left := RC.Right;
  2858. end
  2859. else begin
  2860. if View.Orientation <> tbvoVertical then
  2861. RD := Rect(RC.Right - tbDropdownArrowWidth - tbDropdownArrowMargin, 0,
  2862. RC.Right - tbDropdownArrowMargin, RC.Bottom)
  2863. else
  2864. RD := Rect(0, RC.Bottom - tbDropdownArrowWidth - tbDropdownArrowMargin,
  2865. RC.Right, RC.Bottom - tbDropdownArrowMargin);
  2866. end;
  2867. end
  2868. else
  2869. SetRectEmpty(RD);
  2870. if (IsSelected and ShowEnabled) or Item.Checked or
  2871. (csDesigning in Item.ComponentState) then begin
  2872. if not(tbisCombo in Item.ItemStyle) then
  2873. DrawEdge(Canvas.Handle, RC, EdgeStyles[IsPushed or Item.Checked], BF_RECT)
  2874. else begin
  2875. DrawEdge(Canvas.Handle, RC, EdgeStyles[(IsPushed and View.FCapture) or Item.Checked], BF_RECT);
  2876. if (IsSelected and ShowEnabled) or
  2877. (csDesigning in Item.ComponentState) then
  2878. DrawEdge(Canvas.Handle, RD, EdgeStyles[IsPushed and not View.FCapture], BF_RECT);
  2879. end;
  2880. end;
  2881. if HasArrow then begin
  2882. if not(tbisCombo in Item.ItemStyle) and IsPushed then
  2883. OffsetRect(RD, 1, 1);
  2884. DrawDropdownArrow(RD, not(tbisCombo in Item.ItemStyle) and
  2885. (View.Orientation = tbvoVertical));
  2886. end;
  2887. InflateRect(RC, -1, -1);
  2888. if Item.Checked and not (IsSelected and ShowEnabled) then begin
  2889. Canvas.Brush.Bitmap := GetDitherBitmap;
  2890. Canvas.FillRect(RC);
  2891. Canvas.Brush.Style := bsClear;
  2892. end;
  2893. InflateRect(RC, -1, -1);
  2894. if Item.Checked or
  2895. ((IsSelected and IsPushed) and
  2896. (not(tbisCombo in Item.ItemStyle) or View.FCapture)) then
  2897. OffsetRect(RC, 1, 1);
  2898. if HasArrow and not(tbisCombo in Item.ItemStyle) then begin
  2899. if View.Orientation <> tbvoVertical then
  2900. Dec(RC.Right, tbDropdownArrowWidth)
  2901. else
  2902. Dec(RC.Bottom, tbDropdownArrowWidth);
  2903. end;
  2904. end
  2905. else begin
  2906. { On selected menu items, fill the background with the selected color.
  2907. Note: This assumes the brush color was not changed from the initial
  2908. value. }
  2909. if IsSelected then begin
  2910. R := RC;
  2911. if ImageIsShown or Item.Checked then
  2912. Inc(R.Left, LeftMargin + tbMenuImageTextSpace);
  2913. if (tbisCombo in Item.ItemStyle) and IsSelected and ShowEnabled then
  2914. Dec(R.Right, MenuCheckWidth);
  2915. Canvas.FillRect(R);
  2916. end;
  2917. end;
  2918. { Adjust brush & font }
  2919. Canvas.Brush.Style := bsClear;
  2920. if tboDefault in Item.EffectiveOptions then
  2921. with Canvas.Font do Style := Style + [fsBold];
  2922. GetTextMetrics(Canvas.Handle, TextMetrics);
  2923. { Caption }
  2924. if CaptionShown then begin
  2925. S := GetCaptionText;
  2926. R := RC;
  2927. DrawTextFlags := GetDrawTextFlags;
  2928. if ToolbarStyle then begin
  2929. if ImageIsShown then begin
  2930. if not IsRotated and not(tboImageAboveCaption in Item.EffectiveOptions) then
  2931. Inc(R.Left, ImgList.Width + 1)
  2932. else
  2933. Inc(R.Top, ImgList.Height + 1);
  2934. end;
  2935. DrawItemCaption(Canvas, R, S, UseDisabledShadow,
  2936. DT_SINGLELINE or DT_CENTER or DT_VCENTER or DrawTextFlags)
  2937. end
  2938. else begin
  2939. Inc(R.Left, LeftMargin + tbMenuImageTextSpace + tbMenuLeftTextMargin);
  2940. { Like standard menus, shift the text up one pixel if the text height
  2941. is 4 pixels less than the total item height. This is done so underlined
  2942. characters aren't displayed too low. }
  2943. if (R.Bottom - R.Top) - (TextMetrics.tmHeight + TextMetrics.tmExternalLeading) = tbMenuVerticalMargin then
  2944. Dec(R.Bottom);
  2945. Inc(R.Top, TextMetrics.tmExternalLeading);
  2946. DrawItemCaption(Canvas, R, S, UseDisabledShadow,
  2947. DT_SINGLELINE or DT_LEFT or DT_VCENTER or DrawTextFlags);
  2948. end;
  2949. end;
  2950. { Shortcut and/or submenu arrow (menus only) }
  2951. if not ToolbarStyle then begin
  2952. S := Item.GetShortCutText;
  2953. if S <> '' then begin
  2954. R := RC;
  2955. R.Left := R.Right - (R.Bottom - R.Top) - GetTextWidth(Canvas.Handle, S, True);
  2956. { Like standard menus, shift the text up one pixel if the text height
  2957. is 4 pixels less than the total item height. This is done so underlined
  2958. characters aren't displayed too low. }
  2959. if (R.Bottom - R.Top) - (TextMetrics.tmHeight + TextMetrics.tmExternalLeading) = tbMenuVerticalMargin then
  2960. Dec(R.Bottom);
  2961. Inc(R.Top, TextMetrics.tmExternalLeading);
  2962. DrawItemCaption(Canvas, R, S, UseDisabledShadow,
  2963. DT_SINGLELINE or DT_LEFT or DT_VCENTER or DT_NOPREFIX);
  2964. end;
  2965. if tbisSubmenu in Item.ItemStyle then begin
  2966. if tbisCombo in Item.ItemStyle then begin
  2967. R := RC;
  2968. R.Left := R.Right - MenuCheckWidth;
  2969. if IsSelected and ShowEnabled then
  2970. DrawEdge(Canvas.Handle, R, BDR_SUNKENOUTER, BF_RECT or BF_MIDDLE)
  2971. else begin
  2972. Dec(R.Left);
  2973. if not IsSelected then
  2974. DrawEdge(Canvas.Handle, R, EDGE_ETCHED, BF_LEFT)
  2975. else
  2976. DrawEdge(Canvas.Handle, R, BDR_SUNKENOUTER, BF_LEFT);
  2977. end;
  2978. end;
  2979. DrawSubmenuArrow;
  2980. end;
  2981. end;
  2982. { Image, or check box }
  2983. if ImageIsShown or (not ToolbarStyle and Item.Checked) then begin
  2984. R := RC;
  2985. if ToolbarStyle then begin
  2986. if not IsRotated and not(tboImageAboveCaption in Item.EffectiveOptions) then
  2987. R.Right := R.Left + ImgList.Width + 2
  2988. else
  2989. R.Bottom := R.Top + ImgList.Height + 2;
  2990. end
  2991. else begin
  2992. R.Right := R.Left + LeftMargin;
  2993. if (IsSelected and ShowEnabled) or Item.Checked then
  2994. DrawEdge(Canvas.Handle, R, EdgeStyles[Item.Checked], BF_RECT or BF_MIDDLE);
  2995. if Item.Checked and not IsSelected then begin
  2996. InflateRect(R, -1, -1);
  2997. Canvas.Brush.Bitmap := GetDitherBitmap;
  2998. Canvas.FillRect(R);
  2999. Canvas.Brush.Style := bsClear;
  3000. InflateRect(R, 1, 1);
  3001. end;
  3002. if Item.Checked then
  3003. OffsetRect(R, 1, 1);
  3004. end;
  3005. if ImageIsShown then begin
  3006. X := R.Left + ((R.Right - R.Left) - ImgList.Width) div 2;
  3007. Y := R.Top + ((R.Bottom - R.Top) - ImgList.Height) div 2;
  3008. if ImgList is TTBCustomImageList then
  3009. TTBCustomImageList(ImgList).DrawState(Canvas, X, Y, Item.ImageIndex,
  3010. ShowEnabled, IsSelected, Item.Checked)
  3011. else
  3012. ImgList.Draw(Canvas, X, Y, Item.ImageIndex, ShowEnabled);
  3013. end
  3014. else
  3015. if not ToolbarStyle and Item.Checked then begin
  3016. { Draw default check mark or radio button image when user hasn't
  3017. specified their own }
  3018. X := (R.Left + R.Right) div 2;
  3019. Y := (R.Top + R.Bottom) div 2;
  3020. if Item.RadioItem then begin
  3021. Canvas.Pen.Color := clBtnText;
  3022. Canvas.Brush.Color := clBtnText;
  3023. Canvas.RoundRect(X-3, Y-3, X+2, Y+2, 2, 2);
  3024. Canvas.Pen.Color := clBtnHighlight;
  3025. Canvas.Brush.Style := bsClear;
  3026. Canvas.RoundRect(X-4, Y-4, X+3, Y+3, 6, 6);
  3027. end
  3028. else begin
  3029. Dec(X, 2);
  3030. Inc(Y);
  3031. System.Move(CheckMarkPoints, Points, 12 * SizeOf(TPoint));
  3032. for I := Low(Points) to High(Points) do begin
  3033. Inc(Points[I].X, X);
  3034. Inc(Points[I].Y, Y);
  3035. end;
  3036. Canvas.Pen.Color := clBtnText;
  3037. Polyline(Canvas.Handle, Points[0], 7);
  3038. Canvas.Pen.Color := clBtnHighlight;
  3039. Polyline(Canvas.Handle, Points[7], 5);
  3040. end;
  3041. end;
  3042. end;
  3043. end;
  3044. procedure TTBItemViewer.GetCursor(const Pt: TPoint; var ACursor: HCURSOR);
  3045. begin
  3046. end;
  3047. function TTBItemViewer.GetIndex: Integer;
  3048. begin
  3049. Result := View.IndexOf(Self);
  3050. end;
  3051. function TTBItemViewer.IsToolbarSize: Boolean;
  3052. begin
  3053. Result := View.FIsToolbar or (tboToolbarSize in Item.FEffectiveOptions);
  3054. end;
  3055. function TTBItemViewer.IsToolbarStyle: Boolean;
  3056. begin
  3057. Result := View.FIsToolbar or (tboToolbarStyle in Item.FEffectiveOptions);
  3058. end;
  3059. function TTBItemViewer.IsPtInButtonPart(X, Y: Integer): Boolean;
  3060. var
  3061. W: Integer;
  3062. begin
  3063. Result := not(tbisSubmenu in Item.ItemStyle);
  3064. if tbisCombo in Item.ItemStyle then begin
  3065. if IsToolbarStyle then
  3066. W := tbDropdownComboArrowWidth
  3067. else
  3068. W := GetSystemMetricsForControl(View.FWindow, SM_CXMENUCHECK);
  3069. Result := X < (BoundsRect.Right - BoundsRect.Left) - W;
  3070. end;
  3071. end;
  3072. procedure TTBItemViewer.MouseDown(Shift: TShiftState; X, Y: Integer;
  3073. var MouseDownOnMenu: Boolean);
  3074. procedure HandleDefaultDoubleClick(const View: TTBView);
  3075. { Looks for a tboDefault item in View and ends the modal loop if it finds
  3076. one. }
  3077. var
  3078. I: Integer;
  3079. Viewer: TTBItemViewer;
  3080. Item: TTBCustomItem;
  3081. begin
  3082. for I := 0 to View.FViewerCount-1 do begin
  3083. Viewer := View.FViewers[I];
  3084. Item := Viewer.Item;
  3085. if (Viewer.Show or Viewer.Clipped) and (tboDefault in Item.EffectiveOptions) and
  3086. (tbisSelectable in Item.ItemStyle) and Item.Enabled and Item.Visible then begin
  3087. Viewer.Execute(True);
  3088. Break;
  3089. end;
  3090. end;
  3091. end;
  3092. var
  3093. WasAlreadyOpen: Boolean;
  3094. begin
  3095. if not Item.Enabled then begin
  3096. if (View.FParentView = nil) and not View.FIsPopup then
  3097. View.EndModal;
  3098. Exit;
  3099. end;
  3100. if IsPtInButtonPart(X, Y) then begin
  3101. if IsToolbarStyle then begin
  3102. View.CancelChildPopups;
  3103. View.SetCapture;
  3104. View.Invalidate(Self);
  3105. end;
  3106. end
  3107. else begin
  3108. WasAlreadyOpen := (View.FOpenViewer = Self);
  3109. if View.OpenChildPopup(False) then begin
  3110. if WasAlreadyOpen and ((View.FParentView = nil) and not View.FIsPopup) then
  3111. MouseDownOnMenu := True;
  3112. if (ssDouble in Shift) and not(tbisCombo in Item.ItemStyle) then
  3113. HandleDefaultDoubleClick(View.FOpenViewerView);
  3114. end;
  3115. end;
  3116. end;
  3117. procedure TTBItemViewer.MouseMove(X, Y: Integer);
  3118. begin
  3119. end;
  3120. procedure TTBItemViewer.MouseUp(X, Y: Integer; MouseWasDownOnMenu: Boolean);
  3121. var
  3122. HadCapture, IsToolbarItem: Boolean;
  3123. begin
  3124. HadCapture := View.FCapture;
  3125. View.CancelCapture;
  3126. IsToolbarItem := (View.FParentView = nil) and not View.FIsPopup;
  3127. if not View.FMouseOverSelected or not Item.Enabled or
  3128. (tbisClicksTransparent in Item.ItemStyle) then begin
  3129. if IsToolbarItem then
  3130. View.EndModal;
  3131. Exit;
  3132. end;
  3133. if (tbisSubmenu in Item.ItemStyle) and not IsPtInButtonPart(X, Y) then begin
  3134. if IsToolbarItem and MouseWasDownOnMenu then
  3135. View.EndModal;
  3136. end
  3137. else begin
  3138. { it's a 'normal' item }
  3139. if not IsToolbarStyle or HadCapture then
  3140. Execute(True);
  3141. end;
  3142. end;
  3143. procedure TTBItemViewer.MouseWheel(WheelDelta, X, Y: Integer);
  3144. begin
  3145. end;
  3146. procedure TTBItemViewer.LosingCapture;
  3147. begin
  3148. View.Invalidate(Self);
  3149. end;
  3150. procedure TTBItemViewer.Entering(OldSelected: TTBItemViewer);
  3151. begin
  3152. if Assigned(Item.FOnSelect) then
  3153. Item.FOnSelect(Item, Self, True);
  3154. end;
  3155. procedure TTBItemViewer.Leaving;
  3156. begin
  3157. if Assigned(Item.FOnSelect) then
  3158. Item.FOnSelect(Item, Self, False);
  3159. end;
  3160. procedure TTBItemViewer.KeyDown(var Key: Word; Shift: TShiftState);
  3161. begin
  3162. end;
  3163. function TTBItemViewer.ScreenToClient(const P: TPoint): TPoint;
  3164. begin
  3165. Result := View.FWindow.ScreenToClient(P);
  3166. Dec(Result.X, BoundsRect.Left);
  3167. Dec(Result.Y, BoundsRect.Top);
  3168. end;
  3169. function TTBItemViewer.UsesSameWidth: Boolean;
  3170. { If UsesSameWidth returns True, the item viewer's width will be expanded to
  3171. match the widest item viewer on the same view whose UsesSameWidth method
  3172. also returns True. }
  3173. begin
  3174. Result := (tboImageAboveCaption in Item.FEffectiveOptions) and
  3175. (tboSameWidth in Item.FEffectiveOptions) and IsToolbarSize;
  3176. end;
  3177. function TTBItemViewer.DoExecute: Boolean;
  3178. { Low-level 'execute' handler. Returns True if the caller should call
  3179. GivePriority on the viewer (normally, if the 'execute' operation was a
  3180. success and the modal loop is ending). }
  3181. begin
  3182. View.EndModalWithClick(Self);
  3183. Result := True;
  3184. end;
  3185. procedure TTBItemViewer.Execute(AGivePriority: Boolean);
  3186. { Calls DoExecute and, if applicable, View.GivePriority. Note that it is up to
  3187. the caller to check the viewer's visibility and enabled state. }
  3188. begin
  3189. if DoExecute and AGivePriority then
  3190. View.GivePriority(Self);
  3191. end;
  3192. function TTBItemViewer.GetAccRole: Integer;
  3193. { Returns the MSAA "role" of the viewer. }
  3194. const
  3195. { Constants from OleAcc.h }
  3196. ROLE_SYSTEM_CLIENT = $a;
  3197. ROLE_SYSTEM_MENUITEM = $c;
  3198. ROLE_SYSTEM_SEPARATOR = $15;
  3199. ROLE_SYSTEM_PUSHBUTTON = $2b;
  3200. ROLE_SYSTEM_BUTTONMENU = $39;
  3201. begin
  3202. if Item is TTBControlItem then
  3203. Result := ROLE_SYSTEM_CLIENT
  3204. else if tbisSeparator in Item.ItemStyle then
  3205. Result := ROLE_SYSTEM_SEPARATOR
  3206. else if View.IsPopup or (vsMenuBar in View.Style) then
  3207. Result := ROLE_SYSTEM_MENUITEM
  3208. else if tbisSubmenu in Item.ItemStyle then
  3209. Result := ROLE_SYSTEM_BUTTONMENU
  3210. else
  3211. Result := ROLE_SYSTEM_PUSHBUTTON;
  3212. end;
  3213. function TTBItemViewer.GetAccValue(var Value: WideString): Boolean;
  3214. { Gets the MSAA "value" text of the viewer. Returns True if something was
  3215. assigned to Value, or False if the viewer does not possess a "value". }
  3216. begin
  3217. Result := False;
  3218. end;
  3219. { TTBView }
  3220. constructor TTBView.CreateView(AOwner: TComponent; AParentView: TTBView;
  3221. AParentItem: TTBCustomItem; AWindow: TWinControl;
  3222. AIsToolbar, ACustomizing, AUsePriorityList: Boolean);
  3223. begin
  3224. Create(AOwner);
  3225. FBackgroundColor := clDefault;
  3226. FCustomizing := ACustomizing;
  3227. FIsPopup := not AIsToolbar;
  3228. FIsToolbar := AIsToolbar;
  3229. FNewViewersGetHighestPriority := True;
  3230. FParentView := AParentView;
  3231. FParentItem := AParentItem;
  3232. if Assigned(FParentItem) then begin
  3233. //FIsToolbar := FIsToolbar or FParentItem.FDisplayAsToolbar;
  3234. FParentItem.RegisterNotification(LinkNotification);
  3235. FParentItem.FreeNotification(Self);
  3236. end;
  3237. FUsePriorityList := AUsePriorityList;
  3238. FWindow := AWindow;
  3239. UpdateCurParentItem;
  3240. end;
  3241. destructor TTBView.Destroy;
  3242. begin
  3243. CloseChildPopups;
  3244. if Assigned(FAccObjectInstance) then begin
  3245. FAccObjectInstance.ClientIsDestroying;
  3246. { Get rid of our own reference to FAccObjectInstance. Normally the
  3247. reference count will be now be zero and FAccObjectInstance will be
  3248. freed, unless MSAA still holds a reference. }
  3249. FAccObjectInstance._Release;
  3250. FAccObjectInstance := nil;
  3251. end;
  3252. { If parent view is a toolbar, invalidate the open item so that it's
  3253. redrawn back in the "up" position }
  3254. if Assigned(ParentView) and ParentView.FIsToolbar then begin
  3255. Include(ParentView.FState, vsNoAnimation);
  3256. if Assigned(ParentView.FOpenViewer) then
  3257. ParentView.Invalidate(ParentView.FOpenViewer);
  3258. end;
  3259. if Assigned(FCurParentItem) then
  3260. FCurParentItem.UnregisterNotification(ItemNotification);
  3261. if Assigned(FParentItem) then
  3262. FParentItem.UnregisterNotification(LinkNotification);
  3263. inherited;
  3264. FPriorityList.Free;
  3265. FreeViewers;
  3266. { Now that we're destroyed, "focus" the parent view }
  3267. if Assigned(FParentView) then
  3268. FParentView.NotifyFocusEvent;
  3269. end;
  3270. function TTBView.GetAccObject: IDispatch;
  3271. begin
  3272. if FAccObjectInstance = nil then begin
  3273. FAccObjectInstance := TTBViewAccObject.Create(Self);
  3274. { Strictly as an optimization, take a reference for ourself and keep it
  3275. for the lifetime of the view. (Destroy calls _Release.) }
  3276. FAccObjectInstance._AddRef;
  3277. end;
  3278. Result := FAccObjectInstance;
  3279. end;
  3280. function TTBView.HandleWMGetObject(var Message: TMessage): Boolean;
  3281. begin
  3282. if (Message.LParam = Integer(OBJID_CLIENT)) then begin
  3283. Message.Result := LresultFromObject(ITBAccessible, Message.WParam, GetAccObject);
  3284. Result := True;
  3285. end
  3286. else
  3287. Result := False;
  3288. end;
  3289. procedure TTBView.UpdateCurParentItem;
  3290. var
  3291. Value: TTBCustomItem;
  3292. begin
  3293. Value := ItemContainingItems(FParentItem);
  3294. if FCurParentItem <> Value then begin
  3295. CloseChildPopups;
  3296. if Assigned(FCurParentItem) then
  3297. FCurParentItem.UnregisterNotification(ItemNotification);
  3298. FCurParentItem := Value;
  3299. if Assigned(Value) then
  3300. Value.RegisterNotification(ItemNotification);
  3301. RecreateAllViewers;
  3302. if Assigned(Value) and not(csDesigning in Value.ComponentState) then
  3303. InitiateActions;
  3304. end;
  3305. end;
  3306. procedure TTBView.InitiateActions;
  3307. var
  3308. I: Integer;
  3309. begin
  3310. { Use a 'while' instead of a 'for' since an InitiateAction implementation
  3311. may add/delete items }
  3312. I := 0;
  3313. while I < FViewerCount do begin
  3314. FViewers[I].Item.InitiateAction;
  3315. Inc(I);
  3316. end;
  3317. end;
  3318. procedure TTBView.Notification(AComponent: TComponent; Operation: TOperation);
  3319. begin
  3320. inherited;
  3321. if Operation = opRemove then begin
  3322. if AComponent = FParentItem then begin
  3323. FParentItem := nil;
  3324. UpdateCurParentItem;
  3325. if Assigned(FParentView) then
  3326. FParentView.CloseChildPopups;
  3327. end
  3328. else if AComponent = FOpenViewerWindow then begin
  3329. FOpenViewerWindow := nil;
  3330. FOpenViewerView := nil;
  3331. FOpenViewer := nil;
  3332. end
  3333. else if AComponent = FChevronParentView then
  3334. FChevronParentView := nil;
  3335. end
  3336. end;
  3337. function TTBView.ContainsView(AView: TTBView): Boolean;
  3338. begin
  3339. while Assigned(AView) and (AView <> Self) do
  3340. AView := AView.FParentView;
  3341. Result := Assigned(AView);
  3342. end;
  3343. function TTBView.GetRootView: TTBView;
  3344. begin
  3345. Result := Self;
  3346. while Assigned(Result.FParentView) do
  3347. Result := Result.FParentView;
  3348. end;
  3349. function TTBView.GetParentToolbarView: TTBView;
  3350. begin
  3351. Result := Self;
  3352. while Assigned(Result) and not Result.FIsToolbar do
  3353. Result := Result.FParentView;
  3354. end;
  3355. procedure TTBView.FreeViewers;
  3356. var
  3357. VI: PTBItemViewerArray;
  3358. I, C: Integer;
  3359. begin
  3360. if Assigned(FViewers) then begin
  3361. VI := FViewers;
  3362. C := FViewerCount;
  3363. FViewers := nil;
  3364. FViewerCount := 0;
  3365. for I := C-1 downto 0 do
  3366. FreeAndNil(VI[I]);
  3367. FreeMem(VI);
  3368. end;
  3369. end;
  3370. procedure TTBView.InvalidatePositions;
  3371. begin
  3372. if FValidated then begin
  3373. FValidated := False;
  3374. if Assigned(FWindow) and FWindow.HandleAllocated then
  3375. InvalidateRect(FWindow.Handle, nil, True);
  3376. end;
  3377. end;
  3378. procedure TTBView.ValidatePositions;
  3379. begin
  3380. if not FValidated then
  3381. UpdatePositions;
  3382. end;
  3383. procedure TTBView.TryValidatePositions;
  3384. begin
  3385. if (FUpdating = 0) and
  3386. (not Assigned(FParentItem) or not(csLoading in FParentItem.ComponentState)) and
  3387. (not Assigned(FParentItem.Owner) or not(csLoading in FParentItem.Owner.ComponentState)) then
  3388. ValidatePositions;
  3389. end;
  3390. (*procedure TTBView.TryRevalidatePositions;
  3391. begin
  3392. if FValidated then begin
  3393. if FUpdating = 0 then begin
  3394. FreePositions;
  3395. UpdatePositions;
  3396. end
  3397. else
  3398. InvalidatePositions;
  3399. end;
  3400. end;*)
  3401. function TTBView.Find(Item: TTBCustomItem): TTBItemViewer;
  3402. var
  3403. I: Integer;
  3404. begin
  3405. for I := 0 to FViewerCount-1 do
  3406. if FViewers[I].Item = Item then begin
  3407. Result := FViewers[I];
  3408. Exit;
  3409. end;
  3410. raise ETBItemError.Create(STBViewerNotFound);
  3411. end;
  3412. function TTBView.IndexOf(AViewer: TTBItemViewer): Integer;
  3413. var
  3414. I: Integer;
  3415. begin
  3416. if Assigned(AViewer) then
  3417. for I := 0 to FViewerCount-1 do
  3418. if FViewers[I] = AViewer then begin
  3419. Result := I;
  3420. Exit;
  3421. end;
  3422. Result := -1;
  3423. end;
  3424. procedure TTBView.DeletingViewer(Viewer: TTBItemViewer);
  3425. begin
  3426. if FSelected = Viewer then
  3427. FSelected := nil;
  3428. if FOpenViewer = Viewer then
  3429. CloseChildPopups;
  3430. end;
  3431. procedure TTBView.RecreateItemViewer(const I: Integer);
  3432. var
  3433. OldViewer, NewViewer: TTBItemViewer;
  3434. J: Integer;
  3435. begin
  3436. OldViewer := FViewers[I];
  3437. DeletingViewer(OldViewer);
  3438. NewViewer := OldViewer.Item.GetItemViewerClass(Self).Create(Self,
  3439. OldViewer.Item, OldViewer.FGroupLevel);
  3440. FViewers[I] := NewViewer;
  3441. if Assigned(FPriorityList) then begin
  3442. J := FPriorityList.IndexOf(OldViewer);
  3443. if J <> -1 then
  3444. FPriorityList[J] := NewViewer;
  3445. end;
  3446. OldViewer.Free;
  3447. end;
  3448. function TTBView.InsertItemViewers(const NewIndex: Integer;
  3449. const AItem: TTBCustomItem; const AGroupLevel: Integer;
  3450. const AddToPriorityList, TopOfPriorityList: Boolean): Integer;
  3451. var
  3452. NewViewer: TTBItemViewer;
  3453. LinkItem: TTBCustomItem;
  3454. I: Integer;
  3455. begin
  3456. if AGroupLevel > MaxGroupLevel then begin
  3457. Result := 0;
  3458. Exit;
  3459. end;
  3460. NewViewer := AItem.GetItemViewerClass(Self).Create(Self, AItem,
  3461. AGroupLevel);
  3462. InsertIntoViewerArray(FViewers, FViewerCount, NewIndex,
  3463. NewViewer);
  3464. if AddToPriorityList and FUsePriorityList then begin
  3465. if not TopOfPriorityList then
  3466. AddToList(FPriorityList, NewViewer)
  3467. else
  3468. { When new items are inserted programmatically at run-time, place
  3469. them at the top of FPriorityList }
  3470. AddToFrontOfList(FPriorityList, NewViewer);
  3471. end;
  3472. Result := 1;
  3473. { If a new group item is being inserted, insert all its child items too }
  3474. if not FCustomizing and (tbisEmbeddedGroup in AItem.ItemStyle) then begin
  3475. LinkItem := ItemContainingItems(AItem);
  3476. for I := 0 to LinkItem.Count-1 do begin
  3477. Inc(Result, InsertItemViewers(NewIndex + Result, LinkItem.FItems[I].Item,
  3478. AGroupLevel + 1, AddToPriorityList, TopOfPriorityList));
  3479. end;
  3480. end;
  3481. end;
  3482. procedure TTBView.ItemNotification(Ancestor: TTBCustomItem; Relayed: Boolean;
  3483. Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem);
  3484. procedure ItemInserted;
  3485. var
  3486. NewLevel, Start, InsertPoint, Last: Integer;
  3487. GroupItem, NextItem: TTBCustomItem;
  3488. Found, SearchAgain: Boolean;
  3489. begin
  3490. InvalidatePositions;
  3491. NewLevel := 0;
  3492. Start := 0;
  3493. if Ancestor = FCurParentItem then
  3494. InsertPoint := FViewerCount
  3495. else begin
  3496. { Ancestor <> FCurParentItem, so apparently an item has been inserted
  3497. inside a group item }
  3498. repeat
  3499. Found := False;
  3500. while Start < FViewerCount do begin
  3501. GroupItem := FViewers[Start].Item;
  3502. if (tbisEmbeddedGroup in GroupItem.ItemStyle) and (GroupItem = Ancestor) then begin
  3503. NewLevel := FViewers[Start].FGroupLevel + 1;
  3504. Inc(Start);
  3505. Found := True;
  3506. Break;
  3507. end;
  3508. Inc(Start);
  3509. end;
  3510. if not Found then
  3511. { Couldn't find Ancestor; it shouldn't get here }
  3512. Exit;
  3513. InsertPoint := Start;
  3514. SearchAgain := False;
  3515. while (InsertPoint < FViewerCount) and
  3516. (FViewers[InsertPoint].FGroupLevel >= NewLevel) do begin
  3517. if (FViewers[InsertPoint].Item = Item) and
  3518. (FViewers[InsertPoint].FGroupLevel = NewLevel) then begin
  3519. { If the item we were going to insert already exists, then there
  3520. must be multiple instances of the same group item. This can
  3521. happen when are two group items on the same toolbar each
  3522. linking to the same submenu item, with the submenu item
  3523. containing a group item of its own, and an item is inserted
  3524. inside that. }
  3525. SearchAgain := True;
  3526. Break;
  3527. end;
  3528. Inc(InsertPoint);
  3529. end;
  3530. until not SearchAgain;
  3531. end;
  3532. if InsertPoint = FViewerCount then begin
  3533. { Don't add items after the chevron or MDI buttons item }
  3534. Dec(InsertPoint, FInternalViewersAtEnd);
  3535. if InsertPoint < 0 then
  3536. InsertPoint := 0; { just in case? }
  3537. end;
  3538. { If the new item wasn't placed at the end, adjust InsertPoint accordingly }
  3539. if Index < Item.Parent.Count-1 then begin
  3540. Last := InsertPoint;
  3541. InsertPoint := Start;
  3542. NextItem := Item.Parent.FItems[Index+1].Item;
  3543. while (InsertPoint < Last) and
  3544. ((FViewers[InsertPoint].Item <> NextItem) or
  3545. (FViewers[InsertPoint].FGroupLevel <> NewLevel)) do
  3546. Inc(InsertPoint);
  3547. end;
  3548. InsertItemViewers(InsertPoint, Item, NewLevel, True,
  3549. not(csLoading in Item.ComponentState) and FNewViewersGetHighestPriority);
  3550. end;
  3551. procedure ItemDeleting;
  3552. procedure DeleteItem(DeleteIndex: Integer);
  3553. var
  3554. Viewer: TTBItemViewer;
  3555. begin
  3556. Viewer := FViewers[DeleteIndex];
  3557. DeletingViewer(Viewer);
  3558. RemoveFromList(FPriorityList, Viewer);
  3559. FreeAndNil(Viewer);
  3560. DeleteFromViewerArray(FViewers, FViewerCount, DeleteIndex);
  3561. end;
  3562. var
  3563. I: Integer;
  3564. DeleteLevel: Integer;
  3565. begin
  3566. InvalidatePositions;
  3567. I := 0;
  3568. DeleteLevel := 0;
  3569. while I < FViewerCount do begin
  3570. if DeleteLevel > 0 then begin
  3571. if FViewers[I].FGroupLevel >= DeleteLevel then begin
  3572. DeleteItem(I);
  3573. Continue;
  3574. end
  3575. else
  3576. DeleteLevel := 0;
  3577. end;
  3578. if FViewers[I].Item = Item then begin
  3579. { Delete the item, and any group item children afterward }
  3580. DeleteLevel := FViewers[I].FGroupLevel + 1;
  3581. DeleteItem(I);
  3582. Continue;
  3583. end;
  3584. Inc(I);
  3585. end;
  3586. end;
  3587. var
  3588. I: Integer;
  3589. begin
  3590. case Action of
  3591. tbicInserted: ItemInserted;
  3592. tbicDeleting: ItemDeleting;
  3593. tbicSubitemsChanged: begin
  3594. { If Relayed=True, LinkSubitems must have changed on a child group
  3595. item. Currently there isn't any optimized way of handling this
  3596. situation; just recreate all viewers. }
  3597. if Relayed then
  3598. RecreateAllViewers;
  3599. end;
  3600. tbicSubitemsBeginUpdate: BeginUpdate;
  3601. tbicSubitemsEndUpdate: EndUpdate;
  3602. tbicInvalidate: begin
  3603. for I := 0 to FViewerCount-1 do
  3604. if FViewers[I].Item = Item then
  3605. Invalidate(FViewers[I]);
  3606. end;
  3607. tbicInvalidateAndResize: InvalidatePositions;
  3608. tbicRecreateItemViewers: begin
  3609. InvalidatePositions;
  3610. for I := 0 to FViewerCount-1 do
  3611. if FViewers[I].Item = Item then
  3612. RecreateItemViewer(I);
  3613. end;
  3614. tbicSubMenuImagesChanged: ImagesChanged;
  3615. else
  3616. { Prevent TryValidatePositions from being called below on Actions other than
  3617. those listed above. Currently there are no other Actions, but for forward
  3618. compatibility, we should ignore unknown Actions completely. }
  3619. Exit;
  3620. end;
  3621. TryValidatePositions;
  3622. end;
  3623. procedure TTBView.LinkNotification(Ancestor: TTBCustomItem; Relayed: Boolean;
  3624. Action: TTBItemChangedAction; Index: Integer; Item: TTBCustomItem);
  3625. { This notification procedure watches for tbicSubitemsChanged notifications
  3626. from FParentItem }
  3627. begin
  3628. case Action of
  3629. tbicSubitemsChanged: begin
  3630. { LinkSubitems may have changed on FParentItem, e.g. on the root item
  3631. of a toolbar, so see if FCurParentItem needs updating }
  3632. UpdateCurParentItem;
  3633. end;
  3634. tbicSubMenuImagesChanged: begin
  3635. { In case the images were inherited from the actual parent instead of
  3636. the linked parent... }
  3637. if FParentItem <> FCurParentItem then
  3638. ImagesChanged;
  3639. end;
  3640. end;
  3641. end;
  3642. procedure TTBView.ImagesChanged;
  3643. begin
  3644. InvalidatePositions;
  3645. TryValidatePositions;
  3646. if Assigned(FOpenViewerView) then
  3647. FOpenViewerView.ImagesChanged;
  3648. end;
  3649. procedure TTBView.GivePriority(AViewer: TTBItemViewer);
  3650. { Move item to top of priority list. Rearranges items if necessary. }
  3651. var
  3652. I: Integer;
  3653. begin
  3654. if Assigned(FChevronParentView) then begin
  3655. I := AViewer.Index + FChevronParentView.FInternalViewersAtFront;
  3656. if I < FChevronParentView.FViewerCount then { range check just in case }
  3657. FChevronParentView.GivePriority(FChevronParentView.FViewers[I]);
  3658. Exit;
  3659. end;
  3660. if Assigned(FPriorityList) then begin
  3661. I := FPriorityList.IndexOf(AViewer);
  3662. if I <> -1 then begin
  3663. FPriorityList.Move(I, 0);
  3664. if not FValidated or AViewer.OffEdge then
  3665. UpdatePositions;
  3666. end;
  3667. end;
  3668. { Call GivePriority on parent view, so that if an item on a submenu is
  3669. clicked, the parent item of the submenu gets priority. }
  3670. if Assigned(FParentView) and Assigned(FParentView.FOpenViewer) then
  3671. FParentView.GivePriority(FParentView.FOpenViewer);
  3672. end;
  3673. function TTBView.HighestPriorityViewer: TTBItemViewer;
  3674. { Returns index of first visible, non-separator item at top of priority list,
  3675. or -1 if there are no items found }
  3676. var
  3677. I: Integer;
  3678. J: TTBItemViewer;
  3679. begin
  3680. ValidatePositions;
  3681. Result := nil;
  3682. if Assigned(FPriorityList) then begin
  3683. for I := 0 to FPriorityList.Count-1 do begin
  3684. J := FPriorityList[I];
  3685. if J.Show and not(tbisSeparator in J.Item.ItemStyle) then begin
  3686. Result := J;
  3687. Break;
  3688. end;
  3689. end;
  3690. end
  3691. else begin
  3692. for I := 0 to FViewerCount-1 do begin
  3693. J := FViewers[I];
  3694. if J.Show and not(tbisSeparator in J.Item.ItemStyle) then begin
  3695. Result := J;
  3696. Break;
  3697. end;
  3698. end;
  3699. end;
  3700. end;
  3701. procedure TTBView.StartTimer(const ATimer: TTBViewTimerID;
  3702. const Interval: Integer);
  3703. { Starts a timer. Stops any previously set timer of the same ID first.
  3704. Note: WM_TIMER messages generated by timers set by the method are handled
  3705. in PopupMessageLoop. }
  3706. begin
  3707. StopTimer(ATimer);
  3708. if (FWindow is TTBPopupWindow) and FWindow.HandleAllocated then begin
  3709. SetTimer(FWindow.Handle, ViewTimerBaseID + Ord(ATimer), Interval, nil);
  3710. Include(FActiveTimers, ATimer);
  3711. end;
  3712. end;
  3713. procedure TTBView.StopAllTimers;
  3714. var
  3715. I: TTBViewTimerID;
  3716. begin
  3717. for I := Low(I) to High(I) do
  3718. StopTimer(I);
  3719. end;
  3720. procedure TTBView.StopTimer(const ATimer: TTBViewTimerID);
  3721. begin
  3722. if ATimer in FActiveTimers then begin
  3723. if (FWindow is TTBPopupWindow) and FWindow.HandleAllocated then
  3724. KillTimer(FWindow.Handle, ViewTimerBaseID + Ord(ATimer));
  3725. Exclude(FActiveTimers, ATimer);
  3726. end;
  3727. end;
  3728. function TTBView.OpenChildPopup(const SelectFirstItem: Boolean): Boolean;
  3729. var
  3730. Item: TTBCustomItem;
  3731. begin
  3732. StopTimer(tiClose);
  3733. StopTimer(tiOpen);
  3734. if FSelected <> FOpenViewer then begin
  3735. CloseChildPopups;
  3736. if Assigned(FSelected) then begin
  3737. Item := FSelected.Item;
  3738. if Item.Enabled and (tbisSubmenu in Item.ItemStyle) then
  3739. Item.CreatePopup(Self, FSelected, not FIsToolbar, SelectFirstItem,
  3740. False, Point(0, 0), tbpaLeft);
  3741. end;
  3742. end;
  3743. Result := Assigned(FOpenViewer);
  3744. end;
  3745. procedure TTBView.CloseChildPopups;
  3746. begin
  3747. if Assigned(FOpenViewerView) then
  3748. FOpenViewerView.CloseChildPopups;
  3749. StopTimer(tiClose);
  3750. FOpenViewerWindow.Free;
  3751. FOpenViewerWindow := nil;
  3752. FOpenViewerView := nil;
  3753. FOpenViewer := nil;
  3754. end;
  3755. procedure TTBView.CancelChildPopups;
  3756. begin
  3757. if FIsToolbar then
  3758. Exclude(FState, vsDropDownMenus);
  3759. {MP}
  3760. if Assigned(FOpenViewerWindow) then
  3761. FOpenViewerWindow.Cancel;
  3762. CloseChildPopups;
  3763. end;
  3764. function TTBView.ViewerFromPoint(const P: TPoint): TTBItemViewer;
  3765. var
  3766. I: Integer;
  3767. begin
  3768. ValidatePositions;
  3769. for I := 0 to FViewerCount-1 do begin
  3770. if FViewers[I].Show and
  3771. PtInRect(FViewers[I].BoundsRect, P) then begin
  3772. Result := FViewers[I];
  3773. Exit;
  3774. end;
  3775. end;
  3776. Result := nil;
  3777. end;
  3778. procedure TTBView.NotifyFocusEvent;
  3779. { Notifies Active Accessibility of a change in "focus". Has no effect if the
  3780. view or the root view lacks the vsModal state, or if the modal loop is
  3781. ending (EndModal* was called). }
  3782. var
  3783. I, ChildID, J: Integer;
  3784. begin
  3785. { Note: We don't notify about windows not yet shown (e.g. a popup menu that
  3786. is still initializing) because that would probably confuse screen readers.
  3787. Also allocating a window handle at this point *might* not be a good idea. }
  3788. if (vsModal in FState) and (vsModal in GetRootView.FState) and
  3789. not IsModalEnding and
  3790. FWindow.HandleAllocated and IsWindowVisible(FWindow.Handle) then begin
  3791. if Assigned(FSelected) and FSelected.IsAccessible then
  3792. I := IndexOf(FSelected)
  3793. else
  3794. I := -1;
  3795. if (I < 0) and Assigned(FParentView) then begin
  3796. { If we have no selected item, report the the selected item on the parent
  3797. view as having the "focus".
  3798. Note: With standard menus, when you go from having a selection to no
  3799. selection on a submenu, it sends two focus events - first with the
  3800. client window as having the focus, then with the parent item. I
  3801. figure that's probably a bug, so I don't try to emulate that behavior
  3802. here. }
  3803. FParentView.NotifyFocusEvent;
  3804. end
  3805. else begin
  3806. if I >= 0 then begin
  3807. { Convert viewer index into a one-based child index.
  3808. (TTBViewAccObject.get_accChild does the inverse.) }
  3809. ChildID := 1;
  3810. for J := 0 to I-1 do
  3811. if FViewers[J].IsAccessible then
  3812. Inc(ChildID);
  3813. end
  3814. else begin
  3815. { If there is no (accessible) selection and no parent view, report
  3816. the client window itself as being "focused". This is what happens
  3817. when a standard context menu has no selection. }
  3818. ChildID := CHILDID_SELF;
  3819. end;
  3820. NotifyWinEvent(EVENT_OBJECT_FOCUS, FWindow.Handle, OBJID_CLIENT, ChildID);
  3821. end;
  3822. end;
  3823. end;
  3824. procedure TTBView.SetSelected(Value: TTBItemViewer);
  3825. begin
  3826. Select(Value, False);
  3827. end;
  3828. procedure TTBView.Select(Value: TTBItemViewer; ViaMouse: Boolean);
  3829. { Sets the current selection.
  3830. When the selection is changing it will also, if necessary, open/close child
  3831. popups. How exactly this works depends on the setting of ViaMouse. If
  3832. ViaMouse is True it will delay the opening/closing of popups using timers. }
  3833. var
  3834. OldSelected: TTBItemViewer;
  3835. NewMouseOverSelected: Boolean;
  3836. P: TPoint;
  3837. begin
  3838. OldSelected := FSelected;
  3839. if Value <> OldSelected then begin
  3840. { If there's a new selection and the parent item on the parent view
  3841. isn't currently selected, select it. Also stop any timer running on
  3842. the parent view. }
  3843. if Assigned(Value) and Assigned(FParentView) and
  3844. Assigned(FParentView.FOpenViewer) and
  3845. (FParentView.FSelected <> FParentView.FOpenViewer) then begin
  3846. FParentView.Selected := FParentView.FOpenViewer;
  3847. FParentView.StopTimer(tiClose);
  3848. FParentView.StopTimer(tiOpen);
  3849. end;
  3850. { Handle automatic closing of child popups }
  3851. if vsModal in FState then begin
  3852. { If the view is a toolbar, or if the new selection didn't come from
  3853. the mouse, close child popups immediately }
  3854. if FIsToolbar or not ViaMouse then begin
  3855. { Always stop any close timer because CloseChildPopups may not be
  3856. called below }
  3857. StopTimer(tiClose);
  3858. if Value <> FOpenViewer then
  3859. { ^ But don't close if selection is returning to the open item.
  3860. Needed for the "FParentView.Selected := FParentView.FOpenViewer"
  3861. line above to work. }
  3862. CloseChildPopups;
  3863. end
  3864. else begin
  3865. { Otherwise, delay-close any child popup }
  3866. if Assigned(FOpenViewerView) and not(tiClose in FActiveTimers) then
  3867. StartTimer(tiClose, GetMenuShowDelay);
  3868. end;
  3869. end;
  3870. CancelCapture;
  3871. if Assigned(OldSelected) then
  3872. OldSelected.Leaving;
  3873. FSelected := Value;
  3874. FSelectedViaMouse := ViaMouse;
  3875. end;
  3876. NewMouseOverSelected := False;
  3877. if Assigned(Value) and Assigned(FWindow) then begin
  3878. P := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  3879. if FindDragTarget(P, True) = FWindow then begin
  3880. P := FWindow.ScreenToClient(P);
  3881. NewMouseOverSelected := (ViewerFromPoint(P) = Value);
  3882. if NewMouseOverSelected and FCapture and
  3883. not Value.IsPtInButtonPart(P.X - Value.BoundsRect.Left,
  3884. P.Y - Value.BoundsRect.Top) then
  3885. NewMouseOverSelected := False;
  3886. end;
  3887. end;
  3888. if Value <> OldSelected then begin
  3889. FMouseOverSelected := NewMouseOverSelected;
  3890. if Assigned(OldSelected) and (tbisRedrawOnSelChange in OldSelected.Item.ItemStyle) then
  3891. Invalidate(OldSelected);
  3892. if Assigned(Value) then begin
  3893. if tbisRedrawOnSelChange in Value.Item.ItemStyle then
  3894. Invalidate(Value);
  3895. Value.Entering(OldSelected);
  3896. end;
  3897. NotifyFocusEvent;
  3898. { Handle automatic opening of a child popup }
  3899. if vsModal in FState then begin
  3900. { If the view is a toolbar, immediately open any child popup }
  3901. if FIsToolbar then begin
  3902. if Assigned(Value) then begin
  3903. if ViaMouse and Assigned(FParentView) then begin
  3904. { On chevron popups, always drop down menus when mouse passes
  3905. over them, like Office 2000 }
  3906. Include(FState, vsDropDownMenus);
  3907. end;
  3908. if (vsDropDownMenus in FState) and
  3909. (ViaMouse or not(tbisNoAutoOpen in Value.Item.ItemStyle)) then
  3910. OpenChildPopup(not ViaMouse);
  3911. end;
  3912. end
  3913. else begin
  3914. { Otherwise, delay-open any child popup if the selection came from
  3915. the mouse }
  3916. StopTimer(tiOpen);
  3917. if ViaMouse and Assigned(Value) and (tbisSubmenu in Value.Item.ItemStyle) then
  3918. StartTimer(tiOpen, GetMenuShowDelay);
  3919. end;
  3920. end;
  3921. end
  3922. else if FMouseOverSelected <> NewMouseOverSelected then begin
  3923. FMouseOverSelected := NewMouseOverSelected;
  3924. if Assigned(Value) and FCapture and (tbisRedrawOnMouseOverChange in Value.Item.ItemStyle) then
  3925. Invalidate(Value);
  3926. end;
  3927. end;
  3928. procedure TTBView.UpdateSelection(const P: PPoint; const AllowNewSelection: Boolean);
  3929. { Called in response to a mouse movement, this method updates the current
  3930. selection, updates the vsMouseInWindow view state, and enables/disables
  3931. scroll timers. }
  3932. function IsPtInScrollArrow(ADownArrow: Boolean): Boolean;
  3933. var
  3934. P2: TPoint;
  3935. R: TRect;
  3936. begin
  3937. Result := False;
  3938. if (vsModal in FState) and (vsMouseInWindow in FState) and
  3939. Assigned(P) then begin
  3940. P2 := FWindow.ScreenToClient(P^);
  3941. R := FWindow.ClientRect;
  3942. if PtInRect(R, P2) then begin
  3943. if ADownArrow then
  3944. Result := FShowDownArrow and (P2.Y >= R.Bottom - tbMenuScrollArrowHeight)
  3945. else
  3946. Result := FShowUpArrow and (P2.Y < tbMenuScrollArrowHeight);
  3947. end;
  3948. end;
  3949. end;
  3950. var
  3951. NewSelected, ViewerAtPoint: TTBItemViewer;
  3952. P2: TPoint;
  3953. MouseWasInWindow: Boolean;
  3954. begin
  3955. ValidatePositions;
  3956. { If modal, default to keeping the existing selection }
  3957. if vsModal in FState then
  3958. NewSelected := FSelected
  3959. else
  3960. NewSelected := nil;
  3961. { Is the mouse inside the window? }
  3962. MouseWasInWindow := vsMouseInWindow in FState;
  3963. if Assigned(P) and Assigned(FWindow) and (FindDragTarget(P^, True) = FWindow) then begin
  3964. { If we're a popup window and the mouse is inside, default to no selection }
  3965. if FIsPopup then
  3966. NewSelected := nil;
  3967. Include(FState, vsMouseInWindow);
  3968. if AllowNewSelection or Assigned(FSelected) then begin
  3969. P2 := FWindow.ScreenToClient(P^);
  3970. ViewerAtPoint := ViewerFromPoint(P2);
  3971. if Assigned(ViewerAtPoint) then
  3972. NewSelected := ViewerAtPoint;
  3973. end;
  3974. end
  3975. else
  3976. Exclude(FState, vsMouseInWindow);
  3977. { If FCapture is True, don't allow the selection to change }
  3978. if FCapture and (NewSelected <> FSelected) then
  3979. NewSelected := FSelected;
  3980. { If we're a popup window and there is a selection... }
  3981. if FIsPopup and Assigned(NewSelected) then begin
  3982. { If the mouse just moved out of the window and no submenu was open,
  3983. remove the highlight }
  3984. if not FCapture and MouseWasInWindow and not(vsMouseInWindow in FState) and
  3985. (not Assigned(FOpenViewerView) or not(tbisSubmenu in NewSelected.Item.ItemStyle)) then
  3986. NewSelected := nil;
  3987. end;
  3988. { Now we set the new Selected value }
  3989. Select(NewSelected, True);
  3990. { Update scroll arrow timers }
  3991. if IsPtInScrollArrow(False) then begin
  3992. StopTimer(tiScrollDown);
  3993. if not(tiScrollUp in FActiveTimers) then
  3994. StartTimer(tiScrollUp, 100);
  3995. end
  3996. else if IsPtInScrollArrow(True) then begin
  3997. StopTimer(tiScrollUp);
  3998. if not(tiScrollDown in FActiveTimers) then
  3999. StartTimer(tiScrollDown, 100);
  4000. end
  4001. else begin
  4002. StopTimer(tiScrollUp);
  4003. StopTimer(tiScrollDown);
  4004. end;
  4005. end;
  4006. procedure TTBView.RecreateAllViewers;
  4007. var
  4008. Item: TTBCustomItem;
  4009. I: Integer;
  4010. begin
  4011. { Since the FViewers list is being rebuilt, FOpenViewer and FSelected
  4012. will no longer be valid, so ensure they're set to nil. }
  4013. CloseChildPopups;
  4014. Selected := nil;
  4015. InvalidatePositions;
  4016. FreeAndNil(FPriorityList);
  4017. FreeViewers;
  4018. FInternalViewersAtFront := 0;
  4019. FInternalViewersAtEnd := 0;
  4020. { MDI system menu item }
  4021. Item := GetMDISystemMenuItem;
  4022. if Assigned(Item) then
  4023. Inc(FInternalViewersAtFront, InsertItemViewers(FViewerCount, Item, 0,
  4024. False, False));
  4025. { Items }
  4026. if Assigned(FCurParentItem) then begin
  4027. for I := 0 to FCurParentItem.Count-1 do
  4028. InsertItemViewers(FViewerCount, FCurParentItem.FItems[I].Item, 0,
  4029. True, False);
  4030. end;
  4031. { MDI buttons item }
  4032. Item := GetMDIButtonsItem;
  4033. if Assigned(Item) then begin
  4034. for I := 0 to Item.Count-1 do
  4035. Inc(FInternalViewersAtEnd, InsertItemViewers(FViewerCount,
  4036. Item.FItems[I].Item, 0, False, False));
  4037. end;
  4038. { Chevron item }
  4039. Item := GetChevronItem;
  4040. if Assigned(Item) then
  4041. Inc(FInternalViewersAtEnd, InsertItemViewers(FViewerCount, Item, 0,
  4042. False, False));
  4043. end;
  4044. function TTBView.CalculatePositions(const CanMoveControls: Boolean;
  4045. const AOrientation: TTBViewOrientation;
  4046. AWrapOffset, AChevronOffset, AChevronSize: Integer;
  4047. var ABaseSize, TotalSize: TPoint;
  4048. var AWrappedLines: Integer): Boolean;
  4049. { Returns True if the positions have changed }
  4050. type
  4051. PTempPosition = ^TTempPosition;
  4052. TTempPosition = record
  4053. BoundsRect: TRect;
  4054. Show, OffEdge, LineSep, Clipped, SameWidth: Boolean;
  4055. end;
  4056. PTempPositionArray = ^TTempPositionArray;
  4057. TTempPositionArray = array[0..$7FFFFFFF div SizeOf(TTempPosition)-1] of TTempPosition;
  4058. var
  4059. DC: HDC;
  4060. LeftX, TopY, CurX, CurY, I: Integer;
  4061. NewPositions: PTempPositionArray;
  4062. GroupSplit, DidWrap: Boolean;
  4063. LineStart, HighestHeightOnLine, HighestWidthOnLine: Integer;
  4064. function GetSizeOfGroup(const StartingIndex: Integer): Integer;
  4065. var
  4066. I: Integer;
  4067. begin
  4068. Result := 0;
  4069. for I := StartingIndex to FViewerCount-1 do begin
  4070. if not NewPositions[I].Show then
  4071. Continue;
  4072. if tbisSeparator in FViewers[I].Item.ItemStyle then
  4073. Break;
  4074. with NewPositions[I] do begin
  4075. if AOrientation <> tbvoVertical then
  4076. Inc(Result, BoundsRect.Right)
  4077. else
  4078. Inc(Result, BoundsRect.Bottom);
  4079. end;
  4080. end;
  4081. end;
  4082. procedure Mirror;
  4083. { Reverses the horizontal ordering (i.e. first item becomes last) }
  4084. var
  4085. I, NewRight: Integer;
  4086. begin
  4087. for I := 0 to FViewerCount-1 do
  4088. with NewPositions[I] do
  4089. if Show then begin
  4090. NewRight := TotalSize.X - BoundsRect.Left;
  4091. BoundsRect.Left := TotalSize.X - BoundsRect.Right;
  4092. BoundsRect.Right := NewRight;
  4093. end;
  4094. end;
  4095. procedure HandleMaxHeight;
  4096. { Decreases, if necessary, the height of the view to FMaxHeight, and adjusts
  4097. the visibility of the scroll arrows }
  4098. var
  4099. MaxOffset, I, MaxTop, MaxBottom: Integer;
  4100. begin
  4101. FShowUpArrow := False;
  4102. FShowDownArrow := False;
  4103. if (FMaxHeight > 0) and (TotalSize.Y > FMaxHeight) then begin
  4104. MaxOffset := TotalSize.Y - FMaxHeight;
  4105. if FScrollOffset > MaxOffset then
  4106. FScrollOffset := MaxOffset;
  4107. if FScrollOffset < 0 then
  4108. FScrollOffset := 0;
  4109. FShowUpArrow := (FScrollOffset > 0);
  4110. FShowDownArrow := (FScrollOffset < MaxOffset);
  4111. MaxTop := 0;
  4112. if FShowUpArrow then
  4113. MaxTop := tbMenuScrollArrowHeight;
  4114. MaxBottom := FMaxHeight;
  4115. if FShowDownArrow then
  4116. Dec(MaxBottom, tbMenuScrollArrowHeight);
  4117. for I := 0 to FViewerCount-1 do begin
  4118. if not IsRectEmpty(NewPositions[I].BoundsRect) then begin
  4119. OffsetRect(NewPositions[I].BoundsRect, 0, -FScrollOffset);
  4120. if NewPositions[I].Show and
  4121. ((NewPositions[I].BoundsRect.Top < MaxTop) or
  4122. (NewPositions[I].BoundsRect.Bottom > MaxBottom)) then begin
  4123. NewPositions[I].Show := False;
  4124. NewPositions[I].Clipped := True;
  4125. end;
  4126. end;
  4127. end;
  4128. TotalSize.Y := FMaxHeight;
  4129. end
  4130. else
  4131. FScrollOffset := 0;
  4132. end;
  4133. procedure FinalizeLine(const LineEnd: Integer; const LastLine: Boolean);
  4134. var
  4135. I, RightAlignStart: Integer;
  4136. Item: TTBCustomItem;
  4137. IsButton: Boolean;
  4138. Pos: PTempPosition;
  4139. Z: Integer;
  4140. begin
  4141. if LineStart <> -1 then begin
  4142. if DidWrap and (FChevronParentView = nil) then begin
  4143. { When wrapping on a docked toolbar, extend TotalSize.X/Y to
  4144. AWrapOffset so that the toolbar always fills the whole row }
  4145. if (AOrientation = tbvoHorizontal) and (TotalSize.X < AWrapOffset) then
  4146. TotalSize.X := AWrapOffset
  4147. else if (AOrientation = tbvoVertical) and (TotalSize.Y < AWrapOffset) then
  4148. TotalSize.Y := AWrapOffset;
  4149. end;
  4150. RightAlignStart := -1;
  4151. for I := LineStart to LineEnd do begin
  4152. Pos := @NewPositions[I];
  4153. if not Pos.Show then
  4154. Continue;
  4155. Item := FViewers[I].Item;
  4156. if (RightAlignStart < 0) and (tbisRightAlign in Item.ItemStyle) then
  4157. RightAlignStart := I;
  4158. IsButton := FIsToolbar or (tboToolbarSize in Item.FEffectiveOptions);
  4159. if FIsToolbar then begin
  4160. if LastLine and not DidWrap and (AOrientation <> tbvoFloating) then begin
  4161. { In case the toolbar is docked next to a taller/wider toolbar... }
  4162. HighestWidthOnLine := TotalSize.X;
  4163. HighestHeightOnLine := TotalSize.Y;
  4164. end;
  4165. { Make separators on toolbars as tall/wide as the tallest/widest item }
  4166. if [tbisSeparator, tbisStretch] * Item.ItemStyle <> [] then begin
  4167. if AOrientation <> tbvoVertical then
  4168. Pos.BoundsRect.Bottom := Pos.BoundsRect.Top + HighestHeightOnLine
  4169. else
  4170. Pos.BoundsRect.Right := Pos.BoundsRect.Left + HighestWidthOnLine;
  4171. end
  4172. else begin
  4173. { Center the item }
  4174. if AOrientation <> tbvoVertical then begin
  4175. Z := (HighestHeightOnLine - (Pos.BoundsRect.Bottom - Pos.BoundsRect.Top)) div 2;
  4176. Inc(Pos.BoundsRect.Top, Z);
  4177. Inc(Pos.BoundsRect.Bottom, Z);
  4178. end
  4179. else begin
  4180. Z := (HighestWidthOnLine - (Pos.BoundsRect.Right - Pos.BoundsRect.Left)) div 2;
  4181. Inc(Pos.BoundsRect.Left, Z);
  4182. Inc(Pos.BoundsRect.Right, Z);
  4183. end;
  4184. end;
  4185. end
  4186. else begin
  4187. { Make items in a menu as wide as the widest item }
  4188. if not IsButton then begin
  4189. with Pos.BoundsRect do Right := Left + HighestWidthOnLine;
  4190. end;
  4191. end;
  4192. end;
  4193. if RightAlignStart >= 0 then begin
  4194. Z := 0;
  4195. for I := LineEnd downto RightAlignStart do begin
  4196. Pos := @NewPositions[I];
  4197. if not Pos.Show then
  4198. Continue;
  4199. if AOrientation <> tbvoVertical then
  4200. Z := Min(AWrapOffset, TotalSize.X) - Pos.BoundsRect.Right
  4201. else
  4202. Z := Min(AWrapOffset, TotalSize.Y) - Pos.BoundsRect.Bottom;
  4203. Break;
  4204. end;
  4205. if Z > 0 then begin
  4206. for I := RightAlignStart to LineEnd do begin
  4207. Pos := @NewPositions[I];
  4208. if not Pos.Show then
  4209. Continue;
  4210. if AOrientation <> tbvoVertical then begin
  4211. Inc(Pos.BoundsRect.Left, Z);
  4212. Inc(Pos.BoundsRect.Right, Z);
  4213. end
  4214. else begin
  4215. Inc(Pos.BoundsRect.Top, Z);
  4216. Inc(Pos.BoundsRect.Bottom, Z);
  4217. end;
  4218. end;
  4219. end;
  4220. end;
  4221. end;
  4222. LineStart := -1;
  4223. HighestHeightOnLine := 0;
  4224. HighestWidthOnLine := 0;
  4225. end;
  4226. procedure PositionItem(const CurIndex: Integer; var Pos: TTempPosition);
  4227. var
  4228. O, X, Y: Integer;
  4229. IsLineSep, Vert: Boolean;
  4230. begin
  4231. if LineStart = -1 then begin
  4232. LineStart := CurIndex;
  4233. HighestHeightOnLine := 0;
  4234. HighestWidthOnLine := 0;
  4235. end;
  4236. IsLineSep := False;
  4237. Vert := (AOrientation = tbvoVertical);
  4238. if not Vert then
  4239. O := CurX
  4240. else
  4241. O := CurY;
  4242. if (AWrapOffset > 0) and (O > 0) then begin
  4243. if not Vert then
  4244. Inc(O, Pos.BoundsRect.Right)
  4245. else
  4246. Inc(O, Pos.BoundsRect.Bottom);
  4247. if (tbisSeparator in FViewers[CurIndex].Item.ItemStyle) and
  4248. ((GroupSplit and not(tbisNoLineBreak in FViewers[CurIndex].Item.ItemStyle))
  4249. or (O + GetSizeOfGroup(CurIndex+1) > AWrapOffset)) then begin
  4250. DidWrap := True;
  4251. Inc(AWrappedLines);
  4252. if not Vert then begin
  4253. CurX := 0;
  4254. Inc(CurY, HighestHeightOnLine);
  4255. end
  4256. else begin
  4257. CurY := 0;
  4258. Inc(CurX, HighestWidthOnLine);
  4259. end;
  4260. FinalizeLine(CurIndex-1, False);
  4261. LineStart := CurIndex+1;
  4262. if not Vert then begin
  4263. Pos.BoundsRect.Right := 0;
  4264. Pos.BoundsRect.Bottom := tbLineSpacing;
  4265. end
  4266. else begin
  4267. Pos.BoundsRect.Right := tbLineSpacing;
  4268. Pos.BoundsRect.Bottom := 0;
  4269. end;
  4270. Pos.LineSep := True;
  4271. IsLineSep := True;
  4272. end
  4273. else if O > AWrapOffset then begin
  4274. { proceed to next row }
  4275. DidWrap := True;
  4276. Inc(AWrappedLines);
  4277. if not Vert then begin
  4278. CurX := LeftX;
  4279. Inc(CurY, HighestHeightOnLine);
  4280. end
  4281. else begin
  4282. CurY := TopY;
  4283. Inc(CurX, HighestWidthOnLine);
  4284. end;
  4285. GroupSplit := True;
  4286. FinalizeLine(CurIndex-1, False);
  4287. LineStart := CurIndex;
  4288. end;
  4289. end;
  4290. if Pos.BoundsRect.Bottom > HighestHeightOnLine then
  4291. HighestHeightOnLine := Pos.BoundsRect.Bottom;
  4292. if Pos.BoundsRect.Right > HighestWidthOnLine then
  4293. HighestWidthOnLine := Pos.BoundsRect.Right;
  4294. X := CurX;
  4295. Y := CurY;
  4296. if X < 0 then X := 0;
  4297. if Y < 0 then Y := 0;
  4298. OffsetRect(Pos.BoundsRect, X, Y);
  4299. if IsLineSep then begin
  4300. if not Vert then begin
  4301. CurX := LeftX;
  4302. Inc(CurY, tbLineSpacing);
  4303. end
  4304. else begin
  4305. CurY := TopY;
  4306. Inc(CurX, tbLineSpacing);
  4307. end;
  4308. GroupSplit := False;
  4309. end;
  4310. end;
  4311. var
  4312. SaveOrientation: TTBViewOrientation;
  4313. ChevronItem: TTBCustomItem;
  4314. CalcCanvas: TCanvas;
  4315. LastWasSep, LastWasButton, IsButton, IsControl: Boolean;
  4316. Item: TTBCustomItem;
  4317. Ctl: TControl;
  4318. ChangedBold: Boolean;
  4319. HighestSameWidthViewerWidth, Total, J, TotalVisibleItems: Integer;
  4320. IsFirst: Boolean;
  4321. Viewer: TTBItemViewer;
  4322. UseChevron, NonControlsOffEdge, TempViewerCreated: Boolean;
  4323. Margins: TRect;
  4324. label 1;
  4325. begin
  4326. SaveOrientation := FOrientation;
  4327. AWrappedLines := 1;
  4328. ChevronItem := GetChevronItem;
  4329. NewPositions := nil;
  4330. DC := 0;
  4331. CalcCanvas := nil;
  4332. try
  4333. FOrientation := AOrientation;
  4334. CalcCanvas := TCanvas.Create;
  4335. DC := GetDC(0);
  4336. CalcCanvas.Handle := DC;
  4337. CalcCanvas.Font.Assign(GetFont);
  4338. NewPositions := AllocMem(FViewerCount * SizeOf(TTempPosition));
  4339. { Figure out which items should be shown }
  4340. LastWasSep := True; { set to True initially so it won't show leading seps }
  4341. for I := 0 to FViewerCount-1 do begin
  4342. Item := FViewers[I].Item;
  4343. IsControl := Item is TTBControlItem;
  4344. with NewPositions[I] do begin
  4345. { Show is initially False since AllocMem initializes to zero }
  4346. if Item = ChevronItem then
  4347. Continue;
  4348. if Assigned(FChevronParentView) then begin
  4349. if IsControl then
  4350. Continue;
  4351. FChevronParentView.ValidatePositions;
  4352. J := I + FChevronParentView.FInternalViewersAtFront;
  4353. if J < FChevronParentView.FViewerCount then
  4354. { range check just in case }
  4355. Viewer := FChevronParentView.FViewers[J]
  4356. else
  4357. Viewer := nil;
  4358. if (Viewer = nil) or (not Viewer.OffEdge and not(tbisSeparator in Item.ItemStyle)) then
  4359. Continue;
  4360. end;
  4361. if not IsControl then begin
  4362. if not(tbisEmbeddedGroup in Item.ItemStyle) or FCustomizing then begin
  4363. Show := Item.Visible;
  4364. { Don't display two consecutive separators }
  4365. if Show then begin
  4366. if (tbisSeparator in Item.ItemStyle) and LastWasSep then
  4367. Show := False;
  4368. LastWasSep := tbisSeparator in Item.ItemStyle;
  4369. end;
  4370. end;
  4371. end
  4372. else begin
  4373. { Controls can only be rendered on a single Parent, so only
  4374. include the control if its parent is currently equal to
  4375. FWindow }
  4376. Ctl := TTBControlItem(Item).FControl;
  4377. if Assigned(Ctl) and Assigned(FWindow) and (Ctl.Parent = FWindow) and
  4378. (Ctl.Visible or (csDesigning in Ctl.ComponentState)) then begin
  4379. Show := True;
  4380. LastWasSep := False;
  4381. end;
  4382. end;
  4383. end;
  4384. end;
  4385. { Hide any trailing separators, so that they aren't included in the
  4386. base size }
  4387. for I := FViewerCount-1 downto 0 do begin
  4388. with NewPositions[I] do
  4389. if Show then begin
  4390. if not(tbisSeparator in FViewers[I].Item.ItemStyle) then
  4391. Break;
  4392. Show := False;
  4393. end;
  4394. end;
  4395. { Calculate sizes of all the items }
  4396. HighestSameWidthViewerWidth := 0;
  4397. for I := 0 to FViewerCount-1 do begin
  4398. Item := FViewers[I].Item;
  4399. IsControl := Item is TTBControlItem;
  4400. with NewPositions[I] do begin
  4401. { BoundsRect is currently empty since AllocMem initializes to zero }
  4402. if not Show then
  4403. Continue;
  4404. if not IsControl then begin
  4405. ChangedBold := False;
  4406. if tboDefault in Item.EffectiveOptions then
  4407. with CalcCanvas.Font do
  4408. if not(fsBold in Style) then begin
  4409. ChangedBold := True;
  4410. Style := Style + [fsBold];
  4411. end;
  4412. Viewer := FViewers[I];
  4413. TempViewerCreated := False;
  4414. if Item.NeedToRecreateViewer(Viewer) then begin
  4415. if CanMoveControls then begin
  4416. RecreateItemViewer(I);
  4417. Viewer := FViewers[I];
  4418. end
  4419. else begin
  4420. Viewer := Item.GetItemViewerClass(Self).Create(Self, Item, 0);
  4421. TempViewerCreated := True;
  4422. end;
  4423. end;
  4424. try
  4425. Viewer.CalcSize(CalcCanvas, BoundsRect.Right, BoundsRect.Bottom);
  4426. if Viewer.UsesSameWidth then begin
  4427. SameWidth := True;
  4428. if (BoundsRect.Right > HighestSameWidthViewerWidth) then
  4429. HighestSameWidthViewerWidth := BoundsRect.Right;
  4430. end;
  4431. finally
  4432. if TempViewerCreated then
  4433. Viewer.Free;
  4434. end;
  4435. if ChangedBold then
  4436. with CalcCanvas.Font do
  4437. Style := Style - [fsBold];
  4438. end
  4439. else begin
  4440. Ctl := TTBControlItem(Item).FControl;
  4441. BoundsRect.Right := Ctl.Width;
  4442. BoundsRect.Bottom := Ctl.Height;
  4443. end;
  4444. end;
  4445. end;
  4446. { Increase widths of SameWidth items if necessary. Also calculate
  4447. ABaseSize.X (or Y). }
  4448. ABaseSize.X := 0;
  4449. ABaseSize.Y := 0;
  4450. for I := 0 to FViewerCount-1 do begin
  4451. with NewPositions[I] do begin
  4452. if SameWidth and (BoundsRect.Right < HighestSameWidthViewerWidth) then
  4453. BoundsRect.Right := HighestSameWidthViewerWidth;
  4454. if AOrientation <> tbvoVertical then
  4455. Inc(ABaseSize.X, BoundsRect.Right)
  4456. else
  4457. Inc(ABaseSize.Y, BoundsRect.Bottom);
  4458. end;
  4459. end;
  4460. { Hide partially visible items, mark them as 'OffEdge' }
  4461. if AOrientation <> tbvoVertical then
  4462. Total := ABaseSize.X
  4463. else
  4464. Total := ABaseSize.Y;
  4465. NonControlsOffEdge := False;
  4466. UseChevron := Assigned(ChevronItem) and (AChevronOffset > 0) and
  4467. (Total > AChevronOffset);
  4468. if UseChevron then begin
  4469. Dec(AChevronOffset, AChevronSize);
  4470. while Total > AChevronOffset do begin
  4471. { Count number of items. Stop loop if <= 1 }
  4472. TotalVisibleItems := 0;
  4473. for I := FViewerCount-1 downto 0 do begin
  4474. if NewPositions[I].Show and not(tbisSeparator in FViewers[I].Item.ItemStyle) then
  4475. Inc(TotalVisibleItems);
  4476. end;
  4477. if TotalVisibleItems <= 1 then
  4478. Break;
  4479. { Hide any trailing separators }
  4480. for I := FViewerCount-1 downto 0 do begin
  4481. with NewPositions[I] do
  4482. if Show then begin
  4483. if not(tbisSeparator in FViewers[I].Item.ItemStyle) then
  4484. Break;
  4485. Show := False;
  4486. if AOrientation <> tbvoVertical then
  4487. Dec(Total, BoundsRect.Right)
  4488. else
  4489. Dec(Total, BoundsRect.Bottom);
  4490. goto 1;
  4491. end;
  4492. end;
  4493. { Find an item to hide }
  4494. if Assigned(FPriorityList) then
  4495. I := FPriorityList.Count-1
  4496. else
  4497. I := FViewerCount-1;
  4498. while I >= 0 do begin
  4499. if Assigned(FPriorityList) then begin
  4500. Viewer := FPriorityList[I];
  4501. J := Viewer.Index;
  4502. end
  4503. else begin
  4504. Viewer := FViewers[I];
  4505. J := I;
  4506. end;
  4507. if NewPositions[J].Show and not(tbisSeparator in Viewer.Item.ItemStyle) then begin
  4508. NewPositions[J].Show := False;
  4509. NewPositions[J].OffEdge := True;
  4510. if AOrientation <> tbvoVertical then
  4511. Dec(Total, NewPositions[J].BoundsRect.Right)
  4512. else
  4513. Dec(Total, NewPositions[J].BoundsRect.Bottom);
  4514. if not NonControlsOffEdge and not(Viewer.Item is TTBControlItem) then
  4515. NonControlsOffEdge := True;
  4516. goto 1;
  4517. end;
  4518. Dec(I);
  4519. end;
  4520. Break; { prevent endless loop }
  4521. 1:
  4522. { Don't show two consecutive separators }
  4523. LastWasSep := True; { set to True initially so it won't show leading seps }
  4524. for J := 0 to FViewerCount-1 do begin
  4525. Item := FViewers[J].Item;
  4526. with NewPositions[J] do begin
  4527. if Show then begin
  4528. if (tbisSeparator in Item.ItemStyle) and LastWasSep then begin
  4529. Show := False;
  4530. if AOrientation <> tbvoVertical then
  4531. Dec(Total, BoundsRect.Right)
  4532. else
  4533. Dec(Total, BoundsRect.Bottom);
  4534. end;
  4535. LastWasSep := tbisSeparator in Item.ItemStyle;
  4536. end;
  4537. end;
  4538. end;
  4539. end;
  4540. end;
  4541. { Hide any trailing separators after items were hidden }
  4542. for I := FViewerCount-1 downto 0 do begin
  4543. with NewPositions[I] do
  4544. if Show then begin
  4545. if not(tbisSeparator in FViewers[I].Item.ItemStyle) then
  4546. Break;
  4547. Show := False;
  4548. end;
  4549. end;
  4550. { Set the ABaseSize.Y (or X) *after* items were hidden }
  4551. for I := 0 to FViewerCount-1 do begin
  4552. with NewPositions[I] do
  4553. if Show then begin
  4554. if AOrientation <> tbvoVertical then begin
  4555. if BoundsRect.Bottom > ABaseSize.Y then
  4556. ABaseSize.Y := BoundsRect.Bottom;
  4557. end
  4558. else begin
  4559. if BoundsRect.Right > ABaseSize.X then
  4560. ABaseSize.X := BoundsRect.Right;
  4561. end;
  4562. end;
  4563. end;
  4564. { On menus, set all non-separator items to be as tall as the tallest item }
  4565. {if not FIsToolbar then begin
  4566. J := 0;
  4567. for I := 0 to FViewerCount-1 do begin
  4568. Item := FViewers[I].Item;
  4569. with NewPositions[I] do
  4570. if Show and not(tbisSeparator in Item.ItemStyle) and
  4571. not(tboToolbarSize in Item.FEffectiveOptions) and
  4572. (BoundsRect.Bottom - BoundsRect.Top > J) then
  4573. J := BoundsRect.Bottom - BoundsRect.Top;
  4574. end;
  4575. for I := 0 to FViewerCount-1 do begin
  4576. Item := FViewers[I].Item;
  4577. with NewPositions[I] do
  4578. if Show and not(tbisSeparator in Item.ItemStyle) and
  4579. not(tboToolbarSize in Item.FEffectiveOptions) then
  4580. BoundsRect.Bottom := BoundsRect.Top + J;
  4581. end;
  4582. end;}
  4583. { Calculate the position of the items }
  4584. GetMargins(AOrientation, Margins);
  4585. LeftX := Margins.Left;
  4586. TopY := Margins.Top;
  4587. if AWrapOffset > 0 then begin
  4588. Dec(AWrapOffset, Margins.Right);
  4589. if AWrapOffset < 1 then AWrapOffset := 1;
  4590. end;
  4591. CurX := LeftX;
  4592. CurY := TopY;
  4593. GroupSplit := False;
  4594. DidWrap := False;
  4595. LastWasButton := FIsToolbar;
  4596. LineStart := -1;
  4597. for I := 0 to FViewerCount-1 do begin
  4598. Item := FViewers[I].Item;
  4599. with NewPositions[I] do begin
  4600. if not Show then
  4601. Continue;
  4602. IsButton := FIsToolbar or (tboToolbarSize in Item.FEffectiveOptions);
  4603. if LastWasButton and not IsButton then begin
  4604. { On a menu, if last item was a button and the current item isn't,
  4605. proceed to next row }
  4606. CurX := LeftX;
  4607. CurY := TotalSize.Y;
  4608. end;
  4609. LastWasButton := IsButton;
  4610. PositionItem(I, NewPositions[I]);
  4611. if IsButton and (AOrientation <> tbvoVertical) then
  4612. Inc(CurX, BoundsRect.Right - BoundsRect.Left)
  4613. else
  4614. Inc(CurY, BoundsRect.Bottom - BoundsRect.Top);
  4615. if BoundsRect.Right > TotalSize.X then
  4616. TotalSize.X := BoundsRect.Right;
  4617. if BoundsRect.Bottom > TotalSize.Y then
  4618. TotalSize.Y := BoundsRect.Bottom;
  4619. end;
  4620. end;
  4621. if FViewerCount <> 0 then
  4622. FinalizeLine(FViewerCount-1, True);
  4623. Inc(TotalSize.X, Margins.Right);
  4624. Inc(TotalSize.Y, Margins.Bottom);
  4625. if AOrientation = tbvoVertical then
  4626. Mirror;
  4627. HandleMaxHeight;
  4628. if CanMoveControls then begin
  4629. for I := 0 to FViewerCount-1 do begin
  4630. Item := FViewers[I].Item;
  4631. if Item is TTBControlItem then begin
  4632. if NewPositions[I].Show then begin
  4633. Ctl := TTBControlItem(Item).FControl;
  4634. if not EqualRect(NewPositions[I].BoundsRect, Ctl.BoundsRect) then
  4635. Ctl.BoundsRect := NewPositions[I].BoundsRect;
  4636. end
  4637. else if NewPositions[I].OffEdge or NewPositions[I].Clipped then begin
  4638. { Simulate hiding of OddEdge controls by literally moving them
  4639. off the edge. Do the same for Clipped controls. }
  4640. Ctl := TTBControlItem(Item).FControl;
  4641. Ctl.SetBounds(FWindow.ClientWidth, FWindow.ClientHeight,
  4642. Ctl.Width, Ctl.Height);
  4643. end;
  4644. end;
  4645. end;
  4646. end;
  4647. { Set size of line separators }
  4648. if FIsToolbar then
  4649. for I := 0 to FViewerCount-1 do begin
  4650. Item := FViewers[I].Item;
  4651. with NewPositions[I] do
  4652. if Show and (tbisSeparator in Item.ItemStyle) and
  4653. LineSep then begin
  4654. if AOrientation <> tbvoVertical then
  4655. BoundsRect.Right := TotalSize.X
  4656. else
  4657. BoundsRect.Bottom := TotalSize.Y;
  4658. end;
  4659. end;
  4660. { Position the chevron item }
  4661. if UseChevron then begin
  4662. if CanMoveControls then
  4663. ChevronItem.Enabled := NonControlsOffEdge;
  4664. NewPositions[FViewerCount-1].Show := True;
  4665. I := AChevronOffset;
  4666. if AOrientation <> tbvoVertical then begin
  4667. if I < TotalSize.X then
  4668. I := TotalSize.X;
  4669. NewPositions[FViewerCount-1].BoundsRect := Bounds(I, 0,
  4670. AChevronSize, TotalSize.Y);
  4671. end
  4672. else begin
  4673. if I < TotalSize.Y then
  4674. I := TotalSize.Y;
  4675. NewPositions[FViewerCount-1].BoundsRect := Bounds(0, I,
  4676. TotalSize.X, AChevronSize);
  4677. end;
  4678. end;
  4679. { Commit changes }
  4680. Result := False;
  4681. if CanMoveControls then begin
  4682. for I := 0 to FViewerCount-1 do begin
  4683. if not Result and
  4684. (not EqualRect(FViewers[I].BoundsRect, NewPositions[I].BoundsRect) or
  4685. (FViewers[I].Show <> NewPositions[I].Show) or
  4686. (tbisLineSep in FViewers[I].State <> NewPositions[I].LineSep)) then
  4687. Result := True;
  4688. FViewers[I].FBoundsRect := NewPositions[I].BoundsRect;
  4689. FViewers[I].FShow := NewPositions[I].Show;
  4690. FViewers[I].FOffEdge := NewPositions[I].OffEdge;
  4691. FViewers[I].FClipped := NewPositions[I].Clipped;
  4692. if NewPositions[I].LineSep then
  4693. Include(FViewers[I].State, tbisLineSep)
  4694. else
  4695. Exclude(FViewers[I].State, tbisLineSep);
  4696. end;
  4697. end;
  4698. finally
  4699. FOrientation := SaveOrientation;
  4700. if Assigned(CalcCanvas) then
  4701. CalcCanvas.Handle := 0;
  4702. if DC <> 0 then ReleaseDC(0, DC);
  4703. CalcCanvas.Free;
  4704. FreeMem(NewPositions);
  4705. end;
  4706. if (ABaseSize.X = 0) or (ABaseSize.Y = 0) then begin
  4707. { If there are no visible items... }
  4708. {}{scale this?}
  4709. ABaseSize.X := 23;
  4710. ABaseSize.Y := 22;
  4711. if TotalSize.X < 23 then TotalSize.X := 23;
  4712. if TotalSize.Y < 22 then TotalSize.Y := 22;
  4713. end;
  4714. end;
  4715. procedure TTBView.DoUpdatePositions(var ASize: TPoint);
  4716. { This is called by UpdatePositions }
  4717. var
  4718. Bmp: TBitmap;
  4719. CtlCanvas: TControlCanvas;
  4720. WrappedLines: Integer;
  4721. begin
  4722. { Don't call InvalidatePositions before CalculatePositions so that
  4723. endless recursion doesn't happen if an item's CalcSize uses a method that
  4724. calls ValidatePositions }
  4725. if not CalculatePositions(True, FOrientation, FWrapOffset, FChevronOffset,
  4726. FChevronSize, FBaseSize, ASize, WrappedLines) then begin
  4727. { If the new positions are identical to the previous ones, continue using
  4728. the previous ones, and don't redraw }
  4729. FValidated := True;
  4730. { Just because the positions are the same doesn't mean the size hasn't
  4731. changed. (If a shrunken toolbar moves between docks, the positions of
  4732. the non-OffEdge items may be the same on the new dock as on the old
  4733. dock.) }
  4734. AutoSize(ASize.X, ASize.Y);
  4735. end
  4736. else begin
  4737. if not(csDesigning in ComponentState) then begin
  4738. FValidated := True;
  4739. { Need to call ValidateRect before AutoSize, otherwise Windows will
  4740. erase the client area during a resize }
  4741. if FWindow.HandleAllocated then
  4742. ValidateRect(FWindow.Handle, nil);
  4743. AutoSize(ASize.X, ASize.Y);
  4744. if Assigned(FWindow) and FWindow.HandleAllocated and
  4745. IsWindowVisible(FWindow.Handle) and
  4746. (FWindow.ClientWidth > 0) and (FWindow.ClientHeight > 0) then begin
  4747. CtlCanvas := nil;
  4748. Bmp := TBitmap.Create;
  4749. try
  4750. CtlCanvas := TControlCanvas.Create;
  4751. CtlCanvas.Control := FWindow;
  4752. Bmp.Width := FWindow.ClientWidth;
  4753. Bmp.Height := FWindow.ClientHeight;
  4754. SendMessage(FWindow.Handle, WM_ERASEBKGND, WPARAM(Bmp.Canvas.Handle), 0);
  4755. SendMessage(FWindow.Handle, WM_PAINT, WPARAM(Bmp.Canvas.Handle), 0);
  4756. BitBlt(CtlCanvas.Handle, 0, 0, Bmp.Width, Bmp.Height,
  4757. Bmp.Canvas.Handle, 0, 0, SRCCOPY);
  4758. ValidateRect(FWindow.Handle, nil);
  4759. finally
  4760. CtlCanvas.Free;
  4761. Bmp.Free;
  4762. end;
  4763. end;
  4764. end
  4765. else begin
  4766. { Delphi's handling of canvases is different at design time -- child
  4767. controls aren't clipped from a parent control's canvas, so the above
  4768. offscreen rendering code doesn't work right at design-time }
  4769. InvalidatePositions;
  4770. FValidated := True;
  4771. AutoSize(ASize.X, ASize.Y);
  4772. end;
  4773. end;
  4774. end;
  4775. function TTBView.UpdatePositions: TPoint;
  4776. { Called whenever the size or orientation of a view changes. When items are
  4777. added or removed from the view, InvalidatePositions must be called instead,
  4778. otherwise the view may not be redrawn properly. }
  4779. begin
  4780. Result.X := 0;
  4781. Result.Y := 0;
  4782. DoUpdatePositions(Result);
  4783. end;
  4784. procedure TTBView.AutoSize(AWidth, AHeight: Integer);
  4785. begin
  4786. end;
  4787. function TTBView.GetChevronItem: TTBCustomItem;
  4788. begin
  4789. Result := nil;
  4790. end;
  4791. procedure TTBView.GetMargins(AOrientation: TTBViewOrientation;
  4792. var Margins: TRect);
  4793. begin
  4794. if AOrientation = tbvoFloating then begin
  4795. Margins.Left := 4;
  4796. Margins.Top := 2;
  4797. Margins.Right := 4;
  4798. Margins.Bottom := 1;
  4799. end
  4800. else begin
  4801. Margins.Left := 0;
  4802. Margins.Top := 0;
  4803. Margins.Right := 0;
  4804. Margins.Bottom := 0;
  4805. end;
  4806. end;
  4807. function TTBView.GetMDIButtonsItem: TTBCustomItem;
  4808. begin
  4809. Result := nil;
  4810. end;
  4811. function TTBView.GetMDISystemMenuItem: TTBCustomItem;
  4812. begin
  4813. Result := nil;
  4814. end;
  4815. function TTBView.GetFont: TFont;
  4816. begin
  4817. Result := GetToolbarFont(GetMonitorPixelsPerInch(GetMonitor));
  4818. if not Assigned(Result) then
  4819. begin
  4820. { ToolbarFont is destroyed during unit finalization, but in rare cases
  4821. this method may end up being called from ValidatePositions *after*
  4822. unit finalization if Application.Run is never called; see the
  4823. "EConvertError" newsgroup thread. We can't return nil because that would
  4824. cause an exception in the calling function, so just return the window
  4825. font. It's not the *right* font, but it shouldn't matter since the app
  4826. is exiting anyway. }
  4827. Result := TControlAccess(FWindow).Font;
  4828. end;
  4829. end;
  4830. procedure TTBView.DrawItem(Viewer: TTBItemViewer; DrawTo: TCanvas;
  4831. Offscreen: Boolean);
  4832. const
  4833. COLOR_MENUHILIGHT = 29;
  4834. clMenuHighlight = TColor(COLOR_MENUHILIGHT or $80000000);
  4835. var
  4836. Bmp: TBitmap;
  4837. DrawToDC, BmpDC: HDC;
  4838. DrawCanvas: TCanvas;
  4839. R1, R2, R3: TRect;
  4840. IsOpen, IsSelected, IsPushed: Boolean;
  4841. ToolbarStyle: Boolean;
  4842. UseDisabledShadow: Boolean;
  4843. SaveIndex, SaveIndex2: Integer;
  4844. BkColor: TColor;
  4845. begin
  4846. ValidatePositions;
  4847. if tbisInvalidated in Viewer.State then begin
  4848. Offscreen := True;
  4849. Exclude(Viewer.State, tbisInvalidated);
  4850. end;
  4851. R1 := Viewer.BoundsRect;
  4852. if not Viewer.Show or IsRectEmpty(R1) or (Viewer.Item is TTBControlItem) then
  4853. Exit;
  4854. R2 := R1;
  4855. OffsetRect(R2, -R2.Left, -R2.Top);
  4856. IsOpen := FOpenViewer = Viewer;
  4857. IsSelected := (FSelected = Viewer);
  4858. IsPushed := IsSelected and (IsOpen or (FMouseOverSelected and FCapture));
  4859. ToolbarStyle := Viewer.IsToolbarStyle;
  4860. DrawToDC := DrawTo.Handle;
  4861. Bmp := nil;
  4862. { Must deselect any currently selected handles before calling SaveDC, because
  4863. if they are left selected and DeleteObject gets called on them after the
  4864. SaveDC call, it will fail on Win9x/Me, and thus leak GDI resources. }
  4865. DrawTo.Refresh;
  4866. SaveIndex := SaveDC(DrawToDC);
  4867. try
  4868. IntersectClipRect(DrawToDC, R1.Left, R1.Top, R1.Right, R1.Bottom);
  4869. GetClipBox(DrawToDC, R3);
  4870. if IsRectEmpty(R3) then
  4871. Exit;
  4872. if not Offscreen then begin
  4873. MoveWindowOrg(DrawToDC, R1.Left, R1.Top);
  4874. { Tweak the brush origin so that the checked background drawn behind
  4875. checked items always looks the same regardless of whether the item
  4876. is positioned on an even or odd Left or Top coordinate. }
  4877. SetBrushOrgEx(DrawToDC, R1.Left and 1, R1.Top and 1, nil);
  4878. DrawCanvas := DrawTo;
  4879. end
  4880. else begin
  4881. Bmp := TBitmap.Create;
  4882. Bmp.Width := R2.Right;
  4883. Bmp.Height := R2.Bottom;
  4884. DrawCanvas := Bmp.Canvas;
  4885. BmpDC := DrawCanvas.Handle;
  4886. SaveIndex2 := SaveDC(BmpDC);
  4887. SetWindowOrgEx(BmpDC, R1.Left, R1.Top, nil);
  4888. FWindow.Perform(WM_ERASEBKGND, WPARAM(BmpDC), 0);
  4889. RestoreDC(BmpDC, SaveIndex2);
  4890. end;
  4891. { Initialize brush }
  4892. if not ToolbarStyle and IsSelected then begin
  4893. {$IFNDEF TB2K_USE_STRICT_O2K_MENU_STYLE}
  4894. if AreFlatMenusEnabled then
  4895. { Windows XP uses a different fill color for selected menu items when
  4896. flat menus are enabled }
  4897. DrawCanvas.Brush.Color := clMenuHighlight
  4898. else
  4899. {$ENDIF}
  4900. DrawCanvas.Brush.Color := clHighlight;
  4901. end
  4902. else
  4903. DrawCanvas.Brush.Style := bsClear;
  4904. { Initialize font }
  4905. DrawCanvas.Font.Assign(GetFont);
  4906. if Viewer.Item.Enabled then begin
  4907. if not ToolbarStyle and IsSelected then
  4908. DrawCanvas.Font.Color := clHighlightText
  4909. else begin
  4910. if ToolbarStyle then
  4911. DrawCanvas.Font.Color := clBtnText
  4912. else
  4913. DrawCanvas.Font.Color := tbMenuTextColor;
  4914. end;
  4915. UseDisabledShadow := False;
  4916. end
  4917. else begin
  4918. DrawCanvas.Font.Color := clGrayText;
  4919. { Use the disabled shadow if either:
  4920. 1. The item is a toolbar-style item.
  4921. 2. The item is not selected, and the background color equals the
  4922. button-face color.
  4923. 3. The gray-text color is the same as the background color.
  4924. Note: Windows actually uses dithered text in this case. }
  4925. BkColor := ColorToRGB(TControlAccess(FWindow).Color);
  4926. UseDisabledShadow := ToolbarStyle or
  4927. (not IsSelected and (BkColor = ColorToRGB(clBtnFace))) or
  4928. (ColorToRGB(clGrayText) = BkColor);
  4929. end;
  4930. Viewer.Paint(DrawCanvas, R2, IsSelected, IsPushed, UseDisabledShadow);
  4931. if Offscreen then
  4932. BitBlt(DrawToDC, R1.Left, R1.Top, Bmp.Width, Bmp.Height, DrawCanvas.Handle,
  4933. 0, 0, SRCCOPY);
  4934. finally
  4935. DrawTo.Refresh; { must do this before a RestoreDC }
  4936. RestoreDC(DrawToDC, SaveIndex);
  4937. Bmp.Free;
  4938. end;
  4939. end;
  4940. procedure TTBView.DrawSubitems(ACanvas: TCanvas);
  4941. var
  4942. I: Integer;
  4943. begin
  4944. for I := 0 to FViewerCount-1 do begin
  4945. if (vsDrawInOrder in FState) or (FViewers[I] <> FSelected) then
  4946. DrawItem(FViewers[I], ACanvas, False);
  4947. end;
  4948. if not(vsDrawInOrder in FState) and Assigned(FSelected) then
  4949. DrawItem(FSelected, ACanvas, False);
  4950. Exclude(FState, vsDrawInOrder);
  4951. end;
  4952. procedure TTBView.Invalidate(AViewer: TTBItemViewer);
  4953. begin
  4954. if not FValidated or not Assigned(FWindow) or not FWindow.HandleAllocated then
  4955. Exit;
  4956. if AViewer.Show and not IsRectEmpty(AViewer.BoundsRect) and
  4957. not(AViewer.Item is TTBControlItem) then begin
  4958. Include(AViewer.State, tbisInvalidated);
  4959. InvalidateRect(FWindow.Handle, @AViewer.BoundsRect, False);
  4960. end;
  4961. end;
  4962. procedure TTBView.SetAccelsVisibility(AShowAccels: Boolean);
  4963. var
  4964. I: Integer;
  4965. Viewer: TTBItemViewer;
  4966. begin
  4967. { Always show accels when keyboard cues are enabled }
  4968. AShowAccels := AShowAccels or not(vsUseHiddenAccels in FStyle) or
  4969. AreKeyboardCuesEnabled;
  4970. if AShowAccels <> (vsShowAccels in FState) then begin
  4971. if AShowAccels then
  4972. Include(FState, vsShowAccels)
  4973. else
  4974. Exclude(FState, vsShowAccels);
  4975. if Assigned(FWindow) and FWindow.HandleAllocated and
  4976. IsWindowVisible(FWindow.Handle) then
  4977. { ^ the visibility check is just an optimization }
  4978. for I := 0 to FViewerCount-1 do begin
  4979. Viewer := FViewers[I];
  4980. if Viewer.CaptionShown and
  4981. (FindAccelChar(Viewer.GetCaptionText) <> #0) then
  4982. Invalidate(Viewer);
  4983. end;
  4984. end;
  4985. end;
  4986. function TTBView.FirstSelectable: TTBItemViewer;
  4987. var
  4988. FirstViewer: TTBItemViewer;
  4989. begin
  4990. Result := NextSelectable(nil, True);
  4991. if Assigned(Result) then begin
  4992. FirstViewer := Result;
  4993. while tbisDontSelectFirst in Result.Item.ItemStyle do begin
  4994. Result := NextSelectable(Result, True);
  4995. if Result = FirstViewer then
  4996. { don't loop endlessly if all items have the tbisDontSelectFirst style }
  4997. Break;
  4998. end;
  4999. end;
  5000. end;
  5001. function TTBView.NextSelectable(CurViewer: TTBItemViewer;
  5002. GoForward: Boolean): TTBItemViewer;
  5003. var
  5004. I, J: Integer;
  5005. begin
  5006. ValidatePositions;
  5007. Result := nil;
  5008. if FViewerCount = 0 then Exit;
  5009. J := -1;
  5010. I := IndexOf(CurViewer);
  5011. while True do begin
  5012. if GoForward then begin
  5013. Inc(I);
  5014. if I >= FViewerCount then I := 0;
  5015. end
  5016. else begin
  5017. Dec(I);
  5018. if I < 0 then I := FViewerCount-1;
  5019. end;
  5020. if J = -1 then
  5021. J := I
  5022. else
  5023. if I = J then
  5024. Exit;
  5025. if (FViewers[I].Show or FViewers[I].Clipped) and FViewers[I].Item.Visible and
  5026. (tbisSelectable in FViewers[I].Item.ItemStyle) then
  5027. Break;
  5028. end;
  5029. Result := FViewers[I];
  5030. end;
  5031. function TTBView.NextSelectableWithAccel(CurViewer: TTBItemViewer;
  5032. Key: Char; RequirePrimaryAccel: Boolean; var IsOnlyItemWithAccel: Boolean): TTBItemViewer;
  5033. function IsAccelItem(const Index: Integer;
  5034. const Primary, EnabledItems: Boolean): Boolean;
  5035. var
  5036. S: String;
  5037. LastAccel: Char;
  5038. Viewer: TTBItemViewer;
  5039. Item: TTBCustomItem;
  5040. begin
  5041. Result := False;
  5042. Viewer := FViewers[Index];
  5043. Item := Viewer.Item;
  5044. if (Viewer.Show or Viewer.Clipped) and (tbisSelectable in Item.ItemStyle) and
  5045. (Item.Enabled = EnabledItems) and
  5046. Item.Visible and Viewer.CaptionShown then begin
  5047. S := Viewer.GetCaptionText;
  5048. if S <> '' then begin
  5049. LastAccel := FindAccelChar(S);
  5050. if Primary then begin
  5051. if LastAccel <> #0 then
  5052. Result := AnsiCompareText(LastAccel, Key) = 0;
  5053. end
  5054. else
  5055. if (LastAccel = #0) and (Key <> ' ') then
  5056. Result := AnsiCompareText(S[1], Key) = 0;
  5057. end;
  5058. end;
  5059. end;
  5060. function FindAccel(I: Integer;
  5061. const Primary, EnabledItems: Boolean): Integer;
  5062. var
  5063. J: Integer;
  5064. begin
  5065. Result := -1;
  5066. J := -1;
  5067. while True do begin
  5068. Inc(I);
  5069. if I >= FViewerCount then I := 0;
  5070. if J = -1 then
  5071. J := I
  5072. else
  5073. if I = J then
  5074. Break;
  5075. if IsAccelItem(I, Primary, EnabledItems) then begin
  5076. Result := I;
  5077. Break;
  5078. end;
  5079. end;
  5080. end;
  5081. var
  5082. Start, I: Integer;
  5083. Primary, EnabledItems: Boolean;
  5084. begin
  5085. ValidatePositions;
  5086. Result := nil;
  5087. IsOnlyItemWithAccel := False;
  5088. if FViewerCount = 0 then Exit;
  5089. Start := IndexOf(CurViewer);
  5090. for Primary := True downto False do
  5091. if not RequirePrimaryAccel or Primary then
  5092. for EnabledItems := True downto False do begin
  5093. I := FindAccel(Start, Primary, EnabledItems);
  5094. if I <> -1 then begin
  5095. Result := FViewers[I];
  5096. IsOnlyItemWithAccel := not EnabledItems or
  5097. (FindAccel(I, Primary, EnabledItems) = I);
  5098. Exit;
  5099. end;
  5100. end;
  5101. end;
  5102. procedure TTBView.EnterToolbarLoop(Options: TTBEnterToolbarLoopOptions);
  5103. var
  5104. ModalHandler: TTBModalHandler;
  5105. P: TPoint;
  5106. begin
  5107. if vsModal in FState then Exit;
  5108. ModalHandler := TTBModalHandler.Create(FWindow.Handle);
  5109. try
  5110. { remove all states except... }
  5111. FState := FState * [vsShowAccels];
  5112. try
  5113. Include(FState, vsModal);
  5114. { Must ensure that DoneAction is reset to tbdaNone *before* calling
  5115. NotifyFocusEvent so that the IsModalEnding call it makes won't return
  5116. True }
  5117. FDoneActionData.DoneAction := tbdaNone;
  5118. { Now that the vsModal state has been added, send an MSAA focus event }
  5119. if Assigned(Selected) then
  5120. NotifyFocusEvent;
  5121. ModalHandler.Loop(Self, tbetMouseDown in Options,
  5122. tbetExecuteSelected in Options, tbetFromMSAA in Options, False);
  5123. finally
  5124. { Remove vsModal state from the root view before any TTBView.Destroy
  5125. methods get called (as a result of the CloseChildPopups call below),
  5126. so that NotifyFocusEvent becomes a no-op }
  5127. Exclude(FState, vsModal);
  5128. StopAllTimers;
  5129. CloseChildPopups;
  5130. GetCursorPos(P);
  5131. UpdateSelection(@P, True);
  5132. end;
  5133. finally
  5134. ModalHandler.Free;
  5135. end;
  5136. SetAccelsVisibility(False);
  5137. Selected := nil;
  5138. // caused flicker: FWindow.Update;
  5139. ProcessDoneAction(FDoneActionData, False);
  5140. end;
  5141. procedure TTBView.SetCustomizing(Value: Boolean);
  5142. begin
  5143. if FCustomizing <> Value then begin
  5144. FCustomizing := Value;
  5145. RecreateAllViewers;
  5146. end;
  5147. end;
  5148. procedure TTBView.BeginUpdate;
  5149. begin
  5150. Inc(FUpdating);
  5151. end;
  5152. procedure TTBView.EndUpdate;
  5153. begin
  5154. Dec(FUpdating);
  5155. if FUpdating = 0 then
  5156. TryValidatePositions;
  5157. end;
  5158. procedure TTBView.GetOffEdgeControlList(const List: TList);
  5159. var
  5160. I: Integer;
  5161. Item: TTBCustomItem;
  5162. begin
  5163. for I := 0 to FViewerCount-1 do begin
  5164. Item := FViewers[I].Item;
  5165. if (Item is TTBControlItem) and FViewers[I].OffEdge and
  5166. (TTBControlItem(Item).FControl is TWinControl) then
  5167. List.Add(TTBControlItem(Item).FControl);
  5168. end;
  5169. end;
  5170. procedure TTBView.SetCapture;
  5171. begin
  5172. FCapture := True;
  5173. end;
  5174. procedure TTBView.CancelCapture;
  5175. begin
  5176. if FCapture then begin
  5177. FCapture := False;
  5178. LastPos.X := Low(LastPos.X);
  5179. if Assigned(FSelected) then
  5180. FSelected.LosingCapture;
  5181. end;
  5182. end;
  5183. procedure TTBView.KeyDown(var Key: Word; Shift: TShiftState);
  5184. procedure SelNextItem(const ParentView: TTBView; const GoForward: Boolean);
  5185. begin
  5186. ParentView.Selected := ParentView.NextSelectable(ParentView.FSelected,
  5187. GoForward);
  5188. ParentView.ScrollSelectedIntoView;
  5189. end;
  5190. procedure HelpKey;
  5191. var
  5192. V: TTBView;
  5193. ContextID: Integer;
  5194. { MP }
  5195. HelpKeyword: string;
  5196. begin
  5197. ContextID := 0;
  5198. V := Self;
  5199. while Assigned(V) do begin
  5200. if Assigned(V.FSelected) then begin
  5201. ContextID := V.FSelected.Item.HelpContext;
  5202. if ContextID <> 0 then Break;
  5203. end;
  5204. V := V.FParentView;
  5205. end;
  5206. { MP }
  5207. if ContextID <> 0 then
  5208. begin
  5209. EndModalWithHelp(ContextID);
  5210. Exit;
  5211. end;
  5212. HelpKeyword := '';
  5213. V := Self;
  5214. while Assigned(V) do begin
  5215. if Assigned(V.FSelected) then begin
  5216. HelpKeyword := V.FSelected.Item.HelpKeyword;
  5217. if HelpKeyword <> '' then Break;
  5218. end;
  5219. V := V.FParentView;
  5220. end;
  5221. if HelpKeyword <> '' then
  5222. EndModalWithHelp(HelpKeyword);
  5223. { /MP }
  5224. end;
  5225. var
  5226. ParentTBView: TTBView;
  5227. begin
  5228. ParentTBView := GetParentToolbarView;
  5229. case Key of
  5230. VK_TAB: begin
  5231. SelNextItem(Self, GetKeyState(VK_SHIFT) >= 0);
  5232. end;
  5233. VK_RETURN: begin
  5234. ExecuteSelected(True);
  5235. end;
  5236. VK_MENU, VK_F10: begin
  5237. EndModal;
  5238. end;
  5239. VK_ESCAPE: begin
  5240. Key := 0;
  5241. if FParentView = nil then
  5242. EndModal
  5243. else
  5244. FParentView.CancelChildPopups;
  5245. end;
  5246. VK_LEFT, VK_RIGHT: begin
  5247. if (Self = ParentTBView) and (Orientation = tbvoVertical) then
  5248. OpenChildPopup(True)
  5249. else if Key = VK_LEFT then begin
  5250. if Assigned(ParentTBView) and (ParentTBView.Orientation <> tbvoVertical) then begin
  5251. if (Self = ParentTBView) or
  5252. (FParentView = ParentTBView) then
  5253. SelNextItem(ParentTBView, False)
  5254. else
  5255. FParentView.CloseChildPopups;
  5256. end
  5257. else begin
  5258. if Assigned(FParentView) then
  5259. FParentView.CancelChildPopups;
  5260. end;
  5261. end
  5262. else begin
  5263. if ((Self = ParentTBView) or not OpenChildPopup(True)) and
  5264. (Assigned(ParentTBView) and (ParentTBView.Orientation <> tbvoVertical)) then begin
  5265. { If we're on ParentTBView, or if the selected item can't display
  5266. a submenu, proceed to next item on ParentTBView }
  5267. SelNextItem(ParentTBView, True);
  5268. end;
  5269. end;
  5270. end;
  5271. VK_UP, VK_DOWN: begin
  5272. if (Self = ParentTBView) and (Orientation <> tbvoVertical) then
  5273. OpenChildPopup(True)
  5274. else
  5275. SelNextItem(Self, Key = VK_DOWN);
  5276. end;
  5277. VK_HOME, VK_END: begin
  5278. Selected := NextSelectable(nil, Key = VK_HOME);
  5279. ScrollSelectedIntoView;
  5280. end;
  5281. VK_F1: HelpKey;
  5282. else
  5283. Exit; { don't set Key to 0 for unprocessed keys }
  5284. end;
  5285. Key := 0;
  5286. end;
  5287. function TTBView.IsModalEnding: Boolean;
  5288. begin
  5289. Result := (GetRootView.FDoneActionData.DoneAction <> tbdaNone);
  5290. end;
  5291. procedure TTBView.EndModal;
  5292. var
  5293. RootView: TTBView;
  5294. begin
  5295. RootView := GetRootView;
  5296. RootView.FDoneActionData.DoneAction := tbdaCancel;
  5297. end;
  5298. procedure TTBView.EndModalWithClick(AViewer: TTBItemViewer);
  5299. var
  5300. RootView: TTBView;
  5301. begin
  5302. RootView := GetRootView;
  5303. RootView.FDoneActionData.ClickItem := AViewer.Item;
  5304. RootView.FDoneActionData.Sound := AViewer.FView.FIsPopup;
  5305. RootView.FDoneActionData.DoneAction := tbdaClickItem;
  5306. end;
  5307. procedure TTBView.EndModalWithHelp(AContextID: Integer);
  5308. var
  5309. RootView: TTBView;
  5310. begin
  5311. RootView := GetRootView;
  5312. RootView.FDoneActionData.ContextID := AContextID;
  5313. RootView.FDoneActionData.DoneAction := tbdaHelpContext;
  5314. end;
  5315. { MP }
  5316. procedure TTBView.EndModalWithHelp(HelpKeyword: string);
  5317. var
  5318. RootView: TTBView;
  5319. begin
  5320. RootView := GetRootView;
  5321. RootView.FDoneActionData.HelpKeyword := ShortString(HelpKeyword);
  5322. RootView.FDoneActionData.DoneAction := tbdaHelpKeyword;
  5323. end;
  5324. { /MP }
  5325. procedure TTBView.EndModalWithSystemMenu(AWnd: HWND; AKey: Cardinal);
  5326. var
  5327. RootView: TTBView;
  5328. begin
  5329. RootView := GetRootView;
  5330. RootView.FDoneActionData.Wnd := AWnd;
  5331. RootView.FDoneActionData.Key := AKey;
  5332. RootView.FDoneActionData.DoneAction := tbdaOpenSystemMenu;
  5333. end;
  5334. procedure TTBView.ExecuteSelected(AGivePriority: Boolean);
  5335. { Normally called after an Enter or accelerator key press on the view, this
  5336. method 'executes' or opens the selected item. It ends the modal loop, except
  5337. when a submenu is opened. }
  5338. var
  5339. Item: TTBCustomItem;
  5340. begin
  5341. if Assigned(FSelected) and FSelected.Item.Enabled then begin
  5342. Item := FSelected.Item;
  5343. if (tbisCombo in Item.ItemStyle) or not OpenChildPopup(True) then begin
  5344. if tbisSelectable in Item.ItemStyle then
  5345. FSelected.Execute(AGivePriority)
  5346. else
  5347. EndModal;
  5348. end
  5349. end
  5350. else
  5351. EndModal;
  5352. Exit; asm db 0,'Toolbar2000 (C) 1998-2005 Jordan Russell',0 end;
  5353. end;
  5354. procedure TTBView.Scroll(ADown: Boolean);
  5355. var
  5356. CurPos, NewPos, I: Integer;
  5357. begin
  5358. ValidatePositions;
  5359. if ADown then begin
  5360. NewPos := High(NewPos);
  5361. CurPos := FMaxHeight - tbMenuScrollArrowHeight;
  5362. for I := 0 to FViewerCount-1 do begin
  5363. with FViewers[I] do
  5364. if Clipped and not(tbisSeparator in Item.ItemStyle) and
  5365. (BoundsRect.Bottom < NewPos) and (BoundsRect.Bottom > CurPos) then
  5366. NewPos := BoundsRect.Bottom;
  5367. end;
  5368. if NewPos = High(NewPos) then
  5369. Exit;
  5370. Dec(NewPos, FMaxHeight - tbMenuScrollArrowHeight);
  5371. end
  5372. else begin
  5373. NewPos := Low(NewPos);
  5374. CurPos := tbMenuScrollArrowHeight;
  5375. for I := 0 to FViewerCount-1 do begin
  5376. with FViewers[I] do
  5377. if Clipped and not(tbisSeparator in Item.ItemStyle) and
  5378. (BoundsRect.Top > NewPos) and (BoundsRect.Top < CurPos) then
  5379. NewPos := BoundsRect.Top;
  5380. end;
  5381. if NewPos = Low(NewPos) then
  5382. Exit;
  5383. Dec(NewPos, tbMenuScrollArrowHeight);
  5384. end;
  5385. Inc(FScrollOffset, NewPos);
  5386. UpdatePositions;
  5387. end;
  5388. procedure TTBView.ScrollSelectedIntoView;
  5389. begin
  5390. ValidatePositions;
  5391. if (FSelected = nil) or not FSelected.Clipped then
  5392. Exit;
  5393. if FSelected.BoundsRect.Top < tbMenuScrollArrowHeight then begin
  5394. Dec(FScrollOffset, tbMenuScrollArrowHeight - FSelected.BoundsRect.Top);
  5395. UpdatePositions;
  5396. end
  5397. else if FSelected.BoundsRect.Bottom > FMaxHeight - tbMenuScrollArrowHeight then begin
  5398. Dec(FScrollOffset, (FMaxHeight - tbMenuScrollArrowHeight) -
  5399. FSelected.BoundsRect.Bottom);
  5400. UpdatePositions;
  5401. end;
  5402. end;
  5403. procedure TTBView.SetUsePriorityList(Value: Boolean);
  5404. begin
  5405. if FUsePriorityList <> Value then begin
  5406. FUsePriorityList := Value;
  5407. RecreateAllViewers;
  5408. end;
  5409. end;
  5410. function TTBView.GetCaptureWnd: HWND;
  5411. begin
  5412. Result := GetRootView.FCaptureWnd;
  5413. end;
  5414. procedure TTBView.CancelMode;
  5415. var
  5416. View: TTBView;
  5417. begin
  5418. EndModal;
  5419. { Hide all parent/child popup windows. Can't actually destroy them using
  5420. CloseChildPopups because this method may be called while inside
  5421. TTBEditItemViewer's message loop, and it could result in the active
  5422. TTBEditItemViewer instance being destroyed (leading to an AV). }
  5423. View := Self;
  5424. while Assigned(View.FOpenViewerView) do
  5425. View := View.FOpenViewerView;
  5426. repeat
  5427. View.StopAllTimers;
  5428. if View.FWindow is TTBPopupWindow then
  5429. View.FWindow.Visible := False;
  5430. View := View.FParentView;
  5431. until View = nil;
  5432. { Note: This doesn't remove the selection from a top-level toolbar item.
  5433. Unfortunately, we can't do 'Selected := nil' because it would destroy
  5434. child popups and that must'nt happen for the reason stated above. }
  5435. end;
  5436. procedure TTBView.SetState(AState: TTBViewState);
  5437. begin
  5438. FState := AState;
  5439. end;
  5440. function TTBView.GetMonitor: TMonitor;
  5441. begin
  5442. if ParentView <> nil then
  5443. begin
  5444. Result := ParentView.GetMonitor;
  5445. end
  5446. else
  5447. if not IsRectEmpty(FMonitorRect) then
  5448. begin
  5449. Result := Screen.MonitorFromRect(FMonitorRect);
  5450. end
  5451. else
  5452. begin
  5453. Result := GetMonitorFromControl(Window);
  5454. end;
  5455. end;
  5456. { TTBModalHandler }
  5457. const
  5458. LSFW_LOCK = 1;
  5459. LSFW_UNLOCK = 2;
  5460. var
  5461. LockSetForegroundWindowInited: BOOL;
  5462. LockSetForegroundWindow: function(uLockCode: UINT): BOOL; stdcall;
  5463. constructor TTBModalHandler.Create(AExistingWnd: HWND);
  5464. begin
  5465. inherited Create;
  5466. LastPos := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  5467. if AExistingWnd <> 0 then
  5468. FWnd := AExistingWnd
  5469. else begin
  5470. FWnd := Classes.AllocateHWnd(WndProc);
  5471. FCreatedWnd := True;
  5472. end;
  5473. { Like standard menus, don't allow other apps to steal the focus during
  5474. our modal loop. This also prevents us from losing activation when
  5475. "active window tracking" is enabled and the user moves the mouse over
  5476. another application's window. }
  5477. LockForegroundWindow;
  5478. SetCapture(FWnd);
  5479. SetCursor(LoadCursor(0, IDC_ARROW));
  5480. NotifyWinEvent(EVENT_SYSTEM_MENUSTART, FWnd, OBJID_CLIENT, CHILDID_SELF);
  5481. FInited := True;
  5482. end;
  5483. class procedure TTBModalHandler.DoLockForegroundWindow(LockCode: Cardinal);
  5484. begin
  5485. if not LockSetForegroundWindowInited then begin
  5486. LockSetForegroundWindow := GetProcAddress(GetModuleHandle(user32),
  5487. 'LockSetForegroundWindow');
  5488. InterlockedExchange(Integer(LockSetForegroundWindowInited), Ord(True));
  5489. end;
  5490. { Should always, as supported since Windows 2000 }
  5491. if Assigned(LockSetForegroundWindow) then
  5492. LockSetForegroundWindow(LockCode);
  5493. end;
  5494. class procedure TTBModalHandler.LockForegroundWindow;
  5495. begin
  5496. DoLockForegroundWindow(LSFW_LOCK);
  5497. end;
  5498. class procedure TTBModalHandler.UnlockForegroundWindow;
  5499. begin
  5500. DoLockForegroundWindow(LSFW_UNLOCK);
  5501. end;
  5502. destructor TTBModalHandler.Destroy;
  5503. begin
  5504. UnlockForegroundWindow;
  5505. if FWnd <> 0 then begin
  5506. if GetCapture = FWnd then
  5507. ReleaseCapture;
  5508. if FInited then
  5509. NotifyWinEvent(EVENT_SYSTEM_MENUEND, FWnd, OBJID_CLIENT, CHILDID_SELF);
  5510. if FCreatedWnd then
  5511. Classes.DeallocateHWnd(FWnd);
  5512. end;
  5513. inherited;
  5514. end;
  5515. procedure TTBModalHandler.WndProc(var Msg: TMessage);
  5516. begin
  5517. Msg.Result := DefWindowProc(FWnd, Msg.Msg, Msg.WParam, Msg.LParam);
  5518. if (Msg.Msg = WM_CANCELMODE) and Assigned(FRootPopup) then begin
  5519. try
  5520. { We can receive a WM_CANCELMODE message during a modal loop if a dialog
  5521. pops up. Respond by hiding menus to make it look like the modal loop
  5522. has returned, even though it really hasn't yet.
  5523. Note: Similar code in TTBCustomToolbar.WMCancelMode. }
  5524. FRootPopup.View.CancelMode;
  5525. except
  5526. Application.HandleException(Self);
  5527. end;
  5528. end;
  5529. end;
  5530. procedure TTBModalHandler.Loop(const RootView: TTBView;
  5531. const AMouseDown, AExecuteSelected, AFromMSAA, TrackRightButton: Boolean);
  5532. var
  5533. OriginalActiveWindow: HWND;
  5534. function GetActiveView: TTBView;
  5535. begin
  5536. Result := RootView;
  5537. while Assigned(Result.FOpenViewerView) do
  5538. Result := Result.FOpenViewerView;
  5539. end;
  5540. procedure UpdateAllSelections(const P: TPoint; const AllowNewSelection: Boolean);
  5541. var
  5542. View, CapView: TTBView;
  5543. begin
  5544. View := GetActiveView;
  5545. CapView := View;
  5546. while Assigned(CapView) and not CapView.FCapture do
  5547. CapView := CapView.FParentView;
  5548. while Assigned(View) do begin
  5549. if (CapView = nil) or (View = CapView) then
  5550. View.UpdateSelection(@P, AllowNewSelection);
  5551. View := View.FParentView;
  5552. end;
  5553. end;
  5554. function GetSelectedViewer(var AView: TTBView; var AViewer: TTBItemViewer): Boolean;
  5555. { Returns True if AViewer <> nil. }
  5556. var
  5557. View: TTBView;
  5558. begin
  5559. AView := nil;
  5560. AViewer := nil;
  5561. { Look for a capture item first }
  5562. View := RootView;
  5563. repeat
  5564. if View.FCapture then begin
  5565. AView := View;
  5566. AViewer := View.FSelected;
  5567. Break;
  5568. end;
  5569. View := View.FOpenViewerView;
  5570. until View = nil;
  5571. if View = nil then begin
  5572. View := RootView;
  5573. repeat
  5574. if Assigned(View.FSelected) and View.FMouseOverSelected then begin
  5575. AView := View;
  5576. AViewer := View.FSelected;
  5577. Break;
  5578. end;
  5579. if vsMouseInWindow in View.FState then begin
  5580. { ...there is no current selection, but the mouse is still in the
  5581. window. This can happen if the mouse is over the non-client area
  5582. of the toolbar or popup window, or in an area not containing an
  5583. item. }
  5584. AView := View;
  5585. Break;
  5586. end;
  5587. View := View.FOpenViewerView;
  5588. until View = nil;
  5589. end;
  5590. Result := Assigned(AViewer);
  5591. end;
  5592. function ContinueLoop: Boolean;
  5593. begin
  5594. { Don't continue if the mouse capture is lost, if a (modeless) top-level
  5595. window is shown causing the active window to change, or if EndModal* was
  5596. called. }
  5597. Result := (GetCapture = FWnd) and (GetActiveWindow = OriginalActiveWindow)
  5598. and not RootView.IsModalEnding;
  5599. end;
  5600. function SendKeyEvent(const View: TTBView; var Key: Word;
  5601. const Shift: TShiftState): Boolean;
  5602. begin
  5603. Result := True;
  5604. if Assigned(View.FSelected) then begin
  5605. View.FSelected.KeyDown(Key, Shift);
  5606. if RootView.IsModalEnding then
  5607. Exit;
  5608. end;
  5609. if Key <> 0 then begin
  5610. View.KeyDown(Key, Shift);
  5611. if RootView.IsModalEnding then
  5612. Exit;
  5613. end;
  5614. Result := False;
  5615. end;
  5616. procedure DoHintMouseMessage(const Ctl: TControl; const P: TPoint);
  5617. var
  5618. M: TWMMouseMove;
  5619. begin
  5620. M.Msg := WM_MOUSEMOVE;
  5621. M.Keys := 0;
  5622. M.Pos := PointToSmallPoint(P);
  5623. Application.HintMouseMessage(Ctl, TMessage(M));
  5624. end;
  5625. procedure MouseMoved;
  5626. var
  5627. View: TTBView;
  5628. Cursor: HCURSOR;
  5629. Item: TTBCustomItem;
  5630. P: TPoint;
  5631. R: TRect;
  5632. begin
  5633. UpdateAllSelections(LastPos, True);
  5634. View := GetActiveView;
  5635. Cursor := 0;
  5636. if Assigned(View.FSelected) and Assigned(View.FWindow) then begin
  5637. Item := View.FSelected.Item;
  5638. P := View.FWindow.ScreenToClient(LastPos);
  5639. if ((vsAlwaysShowHints in View.FStyle) or
  5640. (tboShowHint in Item.FEffectiveOptions)) and not View.FCapture then begin
  5641. { Display popup hint for the item. Update is called
  5642. first to minimize flicker caused by the hiding &
  5643. showing of the hint window. }
  5644. View.FWindow.Update;
  5645. DoHintMouseMessage(View.FWindow, P);
  5646. end
  5647. else
  5648. Application.CancelHint;
  5649. R := View.FSelected.BoundsRect;
  5650. Dec(P.X, R.Left);
  5651. Dec(P.Y, R.Top);
  5652. View.FSelected.GetCursor(P, Cursor);
  5653. end
  5654. else
  5655. Application.CancelHint;
  5656. if Cursor = 0 then
  5657. Cursor := LoadCursor(0, IDC_ARROW);
  5658. SetCursor(Cursor);
  5659. end;
  5660. procedure UpdateAppHint;
  5661. var
  5662. View: TTBView;
  5663. begin
  5664. View := RootView;
  5665. while Assigned(View.FOpenViewerView) and Assigned(View.FOpenViewerView.FSelected) do
  5666. View := View.FOpenViewerView;
  5667. if Assigned(View.FSelected) then
  5668. Application.Hint := GetLongHint(View.FSelected.Item.Hint)
  5669. else
  5670. Application.Hint := '';
  5671. end;
  5672. procedure HandleTimer(const View: TTBView; const ID: TTBViewTimerID);
  5673. begin
  5674. case ID of
  5675. tiOpen: begin
  5676. { Similar to standard menus, always close child popups, even if
  5677. Selected = OpenViewer.
  5678. Note: CloseChildPopups and OpenChildPopup will stop the tiClose
  5679. and tiOpen timers respectively. }
  5680. View.CloseChildPopups;
  5681. View.OpenChildPopup(False);
  5682. end;
  5683. tiClose: begin
  5684. { Note: CloseChildPopups stops the tiClose timer. }
  5685. View.CloseChildPopups;
  5686. end;
  5687. tiScrollUp: begin
  5688. if View.FShowUpArrow then
  5689. View.Scroll(False)
  5690. else
  5691. View.StopTimer(tiScrollUp);
  5692. end;
  5693. tiScrollDown: begin
  5694. if View.FShowDownArrow then
  5695. View.Scroll(True)
  5696. else
  5697. View.StopTimer(tiScrollDown);
  5698. end;
  5699. end;
  5700. end;
  5701. var
  5702. MouseDownOnMenu: Boolean;
  5703. Msg: TMsg;
  5704. P: TPoint;
  5705. Ctl: TControl;
  5706. View: TTBView;
  5707. IsOnlyItemWithAccel: Boolean;
  5708. MouseIsDown: Boolean;
  5709. Key: Word;
  5710. Shift: TShiftState;
  5711. Viewer: TTBItemViewer;
  5712. begin
  5713. FillChar(RootView.FDoneActionData, SizeOf(RootView.FDoneActionData), 0);
  5714. RootView.ValidatePositions;
  5715. try
  5716. try
  5717. RootView.FCaptureWnd := FWnd;
  5718. MouseDownOnMenu := False;
  5719. if AMouseDown then begin
  5720. P := RootView.FSelected.ScreenToClient(SmallPointToPoint(TSmallPoint(GetMessagePos())));
  5721. RootView.FSelected.MouseDown([], P.X, P.Y, MouseDownOnMenu);
  5722. if RootView.IsModalEnding then
  5723. Exit;
  5724. MouseDownOnMenu := False; { never set MouseDownOnMenu to True on first click }
  5725. end
  5726. else if AExecuteSelected then begin
  5727. RootView.ExecuteSelected(not AFromMSAA);
  5728. if RootView.IsModalEnding then
  5729. Exit;
  5730. end;
  5731. OriginalActiveWindow := GetActiveWindow;
  5732. while ContinueLoop do begin
  5733. { Examine the next message before popping it out of the queue }
  5734. if not PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then begin
  5735. WaitMessage;
  5736. Continue;
  5737. end;
  5738. case Msg.message of
  5739. WM_LBUTTONDOWN, WM_RBUTTONDOWN: begin
  5740. P := SmallPointToPoint(TSmallPoint(Msg.lParam));
  5741. Windows.ClientToScreen(Msg.hwnd, P);
  5742. Ctl := FindDragTarget(P, True);
  5743. { Was the mouse not clicked on a popup, or was it clicked on a
  5744. popup that is not a child of RootView?
  5745. (The latter can happen when in customization mode, for example,
  5746. if the user right-clicks a popup menu being customized and
  5747. the context menu is displayed.) }
  5748. if not(Ctl is TTBPopupWindow) or
  5749. not RootView.ContainsView(TTBPopupWindow(Ctl).View) then begin
  5750. { If the root view is a popup, or if the root view is a toolbar
  5751. and the user clicked outside the toolbar or in its non-client
  5752. area (e.g. on its drag handle), exit }
  5753. if RootView.FIsPopup or (Ctl <> RootView.FWindow) or
  5754. not PtInRect(RootView.FWindow.ClientRect, RootView.FWindow.ScreenToClient(P)) then
  5755. Exit
  5756. else
  5757. if Msg.message = WM_LBUTTONDOWN then begin
  5758. { If the user clicked inside a toolbar on anything but an
  5759. item, exit }
  5760. UpdateAllSelections(P, True);
  5761. if (RootView.FSelected = nil) or not RootView.FMouseOverSelected or
  5762. (tbisClicksTransparent in RootView.FSelected.Item.ItemStyle) then
  5763. Exit;
  5764. end;
  5765. end;
  5766. end;
  5767. end;
  5768. { Now pop the message out of the queue }
  5769. if not PeekMessage(Msg, 0, Msg.message, Msg.message, PM_REMOVE or PM_NOYIELD) then
  5770. Continue;
  5771. case Msg.message of
  5772. $4D:
  5773. { This undocumented message is sent to the focused window when
  5774. F1 is pressed. Windows handles it by sending a WM_HELP message
  5775. to the same window. We don't want this to happen while a menu
  5776. is up, so swallow the message. }
  5777. ;
  5778. WM_CONTEXTMENU:
  5779. { Windows still sends WM_CONTEXTMENU messages for "context menu"
  5780. keystrokes even if WM_KEYUP messages are never dispatched,
  5781. so it must specifically ignore this message }
  5782. ;
  5783. WM_KEYFIRST..WM_KEYLAST: begin
  5784. Application.CancelHint;
  5785. MouseIsDown := (GetKeyState(VK_LBUTTON) < 0) or
  5786. (TrackRightButton and (GetKeyState(VK_RBUTTON) < 0));
  5787. case Msg.message of
  5788. WM_KEYDOWN, WM_SYSKEYDOWN:
  5789. begin
  5790. if Msg.wParam = VK_PROCESSKEY then
  5791. { Don't let IME process the key }
  5792. Msg.wParam := ImmGetVirtualKey(Msg.hwnd);
  5793. if not MouseIsDown or (Msg.wParam = VK_F1) then begin
  5794. Key := Word(Msg.wParam);
  5795. if SendKeyEvent(GetActiveView, Key,
  5796. KeyDataToShiftState(Msg.lParam)) then
  5797. Exit;
  5798. { If it's not handled by a KeyDown method, translate
  5799. it into a WM_*CHAR message }
  5800. if Key <> 0 then
  5801. TranslateMessage(Msg);
  5802. end;
  5803. end;
  5804. WM_CHAR, WM_SYSCHAR:
  5805. if not MouseIsDown then begin
  5806. View := GetActiveView;
  5807. Viewer := View.NextSelectableWithAccel(View.FSelected,
  5808. Chr(Msg.WParam), False, IsOnlyItemWithAccel);
  5809. if Viewer = nil then begin
  5810. if (Msg.WParam in [VK_SPACE, Ord('-')]) and
  5811. not RootView.FIsPopup and (View = RootView) and
  5812. (GetActiveWindow <> 0) then begin
  5813. RootView.EndModalWithSystemMenu(GetActiveWindow,
  5814. Msg.WParam);
  5815. Exit;
  5816. end
  5817. else
  5818. MessageBeep(0);
  5819. end
  5820. else begin
  5821. View.Selected := Viewer;
  5822. View.ScrollSelectedIntoView;
  5823. if IsOnlyItemWithAccel then
  5824. View.ExecuteSelected(True);
  5825. end;
  5826. end;
  5827. end;
  5828. end;
  5829. WM_TIMER:
  5830. begin
  5831. Ctl := FindControl(Msg.hwnd);
  5832. if Assigned(Ctl) and (Ctl is TTBPopupWindow) and
  5833. (Msg.wParam >= ViewTimerBaseID + Ord(Low(TTBViewTimerID))) and
  5834. (Msg.wParam <= ViewTimerBaseID + Ord(High(TTBViewTimerID))) then begin
  5835. if Assigned(TTBPopupWindow(Ctl).FView) then
  5836. HandleTimer(TTBPopupWindow(Ctl).FView,
  5837. TTBViewTimerID(WPARAM(Msg.wParam - ViewTimerBaseID)));
  5838. end
  5839. else
  5840. DispatchMessage(Msg);
  5841. end;
  5842. $118: ;
  5843. { ^ Like standard menus, don't dispatch WM_SYSTIMER messages
  5844. (the internal Windows message used for things like caret
  5845. blink and list box scrolling). }
  5846. WM_MOUSEFIRST..WM_MOUSELAST:
  5847. case Msg.message of
  5848. WM_MOUSEMOVE: begin
  5849. if (Msg.pt.X <> LastPos.X) or (Msg.pt.Y <> LastPos.Y) then begin
  5850. LastPos := Msg.pt;
  5851. MouseMoved;
  5852. end;
  5853. if GetSelectedViewer(View, Viewer) then begin
  5854. P := Viewer.ScreenToClient(Msg.pt);
  5855. Viewer.MouseMove(P.X, P.Y);
  5856. end;
  5857. end;
  5858. WM_MOUSEWHEEL:
  5859. if GetSelectedViewer(View, Viewer) then begin
  5860. P := Viewer.ScreenToClient(Msg.pt);
  5861. Viewer.MouseWheel(Smallint(LongRec(Msg.wParam).Hi), P.X, P.Y);
  5862. end;
  5863. WM_LBUTTONDOWN, WM_LBUTTONDBLCLK, WM_RBUTTONDOWN:
  5864. if (Msg.message <> WM_RBUTTONDOWN) or TrackRightButton then begin
  5865. Application.CancelHint;
  5866. MouseDownOnMenu := False;
  5867. Exclude(RootView.FState, vsIgnoreFirstMouseUp);
  5868. UpdateAllSelections(Msg.pt, True);
  5869. if GetSelectedViewer(View, Viewer) then begin
  5870. if Msg.message <> WM_LBUTTONDBLCLK then
  5871. Shift := []
  5872. else
  5873. Shift := [ssDouble];
  5874. P := Viewer.ScreenToClient(Msg.pt);
  5875. Viewer.MouseDown(Shift, P.X, P.Y, MouseDownOnMenu);
  5876. LastPos := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  5877. end;
  5878. end;
  5879. WM_LBUTTONUP, WM_RBUTTONUP:
  5880. if (Msg.message = WM_LBUTTONUP) or TrackRightButton then begin
  5881. UpdateAllSelections(Msg.pt, False);
  5882. { ^ False is used so that when a popup menu is
  5883. displayed with the cursor currently inside it, the item
  5884. under the cursor won't be accidentally selected when the
  5885. user releases the button. The user must move the mouse at
  5886. at least one pixel (generating a WM_MOUSEMOVE message),
  5887. and then release the button. }
  5888. if not GetSelectedViewer(View, Viewer) then begin
  5889. { Mouse was not released over any item. Cancel out of the
  5890. loop if it's outside all views, or is inside unused
  5891. space on a topmost toolbar }
  5892. if not Assigned(View) or
  5893. ((View = RootView) and RootView.FIsToolbar) then begin
  5894. if not(vsIgnoreFirstMouseUp in RootView.FState) then
  5895. Exit
  5896. else
  5897. Exclude(RootView.FState, vsIgnoreFirstMouseUp);
  5898. end;
  5899. end
  5900. else begin
  5901. P := Viewer.ScreenToClient(Msg.pt);
  5902. Viewer.MouseUp(P.X, P.Y, MouseDownOnMenu);
  5903. end;
  5904. end;
  5905. end;
  5906. else
  5907. DispatchMessage(Msg);
  5908. end;
  5909. if not ContinueLoop then
  5910. begin
  5911. Exit;
  5912. end;
  5913. if LastPos.X = Low(LastPos.X) then begin
  5914. LastPos := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  5915. MouseMoved;
  5916. end;
  5917. UpdateAppHint;
  5918. end;
  5919. finally
  5920. RootView.CancelCapture;
  5921. end;
  5922. finally
  5923. RootView.FCaptureWnd := 0;
  5924. Application.Hint := '';
  5925. { Make sure there are no outstanding WM_*CHAR messages }
  5926. RemoveMessages(WM_CHAR, WM_DEADCHAR);
  5927. RemoveMessages(WM_SYSCHAR, WM_SYSDEADCHAR);
  5928. { Nor any outstanding 'send WM_HELP' messages caused by an earlier press
  5929. of the F1 key }
  5930. RemoveMessages($4D, $4D);
  5931. end;
  5932. end;
  5933. { TTBPopupView }
  5934. procedure TTBPopupView.AutoSize(AWidth, AHeight: Integer);
  5935. begin
  5936. with TTBPopupWindow(FWindow) do
  5937. with GetNCSize do
  5938. SetBounds(Left, Top, AWidth + (X * 2),
  5939. AHeight + (Y * 2));
  5940. end;
  5941. function TTBPopupView.GetMonitor: TMonitor;
  5942. begin
  5943. Result := Screen.MonitorFromRect(FWindow.BoundsRect);
  5944. end;
  5945. function TTBPopupView.GetFont: TFont;
  5946. begin
  5947. Result := (Owner as TTBPopupWindow).Font;
  5948. end;
  5949. { TTBPopupWindow }
  5950. constructor TTBPopupWindow.CreatePopupWindow(AOwner: TComponent;
  5951. const AParentView: TTBView; const AItem: TTBCustomItem;
  5952. const ACustomizing: Boolean; const PopupPoint: TPoint);
  5953. begin
  5954. inherited Create(AOwner);
  5955. Visible := False;
  5956. SetBounds(PopupPoint.X, PopupPoint.Y, 320, 240);
  5957. ControlStyle := ControlStyle - [csCaptureMouse];
  5958. ShowHint := True;
  5959. Color := tbMenuBkColor;
  5960. FView := GetViewClass.CreateView(Self, AParentView, AItem, Self, False,
  5961. ACustomizing, False);
  5962. Include(FView.FState, vsModal);
  5963. { Inherit the font from the parent view, or use the system menu font if
  5964. there is no parent view }
  5965. if Assigned(AParentView) then
  5966. Font.Assign(AParentView.GetFont)
  5967. else
  5968. Font.Assign(GetToolbarFont(GetMonitorPixelsPerInch(Screen.MonitorFromPoint(PopupPoint))));
  5969. { Inherit the accelerator visibility state from the parent view. If there
  5970. is no parent view (i.e. it's a standalone popup menu), then default to
  5971. hiding accelerator keys, but change this in CreateWnd if the last input
  5972. came from the keyboard. }
  5973. if Assigned(AParentView) then begin
  5974. if vsUseHiddenAccels in AParentView.FStyle then
  5975. Include(FView.FStyle, vsUseHiddenAccels);
  5976. if vsShowAccels in AParentView.FState then
  5977. Include(FView.FState, vsShowAccels);
  5978. end
  5979. else
  5980. Include(FView.FStyle, vsUseHiddenAccels);
  5981. if Application.Handle <> 0 then
  5982. { Use Application.Handle if possible so that the taskbar button for the app
  5983. doesn't pop up when a TTBEditItem on a popup menu is focused }
  5984. ParentWindow := Application.Handle
  5985. else
  5986. { When Application.Handle is zero, use GetDesktopWindow() as the parent
  5987. window, not zero, otherwise UpdateControlState won't show the window }
  5988. ParentWindow := GetDesktopWindow;
  5989. end;
  5990. destructor TTBPopupWindow.Destroy;
  5991. begin
  5992. Destroying;
  5993. { Ensure window handle is destroyed *before* FView is freed, since
  5994. DestroyWindowHandle calls NotifyWinEvent which may result in
  5995. FView.HandleWMObject being called }
  5996. if HandleAllocated then
  5997. DestroyWindowHandle;
  5998. FreeAndNil(FView);
  5999. inherited;
  6000. end;
  6001. {MP}
  6002. procedure TTBPopupWindow.Cancel;
  6003. begin
  6004. { noop }
  6005. end;
  6006. procedure TTBPopupWindow.BeforeDestruction;
  6007. begin
  6008. { The inherited BeforeDestruction method hides the form. We need to close
  6009. any child popups first, so that pixels behind the popups are properly
  6010. restored without generating a WM_PAINT message. }
  6011. if Assigned(FView) then
  6012. FView.CloseChildPopups;
  6013. inherited;
  6014. end;
  6015. function TTBPopupWindow.GetNCSize: TPoint;
  6016. begin
  6017. Result.X := PopupMenuWindowNCSize;
  6018. Result.Y := PopupMenuWindowNCSize;
  6019. end;
  6020. function TTBPopupWindow.GetViewClass: TTBViewClass;
  6021. begin
  6022. Result := TTBPopupView;
  6023. end;
  6024. procedure TTBPopupWindow.CreateParams(var Params: TCreateParams);
  6025. const
  6026. CS_DROPSHADOW = $00020000;
  6027. begin
  6028. inherited;
  6029. with Params do begin
  6030. Style := (Style and not (WS_CHILD or WS_GROUP or WS_TABSTOP)) or WS_POPUP;
  6031. ExStyle := ExStyle or WS_EX_TOPMOST or WS_EX_TOOLWINDOW;
  6032. WindowClass.Style := WindowClass.Style or CS_SAVEBITS;
  6033. WindowClass.Style := WindowClass.Style or CS_DROPSHADOW;
  6034. end;
  6035. end;
  6036. procedure TTBPopupWindow.CreateWnd;
  6037. const
  6038. WM_CHANGEUISTATE = $0127;
  6039. WM_QUERYUISTATE = $0129;
  6040. UIS_INITIALIZE = 3;
  6041. UISF_HIDEACCEL = $2;
  6042. var
  6043. B: Boolean;
  6044. begin
  6045. inherited;
  6046. { On a top-level popup window, send WM_CHANGEUISTATE & WM_QUERYUISTATE
  6047. messages to the window to see if the last input came from the keyboard
  6048. and if the accelerator keys should be shown }
  6049. if (FView.ParentView = nil) and not FAccelsVisibilitySet then begin
  6050. FAccelsVisibilitySet := True;
  6051. SendMessage(Handle, WM_CHANGEUISTATE, UIS_INITIALIZE, 0);
  6052. B := (SendMessage(Handle, WM_QUERYUISTATE, 0, 0) and UISF_HIDEACCEL = 0);
  6053. FView.SetAccelsVisibility(B);
  6054. end;
  6055. end;
  6056. procedure TTBPopupWindow.DestroyWindowHandle;
  6057. begin
  6058. { Before destroying the window handle, we must stop any animation, otherwise
  6059. the animation thread will use an invalid handle }
  6060. TBEndAnimation(WindowHandle);
  6061. { Cleanly destroy any timers before the window handle is destroyed }
  6062. if Assigned(FView) then
  6063. FView.StopAllTimers;
  6064. NotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, WindowHandle, OBJID_CLIENT,
  6065. CHILDID_SELF);
  6066. inherited;
  6067. end;
  6068. procedure TTBPopupWindow.WMGetObject(var Message: TMessage);
  6069. begin
  6070. if not FView.HandleWMGetObject(Message) then
  6071. inherited;
  6072. end;
  6073. procedure TTBPopupWindow.CMShowingChanged(var Message: TMessage);
  6074. const
  6075. ShowFlags: array[Boolean] of UINT = (
  6076. SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_HIDEWINDOW,
  6077. SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_SHOWWINDOW);
  6078. SPI_GETMENUFADE = $1012;
  6079. var
  6080. Animate: BOOL;
  6081. Blend: Boolean;
  6082. begin
  6083. { Must override TCustomForm/TForm's CM_SHOWINGCHANGED handler so that the
  6084. form doesn't get activated when Visible is set to True. }
  6085. { Handle animation. NOTE: I do not recommend trying to enable animation on
  6086. Windows 95 and NT 4.0 because there's a difference in the way the
  6087. SetWindowPos works on those versions. See the comment in the
  6088. TBStartAnimation function of TB2Anim.pas. }
  6089. {$IFNDEF TB2K_NO_ANIMATION}
  6090. if ((FView.ParentView = nil) or not(vsNoAnimation in FView.FParentView.FState)) and
  6091. Showing and (FView.Selected = nil) and not IsWindowVisible(WindowHandle) and
  6092. SystemParametersInfo(SPI_GETMENUANIMATION, 0, @Animate, 0) and Animate then begin
  6093. Blend := SystemParametersInfo(SPI_GETMENUFADE, 0, @Animate, 0) and Animate;
  6094. if Blend or (FAnimationDirection <> []) then begin
  6095. if SendMessage(WindowHandle, WM_TB2K_POPUPSHOWING, TPS_ANIMSTART, 0) = 0 then
  6096. begin
  6097. { Start animation only if WM_TB2K_POPUPSHOWING returns zero (or not handled) }
  6098. TBStartAnimation(WindowHandle, Blend, FAnimationDirection);
  6099. Exit;
  6100. end;
  6101. end;
  6102. end;
  6103. {$ENDIF}
  6104. { No animation... }
  6105. if not Showing then begin
  6106. { Call TBEndAnimation to ensure WS_EX_LAYERED style is removed before
  6107. hiding, otherwise windows under the popup window aren't repainted
  6108. properly. }
  6109. TBEndAnimation(WindowHandle);
  6110. end;
  6111. SetWindowPos(WindowHandle, 0, 0, 0, 0, 0, ShowFlags[Showing]);
  6112. if Showing then SendNotifyMessage(WindowHandle, WM_TB2K_POPUPSHOWING, TPS_NOANIM, 0);
  6113. end;
  6114. procedure TTBPopupWindow.WMTB2kAnimationEnded(var Message: TMessage);
  6115. begin
  6116. SendNotifyMessage(WindowHandle, WM_TB2K_POPUPSHOWING, TPS_ANIMFINISHED, 0);
  6117. end;
  6118. procedure TTBPopupWindow.WMTB2kStepAnimation(var Message: TMessage);
  6119. begin
  6120. TBStepAnimation(Message);
  6121. end;
  6122. procedure TTBPopupWindow.WMEraseBkgnd(var Message: TWMEraseBkgnd);
  6123. begin
  6124. { May be necessary in some cases... }
  6125. TBEndAnimation(WindowHandle);
  6126. inherited;
  6127. end;
  6128. procedure TTBPopupWindow.WMPaint(var Message: TWMPaint);
  6129. begin
  6130. { Must abort animation when a WM_PAINT message is received }
  6131. TBEndAnimation(WindowHandle);
  6132. inherited;
  6133. end;
  6134. procedure TTBPopupWindow.Paint;
  6135. begin
  6136. FView.DrawSubitems(Canvas);
  6137. PaintScrollArrows;
  6138. end;
  6139. procedure TTBPopupWindow.PaintScrollArrows;
  6140. procedure DrawArrow(const R: TRect; ADown: Boolean);
  6141. var
  6142. X, Y: Integer;
  6143. P: array[0..2] of TPoint;
  6144. begin
  6145. X := (R.Left + R.Right) div 2;
  6146. Y := (R.Top + R.Bottom) div 2;
  6147. Dec(Y);
  6148. P[0].X := X-3;
  6149. P[0].Y := Y;
  6150. P[1].X := X+3;
  6151. P[1].Y := Y;
  6152. P[2].X := X;
  6153. P[2].Y := Y;
  6154. if ADown then
  6155. Inc(P[2].Y, 3)
  6156. else begin
  6157. Inc(P[0].Y, 3);
  6158. Inc(P[1].Y, 3);
  6159. end;
  6160. Canvas.Pen.Color := tbMenuTextColor;
  6161. Canvas.Brush.Color := tbMenuTextColor;
  6162. Canvas.Polygon(P);
  6163. end;
  6164. begin
  6165. if FView.FShowUpArrow then
  6166. DrawArrow(Rect(0, 0, ClientWidth, tbMenuScrollArrowHeight), False);
  6167. if FView.FShowDownArrow then
  6168. DrawArrow(Bounds(0, ClientHeight - tbMenuScrollArrowHeight,
  6169. ClientWidth, tbMenuScrollArrowHeight), True);
  6170. end;
  6171. procedure TTBPopupWindow.WMClose(var Message: TWMClose);
  6172. begin
  6173. { do nothing -- ignore Alt+F4 keypresses }
  6174. end;
  6175. procedure TTBPopupWindow.WMNCCalcSize(var Message: TWMNCCalcSize);
  6176. begin
  6177. with GetNCSize do
  6178. InflateRect(Message.CalcSize_Params^.rgrc[0], -X, -Y);
  6179. inherited;
  6180. end;
  6181. procedure PopupWindowNCPaintProc(Control: TControl; Wnd: HWND; DC: HDC; AppData: Longint);
  6182. var
  6183. R: TRect;
  6184. {$IFNDEF TB2K_USE_STRICT_O2K_MENU_STYLE}
  6185. Brush: HBRUSH;
  6186. {$ENDIF}
  6187. begin
  6188. GetWindowRect(Wnd, R); OffsetRect(R, -R.Left, -R.Top);
  6189. {$IFNDEF TB2K_USE_STRICT_O2K_MENU_STYLE}
  6190. if not AreFlatMenusEnabled then begin
  6191. {$ENDIF}
  6192. DrawEdge(DC, R, EDGE_RAISED, BF_RECT or BF_ADJUST);
  6193. FrameRect(DC, R, GetSysColorBrush(COLOR_BTNFACE));
  6194. {$IFNDEF TB2K_USE_STRICT_O2K_MENU_STYLE}
  6195. end
  6196. else begin
  6197. FrameRect(DC, R, GetSysColorBrush(COLOR_BTNSHADOW));
  6198. Brush := CreateSolidBrush(ColorToRGB(TTBPopupWindow(AppData).Color));
  6199. InflateRect(R, -1, -1);
  6200. FrameRect(DC, R, Brush);
  6201. InflateRect(R, -1, -1);
  6202. FrameRect(DC, R, Brush);
  6203. DeleteObject(Brush);
  6204. end;
  6205. {$ENDIF}
  6206. end;
  6207. procedure TTBPopupWindow.WMNCPaint(var Message: TMessage);
  6208. var
  6209. DC: HDC;
  6210. begin
  6211. DC := GetWindowDC(Handle);
  6212. try
  6213. SelectNCUpdateRgn(Handle, DC, HRGN(Message.WParam));
  6214. PopupWindowNCPaintProc(Self, Handle, DC, Longint(Self));
  6215. finally
  6216. ReleaseDC(Handle, DC);
  6217. end;
  6218. end;
  6219. procedure TTBPopupWindow.WMPrint(var Message: TMessage);
  6220. begin
  6221. HandleWMPrint(Self, Handle, Message, PopupWindowNCPaintProc, Longint(Self));
  6222. end;
  6223. procedure TTBPopupWindow.WMPrintClient(var Message: TMessage);
  6224. begin
  6225. HandleWMPrintClient(Self, Message);
  6226. end;
  6227. procedure TTBPopupWindow.CMHintShow(var Message: TCMHintShow);
  6228. begin
  6229. with Message.HintInfo^ do begin
  6230. HintStr := '';
  6231. if Assigned(FView.Selected) then begin
  6232. CursorRect := FView.Selected.BoundsRect;
  6233. HintStr := FView.FSelected.GetHintText;
  6234. end;
  6235. end;
  6236. end;
  6237. procedure TTBPopupWindow.CMHintShowPause(var Message: TMessage);
  6238. begin
  6239. // Hint was not active previously
  6240. if not Boolean(Message.WParam) then
  6241. begin
  6242. PInteger(Message.LParam)^ := PInteger(Message.LParam)^ * 2;
  6243. end;
  6244. end;
  6245. { TTBItemContainer }
  6246. constructor TTBItemContainer.Create(AOwner: TComponent);
  6247. begin
  6248. inherited;
  6249. FItem := TTBRootItem.Create(Self);
  6250. FItem.ParentComponent := Self;
  6251. end;
  6252. destructor TTBItemContainer.Destroy;
  6253. begin
  6254. FItem.Free;
  6255. inherited;
  6256. end;
  6257. function TTBItemContainer.GetItems: TTBCustomItem;
  6258. begin
  6259. Result := FItem;
  6260. end;
  6261. procedure TTBItemContainer.GetChildren(Proc: TGetChildProc; Root: TComponent);
  6262. begin
  6263. FItem.GetChildren(Proc, Root);
  6264. end;
  6265. function TTBItemContainer.GetImages: TCustomImageList;
  6266. begin
  6267. Result := FItem.SubMenuImages;
  6268. end;
  6269. procedure TTBItemContainer.SetImages(Value: TCustomImageList);
  6270. begin
  6271. FItem.SubMenuImages := Value;
  6272. end;
  6273. { TTBPopupMenu }
  6274. constructor TTBPopupMenu.Create(AOwner: TComponent);
  6275. begin
  6276. inherited;
  6277. FItem := GetRootItemClass.Create(Self);
  6278. FItem.ParentComponent := Self;
  6279. FItem.OnClick := RootItemClick;
  6280. end;
  6281. destructor TTBPopupMenu.Destroy;
  6282. begin
  6283. FItem.Free;
  6284. inherited;
  6285. end;
  6286. function TTBPopupMenu.GetItems: TTBCustomItem;
  6287. begin
  6288. Result := FItem;
  6289. end;
  6290. procedure TTBPopupMenu.GetChildren(Proc: TGetChildProc; Root: TComponent);
  6291. begin
  6292. FItem.GetChildren(Proc, Root);
  6293. end;
  6294. procedure TTBPopupMenu.SetChildOrder(Child: TComponent; Order: Integer);
  6295. begin
  6296. FItem.SetChildOrder(Child, Order);
  6297. end;
  6298. function TTBPopupMenu.GetRootItemClass: TTBRootItemClass;
  6299. begin
  6300. Result := TTBRootItem;
  6301. end;
  6302. function TTBPopupMenu.GetImages: TCustomImageList;
  6303. begin
  6304. Result := FItem.SubMenuImages;
  6305. end;
  6306. function TTBPopupMenu.GetLinkSubitems: TTBCustomItem;
  6307. begin
  6308. Result := FItem.LinkSubitems;
  6309. end;
  6310. function TTBPopupMenu.GetOptions: TTBItemOptions;
  6311. begin
  6312. Result := FItem.Options;
  6313. end;
  6314. procedure TTBPopupMenu.SetImages(Value: TCustomImageList);
  6315. begin
  6316. FItem.SubMenuImages := Value;
  6317. end;
  6318. procedure TTBPopupMenu.SetLinkSubitems(Value: TTBCustomItem);
  6319. begin
  6320. FItem.LinkSubitems := Value;
  6321. end;
  6322. procedure TTBPopupMenu.SetOptions(Value: TTBItemOptions);
  6323. begin
  6324. FItem.Options := Value;
  6325. end;
  6326. procedure TTBPopupMenu.RootItemClick(Sender: TObject);
  6327. begin
  6328. if Sender = FItem then
  6329. Sender := Self;
  6330. DoPopup(Sender);
  6331. end;
  6332. procedure TTBPopupMenu.Popup(X, Y: Integer);
  6333. begin
  6334. PopupEx(X, Y, False);
  6335. end;
  6336. function TTBPopupMenu.PopupEx(X, Y: Integer;
  6337. ReturnClickedItemOnly: Boolean = False): TTBCustomItem;
  6338. begin
  6339. SetPopupPoint(Point(X, Y));
  6340. Result := FItem.Popup(X, Y, TrackButton = tbRightButton,
  6341. TTBPopupAlignment(Alignment), ReturnClickedItemOnly);
  6342. end;
  6343. function TTBPopupMenu.IsShortCut(var Message: TWMKey): Boolean;
  6344. begin
  6345. Result := FItem.IsShortCut(Message);
  6346. end;
  6347. { TTBImageList }
  6348. constructor TTBCustomImageList.Create(AOwner: TComponent);
  6349. begin
  6350. inherited;
  6351. FCheckedImagesChangeLink := TChangeLink.Create;
  6352. FCheckedImagesChangeLink.OnChange := ImageListChanged;
  6353. FDisabledImagesChangeLink := TChangeLink.Create;
  6354. FDisabledImagesChangeLink.OnChange := ImageListChanged;
  6355. FHotImagesChangeLink := TChangeLink.Create;
  6356. FHotImagesChangeLink.OnChange := ImageListChanged;
  6357. FImagesBitmap := TBitmap.Create;
  6358. FImagesBitmap.OnChange := ImagesBitmapChanged;
  6359. FImagesBitmapMaskColor := clFuchsia;
  6360. end;
  6361. destructor TTBCustomImageList.Destroy;
  6362. begin
  6363. FreeAndNil(FImagesBitmap);
  6364. FreeAndNil(FHotImagesChangeLink);
  6365. FreeAndNil(FDisabledImagesChangeLink);
  6366. FreeAndNil(FCheckedImagesChangeLink);
  6367. inherited;
  6368. end;
  6369. procedure TTBCustomImageList.ImagesBitmapChanged(Sender: TObject);
  6370. begin
  6371. if not ImagesBitmap.Empty then begin
  6372. Clear;
  6373. AddMasked(ImagesBitmap, FImagesBitmapMaskColor);
  6374. end;
  6375. end;
  6376. procedure TTBCustomImageList.ImageListChanged(Sender: TObject);
  6377. begin
  6378. Change;
  6379. end;
  6380. procedure TTBCustomImageList.DefineProperties(Filer: TFiler);
  6381. type
  6382. TProc = procedure(ASelf: TObject; Filer: TFiler);
  6383. begin
  6384. if (Filer is TReader) or FImagesBitmap.Empty then
  6385. inherited
  6386. else
  6387. { Bypass TCustomImageList.DefineProperties when we've got an ImageBitmap }
  6388. TProc(@TComponentAccess.DefineProperties)(Self, Filer);
  6389. end;
  6390. procedure TTBCustomImageList.DrawState(Canvas: TCanvas; X, Y, Index: Integer;
  6391. Enabled, Selected, Checked: Boolean);
  6392. begin
  6393. if not Enabled and Assigned(DisabledImages) then
  6394. DisabledImages.Draw(Canvas, X, Y, Index)
  6395. else if Checked and Assigned(CheckedImages) then
  6396. CheckedImages.Draw(Canvas, X, Y, Index, Enabled)
  6397. else if Selected and Assigned(HotImages) then
  6398. HotImages.Draw(Canvas, X, Y, Index, Enabled)
  6399. else
  6400. Draw(Canvas, X, Y, Index, Enabled);
  6401. end;
  6402. procedure TTBCustomImageList.Notification(AComponent: TComponent; Operation: TOperation);
  6403. begin
  6404. inherited;
  6405. if Operation = opRemove then begin
  6406. if AComponent = CheckedImages then CheckedImages := nil;
  6407. if AComponent = DisabledImages then DisabledImages := nil;
  6408. if AComponent = HotImages then HotImages := nil;
  6409. end;
  6410. end;
  6411. procedure TTBCustomImageList.ChangeImages(var AImageList: TCustomImageList;
  6412. Value: TCustomImageList; AChangeLink: TChangeLink);
  6413. begin
  6414. if Value = Self then
  6415. Value := nil;
  6416. if AImageList <> Value then begin
  6417. if Assigned(AImageList) then
  6418. AImageList.UnregisterChanges(AChangeLink);
  6419. AImageList := Value;
  6420. if Assigned(Value) then begin
  6421. Value.RegisterChanges(AChangeLink);
  6422. Value.FreeNotification(Self);
  6423. end;
  6424. { Don't call Change while loading because it causes the Delphi IDE to
  6425. think the form has been modified (?). Also, don't call Change while
  6426. destroying since there's no reason to. }
  6427. if not(csLoading in ComponentState) and
  6428. not(csDestroying in ComponentState) then
  6429. Change;
  6430. end;
  6431. end;
  6432. procedure TTBCustomImageList.SetCheckedImages(Value: TCustomImageList);
  6433. begin
  6434. ChangeImages(FCheckedImages, Value, FCheckedImagesChangeLink);
  6435. end;
  6436. procedure TTBCustomImageList.SetDisabledImages(Value: TCustomImageList);
  6437. begin
  6438. ChangeImages(FDisabledImages, Value, FDisabledImagesChangeLink);
  6439. end;
  6440. procedure TTBCustomImageList.SetHotImages(Value: TCustomImageList);
  6441. begin
  6442. ChangeImages(FHotImages, Value, FHotImagesChangeLink);
  6443. end;
  6444. procedure TTBCustomImageList.SetImagesBitmap(Value: TBitmap);
  6445. begin
  6446. FImagesBitmap.Assign(Value);
  6447. end;
  6448. procedure TTBCustomImageList.SetImagesBitmapMaskColor(Value: TColor);
  6449. begin
  6450. if FImagesBitmapMaskColor <> Value then begin
  6451. FImagesBitmapMaskColor := Value;
  6452. ImagesBitmapChanged(nil);
  6453. end;
  6454. end;
  6455. { TTBBaseAccObject }
  6456. { According to the MSAA docs:
  6457. "With Active Accessibility 2.0, servers can return E_NOTIMPL from IDispatch
  6458. methods and Active Accessibility will implement the IAccessible interface
  6459. for them."
  6460. And there was much rejoicing. }
  6461. function TTBBaseAccObject.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  6462. NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
  6463. begin
  6464. Result := E_NOTIMPL;
  6465. end;
  6466. function TTBBaseAccObject.GetTypeInfo(Index, LocaleID: Integer;
  6467. out TypeInfo): HResult;
  6468. begin
  6469. Result := E_NOTIMPL;
  6470. end;
  6471. function TTBBaseAccObject.GetTypeInfoCount(out Count: Integer): HResult;
  6472. begin
  6473. Result := E_NOTIMPL;
  6474. end;
  6475. function TTBBaseAccObject.Invoke(DispID: Integer; const IID: TGUID;
  6476. LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
  6477. ArgErr: Pointer): HResult;
  6478. begin
  6479. Result := E_NOTIMPL;
  6480. end;
  6481. { Initialization & finalization }
  6482. var
  6483. ToolbarFonts: TDictionary<Integer, TFont>;
  6484. ToolbarFont: TFont;
  6485. function CreateToolbarFont(PixelsPerInch: Integer): HFONT;
  6486. var
  6487. NonClientMetrics: TNonClientMetrics;
  6488. begin
  6489. FillChar(NonClientMetrics, SizeOf(NonClientMetrics), 0);
  6490. NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  6491. Assert(HasSystemParametersInfoForPixelsPerInch);
  6492. if SystemParametersInfoForPixelsPerInch(SPI_GETNONCLIENTMETRICS, SizeOf(NonClientMetrics), @NonClientMetrics, 0, PixelsPerInch) then
  6493. begin
  6494. Result := CreateFontIndirect(NonClientMetrics.lfMenuFont);
  6495. end
  6496. else
  6497. begin
  6498. Result := 0;
  6499. end;
  6500. end;
  6501. function GetToolbarFont(Control: TControl; PixelsPerInch: Integer): TFont; overload;
  6502. var
  6503. H: HFONT;
  6504. begin
  6505. // Temporary redundant legacy fallback to limit impact of per-monitor DPI change
  6506. if not HasSystemParametersInfoForPixelsPerInch then
  6507. begin
  6508. Result := ToolbarFont;
  6509. end
  6510. else
  6511. begin
  6512. // See the comment in TTBView.GetFont
  6513. if not Assigned(ToolbarFonts) then
  6514. begin
  6515. Result := nil;
  6516. end
  6517. else
  6518. begin
  6519. if PixelsPerInch < 0 then
  6520. begin
  6521. PixelsPerInch := GetControlPixelsPerInch(Control);
  6522. end;
  6523. if not ToolbarFonts.TryGetValue(PixelsPerInch, Result) then
  6524. begin
  6525. H := CreateToolbarFont(PixelsPerInch);
  6526. if H <> 0 then
  6527. begin
  6528. Result := TFont.Create;
  6529. Result.Handle := H;
  6530. ToolbarFonts.Add(PixelsPerInch, Result);
  6531. end
  6532. else
  6533. begin
  6534. Result := nil;
  6535. end;
  6536. end
  6537. end;
  6538. end;
  6539. end;
  6540. function GetToolbarFont(PixelsPerInch: Integer): TFont; overload;
  6541. begin
  6542. Result := GetToolbarFont(nil, PixelsPerInch);
  6543. end;
  6544. function GetToolbarFont(Control: TControl): TFont; overload;
  6545. begin
  6546. Result := GetToolbarFont(Control, -1);
  6547. end;
  6548. procedure TBInitToolbarSystemFont;
  6549. var
  6550. NonClientMetrics: TNonClientMetrics;
  6551. FontPair: TPair<Integer, TFont>;
  6552. begin
  6553. NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  6554. if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
  6555. begin
  6556. ToolbarFont.Handle := CreateFontIndirect(NonClientMetrics.lfMenuFont);
  6557. end;
  6558. for FontPair in ToolbarFonts do
  6559. begin
  6560. FontPair.Value.Handle := CreateToolbarFont(FontPair.Key);
  6561. end;
  6562. end;
  6563. procedure TBFinalizeToolbarSystemFont;
  6564. var
  6565. Font: TFont;
  6566. begin
  6567. for Font in ToolbarFonts.Values do
  6568. begin
  6569. Font.Free;
  6570. end;
  6571. end;
  6572. initialization
  6573. ToolbarFonts := TDictionary<Integer, TFont>.Create;
  6574. ToolbarFont := TFont.Create;
  6575. TBInitToolbarSystemFont;
  6576. finalization
  6577. DestroyClickWnd;
  6578. FreeAndNil(ToolbarFont);
  6579. TBFinalizeToolbarSystemFont;
  6580. FreeAndNil(ToolbarFonts);
  6581. end.