TB2Item.pas 229 KB

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