TB2Item.pas 229 KB

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