atlctrlx.h 120 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894
  1. // Windows Template Library - WTL version 10.0
  2. // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
  3. //
  4. // This file is a part of the Windows Template Library.
  5. // The use and distribution terms for this software are covered by the
  6. // Microsoft Public License (http://opensource.org/licenses/MS-PL)
  7. // which can be found in the file MS-PL.txt at the root folder.
  8. #ifndef __ATLCTRLX_H__
  9. #define __ATLCTRLX_H__
  10. #pragma once
  11. #ifndef __ATLAPP_H__
  12. #error atlctrlx.h requires atlapp.h to be included first
  13. #endif
  14. #ifndef __ATLCTRLS_H__
  15. #error atlctrlx.h requires atlctrls.h to be included first
  16. #endif
  17. ///////////////////////////////////////////////////////////////////////////////
  18. // Classes in this file:
  19. //
  20. // CBitmapButtonImpl<T, TBase, TWinTraits>
  21. // CBitmapButton
  22. // CCheckListViewCtrlImpl<T, TBase, TWinTraits>
  23. // CCheckListViewCtrl
  24. // CHyperLinkImpl<T, TBase, TWinTraits>
  25. // CHyperLink
  26. // CWaitCursor
  27. // CCustomWaitCursor
  28. // CMultiPaneStatusBarCtrlImpl<T, TBase>
  29. // CMultiPaneStatusBarCtrl
  30. // CPaneContainerImpl<T, TBase, TWinTraits>
  31. // CPaneContainer
  32. // CSortListViewImpl<T>
  33. // CSortListViewCtrlImpl<T, TBase, TWinTraits>
  34. // CSortListViewCtrl
  35. // CTabViewImpl<T, TBase, TWinTraits>
  36. // CTabView
  37. namespace WTL
  38. {
  39. ///////////////////////////////////////////////////////////////////////////////
  40. // CBitmapButton - bitmap button implementation
  41. // bitmap button extended styles
  42. #define BMPBTN_HOVER 0x00000001
  43. #define BMPBTN_AUTO3D_SINGLE 0x00000002
  44. #define BMPBTN_AUTO3D_DOUBLE 0x00000004
  45. #define BMPBTN_AUTOSIZE 0x00000008
  46. #define BMPBTN_SHAREIMAGELISTS 0x00000010
  47. #define BMPBTN_AUTOFIRE 0x00000020
  48. #define BMPBTN_CHECK 0x00000040
  49. #define BMPBTN_AUTOCHECK 0x00000080
  50. // Note: BMPBTN_CHECK/BMPBTN_AUTOCHECK disables BN_DOUBLECLICKED,
  51. // BMPBTN_AUTOFIRE doesn't work with BMPBTN_CHECK/BMPBTN_AUTOCHECK
  52. template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
  53. class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
  54. {
  55. public:
  56. DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName())
  57. enum
  58. {
  59. _nImageNormal = 0,
  60. _nImagePushed,
  61. _nImageFocusOrHover,
  62. _nImageDisabled,
  63. _nImageCount = 4,
  64. };
  65. enum
  66. {
  67. ID_TIMER_FIRST = 1000,
  68. ID_TIMER_REPEAT = 1001
  69. };
  70. // Bitmap button specific extended styles
  71. DWORD m_dwExtendedStyle;
  72. CImageList m_ImageList;
  73. int m_nImage[_nImageCount];
  74. CToolTipCtrl m_tip;
  75. LPTSTR m_lpstrToolTipText;
  76. // Internal states
  77. unsigned m_fMouseOver:1;
  78. unsigned m_fFocus:1;
  79. unsigned m_fPressed:1;
  80. unsigned m_fChecked:1;
  81. // Constructor/Destructor
  82. CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
  83. m_dwExtendedStyle(dwExtendedStyle), m_ImageList(hImageList),
  84. m_lpstrToolTipText(NULL),
  85. m_fMouseOver(0), m_fFocus(0), m_fPressed(0), m_fChecked(0)
  86. {
  87. m_nImage[_nImageNormal] = -1;
  88. m_nImage[_nImagePushed] = -1;
  89. m_nImage[_nImageFocusOrHover] = -1;
  90. m_nImage[_nImageDisabled] = -1;
  91. #ifdef _DEBUG
  92. if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && IsCheckMode())
  93. ATLTRACE2(atlTraceUI, 0, _T("CBitmapButtonImpl - Check mode and BMPBTN_AUTOFIRE cannot be used together, BMPBTN_AUTOFIRE will be ignored.\n"));
  94. #endif // _DEBUG
  95. }
  96. ~CBitmapButtonImpl()
  97. {
  98. if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0)
  99. m_ImageList.Destroy();
  100. delete [] m_lpstrToolTipText;
  101. }
  102. // overridden to provide proper initialization
  103. BOOL SubclassWindow(HWND hWnd)
  104. {
  105. BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
  106. if(bRet != FALSE)
  107. {
  108. T* pT = static_cast<T*>(this);
  109. pT->Init();
  110. }
  111. return bRet;
  112. }
  113. // Attributes
  114. DWORD GetBitmapButtonExtendedStyle() const
  115. {
  116. return m_dwExtendedStyle;
  117. }
  118. DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
  119. {
  120. DWORD dwPrevStyle = m_dwExtendedStyle;
  121. if(dwMask == 0)
  122. m_dwExtendedStyle = dwExtendedStyle;
  123. else
  124. m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
  125. #ifdef _DEBUG
  126. if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && IsCheckMode())
  127. ATLTRACE2(atlTraceUI, 0, _T("CBitmapButtonImpl - Check mode and BMPBTN_AUTOFIRE cannot be used together, BMPBTN_AUTOFIRE will be ignored.\n"));
  128. #endif // _DEBUG
  129. return dwPrevStyle;
  130. }
  131. HIMAGELIST GetImageList() const
  132. {
  133. return m_ImageList;
  134. }
  135. HIMAGELIST SetImageList(HIMAGELIST hImageList)
  136. {
  137. HIMAGELIST hImageListPrev = m_ImageList;
  138. m_ImageList = hImageList;
  139. if(((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0) && ::IsWindow(this->m_hWnd))
  140. SizeToImage();
  141. return hImageListPrev;
  142. }
  143. int GetToolTipTextLength() const
  144. {
  145. return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText);
  146. }
  147. bool GetToolTipText(LPTSTR lpstrText, int nLength) const
  148. {
  149. ATLASSERT(lpstrText != NULL);
  150. if(m_lpstrToolTipText == NULL)
  151. return false;
  152. errno_t nRet = ATL::Checked::tcsncpy_s(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE);
  153. return ((nRet == 0) || (nRet == STRUNCATE));
  154. }
  155. bool SetToolTipText(LPCTSTR lpstrText)
  156. {
  157. if(m_lpstrToolTipText != NULL)
  158. {
  159. delete [] m_lpstrToolTipText;
  160. m_lpstrToolTipText = NULL;
  161. }
  162. if(lpstrText == NULL)
  163. {
  164. if(m_tip.IsWindow())
  165. m_tip.Activate(FALSE);
  166. return true;
  167. }
  168. int cchLen = lstrlen(lpstrText) + 1;
  169. ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]);
  170. if(m_lpstrToolTipText == NULL)
  171. return false;
  172. ATL::Checked::tcscpy_s(m_lpstrToolTipText, cchLen, lpstrText);
  173. if(m_tip.IsWindow())
  174. {
  175. m_tip.Activate(TRUE);
  176. m_tip.AddTool(this->m_hWnd, m_lpstrToolTipText);
  177. }
  178. return true;
  179. }
  180. bool GetCheck() const
  181. {
  182. return (m_fChecked == 1);
  183. }
  184. void SetCheck(bool bCheck, bool bUpdate = true)
  185. {
  186. m_fChecked = bCheck ? 1 : 0;
  187. if(bUpdate)
  188. {
  189. this->Invalidate();
  190. this->UpdateWindow();
  191. }
  192. }
  193. // Operations
  194. void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)
  195. {
  196. if(nNormal != -1)
  197. m_nImage[_nImageNormal] = nNormal;
  198. if(nPushed != -1)
  199. m_nImage[_nImagePushed] = nPushed;
  200. if(nFocusOrHover != -1)
  201. m_nImage[_nImageFocusOrHover] = nFocusOrHover;
  202. if(nDisabled != -1)
  203. m_nImage[_nImageDisabled] = nDisabled;
  204. }
  205. BOOL SizeToImage()
  206. {
  207. ATLASSERT(::IsWindow(this->m_hWnd) && (m_ImageList.m_hImageList != NULL));
  208. int cx = 0;
  209. int cy = 0;
  210. if(!m_ImageList.GetIconSize(cx, cy))
  211. return FALSE;
  212. return this->ResizeClient(cx, cy);
  213. }
  214. // Overrideables
  215. void DoPaint(CDCHandle dc)
  216. {
  217. ATLASSERT(m_ImageList.m_hImageList != NULL); // image list must be set
  218. ATLASSERT(m_nImage[0] != -1); // main bitmap must be set
  219. // set bitmap according to the current button state
  220. bool bHover = IsHoverMode();
  221. bool bPressed = (m_fPressed == 1) || (IsCheckMode() && (m_fChecked == 1));
  222. int nImage = -1;
  223. if(!this->IsWindowEnabled())
  224. nImage = m_nImage[_nImageDisabled];
  225. else if(bPressed)
  226. nImage = m_nImage[_nImagePushed];
  227. else if((!bHover && (m_fFocus == 1)) || (bHover && (m_fMouseOver == 1)))
  228. nImage = m_nImage[_nImageFocusOrHover];
  229. // if none is set, use default one
  230. if(nImage == -1)
  231. nImage = m_nImage[_nImageNormal];
  232. // draw the button image
  233. bool bAuto3D = (m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0;
  234. int xyPos = (bPressed && bAuto3D && (m_nImage[_nImagePushed] == -1)) ? 1 : 0;
  235. m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL);
  236. // draw 3D border if required
  237. if(bAuto3D)
  238. {
  239. RECT rect = {};
  240. this->GetClientRect(&rect);
  241. if(bPressed)
  242. dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT);
  243. else if(!bHover || (m_fMouseOver == 1))
  244. dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT);
  245. if(!bHover && (m_fFocus == 1))
  246. {
  247. ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE));
  248. dc.DrawFocusRect(&rect);
  249. }
  250. }
  251. }
  252. // Message map and handlers
  253. BEGIN_MSG_MAP(CBitmapButtonImpl)
  254. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  255. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  256. MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
  257. MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
  258. MESSAGE_HANDLER(WM_PAINT, OnPaint)
  259. MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
  260. MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
  261. MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
  262. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
  263. MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
  264. MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
  265. MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
  266. MESSAGE_HANDLER(WM_ENABLE, OnEnable)
  267. MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
  268. MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
  269. MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
  270. MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
  271. MESSAGE_HANDLER(WM_TIMER, OnTimer)
  272. MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
  273. END_MSG_MAP()
  274. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  275. {
  276. T* pT = static_cast<T*>(this);
  277. pT->Init();
  278. bHandled = FALSE;
  279. return 1;
  280. }
  281. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  282. {
  283. if(m_tip.IsWindow())
  284. {
  285. m_tip.DestroyWindow();
  286. m_tip.m_hWnd = NULL;
  287. }
  288. bHandled = FALSE;
  289. return 1;
  290. }
  291. LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  292. {
  293. MSG msg = { this->m_hWnd, uMsg, wParam, lParam };
  294. if(m_tip.IsWindow())
  295. m_tip.RelayEvent(&msg);
  296. bHandled = FALSE;
  297. return 1;
  298. }
  299. LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  300. {
  301. return 1; // no background needed
  302. }
  303. LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  304. {
  305. T* pT = static_cast<T*>(this);
  306. if(wParam != NULL)
  307. {
  308. pT->DoPaint((HDC)wParam);
  309. }
  310. else
  311. {
  312. CPaintDC dc(this->m_hWnd);
  313. pT->DoPaint(dc.m_hDC);
  314. }
  315. return 0;
  316. }
  317. LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  318. {
  319. m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0;
  320. this->Invalidate();
  321. this->UpdateWindow();
  322. bHandled = FALSE;
  323. return 1;
  324. }
  325. LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  326. {
  327. LRESULT lRet = 0;
  328. if(IsHoverMode())
  329. this->SetCapture();
  330. else
  331. lRet = this->DefWindowProc(uMsg, wParam, lParam);
  332. if(::GetCapture() == this->m_hWnd)
  333. {
  334. m_fPressed = 1;
  335. this->Invalidate();
  336. this->UpdateWindow();
  337. }
  338. if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && !IsCheckMode())
  339. {
  340. int nElapse = 250;
  341. int nDelay = 0;
  342. if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
  343. nElapse += nDelay * 250; // all milli-seconds
  344. this->SetTimer(ID_TIMER_FIRST, nElapse);
  345. }
  346. return lRet;
  347. }
  348. LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  349. {
  350. LRESULT lRet = 0;
  351. if(!IsHoverMode() && !IsCheckMode())
  352. lRet = this->DefWindowProc(uMsg, wParam, lParam);
  353. if(::GetCapture() != this->m_hWnd)
  354. this->SetCapture();
  355. if(m_fPressed == 0)
  356. {
  357. m_fPressed = 1;
  358. this->Invalidate();
  359. this->UpdateWindow();
  360. }
  361. return lRet;
  362. }
  363. LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  364. {
  365. if(((m_dwExtendedStyle & BMPBTN_AUTOCHECK) != 0) && (m_fPressed == 1))
  366. SetCheck(!GetCheck(), false);
  367. LRESULT lRet = 0;
  368. if(!IsHoverMode() && !IsCheckMode())
  369. lRet = this->DefWindowProc(uMsg, wParam, lParam);
  370. if(::GetCapture() == this->m_hWnd)
  371. {
  372. if((IsHoverMode() || IsCheckMode()) && (m_fPressed == 1))
  373. this->GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(this->GetDlgCtrlID(), BN_CLICKED), (LPARAM)this->m_hWnd);
  374. ::ReleaseCapture();
  375. }
  376. return lRet;
  377. }
  378. LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  379. {
  380. if(m_fPressed == 1)
  381. {
  382. m_fPressed = 0;
  383. this->Invalidate();
  384. this->UpdateWindow();
  385. }
  386. bHandled = FALSE;
  387. return 1;
  388. }
  389. LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  390. {
  391. this->Invalidate();
  392. this->UpdateWindow();
  393. bHandled = FALSE;
  394. return 1;
  395. }
  396. LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  397. {
  398. if(::GetCapture() == this->m_hWnd)
  399. {
  400. POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  401. this->ClientToScreen(&ptCursor);
  402. RECT rect = {};
  403. this->GetWindowRect(&rect);
  404. unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0;
  405. if(m_fPressed != uPressed)
  406. {
  407. m_fPressed = uPressed;
  408. this->Invalidate();
  409. this->UpdateWindow();
  410. }
  411. }
  412. else if(IsHoverMode() && m_fMouseOver == 0)
  413. {
  414. m_fMouseOver = 1;
  415. this->Invalidate();
  416. this->UpdateWindow();
  417. StartTrackMouseLeave();
  418. }
  419. bHandled = FALSE;
  420. return 1;
  421. }
  422. LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  423. {
  424. if(m_fMouseOver == 1)
  425. {
  426. m_fMouseOver = 0;
  427. this->Invalidate();
  428. this->UpdateWindow();
  429. }
  430. return 0;
  431. }
  432. LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  433. {
  434. if((wParam == VK_SPACE) && IsHoverMode())
  435. return 0; // ignore if in hover mode
  436. if((wParam == VK_SPACE) && (m_fPressed == 0))
  437. {
  438. m_fPressed = 1;
  439. this->Invalidate();
  440. this->UpdateWindow();
  441. }
  442. bHandled = FALSE;
  443. return 1;
  444. }
  445. LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  446. {
  447. if((wParam == VK_SPACE) && IsHoverMode())
  448. return 0; // ignore if in hover mode
  449. if((wParam == VK_SPACE) && (m_fPressed == 1))
  450. {
  451. m_fPressed = 0;
  452. if((m_dwExtendedStyle & BMPBTN_AUTOCHECK) != 0)
  453. SetCheck(!GetCheck(), false);
  454. this->Invalidate();
  455. this->UpdateWindow();
  456. }
  457. bHandled = FALSE;
  458. return 1;
  459. }
  460. LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  461. {
  462. ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0);
  463. switch(wParam) // timer ID
  464. {
  465. case ID_TIMER_FIRST:
  466. this->KillTimer(ID_TIMER_FIRST);
  467. if(m_fPressed == 1)
  468. {
  469. this->GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(this->GetDlgCtrlID(), BN_CLICKED), (LPARAM)this->m_hWnd);
  470. int nElapse = 250;
  471. int nRepeat = 40;
  472. if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
  473. nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated
  474. this->SetTimer(ID_TIMER_REPEAT, nElapse);
  475. }
  476. break;
  477. case ID_TIMER_REPEAT:
  478. if(m_fPressed == 1)
  479. this->GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(this->GetDlgCtrlID(), BN_CLICKED), (LPARAM)this->m_hWnd);
  480. else if(::GetCapture() != this->m_hWnd)
  481. this->KillTimer(ID_TIMER_REPEAT);
  482. break;
  483. default: // not our timer
  484. break;
  485. }
  486. return 0;
  487. }
  488. LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  489. {
  490. // If the control is subclassed or superclassed, this message can cause
  491. // repainting without WM_PAINT. We don't use this state, so just do nothing.
  492. return 0;
  493. }
  494. // Implementation
  495. void Init()
  496. {
  497. // We need this style to prevent Windows from painting the button
  498. this->ModifyStyle(0, BS_OWNERDRAW);
  499. // create a tool tip
  500. m_tip.Create(this->m_hWnd);
  501. ATLASSERT(m_tip.IsWindow());
  502. if(m_tip.IsWindow() && (m_lpstrToolTipText != NULL))
  503. {
  504. m_tip.Activate(TRUE);
  505. m_tip.AddTool(this->m_hWnd, m_lpstrToolTipText);
  506. }
  507. if((m_ImageList.m_hImageList != NULL) && ((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0))
  508. SizeToImage();
  509. }
  510. BOOL StartTrackMouseLeave()
  511. {
  512. TRACKMOUSEEVENT tme = {};
  513. tme.cbSize = sizeof(tme);
  514. tme.dwFlags = TME_LEAVE;
  515. tme.hwndTrack = this->m_hWnd;
  516. return ::TrackMouseEvent(&tme);
  517. }
  518. bool IsHoverMode() const
  519. {
  520. return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0);
  521. }
  522. bool IsCheckMode() const
  523. {
  524. return ((m_dwExtendedStyle & (BMPBTN_CHECK | BMPBTN_AUTOCHECK)) != 0);
  525. }
  526. };
  527. class CBitmapButton : public CBitmapButtonImpl<CBitmapButton>
  528. {
  529. public:
  530. DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName())
  531. CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
  532. CBitmapButtonImpl<CBitmapButton>(dwExtendedStyle, hImageList)
  533. { }
  534. };
  535. ///////////////////////////////////////////////////////////////////////////////
  536. // CCheckListCtrlView - list view control with check boxes
  537. template <DWORD t_dwStyle, DWORD t_dwExStyle, DWORD t_dwExListViewStyle>
  538. class CCheckListViewCtrlImplTraits
  539. {
  540. public:
  541. static DWORD GetWndStyle(DWORD dwStyle)
  542. {
  543. return (dwStyle == 0) ? t_dwStyle : dwStyle;
  544. }
  545. static DWORD GetWndExStyle(DWORD dwExStyle)
  546. {
  547. return (dwExStyle == 0) ? t_dwExStyle : dwExStyle;
  548. }
  549. static DWORD GetExtendedLVStyle()
  550. {
  551. return t_dwExListViewStyle;
  552. }
  553. };
  554. typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT> CCheckListViewCtrlTraits;
  555. template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits>
  556. class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl<T, TBase, TWinTraits >
  557. {
  558. public:
  559. DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName())
  560. // Attributes
  561. static DWORD GetExtendedLVStyle()
  562. {
  563. return TWinTraits::GetExtendedLVStyle();
  564. }
  565. // Operations
  566. BOOL SubclassWindow(HWND hWnd)
  567. {
  568. BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
  569. if(bRet != FALSE)
  570. {
  571. T* pT = static_cast<T*>(this);
  572. pT->Init();
  573. }
  574. return bRet;
  575. }
  576. void CheckSelectedItems(int nCurrItem)
  577. {
  578. // first check if this item is selected
  579. LVITEM lvi = {};
  580. lvi.iItem = nCurrItem;
  581. lvi.iSubItem = 0;
  582. lvi.mask = LVIF_STATE;
  583. lvi.stateMask = LVIS_SELECTED;
  584. this->GetItem(&lvi);
  585. // if item is not selected, don't do anything
  586. if(!(lvi.state & LVIS_SELECTED))
  587. return;
  588. // new check state will be reverse of the current state,
  589. BOOL bCheck = !this->GetCheckState(nCurrItem);
  590. int nItem = -1;
  591. int nOldItem = -1;
  592. while((nItem = this->GetNextItem(nOldItem, LVNI_SELECTED)) != -1)
  593. {
  594. if(nItem != nCurrItem)
  595. this->SetCheckState(nItem, bCheck);
  596. nOldItem = nItem;
  597. }
  598. }
  599. // Implementation
  600. void Init()
  601. {
  602. T* pT = static_cast<T*>(this);
  603. (void)pT; // avoid level 4 warning
  604. ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
  605. this->SetExtendedListViewStyle(pT->GetExtendedLVStyle());
  606. }
  607. // Message map and handlers
  608. BEGIN_MSG_MAP(CCheckListViewCtrlImpl)
  609. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  610. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
  611. MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
  612. MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
  613. END_MSG_MAP()
  614. LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  615. {
  616. // first let list view control initialize everything
  617. LRESULT lRet = this->DefWindowProc(uMsg, wParam, lParam);
  618. if(lRet == 0)
  619. {
  620. T* pT = static_cast<T*>(this);
  621. pT->Init();
  622. }
  623. return lRet;
  624. }
  625. LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  626. {
  627. POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  628. LVHITTESTINFO lvh = {};
  629. lvh.pt = ptMsg;
  630. if((this->HitTest(&lvh) != -1) && (lvh.flags == LVHT_ONITEMSTATEICON) && (::GetKeyState(VK_CONTROL) >= 0))
  631. {
  632. T* pT = static_cast<T*>(this);
  633. pT->CheckSelectedItems(lvh.iItem);
  634. }
  635. bHandled = FALSE;
  636. return 1;
  637. }
  638. LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  639. {
  640. if(wParam == VK_SPACE)
  641. {
  642. int nCurrItem = this->GetNextItem(-1, LVNI_FOCUSED);
  643. if((nCurrItem != -1) && (::GetKeyState(VK_CONTROL) >= 0))
  644. {
  645. T* pT = static_cast<T*>(this);
  646. pT->CheckSelectedItems(nCurrItem);
  647. }
  648. }
  649. bHandled = FALSE;
  650. return 1;
  651. }
  652. };
  653. class CCheckListViewCtrl : public CCheckListViewCtrlImpl<CCheckListViewCtrl>
  654. {
  655. public:
  656. DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName())
  657. };
  658. ///////////////////////////////////////////////////////////////////////////////
  659. // CHyperLink - hyper link control implementation
  660. #define HLINK_UNDERLINED 0x00000000
  661. #define HLINK_NOTUNDERLINED 0x00000001
  662. #define HLINK_UNDERLINEHOVER 0x00000002
  663. #define HLINK_COMMANDBUTTON 0x00000004
  664. #define HLINK_NOTIFYBUTTON 0x0000000C
  665. #define HLINK_USETAGS 0x00000010
  666. #define HLINK_USETAGSBOLD 0x00000030
  667. #define HLINK_NOTOOLTIP 0x00000040
  668. #define HLINK_AUTOCREATELINKFONT 0x00000080
  669. #define HLINK_SINGLELINE 0x00000100
  670. // Notes:
  671. // - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned
  672. // - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored
  673. template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
  674. class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
  675. {
  676. public:
  677. LPTSTR m_lpstrLabel;
  678. LPTSTR m_lpstrHyperLink;
  679. HCURSOR m_hCursor;
  680. HFONT m_hFontLink;
  681. HFONT m_hFontNormal;
  682. RECT m_rcLink;
  683. CToolTipCtrl m_tip;
  684. COLORREF m_clrLink;
  685. COLORREF m_clrVisited;
  686. DWORD m_dwExtendedStyle; // Hyper Link specific extended styles
  687. bool m_bPaintLabel:1;
  688. bool m_bVisited:1;
  689. bool m_bHover:1;
  690. bool m_bInternalLinkFont:1;
  691. bool m_bInternalNormalFont:1;
  692. // Constructor/Destructor
  693. CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) :
  694. m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
  695. m_hCursor(NULL), m_hFontLink(NULL), m_hFontNormal(NULL),
  696. m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)),
  697. m_dwExtendedStyle(dwExtendedStyle),
  698. m_bPaintLabel(true), m_bVisited(false),
  699. m_bHover(false), m_bInternalLinkFont(false), m_bInternalNormalFont(false)
  700. {
  701. ::SetRectEmpty(&m_rcLink);
  702. }
  703. ~CHyperLinkImpl()
  704. {
  705. delete [] m_lpstrLabel;
  706. delete [] m_lpstrHyperLink;
  707. }
  708. // Attributes
  709. DWORD GetHyperLinkExtendedStyle() const
  710. {
  711. return m_dwExtendedStyle;
  712. }
  713. DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
  714. {
  715. DWORD dwPrevStyle = m_dwExtendedStyle;
  716. if(dwMask == 0)
  717. m_dwExtendedStyle = dwExtendedStyle;
  718. else
  719. m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
  720. return dwPrevStyle;
  721. }
  722. bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
  723. {
  724. if(m_lpstrLabel == NULL)
  725. return false;
  726. ATLASSERT(lpstrBuffer != NULL);
  727. if(nLength <= lstrlen(m_lpstrLabel))
  728. return false;
  729. ATL::Checked::tcscpy_s(lpstrBuffer, nLength, m_lpstrLabel);
  730. return true;
  731. }
  732. bool SetLabel(LPCTSTR lpstrLabel)
  733. {
  734. delete [] m_lpstrLabel;
  735. m_lpstrLabel = NULL;
  736. int cchLen = lstrlen(lpstrLabel) + 1;
  737. ATLTRY(m_lpstrLabel = new TCHAR[cchLen]);
  738. if(m_lpstrLabel == NULL)
  739. return false;
  740. ATL::Checked::tcscpy_s(m_lpstrLabel, cchLen, lpstrLabel);
  741. T* pT = static_cast<T*>(this);
  742. pT->CalcLabelRect();
  743. if(this->m_hWnd != NULL)
  744. this->SetWindowText(lpstrLabel); // Set this for accessibility
  745. return true;
  746. }
  747. bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
  748. {
  749. if(m_lpstrHyperLink == NULL)
  750. return false;
  751. ATLASSERT(lpstrBuffer != NULL);
  752. if(nLength <= lstrlen(m_lpstrHyperLink))
  753. return false;
  754. ATL::Checked::tcscpy_s(lpstrBuffer, nLength, m_lpstrHyperLink);
  755. return true;
  756. }
  757. bool SetHyperLink(LPCTSTR lpstrLink)
  758. {
  759. delete [] m_lpstrHyperLink;
  760. m_lpstrHyperLink = NULL;
  761. int cchLen = lstrlen(lpstrLink) + 1;
  762. ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]);
  763. if(m_lpstrHyperLink == NULL)
  764. return false;
  765. ATL::Checked::tcscpy_s(m_lpstrHyperLink, cchLen, lpstrLink);
  766. if(m_lpstrLabel == NULL)
  767. {
  768. T* pT = static_cast<T*>(this);
  769. pT->CalcLabelRect();
  770. }
  771. if(m_tip.IsWindow())
  772. {
  773. m_tip.Activate(TRUE);
  774. m_tip.AddTool(this->m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
  775. }
  776. return true;
  777. }
  778. HFONT GetLinkFont() const
  779. {
  780. return m_hFontLink;
  781. }
  782. void SetLinkFont(HFONT hFont)
  783. {
  784. if(m_bInternalLinkFont)
  785. {
  786. ::DeleteObject(m_hFontLink);
  787. m_bInternalLinkFont = false;
  788. }
  789. m_hFontLink = hFont;
  790. T* pT = static_cast<T*>(this);
  791. pT->CalcLabelRect();
  792. }
  793. int GetIdealHeight() const
  794. {
  795. ATLASSERT(::IsWindow(this->m_hWnd));
  796. if((m_lpstrLabel == NULL) && (m_lpstrHyperLink == NULL))
  797. return -1;
  798. if(!m_bPaintLabel)
  799. return -1;
  800. UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  801. CClientDC dc(this->m_hWnd);
  802. RECT rect = {};
  803. this->GetClientRect(&rect);
  804. HFONT hFontOld = dc.SelectFont(m_hFontNormal);
  805. RECT rcText = rect;
  806. dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | uFormat | DT_CALCRECT);
  807. dc.SelectFont(m_hFontLink);
  808. RECT rcLink = rect;
  809. dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
  810. dc.SelectFont(hFontOld);
  811. return __max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top);
  812. }
  813. bool GetIdealSize(SIZE& size) const
  814. {
  815. int cx = 0, cy = 0;
  816. bool bRet = GetIdealSize(cx, cy);
  817. if(bRet)
  818. {
  819. size.cx = cx;
  820. size.cy = cy;
  821. }
  822. return bRet;
  823. }
  824. bool GetIdealSize(int& cx, int& cy) const
  825. {
  826. ATLASSERT(::IsWindow(this->m_hWnd));
  827. if((m_lpstrLabel == NULL) && (m_lpstrHyperLink == NULL))
  828. return false;
  829. if(!m_bPaintLabel)
  830. return false;
  831. CClientDC dc(this->m_hWnd);
  832. RECT rcClient = {};
  833. this->GetClientRect(&rcClient);
  834. RECT rcAll = rcClient;
  835. if(IsUsingTags())
  836. {
  837. // find tags and label parts
  838. LPTSTR lpstrLeft = NULL;
  839. int cchLeft = 0;
  840. LPTSTR lpstrLink = NULL;
  841. int cchLink = 0;
  842. LPTSTR lpstrRight = NULL;
  843. int cchRight = 0;
  844. const T* pT = static_cast<const T*>(this);
  845. pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
  846. // get label part rects
  847. UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  848. HFONT hFontOld = dc.SelectFont(m_hFontNormal);
  849. RECT rcLeft = rcClient;
  850. dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
  851. dc.SelectFont(m_hFontLink);
  852. RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom };
  853. dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
  854. dc.SelectFont(m_hFontNormal);
  855. RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom };
  856. dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat | DT_CALCRECT);
  857. dc.SelectFont(hFontOld);
  858. int cyMax = __max(rcLeft.bottom, __max(rcLink.bottom, rcRight.bottom));
  859. ::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax);
  860. }
  861. else
  862. {
  863. HFONT hOldFont = NULL;
  864. if(m_hFontLink != NULL)
  865. hOldFont = dc.SelectFont(m_hFontLink);
  866. LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
  867. DWORD dwStyle = this->GetStyle();
  868. UINT uFormat = DT_LEFT;
  869. if (dwStyle & SS_CENTER)
  870. uFormat = DT_CENTER;
  871. else if (dwStyle & SS_RIGHT)
  872. uFormat = DT_RIGHT;
  873. uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  874. dc.DrawText(lpstrText, -1, &rcAll, uFormat | DT_CALCRECT);
  875. if(m_hFontLink != NULL)
  876. dc.SelectFont(hOldFont);
  877. if (dwStyle & SS_CENTER)
  878. {
  879. int dx = (rcClient.right - rcAll.right) / 2;
  880. ::OffsetRect(&rcAll, dx, 0);
  881. }
  882. else if (dwStyle & SS_RIGHT)
  883. {
  884. int dx = rcClient.right - rcAll.right;
  885. ::OffsetRect(&rcAll, dx, 0);
  886. }
  887. }
  888. cx = rcAll.right - rcAll.left;
  889. cy = rcAll.bottom - rcAll.top;
  890. return true;
  891. }
  892. // for command buttons only
  893. bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const
  894. {
  895. ATLASSERT(IsCommandButton());
  896. return GetHyperLink(lpstrBuffer, nLength);
  897. }
  898. bool SetToolTipText(LPCTSTR lpstrToolTipText)
  899. {
  900. ATLASSERT(IsCommandButton());
  901. return SetHyperLink(lpstrToolTipText);
  902. }
  903. // Operations
  904. BOOL SubclassWindow(HWND hWnd)
  905. {
  906. ATLASSERT(this->m_hWnd == NULL);
  907. ATLASSERT(::IsWindow(hWnd));
  908. if(m_hFontNormal == NULL)
  909. m_hFontNormal = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L);
  910. BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
  911. if(bRet != FALSE)
  912. {
  913. T* pT = static_cast<T*>(this);
  914. pT->Init();
  915. }
  916. return bRet;
  917. }
  918. bool Navigate()
  919. {
  920. ATLASSERT(::IsWindow(this->m_hWnd));
  921. bool bRet = true;
  922. if(IsNotifyButton())
  923. {
  924. NMHDR nmhdr = { this->m_hWnd, (UINT_PTR)this->GetDlgCtrlID(), NM_CLICK };
  925. this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&nmhdr);
  926. }
  927. else if(IsCommandButton())
  928. {
  929. this->GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(this->GetDlgCtrlID(), BN_CLICKED), (LPARAM)this->m_hWnd);
  930. }
  931. else
  932. {
  933. ATLASSERT(m_lpstrHyperLink != NULL);
  934. DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
  935. bRet = (dwRet > 32);
  936. ATLASSERT(bRet);
  937. if(bRet)
  938. {
  939. m_bVisited = true;
  940. this->Invalidate();
  941. }
  942. }
  943. return bRet;
  944. }
  945. void CreateLinkFontFromNormal()
  946. {
  947. if(m_bInternalLinkFont)
  948. {
  949. ::DeleteObject(m_hFontLink);
  950. m_bInternalLinkFont = false;
  951. }
  952. CFontHandle font = (m_hFontNormal != NULL) ? m_hFontNormal : (HFONT)::GetStockObject(SYSTEM_FONT);
  953. LOGFONT lf = {};
  954. font.GetLogFont(&lf);
  955. if(IsUsingTagsBold())
  956. lf.lfWeight = FW_BOLD;
  957. else if(!IsNotUnderlined())
  958. lf.lfUnderline = TRUE;
  959. m_hFontLink = ::CreateFontIndirect(&lf);
  960. m_bInternalLinkFont = true;
  961. ATLASSERT(m_hFontLink != NULL);
  962. }
  963. // Message map and handlers
  964. BEGIN_MSG_MAP(CHyperLinkImpl)
  965. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  966. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  967. MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
  968. MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
  969. MESSAGE_HANDLER(WM_PAINT, OnPaint)
  970. MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
  971. MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
  972. MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
  973. MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
  974. MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
  975. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
  976. MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
  977. MESSAGE_HANDLER(WM_CHAR, OnChar)
  978. MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
  979. MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
  980. MESSAGE_HANDLER(WM_ENABLE, OnEnable)
  981. MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
  982. MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
  983. MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
  984. MESSAGE_HANDLER(WM_SIZE, OnSize)
  985. END_MSG_MAP()
  986. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  987. {
  988. T* pT = static_cast<T*>(this);
  989. pT->Init();
  990. return 0;
  991. }
  992. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  993. {
  994. if(m_tip.IsWindow())
  995. {
  996. m_tip.DestroyWindow();
  997. m_tip.m_hWnd = NULL;
  998. }
  999. if(m_bInternalLinkFont)
  1000. {
  1001. ::DeleteObject(m_hFontLink);
  1002. m_hFontLink = NULL;
  1003. m_bInternalLinkFont = false;
  1004. }
  1005. if(m_bInternalNormalFont)
  1006. {
  1007. ::DeleteObject(m_hFontNormal);
  1008. m_hFontNormal = NULL;
  1009. m_bInternalNormalFont = false;
  1010. }
  1011. bHandled = FALSE;
  1012. return 1;
  1013. }
  1014. LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  1015. {
  1016. MSG msg = { this->m_hWnd, uMsg, wParam, lParam };
  1017. if(m_tip.IsWindow() && IsUsingToolTip())
  1018. m_tip.RelayEvent(&msg);
  1019. bHandled = FALSE;
  1020. return 1;
  1021. }
  1022. LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1023. {
  1024. return 1; // no background painting needed (we do it all during WM_PAINT)
  1025. }
  1026. LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  1027. {
  1028. if(!m_bPaintLabel)
  1029. {
  1030. bHandled = FALSE;
  1031. return 1;
  1032. }
  1033. T* pT = static_cast<T*>(this);
  1034. if(wParam != NULL)
  1035. {
  1036. pT->DoEraseBackground((HDC)wParam);
  1037. pT->DoPaint((HDC)wParam);
  1038. }
  1039. else
  1040. {
  1041. CPaintDC dc(this->m_hWnd);
  1042. pT->DoEraseBackground(dc.m_hDC);
  1043. pT->DoPaint(dc.m_hDC);
  1044. }
  1045. return 0;
  1046. }
  1047. LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  1048. {
  1049. if(m_bPaintLabel)
  1050. this->Invalidate();
  1051. else
  1052. bHandled = FALSE;
  1053. return 0;
  1054. }
  1055. LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  1056. {
  1057. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  1058. if(((m_lpstrHyperLink != NULL) || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
  1059. {
  1060. ::SetCursor(m_hCursor);
  1061. if(IsUnderlineHover())
  1062. {
  1063. if(!m_bHover)
  1064. {
  1065. m_bHover = true;
  1066. this->InvalidateRect(&m_rcLink);
  1067. this->UpdateWindow();
  1068. StartTrackMouseLeave();
  1069. }
  1070. }
  1071. }
  1072. else
  1073. {
  1074. if(IsUnderlineHover())
  1075. {
  1076. if(m_bHover)
  1077. {
  1078. m_bHover = false;
  1079. this->InvalidateRect(&m_rcLink);
  1080. this->UpdateWindow();
  1081. }
  1082. }
  1083. bHandled = FALSE;
  1084. }
  1085. return 0;
  1086. }
  1087. LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1088. {
  1089. if(IsUnderlineHover() && m_bHover)
  1090. {
  1091. m_bHover = false;
  1092. this->InvalidateRect(&m_rcLink);
  1093. this->UpdateWindow();
  1094. }
  1095. return 0;
  1096. }
  1097. LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  1098. {
  1099. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  1100. if(::PtInRect(&m_rcLink, pt))
  1101. {
  1102. this->SetFocus();
  1103. this->SetCapture();
  1104. }
  1105. return 0;
  1106. }
  1107. LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  1108. {
  1109. if(GetCapture() == this->m_hWnd)
  1110. {
  1111. ReleaseCapture();
  1112. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  1113. if(::PtInRect(&m_rcLink, pt))
  1114. {
  1115. T* pT = static_cast<T*>(this);
  1116. pT->Navigate();
  1117. }
  1118. }
  1119. return 0;
  1120. }
  1121. LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1122. {
  1123. if((wParam == VK_RETURN) || (wParam == VK_SPACE))
  1124. {
  1125. T* pT = static_cast<T*>(this);
  1126. pT->Navigate();
  1127. }
  1128. return 0;
  1129. }
  1130. LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1131. {
  1132. return DLGC_WANTCHARS;
  1133. }
  1134. LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  1135. {
  1136. POINT pt = {};
  1137. GetCursorPos(&pt);
  1138. this->ScreenToClient(&pt);
  1139. if(((m_lpstrHyperLink != NULL) || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
  1140. {
  1141. return TRUE;
  1142. }
  1143. bHandled = FALSE;
  1144. return FALSE;
  1145. }
  1146. LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1147. {
  1148. this->Invalidate();
  1149. this->UpdateWindow();
  1150. return 0;
  1151. }
  1152. LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1153. {
  1154. return (LRESULT)m_hFontNormal;
  1155. }
  1156. LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  1157. {
  1158. if(m_bInternalNormalFont)
  1159. {
  1160. ::DeleteObject(m_hFontNormal);
  1161. m_bInternalNormalFont = false;
  1162. }
  1163. bool bCreateLinkFont = m_bInternalLinkFont;
  1164. m_hFontNormal = (HFONT)wParam;
  1165. if(bCreateLinkFont || IsAutoCreateLinkFont())
  1166. CreateLinkFontFromNormal();
  1167. T* pT = static_cast<T*>(this);
  1168. pT->CalcLabelRect();
  1169. if((BOOL)lParam)
  1170. {
  1171. this->Invalidate();
  1172. this->UpdateWindow();
  1173. }
  1174. return 0;
  1175. }
  1176. LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1177. {
  1178. // If the control is subclassed or superclassed, this message can cause
  1179. // repainting without WM_PAINT. We don't use this state, so just do nothing.
  1180. return 0;
  1181. }
  1182. LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1183. {
  1184. T* pT = static_cast<T*>(this);
  1185. pT->CalcLabelRect();
  1186. pT->Invalidate();
  1187. return 0;
  1188. }
  1189. // Implementation
  1190. void Init()
  1191. {
  1192. ATLASSERT(::IsWindow(this->m_hWnd));
  1193. // Check if we should paint a label
  1194. const int cchBuff = 8;
  1195. TCHAR szBuffer[cchBuff] = {};
  1196. if(::GetClassName(this->m_hWnd, szBuffer, cchBuff))
  1197. {
  1198. if(lstrcmpi(szBuffer, _T("static")) == 0)
  1199. {
  1200. this->ModifyStyle(0, SS_NOTIFY); // we need this
  1201. DWORD dwStyle = this->GetStyle() & 0x000000FF;
  1202. if((dwStyle == SS_ICON) || (dwStyle == SS_BLACKRECT) || (dwStyle == SS_GRAYRECT) ||
  1203. (dwStyle == SS_WHITERECT) || (dwStyle == SS_BLACKFRAME) || (dwStyle == SS_GRAYFRAME) ||
  1204. (dwStyle == SS_WHITEFRAME) || (dwStyle == SS_OWNERDRAW) ||
  1205. (dwStyle == SS_BITMAP) || (dwStyle == SS_ENHMETAFILE))
  1206. m_bPaintLabel = false;
  1207. }
  1208. }
  1209. // create or load a cursor
  1210. m_hCursor = ::LoadCursor(NULL, IDC_HAND);
  1211. ATLASSERT(m_hCursor != NULL);
  1212. // set fonts
  1213. if(m_bPaintLabel)
  1214. {
  1215. if(m_hFontNormal == NULL)
  1216. {
  1217. m_hFontNormal = AtlCreateControlFont();
  1218. m_bInternalNormalFont = true;
  1219. }
  1220. if(m_hFontLink == NULL)
  1221. CreateLinkFontFromNormal();
  1222. }
  1223. // create a tool tip
  1224. m_tip.Create(this->m_hWnd);
  1225. ATLASSERT(m_tip.IsWindow());
  1226. // set label (defaults to window text)
  1227. if(m_lpstrLabel == NULL)
  1228. {
  1229. int nLen = this->GetWindowTextLength();
  1230. if(nLen > 0)
  1231. {
  1232. ATLTRY(m_lpstrLabel = new TCHAR[nLen + 1]);
  1233. if(m_lpstrLabel != NULL)
  1234. ATLVERIFY(this->GetWindowText(m_lpstrLabel, nLen + 1) > 0);
  1235. }
  1236. }
  1237. T* pT = static_cast<T*>(this);
  1238. pT->CalcLabelRect();
  1239. // set hyperlink (defaults to label), or just activate tool tip if already set
  1240. if((m_lpstrHyperLink == NULL) && !IsCommandButton())
  1241. {
  1242. if(m_lpstrLabel != NULL)
  1243. SetHyperLink(m_lpstrLabel);
  1244. }
  1245. else
  1246. {
  1247. m_tip.Activate(TRUE);
  1248. m_tip.AddTool(this->m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
  1249. }
  1250. // set link colors
  1251. if(m_bPaintLabel)
  1252. {
  1253. ATL::CRegKey rk;
  1254. LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
  1255. if(lRet == ERROR_SUCCESS)
  1256. {
  1257. const int cchValue = 12;
  1258. TCHAR szValue[cchValue] = {};
  1259. ULONG ulCount = cchValue;
  1260. lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount);
  1261. if(lRet == ERROR_SUCCESS)
  1262. {
  1263. COLORREF clr = pT->_ParseColorString(szValue);
  1264. ATLASSERT(clr != CLR_INVALID);
  1265. if(clr != CLR_INVALID)
  1266. m_clrLink = clr;
  1267. }
  1268. ulCount = cchValue;
  1269. lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount);
  1270. if(lRet == ERROR_SUCCESS)
  1271. {
  1272. COLORREF clr = pT->_ParseColorString(szValue);
  1273. ATLASSERT(clr != CLR_INVALID);
  1274. if(clr != CLR_INVALID)
  1275. m_clrVisited = clr;
  1276. }
  1277. }
  1278. }
  1279. }
  1280. static COLORREF _ParseColorString(LPTSTR lpstr)
  1281. {
  1282. int c[3] = { -1, -1, -1 };
  1283. LPTSTR p = NULL;
  1284. for(int i = 0; i < 2; i++)
  1285. {
  1286. for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
  1287. {
  1288. if(*p == _T(','))
  1289. {
  1290. *p = _T('\0');
  1291. c[i] = _ttoi(lpstr);
  1292. lpstr = &p[1];
  1293. break;
  1294. }
  1295. }
  1296. if(c[i] == -1)
  1297. return CLR_INVALID;
  1298. }
  1299. if(*lpstr == _T('\0'))
  1300. return CLR_INVALID;
  1301. c[2] = _ttoi(lpstr);
  1302. return RGB(c[0], c[1], c[2]);
  1303. }
  1304. bool CalcLabelRect()
  1305. {
  1306. if(!::IsWindow(this->m_hWnd))
  1307. return false;
  1308. if((m_lpstrLabel == NULL) && (m_lpstrHyperLink == NULL))
  1309. return false;
  1310. CClientDC dc(this->m_hWnd);
  1311. RECT rcClient = {};
  1312. this->GetClientRect(&rcClient);
  1313. m_rcLink = rcClient;
  1314. if(!m_bPaintLabel)
  1315. return true;
  1316. if(IsUsingTags())
  1317. {
  1318. // find tags and label parts
  1319. LPTSTR lpstrLeft = NULL;
  1320. int cchLeft = 0;
  1321. LPTSTR lpstrLink = NULL;
  1322. int cchLink = 0;
  1323. LPTSTR lpstrRight = NULL;
  1324. int cchRight = 0;
  1325. T* pT = static_cast<T*>(this);
  1326. pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
  1327. ATLASSERT(lpstrLink != NULL);
  1328. ATLASSERT(cchLink > 0);
  1329. // get label part rects
  1330. HFONT hFontOld = dc.SelectFont(m_hFontNormal);
  1331. UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  1332. RECT rcLeft = rcClient;
  1333. if(lpstrLeft != NULL)
  1334. dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
  1335. dc.SelectFont(m_hFontLink);
  1336. RECT rcLink = rcClient;
  1337. if(lpstrLeft != NULL)
  1338. rcLink.left = rcLeft.right;
  1339. dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
  1340. dc.SelectFont(hFontOld);
  1341. m_rcLink = rcLink;
  1342. }
  1343. else
  1344. {
  1345. HFONT hOldFont = NULL;
  1346. if(m_hFontLink != NULL)
  1347. hOldFont = dc.SelectFont(m_hFontLink);
  1348. LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
  1349. DWORD dwStyle = this->GetStyle();
  1350. UINT uFormat = DT_LEFT;
  1351. if (dwStyle & SS_CENTER)
  1352. uFormat = DT_CENTER;
  1353. else if (dwStyle & SS_RIGHT)
  1354. uFormat = DT_RIGHT;
  1355. uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  1356. dc.DrawText(lpstrText, -1, &m_rcLink, uFormat | DT_CALCRECT);
  1357. if(m_hFontLink != NULL)
  1358. dc.SelectFont(hOldFont);
  1359. if (dwStyle & SS_CENTER)
  1360. {
  1361. int dx = (rcClient.right - m_rcLink.right) / 2;
  1362. ::OffsetRect(&m_rcLink, dx, 0);
  1363. }
  1364. else if (dwStyle & SS_RIGHT)
  1365. {
  1366. int dx = rcClient.right - m_rcLink.right;
  1367. ::OffsetRect(&m_rcLink, dx, 0);
  1368. }
  1369. }
  1370. return true;
  1371. }
  1372. void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const
  1373. {
  1374. lpstrLeft = NULL;
  1375. cchLeft = 0;
  1376. lpstrLink = NULL;
  1377. cchLink = 0;
  1378. lpstrRight = NULL;
  1379. cchRight = 0;
  1380. LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
  1381. int cchText = lstrlen(lpstrText);
  1382. bool bOutsideLink = true;
  1383. for(int i = 0; i < cchText; i++)
  1384. {
  1385. if(lpstrText[i] != _T('<'))
  1386. continue;
  1387. if(bOutsideLink)
  1388. {
  1389. if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T("<A>"), 3) == CSTR_EQUAL)
  1390. {
  1391. if(i > 0)
  1392. {
  1393. lpstrLeft = lpstrText;
  1394. cchLeft = i;
  1395. }
  1396. lpstrLink = &lpstrText[i + 3];
  1397. bOutsideLink = false;
  1398. }
  1399. }
  1400. else
  1401. {
  1402. if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T("</A>"), 4) == CSTR_EQUAL)
  1403. {
  1404. cchLink = i - 3 - cchLeft;
  1405. if(lpstrText[i + 4] != 0)
  1406. {
  1407. lpstrRight = &lpstrText[i + 4];
  1408. cchRight = cchText - (i + 4);
  1409. break;
  1410. }
  1411. }
  1412. }
  1413. }
  1414. }
  1415. void DoEraseBackground(CDCHandle dc)
  1416. {
  1417. HBRUSH hBrush = (HBRUSH)this->GetParent().SendMessage(WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)this->m_hWnd);
  1418. if(hBrush != NULL)
  1419. {
  1420. RECT rect = {};
  1421. this->GetClientRect(&rect);
  1422. dc.FillRect(&rect, hBrush);
  1423. }
  1424. }
  1425. void DoPaint(CDCHandle dc)
  1426. {
  1427. if(IsUsingTags())
  1428. {
  1429. // find tags and label parts
  1430. LPTSTR lpstrLeft = NULL;
  1431. int cchLeft = 0;
  1432. LPTSTR lpstrLink = NULL;
  1433. int cchLink = 0;
  1434. LPTSTR lpstrRight = NULL;
  1435. int cchRight = 0;
  1436. T* pT = static_cast<T*>(this);
  1437. pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
  1438. // get label part rects
  1439. RECT rcClient = {};
  1440. this->GetClientRect(&rcClient);
  1441. dc.SetBkMode(TRANSPARENT);
  1442. HFONT hFontOld = dc.SelectFont(m_hFontNormal);
  1443. UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  1444. if(lpstrLeft != NULL)
  1445. dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | uFormat);
  1446. COLORREF clrOld = dc.SetTextColor(this->IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
  1447. if((m_hFontLink != NULL) && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
  1448. dc.SelectFont(m_hFontLink);
  1449. else
  1450. dc.SelectFont(m_hFontNormal);
  1451. dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | uFormat);
  1452. dc.SetTextColor(clrOld);
  1453. dc.SelectFont(m_hFontNormal);
  1454. if(lpstrRight != NULL)
  1455. {
  1456. RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom };
  1457. dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat);
  1458. }
  1459. if(GetFocus() == this->m_hWnd)
  1460. dc.DrawFocusRect(&m_rcLink);
  1461. dc.SelectFont(hFontOld);
  1462. }
  1463. else
  1464. {
  1465. dc.SetBkMode(TRANSPARENT);
  1466. COLORREF clrOld = dc.SetTextColor(this->IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
  1467. HFONT hFontOld = NULL;
  1468. if((m_hFontLink != NULL) && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
  1469. hFontOld = dc.SelectFont(m_hFontLink);
  1470. else
  1471. hFontOld = dc.SelectFont(m_hFontNormal);
  1472. LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
  1473. DWORD dwStyle = this->GetStyle();
  1474. UINT uFormat = DT_LEFT;
  1475. if (dwStyle & SS_CENTER)
  1476. uFormat = DT_CENTER;
  1477. else if (dwStyle & SS_RIGHT)
  1478. uFormat = DT_RIGHT;
  1479. uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  1480. dc.DrawText(lpstrText, -1, &m_rcLink, uFormat);
  1481. if(GetFocus() == this->m_hWnd)
  1482. dc.DrawFocusRect(&m_rcLink);
  1483. dc.SetTextColor(clrOld);
  1484. dc.SelectFont(hFontOld);
  1485. }
  1486. }
  1487. BOOL StartTrackMouseLeave()
  1488. {
  1489. TRACKMOUSEEVENT tme = {};
  1490. tme.cbSize = sizeof(tme);
  1491. tme.dwFlags = TME_LEAVE;
  1492. tme.hwndTrack = this->m_hWnd;
  1493. return ::TrackMouseEvent(&tme);
  1494. }
  1495. // Implementation helpers
  1496. bool IsUnderlined() const
  1497. {
  1498. return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0);
  1499. }
  1500. bool IsNotUnderlined() const
  1501. {
  1502. return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0);
  1503. }
  1504. bool IsUnderlineHover() const
  1505. {
  1506. return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0);
  1507. }
  1508. bool IsCommandButton() const
  1509. {
  1510. return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0);
  1511. }
  1512. bool IsNotifyButton() const
  1513. {
  1514. return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON);
  1515. }
  1516. bool IsUsingTags() const
  1517. {
  1518. return ((m_dwExtendedStyle & HLINK_USETAGS) != 0);
  1519. }
  1520. bool IsUsingTagsBold() const
  1521. {
  1522. return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD);
  1523. }
  1524. bool IsUsingToolTip() const
  1525. {
  1526. return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0);
  1527. }
  1528. bool IsAutoCreateLinkFont() const
  1529. {
  1530. return ((m_dwExtendedStyle & HLINK_AUTOCREATELINKFONT) == HLINK_AUTOCREATELINKFONT);
  1531. }
  1532. bool IsSingleLine() const
  1533. {
  1534. return ((m_dwExtendedStyle & HLINK_SINGLELINE) == HLINK_SINGLELINE);
  1535. }
  1536. };
  1537. class CHyperLink : public CHyperLinkImpl<CHyperLink>
  1538. {
  1539. public:
  1540. DECLARE_WND_CLASS(_T("WTL_HyperLink"))
  1541. };
  1542. ///////////////////////////////////////////////////////////////////////////////
  1543. // CWaitCursor - displays a wait cursor
  1544. class CWaitCursor
  1545. {
  1546. public:
  1547. // Data
  1548. HCURSOR m_hWaitCursor;
  1549. HCURSOR m_hOldCursor;
  1550. bool m_bInUse;
  1551. // Constructor/destructor
  1552. CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false)
  1553. {
  1554. HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance();
  1555. m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor);
  1556. ATLASSERT(m_hWaitCursor != NULL);
  1557. if(bSet)
  1558. Set();
  1559. }
  1560. ~CWaitCursor()
  1561. {
  1562. Restore();
  1563. }
  1564. // Methods
  1565. bool Set()
  1566. {
  1567. if(m_bInUse)
  1568. return false;
  1569. m_hOldCursor = ::SetCursor(m_hWaitCursor);
  1570. m_bInUse = true;
  1571. return true;
  1572. }
  1573. bool Restore()
  1574. {
  1575. if(!m_bInUse)
  1576. return false;
  1577. ::SetCursor(m_hOldCursor);
  1578. m_bInUse = false;
  1579. return true;
  1580. }
  1581. };
  1582. ///////////////////////////////////////////////////////////////////////////////
  1583. // CCustomWaitCursor - for custom and animated cursors
  1584. class CCustomWaitCursor : public CWaitCursor
  1585. {
  1586. public:
  1587. // Constructor/destructor
  1588. CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) :
  1589. CWaitCursor(false, IDC_WAIT, true)
  1590. {
  1591. if(hInstance == NULL)
  1592. hInstance = ModuleHelper::GetResourceInstance();
  1593. m_hWaitCursor = (HCURSOR)::LoadImage(hInstance, cursor.m_lpstr, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
  1594. if(bSet)
  1595. Set();
  1596. }
  1597. ~CCustomWaitCursor()
  1598. {
  1599. Restore();
  1600. ::DestroyCursor(m_hWaitCursor);
  1601. }
  1602. };
  1603. ///////////////////////////////////////////////////////////////////////////////
  1604. // CMultiPaneStatusBarCtrl - Status Bar with multiple panes
  1605. template <class T, class TBase = CStatusBarCtrl>
  1606. class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public ATL::CWindowImpl< T, TBase >
  1607. {
  1608. public:
  1609. DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName())
  1610. // Data
  1611. enum { m_cxPaneMargin = 3 };
  1612. int m_nPanes;
  1613. int* m_pPane;
  1614. // Constructor/destructor
  1615. CMultiPaneStatusBarCtrlImpl() : m_nPanes(0), m_pPane(NULL)
  1616. { }
  1617. ~CMultiPaneStatusBarCtrlImpl()
  1618. {
  1619. delete [] m_pPane;
  1620. }
  1621. // Methods
  1622. HWND Create(HWND hWndParent, LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
  1623. {
  1624. return ATL::CWindowImpl< T, TBase >::Create(hWndParent, this->rcDefault, lpstrText, dwStyle, 0, nID);
  1625. }
  1626. HWND Create(HWND hWndParent, UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
  1627. {
  1628. const int cchMax = 128; // max text length is 127 for status bars (+1 for null)
  1629. TCHAR szText[cchMax] = {};
  1630. ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
  1631. return Create(hWndParent, szText, dwStyle, nID);
  1632. }
  1633. BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true)
  1634. {
  1635. ATLASSERT(::IsWindow(this->m_hWnd));
  1636. ATLASSERT(nPanes > 0);
  1637. m_nPanes = nPanes;
  1638. delete [] m_pPane;
  1639. m_pPane = NULL;
  1640. ATLTRY(m_pPane = new int[nPanes]);
  1641. ATLASSERT(m_pPane != NULL);
  1642. if(m_pPane == NULL)
  1643. return FALSE;
  1644. ATL::CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
  1645. int* pPanesPos = buff.Allocate(nPanes);
  1646. ATLASSERT(pPanesPos != NULL);
  1647. if(pPanesPos == NULL)
  1648. return FALSE;
  1649. ATL::Checked::memcpy_s(m_pPane, nPanes * sizeof(int), pPanes, nPanes * sizeof(int));
  1650. // get status bar DC and set font
  1651. CClientDC dc(this->m_hWnd);
  1652. HFONT hOldFont = dc.SelectFont(this->GetFont());
  1653. // get status bar borders
  1654. int arrBorders[3] = {};
  1655. this->GetBorders(arrBorders);
  1656. const int cchBuff = 128;
  1657. TCHAR szBuff[cchBuff] = {};
  1658. int cxLeft = arrBorders[0];
  1659. // calculate right edge of each part
  1660. for(int i = 0; i < nPanes; i++)
  1661. {
  1662. if(pPanes[i] == ID_DEFAULT_PANE)
  1663. {
  1664. // make very large, will be resized later
  1665. pPanesPos[i] = INT_MAX / 2;
  1666. }
  1667. else
  1668. {
  1669. ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
  1670. SIZE size = {};
  1671. dc.GetTextExtent(szBuff, lstrlen(szBuff), &size);
  1672. T* pT = static_cast<T*>(this);
  1673. (void)pT; // avoid level 4 warning
  1674. pPanesPos[i] = cxLeft + size.cx + arrBorders[2] + 2 * pT->m_cxPaneMargin;
  1675. }
  1676. cxLeft = pPanesPos[i];
  1677. }
  1678. BOOL bRet = this->SetParts(nPanes, pPanesPos);
  1679. if(bRet && bSetText)
  1680. {
  1681. for(int i = 0; i < nPanes; i++)
  1682. {
  1683. if(pPanes[i] != ID_DEFAULT_PANE)
  1684. {
  1685. ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
  1686. SetPaneText(m_pPane[i], szBuff);
  1687. }
  1688. }
  1689. }
  1690. dc.SelectFont(hOldFont);
  1691. return bRet;
  1692. }
  1693. bool GetPaneTextLength(int nPaneID, int* pcchLength = NULL, int* pnType = NULL) const
  1694. {
  1695. ATLASSERT(::IsWindow(this->m_hWnd));
  1696. int nIndex = GetPaneIndexFromID(nPaneID);
  1697. if(nIndex == -1)
  1698. return false;
  1699. int nLength = this->GetTextLength(nIndex, pnType);
  1700. if(pcchLength != NULL)
  1701. *pcchLength = nLength;
  1702. return true;
  1703. }
  1704. BOOL GetPaneText(int nPaneID, LPTSTR lpstrText, int* pcchLength = NULL, int* pnType = NULL) const
  1705. {
  1706. ATLASSERT(::IsWindow(this->m_hWnd));
  1707. int nIndex = GetPaneIndexFromID(nPaneID);
  1708. if(nIndex == -1)
  1709. return FALSE;
  1710. int nLength = this->GetText(nIndex, lpstrText, pnType);
  1711. if(pcchLength != NULL)
  1712. *pcchLength = nLength;
  1713. return TRUE;
  1714. }
  1715. #ifdef __ATLSTR_H__
  1716. BOOL GetPaneText(int nPaneID, ATL::CString& strText, int* pcchLength = NULL, int* pnType = NULL) const
  1717. {
  1718. ATLASSERT(::IsWindow(this->m_hWnd));
  1719. int nIndex = GetPaneIndexFromID(nPaneID);
  1720. if(nIndex == -1)
  1721. return FALSE;
  1722. int nLength = this->GetText(nIndex, strText, pnType);
  1723. if(pcchLength != NULL)
  1724. *pcchLength = nLength;
  1725. return TRUE;
  1726. }
  1727. #endif // __ATLSTR_H__
  1728. BOOL SetPaneText(int nPaneID, LPCTSTR lpstrText, int nType = 0)
  1729. {
  1730. ATLASSERT(::IsWindow(this->m_hWnd));
  1731. int nIndex = GetPaneIndexFromID(nPaneID);
  1732. if(nIndex == -1)
  1733. return FALSE;
  1734. return this->SetText(nIndex, lpstrText, nType);
  1735. }
  1736. BOOL GetPaneRect(int nPaneID, LPRECT lpRect) const
  1737. {
  1738. ATLASSERT(::IsWindow(this->m_hWnd));
  1739. int nIndex = GetPaneIndexFromID(nPaneID);
  1740. if(nIndex == -1)
  1741. return FALSE;
  1742. return this->GetRect(nIndex, lpRect);
  1743. }
  1744. BOOL SetPaneWidth(int nPaneID, int cxWidth)
  1745. {
  1746. ATLASSERT(::IsWindow(this->m_hWnd));
  1747. ATLASSERT(nPaneID != ID_DEFAULT_PANE); // Can't resize this one
  1748. int nIndex = GetPaneIndexFromID(nPaneID);
  1749. if(nIndex == -1)
  1750. return FALSE;
  1751. // get pane positions
  1752. ATL::CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
  1753. int* pPanesPos = buff.Allocate(m_nPanes);
  1754. if(pPanesPos == NULL)
  1755. return FALSE;
  1756. this->GetParts(m_nPanes, pPanesPos);
  1757. // calculate offset
  1758. int cxPaneWidth = pPanesPos[nIndex] - ((nIndex == 0) ? 0 : pPanesPos[nIndex - 1]);
  1759. int cxOff = cxWidth - cxPaneWidth;
  1760. // find variable width pane
  1761. int nDef = m_nPanes;
  1762. for(int i = 0; i < m_nPanes; i++)
  1763. {
  1764. if(m_pPane[i] == ID_DEFAULT_PANE)
  1765. {
  1766. nDef = i;
  1767. break;
  1768. }
  1769. }
  1770. // resize
  1771. if(nIndex < nDef) // before default pane
  1772. {
  1773. for(int i = nIndex; i < nDef; i++)
  1774. pPanesPos[i] += cxOff;
  1775. }
  1776. else // after default one
  1777. {
  1778. for(int i = nDef; i < nIndex; i++)
  1779. pPanesPos[i] -= cxOff;
  1780. }
  1781. // set pane postions
  1782. return this->SetParts(m_nPanes, pPanesPos);
  1783. }
  1784. BOOL GetPaneTipText(int nPaneID, LPTSTR lpstrText, int nSize) const
  1785. {
  1786. ATLASSERT(::IsWindow(this->m_hWnd));
  1787. int nIndex = GetPaneIndexFromID(nPaneID);
  1788. if(nIndex == -1)
  1789. return FALSE;
  1790. this->GetTipText(nIndex, lpstrText, nSize);
  1791. return TRUE;
  1792. }
  1793. BOOL SetPaneTipText(int nPaneID, LPCTSTR lpstrText)
  1794. {
  1795. ATLASSERT(::IsWindow(this->m_hWnd));
  1796. int nIndex = GetPaneIndexFromID(nPaneID);
  1797. if(nIndex == -1)
  1798. return FALSE;
  1799. this->SetTipText(nIndex, lpstrText);
  1800. return TRUE;
  1801. }
  1802. BOOL GetPaneIcon(int nPaneID, HICON& hIcon) const
  1803. {
  1804. ATLASSERT(::IsWindow(this->m_hWnd));
  1805. int nIndex = GetPaneIndexFromID(nPaneID);
  1806. if(nIndex == -1)
  1807. return FALSE;
  1808. hIcon = this->GetIcon(nIndex);
  1809. return TRUE;
  1810. }
  1811. BOOL SetPaneIcon(int nPaneID, HICON hIcon)
  1812. {
  1813. ATLASSERT(::IsWindow(this->m_hWnd));
  1814. int nIndex = GetPaneIndexFromID(nPaneID);
  1815. if(nIndex == -1)
  1816. return FALSE;
  1817. return this->SetIcon(nIndex, hIcon);
  1818. }
  1819. // Message map and handlers
  1820. BEGIN_MSG_MAP(CMultiPaneStatusBarCtrlImpl< T >)
  1821. MESSAGE_HANDLER(WM_SIZE, OnSize)
  1822. END_MSG_MAP()
  1823. LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  1824. {
  1825. LRESULT lRet = this->DefWindowProc(uMsg, wParam, lParam);
  1826. if((wParam != SIZE_MINIMIZED) && (m_nPanes > 0))
  1827. {
  1828. T* pT = static_cast<T*>(this);
  1829. pT->UpdatePanesLayout();
  1830. }
  1831. return lRet;
  1832. }
  1833. // Implementation
  1834. BOOL UpdatePanesLayout()
  1835. {
  1836. // get pane positions
  1837. ATL::CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
  1838. int* pPanesPos = buff.Allocate(m_nPanes);
  1839. ATLASSERT(pPanesPos != NULL);
  1840. if(pPanesPos == NULL)
  1841. return FALSE;
  1842. int nRet = this->GetParts(m_nPanes, pPanesPos);
  1843. ATLASSERT(nRet == m_nPanes);
  1844. if(nRet != m_nPanes)
  1845. return FALSE;
  1846. // calculate offset
  1847. RECT rcClient = {};
  1848. this->GetClientRect(&rcClient);
  1849. int cxOff = rcClient.right - pPanesPos[m_nPanes - 1];
  1850. // Move panes left if size grip box is present
  1851. if((this->GetStyle() & SBARS_SIZEGRIP) != 0)
  1852. cxOff -= ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE);
  1853. // find variable width pane
  1854. int i;
  1855. for(i = 0; i < m_nPanes; i++)
  1856. {
  1857. if(m_pPane[i] == ID_DEFAULT_PANE)
  1858. break;
  1859. }
  1860. // resize all panes from the variable one to the right
  1861. if((i < m_nPanes) && (pPanesPos[i] + cxOff) > ((i == 0) ? 0 : pPanesPos[i - 1]))
  1862. {
  1863. for(; i < m_nPanes; i++)
  1864. pPanesPos[i] += cxOff;
  1865. }
  1866. // set pane postions
  1867. return this->SetParts(m_nPanes, pPanesPos);
  1868. }
  1869. int GetPaneIndexFromID(int nPaneID) const
  1870. {
  1871. for(int i = 0; i < m_nPanes; i++)
  1872. {
  1873. if(m_pPane[i] == nPaneID)
  1874. return i;
  1875. }
  1876. return -1; // not found
  1877. }
  1878. };
  1879. class CMultiPaneStatusBarCtrl : public CMultiPaneStatusBarCtrlImpl<CMultiPaneStatusBarCtrl>
  1880. {
  1881. public:
  1882. DECLARE_WND_SUPERCLASS(_T("WTL_MultiPaneStatusBar"), GetWndClassName())
  1883. };
  1884. ///////////////////////////////////////////////////////////////////////////////
  1885. // CPaneContainer - provides header with title and close button for panes
  1886. // pane container extended styles
  1887. #define PANECNT_NOCLOSEBUTTON 0x00000001
  1888. #define PANECNT_VERTICAL 0x00000002
  1889. #define PANECNT_FLATBORDER 0x00000004
  1890. #define PANECNT_NOBORDER 0x00000008
  1891. #define PANECNT_DIVIDER 0x00000010
  1892. #define PANECNT_GRADIENT 0x00000020
  1893. template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
  1894. class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T >
  1895. {
  1896. public:
  1897. DECLARE_WND_CLASS_EX2(NULL, T, 0, -1)
  1898. // Constants
  1899. enum
  1900. {
  1901. m_cxyBorder = 2,
  1902. m_cxyTextOffset = 4,
  1903. m_cxyBtnOffset = 1,
  1904. m_cchTitle = 80,
  1905. m_cxImageTB = 13,
  1906. m_cyImageTB = 11,
  1907. m_cxyBtnAddTB = 7,
  1908. m_cxToolBar = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + m_cxyBtnOffset,
  1909. m_xBtnImageLeft = 6,
  1910. m_yBtnImageTop = 5,
  1911. m_xBtnImageRight = 12,
  1912. m_yBtnImageBottom = 11,
  1913. m_nCloseBtnID = ID_PANE_CLOSE
  1914. };
  1915. // Data members
  1916. CToolBarCtrl m_tb;
  1917. ATL::CWindow m_wndClient;
  1918. int m_cxyHeader;
  1919. TCHAR m_szTitle[m_cchTitle];
  1920. DWORD m_dwExtendedStyle; // Pane container specific extended styles
  1921. HFONT m_hFont;
  1922. bool m_bInternalFont;
  1923. // Constructor
  1924. CPaneContainerImpl() : m_cxyHeader(0), m_dwExtendedStyle(0), m_hFont(NULL), m_bInternalFont(false)
  1925. {
  1926. m_szTitle[0] = 0;
  1927. }
  1928. // Attributes
  1929. DWORD GetPaneContainerExtendedStyle() const
  1930. {
  1931. return m_dwExtendedStyle;
  1932. }
  1933. DWORD SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
  1934. {
  1935. DWORD dwPrevStyle = m_dwExtendedStyle;
  1936. if(dwMask == 0)
  1937. m_dwExtendedStyle = dwExtendedStyle;
  1938. else
  1939. m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
  1940. if(this->m_hWnd != NULL)
  1941. {
  1942. T* pT = static_cast<T*>(this);
  1943. bool bUpdate = false;
  1944. if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) != 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button
  1945. {
  1946. pT->CreateCloseButton();
  1947. bUpdate = true;
  1948. }
  1949. else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button
  1950. {
  1951. pT->DestroyCloseButton();
  1952. bUpdate = true;
  1953. }
  1954. if((dwPrevStyle & PANECNT_VERTICAL) != (m_dwExtendedStyle & PANECNT_VERTICAL)) // change orientation
  1955. {
  1956. pT->CalcSize();
  1957. bUpdate = true;
  1958. }
  1959. if((dwPrevStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER)) !=
  1960. (m_dwExtendedStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER))) // change border
  1961. {
  1962. bUpdate = true;
  1963. }
  1964. if((dwPrevStyle & PANECNT_GRADIENT) != (m_dwExtendedStyle & PANECNT_GRADIENT)) // change background
  1965. {
  1966. bUpdate = true;
  1967. }
  1968. if(bUpdate)
  1969. pT->UpdateLayout();
  1970. }
  1971. return dwPrevStyle;
  1972. }
  1973. HWND GetClient() const
  1974. {
  1975. return m_wndClient;
  1976. }
  1977. HWND SetClient(HWND hWndClient)
  1978. {
  1979. HWND hWndOldClient = m_wndClient;
  1980. m_wndClient = hWndClient;
  1981. if(this->m_hWnd != NULL)
  1982. {
  1983. T* pT = static_cast<T*>(this);
  1984. pT->UpdateLayout();
  1985. }
  1986. return hWndOldClient;
  1987. }
  1988. BOOL GetTitle(LPTSTR lpstrTitle, int cchLength) const
  1989. {
  1990. ATLASSERT(lpstrTitle != NULL);
  1991. errno_t nRet = ATL::Checked::tcsncpy_s(lpstrTitle, cchLength, m_szTitle, _TRUNCATE);
  1992. return ((nRet == 0) || (nRet == STRUNCATE));
  1993. }
  1994. BOOL SetTitle(LPCTSTR lpstrTitle)
  1995. {
  1996. ATLASSERT(lpstrTitle != NULL);
  1997. errno_t nRet = ATL::Checked::tcsncpy_s(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
  1998. bool bRet = ((nRet == 0) || (nRet == STRUNCATE));
  1999. if(bRet && (this->m_hWnd != NULL))
  2000. {
  2001. T* pT = static_cast<T*>(this);
  2002. pT->UpdateLayout();
  2003. }
  2004. return bRet;
  2005. }
  2006. int GetTitleLength() const
  2007. {
  2008. return lstrlen(m_szTitle);
  2009. }
  2010. // Methods
  2011. HWND Create(HWND hWndParent, LPCTSTR lpstrTitle = NULL, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
  2012. DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
  2013. {
  2014. if(lpstrTitle != NULL)
  2015. ATL::Checked::tcsncpy_s(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
  2016. return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, this->rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
  2017. }
  2018. HWND Create(HWND hWndParent, UINT uTitleID, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
  2019. DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
  2020. {
  2021. if(uTitleID != 0U)
  2022. ::LoadString(ModuleHelper::GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle);
  2023. return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, this->rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
  2024. }
  2025. BOOL SubclassWindow(HWND hWnd)
  2026. {
  2027. BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
  2028. if(bRet != FALSE)
  2029. {
  2030. T* pT = static_cast<T*>(this);
  2031. pT->Init();
  2032. RECT rect = {};
  2033. this->GetClientRect(&rect);
  2034. pT->UpdateLayout(rect.right, rect.bottom);
  2035. }
  2036. return bRet;
  2037. }
  2038. BOOL EnableCloseButton(BOOL bEnable)
  2039. {
  2040. ATLASSERT(::IsWindow(this->m_hWnd));
  2041. T* pT = static_cast<T*>(this);
  2042. (void)pT; // avoid level 4 warning
  2043. return (m_tb.m_hWnd != NULL) ? m_tb.EnableButton(pT->m_nCloseBtnID, bEnable) : FALSE;
  2044. }
  2045. void UpdateLayout()
  2046. {
  2047. RECT rcClient = {};
  2048. this->GetClientRect(&rcClient);
  2049. T* pT = static_cast<T*>(this);
  2050. pT->UpdateLayout(rcClient.right, rcClient.bottom);
  2051. }
  2052. // Message map and handlers
  2053. BEGIN_MSG_MAP(CPaneContainerImpl)
  2054. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  2055. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  2056. MESSAGE_HANDLER(WM_SIZE, OnSize)
  2057. MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
  2058. MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
  2059. MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
  2060. MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
  2061. MESSAGE_HANDLER(WM_PAINT, OnPaint)
  2062. MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
  2063. MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
  2064. MESSAGE_HANDLER(WM_COMMAND, OnCommand)
  2065. FORWARD_NOTIFICATIONS()
  2066. END_MSG_MAP()
  2067. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2068. {
  2069. T* pT = static_cast<T*>(this);
  2070. pT->Init();
  2071. return 0;
  2072. }
  2073. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2074. {
  2075. if(m_bInternalFont)
  2076. {
  2077. ::DeleteObject(m_hFont);
  2078. m_hFont = NULL;
  2079. m_bInternalFont = false;
  2080. }
  2081. return 0;
  2082. }
  2083. LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  2084. {
  2085. T* pT = static_cast<T*>(this);
  2086. pT->UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2087. return 0;
  2088. }
  2089. LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2090. {
  2091. if(m_wndClient.m_hWnd != NULL)
  2092. m_wndClient.SetFocus();
  2093. return 0;
  2094. }
  2095. LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2096. {
  2097. return (LRESULT)m_hFont;
  2098. }
  2099. LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  2100. {
  2101. if(m_bInternalFont)
  2102. {
  2103. ::DeleteObject(m_hFont);
  2104. m_bInternalFont = false;
  2105. }
  2106. m_hFont = (HFONT)wParam;
  2107. T* pT = static_cast<T*>(this);
  2108. pT->CalcSize();
  2109. if((BOOL)lParam != FALSE)
  2110. pT->UpdateLayout();
  2111. return 0;
  2112. }
  2113. LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2114. {
  2115. T* pT = static_cast<T*>(this);
  2116. pT->DrawPaneTitleBackground((HDC)wParam);
  2117. return 1;
  2118. }
  2119. LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2120. {
  2121. T* pT = static_cast<T*>(this);
  2122. if(wParam != NULL)
  2123. {
  2124. pT->DrawPaneTitle((HDC)wParam);
  2125. if(m_wndClient.m_hWnd == NULL) // no client window
  2126. pT->DrawPane((HDC)wParam);
  2127. }
  2128. else
  2129. {
  2130. CPaintDC dc(this->m_hWnd);
  2131. pT->DrawPaneTitle(dc.m_hDC);
  2132. if(m_wndClient.m_hWnd == NULL) // no client window
  2133. pT->DrawPane(dc.m_hDC);
  2134. }
  2135. return 0;
  2136. }
  2137. LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  2138. {
  2139. if(m_tb.m_hWnd == NULL)
  2140. {
  2141. bHandled = FALSE;
  2142. return 1;
  2143. }
  2144. T* pT = static_cast<T*>(this);
  2145. (void)pT; // avoid level 4 warning
  2146. LPNMHDR lpnmh = (LPNMHDR)lParam;
  2147. LRESULT lRet = 0;
  2148. // pass toolbar custom draw notifications to the base class
  2149. if((lpnmh->code == NM_CUSTOMDRAW) && (lpnmh->hwndFrom == m_tb.m_hWnd))
  2150. lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);
  2151. // tooltip notifications come with the tooltip window handle and button ID,
  2152. // pass them to the parent if we don't handle them
  2153. else if((lpnmh->code == TTN_GETDISPINFO) && (lpnmh->idFrom == pT->m_nCloseBtnID))
  2154. bHandled = pT->GetToolTipText(lpnmh);
  2155. // only let notifications not from the toolbar go to the parent
  2156. else if((lpnmh->hwndFrom != m_tb.m_hWnd) && (lpnmh->idFrom != pT->m_nCloseBtnID))
  2157. bHandled = FALSE;
  2158. return lRet;
  2159. }
  2160. LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  2161. {
  2162. // if command comes from the close button, substitute HWND of the pane container instead
  2163. if((m_tb.m_hWnd != NULL) && ((HWND)lParam == m_tb.m_hWnd))
  2164. return this->GetParent().SendMessage(WM_COMMAND, wParam, (LPARAM)this->m_hWnd);
  2165. bHandled = FALSE;
  2166. return 1;
  2167. }
  2168. // Custom draw overrides
  2169. DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
  2170. {
  2171. return CDRF_NOTIFYITEMDRAW; // we need per-item notifications
  2172. }
  2173. DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
  2174. {
  2175. return CDRF_NOTIFYPOSTPAINT;
  2176. }
  2177. DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
  2178. {
  2179. CDCHandle dc = lpNMCustomDraw->hdc;
  2180. RECT& rc = lpNMCustomDraw->rc;
  2181. RECT rcImage = { m_xBtnImageLeft, m_yBtnImageTop, m_xBtnImageRight + 1, m_yBtnImageBottom + 1 };
  2182. ::OffsetRect(&rcImage, rc.left, rc.top);
  2183. T* pT = static_cast<T*>(this);
  2184. if((lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0)
  2185. {
  2186. RECT rcShadow = rcImage;
  2187. ::OffsetRect(&rcShadow, 1, 1);
  2188. CPen pen1;
  2189. pen1.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DHILIGHT));
  2190. pT->DrawButtonImage(dc, rcShadow, pen1);
  2191. CPen pen2;
  2192. pen2.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DSHADOW));
  2193. pT->DrawButtonImage(dc, rcImage, pen2);
  2194. }
  2195. else
  2196. {
  2197. if((lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0)
  2198. ::OffsetRect(&rcImage, 1, 1);
  2199. CPen pen;
  2200. pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
  2201. pT->DrawButtonImage(dc, rcImage, pen);
  2202. }
  2203. return CDRF_DODEFAULT; // continue with the default item painting
  2204. }
  2205. // Implementation - overrideable methods
  2206. void Init()
  2207. {
  2208. if(m_hFont == NULL)
  2209. {
  2210. // The same as AtlCreateControlFont() for horizontal pane
  2211. LOGFONT lf = {};
  2212. ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE);
  2213. if(IsVertical())
  2214. lf.lfEscapement = 900; // 90 degrees
  2215. m_hFont = ::CreateFontIndirect(&lf);
  2216. m_bInternalFont = true;
  2217. }
  2218. T* pT = static_cast<T*>(this);
  2219. pT->CalcSize();
  2220. if((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)
  2221. pT->CreateCloseButton();
  2222. }
  2223. void UpdateLayout(int cxWidth, int cyHeight)
  2224. {
  2225. ATLASSERT(::IsWindow(this->m_hWnd));
  2226. RECT rect = {};
  2227. if(IsVertical())
  2228. {
  2229. ::SetRect(&rect, 0, 0, m_cxyHeader, cyHeight);
  2230. if(m_tb.m_hWnd != NULL)
  2231. m_tb.SetWindowPos(NULL, m_cxyBorder, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  2232. if(m_wndClient.m_hWnd != NULL)
  2233. m_wndClient.SetWindowPos(NULL, m_cxyHeader, 0, cxWidth - m_cxyHeader, cyHeight, SWP_NOZORDER);
  2234. else
  2235. rect.right = cxWidth;
  2236. }
  2237. else
  2238. {
  2239. ::SetRect(&rect, 0, 0, cxWidth, m_cxyHeader);
  2240. if(m_tb.m_hWnd != NULL)
  2241. m_tb.SetWindowPos(NULL, rect.right - m_cxToolBar, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  2242. if(m_wndClient.m_hWnd != NULL)
  2243. m_wndClient.SetWindowPos(NULL, 0, m_cxyHeader, cxWidth, cyHeight - m_cxyHeader, SWP_NOZORDER);
  2244. else
  2245. rect.bottom = cyHeight;
  2246. }
  2247. this->InvalidateRect(&rect);
  2248. }
  2249. void CreateCloseButton()
  2250. {
  2251. ATLASSERT(m_tb.m_hWnd == NULL);
  2252. // create toolbar for the "x" button
  2253. m_tb.Create(this->m_hWnd, this->rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NOMOVEY | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT, 0);
  2254. ATLASSERT(m_tb.IsWindow());
  2255. if(m_tb.m_hWnd != NULL)
  2256. {
  2257. T* pT = static_cast<T*>(this);
  2258. (void)pT; // avoid level 4 warning
  2259. m_tb.SetButtonStructSize();
  2260. TBBUTTON tbbtn = {};
  2261. tbbtn.idCommand = pT->m_nCloseBtnID;
  2262. tbbtn.fsState = TBSTATE_ENABLED;
  2263. tbbtn.fsStyle = BTNS_BUTTON;
  2264. m_tb.AddButtons(1, &tbbtn);
  2265. m_tb.SetBitmapSize(m_cxImageTB, m_cyImageTB);
  2266. m_tb.SetButtonSize(m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB);
  2267. if(IsVertical())
  2268. m_tb.SetWindowPos(NULL, m_cxyBorder + m_cxyBtnOffset, m_cxyBorder + m_cxyBtnOffset, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB + 1, SWP_NOZORDER | SWP_NOACTIVATE);
  2269. else
  2270. m_tb.SetWindowPos(NULL, 0, 0, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB + 1, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  2271. }
  2272. }
  2273. void DestroyCloseButton()
  2274. {
  2275. if(m_tb.m_hWnd != NULL)
  2276. m_tb.DestroyWindow();
  2277. }
  2278. void CalcSize()
  2279. {
  2280. T* pT = static_cast<T*>(this);
  2281. CFontHandle font = pT->GetTitleFont();
  2282. if(font.IsNull())
  2283. font = (HFONT)::GetStockObject(SYSTEM_FONT);
  2284. LOGFONT lf = {};
  2285. font.GetLogFont(lf);
  2286. if(IsVertical())
  2287. {
  2288. m_cxyHeader = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + 1;
  2289. }
  2290. else
  2291. {
  2292. int cyFont = abs(lf.lfHeight) + m_cxyBorder + 2 * m_cxyTextOffset;
  2293. int cyBtn = m_cyImageTB + m_cxyBtnAddTB + m_cxyBorder + 2 * m_cxyBtnOffset + 1;
  2294. m_cxyHeader = __max(cyFont, cyBtn);
  2295. }
  2296. }
  2297. HFONT GetTitleFont() const
  2298. {
  2299. return m_hFont;
  2300. }
  2301. BOOL GetToolTipText(LPNMHDR /*lpnmh*/)
  2302. {
  2303. return FALSE;
  2304. }
  2305. void DrawPaneTitle(CDCHandle dc)
  2306. {
  2307. RECT rect = {};
  2308. this->GetClientRect(&rect);
  2309. UINT uBorder = BF_LEFT | BF_TOP | BF_ADJUST;
  2310. if(IsVertical())
  2311. {
  2312. rect.right = rect.left + m_cxyHeader;
  2313. uBorder |= BF_BOTTOM;
  2314. }
  2315. else
  2316. {
  2317. rect.bottom = rect.top + m_cxyHeader;
  2318. uBorder |= BF_RIGHT;
  2319. }
  2320. if((m_dwExtendedStyle & PANECNT_NOBORDER) == 0)
  2321. {
  2322. if((m_dwExtendedStyle & PANECNT_FLATBORDER) != 0)
  2323. uBorder |= BF_FLAT;
  2324. dc.DrawEdge(&rect, EDGE_ETCHED, uBorder);
  2325. }
  2326. if((m_dwExtendedStyle & PANECNT_DIVIDER) != 0)
  2327. {
  2328. uBorder = BF_FLAT | BF_ADJUST | (IsVertical() ? BF_RIGHT : BF_BOTTOM);
  2329. dc.DrawEdge(&rect, BDR_SUNKENOUTER, uBorder);
  2330. }
  2331. // draw title text
  2332. dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
  2333. dc.SetBkMode(TRANSPARENT);
  2334. T* pT = static_cast<T*>(this);
  2335. HFONT hFontOld = dc.SelectFont(pT->GetTitleFont());
  2336. if(IsVertical())
  2337. {
  2338. rect.top += m_cxyTextOffset;
  2339. rect.bottom -= m_cxyTextOffset;
  2340. if(m_tb.m_hWnd != NULL)
  2341. rect.top += m_cxToolBar;;
  2342. RECT rcCalc = { rect.left, rect.bottom, rect.right, rect.top };
  2343. int cxFont = dc.DrawText(m_szTitle, -1, &rcCalc, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT);
  2344. RECT rcText = {};
  2345. rcText.left = (rect.right - rect.left - cxFont) / 2;
  2346. rcText.right = rcText.left + (rect.bottom - rect.top);
  2347. rcText.top = rect.bottom;
  2348. rcText.bottom = rect.top;
  2349. dc.DrawText(m_szTitle, -1, &rcText, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS);
  2350. }
  2351. else
  2352. {
  2353. rect.left += m_cxyTextOffset;
  2354. rect.right -= m_cxyTextOffset;
  2355. if(m_tb.m_hWnd != NULL)
  2356. rect.right -= m_cxToolBar;;
  2357. dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
  2358. }
  2359. dc.SelectFont(hFontOld);
  2360. }
  2361. void DrawPaneTitleBackground(CDCHandle dc)
  2362. {
  2363. RECT rect = {};
  2364. this->GetClientRect(&rect);
  2365. if(IsVertical())
  2366. rect.right = m_cxyHeader;
  2367. else
  2368. rect.bottom = m_cxyHeader;
  2369. if((m_dwExtendedStyle & PANECNT_GRADIENT) != 0)
  2370. dc.GradientFillRect(rect, ::GetSysColor(COLOR_WINDOW), ::GetSysColor(COLOR_3DFACE), IsVertical());
  2371. else
  2372. dc.FillRect(&rect, COLOR_3DFACE);
  2373. }
  2374. // called only if pane is empty
  2375. void DrawPane(CDCHandle dc)
  2376. {
  2377. RECT rect = {};
  2378. this->GetClientRect(&rect);
  2379. if(IsVertical())
  2380. rect.left += m_cxyHeader;
  2381. else
  2382. rect.top += m_cxyHeader;
  2383. if((this->GetExStyle() & WS_EX_CLIENTEDGE) == 0)
  2384. dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
  2385. dc.FillRect(&rect, COLOR_APPWORKSPACE);
  2386. }
  2387. // drawing helper - draws "x" button image
  2388. void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen)
  2389. {
  2390. HPEN hPenOld = dc.SelectPen(hPen);
  2391. dc.MoveTo(rcImage.left, rcImage.top);
  2392. dc.LineTo(rcImage.right, rcImage.bottom);
  2393. dc.MoveTo(rcImage.left + 1, rcImage.top);
  2394. dc.LineTo(rcImage.right + 1, rcImage.bottom);
  2395. dc.MoveTo(rcImage.left, rcImage.bottom - 1);
  2396. dc.LineTo(rcImage.right, rcImage.top - 1);
  2397. dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
  2398. dc.LineTo(rcImage.right + 1, rcImage.top - 1);
  2399. dc.SelectPen(hPenOld);
  2400. }
  2401. bool IsVertical() const
  2402. {
  2403. return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0);
  2404. }
  2405. };
  2406. class CPaneContainer : public CPaneContainerImpl<CPaneContainer>
  2407. {
  2408. public:
  2409. DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1)
  2410. };
  2411. ///////////////////////////////////////////////////////////////////////////////
  2412. // CSortListViewCtrl - implements sorting for a listview control
  2413. // sort listview extended styles
  2414. #define SORTLV_USESHELLBITMAPS 0x00000001
  2415. // Notification sent to parent when sort column is changed by user clicking header.
  2416. #define SLVN_SORTCHANGED LVN_LAST
  2417. // A LPNMSORTLISTVIEW is sent with the SLVN_SORTCHANGED notification
  2418. typedef struct tagNMSORTLISTVIEW
  2419. {
  2420. NMHDR hdr;
  2421. int iNewSortColumn;
  2422. int iOldSortColumn;
  2423. } NMSORTLISTVIEW, *LPNMSORTLISTVIEW;
  2424. // Column sort types. Can be set on a per-column basis with the SetColumnSortType method.
  2425. enum
  2426. {
  2427. LVCOLSORT_NONE,
  2428. LVCOLSORT_TEXT, // default
  2429. LVCOLSORT_TEXTNOCASE,
  2430. LVCOLSORT_LONG,
  2431. LVCOLSORT_DOUBLE,
  2432. LVCOLSORT_DECIMAL,
  2433. LVCOLSORT_DATETIME,
  2434. LVCOLSORT_DATE,
  2435. LVCOLSORT_TIME,
  2436. LVCOLSORT_CUSTOM,
  2437. LVCOLSORT_LAST = LVCOLSORT_CUSTOM
  2438. };
  2439. template <class T>
  2440. class CSortListViewImpl
  2441. {
  2442. public:
  2443. enum
  2444. {
  2445. m_cchCmpTextMax = 32, // overrideable
  2446. m_cxSortImage = 16,
  2447. m_cySortImage = 15,
  2448. m_cxSortArrow = 11,
  2449. m_cySortArrow = 6,
  2450. m_iSortUp = 0, // index of sort bitmaps
  2451. m_iSortDown = 1,
  2452. m_nShellSortUpID = 133
  2453. };
  2454. // passed to LVCompare functions as lParam1 and lParam2
  2455. struct LVCompareParam
  2456. {
  2457. int iItem;
  2458. DWORD_PTR dwItemData;
  2459. union
  2460. {
  2461. long lValue;
  2462. double dblValue;
  2463. DECIMAL decValue;
  2464. LPCTSTR pszValue;
  2465. };
  2466. };
  2467. // passed to LVCompare functions as the lParamSort parameter
  2468. struct LVSortInfo
  2469. {
  2470. T* pT;
  2471. int iSortCol;
  2472. bool bDescending;
  2473. };
  2474. bool m_bSortDescending;
  2475. bool m_bCommCtrl6;
  2476. int m_iSortColumn;
  2477. CBitmap m_bmSort[2];
  2478. int m_fmtOldSortCol;
  2479. HBITMAP m_hbmOldSortCol;
  2480. DWORD m_dwSortLVExtendedStyle;
  2481. ATL::CSimpleArray<WORD> m_arrColSortType;
  2482. bool m_bUseWaitCursor;
  2483. CSortListViewImpl() :
  2484. m_bSortDescending(false),
  2485. m_bCommCtrl6(false),
  2486. m_iSortColumn(-1),
  2487. m_fmtOldSortCol(0),
  2488. m_hbmOldSortCol(NULL),
  2489. m_dwSortLVExtendedStyle(SORTLV_USESHELLBITMAPS),
  2490. m_bUseWaitCursor(true)
  2491. {
  2492. DWORD dwMajor = 0;
  2493. DWORD dwMinor = 0;
  2494. HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
  2495. m_bCommCtrl6 = SUCCEEDED(hRet) && (dwMajor >= 6);
  2496. }
  2497. // Attributes
  2498. void SetSortColumn(int iCol)
  2499. {
  2500. T* pT = static_cast<T*>(this);
  2501. ATLASSERT(::IsWindow(pT->m_hWnd));
  2502. CHeaderCtrl header = pT->GetHeader();
  2503. ATLASSERT(header.m_hWnd != NULL);
  2504. ATLASSERT((iCol >= -1) && (iCol < m_arrColSortType.GetSize()));
  2505. int iOldSortCol = m_iSortColumn;
  2506. m_iSortColumn = iCol;
  2507. if(m_bCommCtrl6)
  2508. {
  2509. const int nMask = HDF_SORTUP | HDF_SORTDOWN;
  2510. HDITEM hditem = { HDI_FORMAT };
  2511. if((iOldSortCol != iCol) && (iOldSortCol >= 0) && header.GetItem(iOldSortCol, &hditem))
  2512. {
  2513. hditem.fmt &= ~nMask;
  2514. header.SetItem(iOldSortCol, &hditem);
  2515. }
  2516. if((iCol >= 0) && header.GetItem(iCol, &hditem))
  2517. {
  2518. hditem.fmt &= ~nMask;
  2519. hditem.fmt |= m_bSortDescending ? HDF_SORTDOWN : HDF_SORTUP;
  2520. header.SetItem(iCol, &hditem);
  2521. }
  2522. return;
  2523. }
  2524. if(m_bmSort[m_iSortUp].IsNull())
  2525. pT->CreateSortBitmaps();
  2526. // restore previous sort column's bitmap, if any, and format
  2527. HDITEM hditem = { HDI_BITMAP | HDI_FORMAT };
  2528. if((iOldSortCol != iCol) && (iOldSortCol >= 0))
  2529. {
  2530. hditem.hbm = m_hbmOldSortCol;
  2531. hditem.fmt = m_fmtOldSortCol;
  2532. header.SetItem(iOldSortCol, &hditem);
  2533. }
  2534. // save new sort column's bitmap and format, and add our sort bitmap
  2535. if((iCol >= 0) && header.GetItem(iCol, &hditem))
  2536. {
  2537. if(iOldSortCol != iCol)
  2538. {
  2539. m_fmtOldSortCol = hditem.fmt;
  2540. m_hbmOldSortCol = hditem.hbm;
  2541. }
  2542. hditem.fmt &= ~HDF_IMAGE;
  2543. hditem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
  2544. int i = m_bSortDescending ? m_iSortDown : m_iSortUp;
  2545. hditem.hbm = m_bmSort[i];
  2546. header.SetItem(iCol, &hditem);
  2547. }
  2548. }
  2549. int GetSortColumn() const
  2550. {
  2551. return m_iSortColumn;
  2552. }
  2553. void SetColumnSortType(int iCol, WORD wType)
  2554. {
  2555. ATLASSERT((iCol >= 0) && (iCol < m_arrColSortType.GetSize()));
  2556. ATLASSERT((wType >= LVCOLSORT_NONE) && (wType <= LVCOLSORT_LAST));
  2557. m_arrColSortType[iCol] = wType;
  2558. }
  2559. WORD GetColumnSortType(int iCol) const
  2560. {
  2561. ATLASSERT((iCol >= 0) && (iCol < m_arrColSortType.GetSize()));
  2562. return m_arrColSortType[iCol];
  2563. }
  2564. int GetColumnCount() const
  2565. {
  2566. const T* pT = static_cast<const T*>(this);
  2567. ATLASSERT(::IsWindow(pT->m_hWnd));
  2568. CHeaderCtrl header = pT->GetHeader();
  2569. return header.m_hWnd != NULL ? header.GetItemCount() : 0;
  2570. }
  2571. bool IsSortDescending() const
  2572. {
  2573. return m_bSortDescending;
  2574. }
  2575. DWORD GetSortListViewExtendedStyle() const
  2576. {
  2577. return m_dwSortLVExtendedStyle;
  2578. }
  2579. DWORD SetSortListViewExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
  2580. {
  2581. DWORD dwPrevStyle = m_dwSortLVExtendedStyle;
  2582. if(dwMask == 0)
  2583. m_dwSortLVExtendedStyle = dwExtendedStyle;
  2584. else
  2585. m_dwSortLVExtendedStyle = (m_dwSortLVExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
  2586. return dwPrevStyle;
  2587. }
  2588. // Operations
  2589. bool DoSortItems(int iCol, bool bDescending = false)
  2590. {
  2591. T* pT = static_cast<T*>(this);
  2592. ATLASSERT(::IsWindow(pT->m_hWnd));
  2593. ATLASSERT((iCol >= 0) && (iCol < m_arrColSortType.GetSize()));
  2594. WORD wType = m_arrColSortType[iCol];
  2595. if(wType == LVCOLSORT_NONE)
  2596. return false;
  2597. int nCount = pT->GetItemCount();
  2598. if(nCount < 2)
  2599. {
  2600. m_bSortDescending = bDescending;
  2601. SetSortColumn(iCol);
  2602. return true;
  2603. }
  2604. CWaitCursor waitCursor(false);
  2605. if(m_bUseWaitCursor)
  2606. waitCursor.Set();
  2607. LVCompareParam* pParam = NULL;
  2608. ATLTRY(pParam = new LVCompareParam[nCount]);
  2609. PFNLVCOMPARE pFunc = NULL;
  2610. TCHAR pszTemp[pT->m_cchCmpTextMax] = {};
  2611. bool bStrValue = false;
  2612. switch(wType)
  2613. {
  2614. case LVCOLSORT_TEXT:
  2615. pFunc = (PFNLVCOMPARE)pT->LVCompareText;
  2616. case LVCOLSORT_TEXTNOCASE:
  2617. if(pFunc == NULL)
  2618. pFunc = (PFNLVCOMPARE)pT->LVCompareTextNoCase;
  2619. case LVCOLSORT_CUSTOM:
  2620. {
  2621. if(pFunc == NULL)
  2622. pFunc = (PFNLVCOMPARE)pT->LVCompareCustom;
  2623. for(int i = 0; i < nCount; i++)
  2624. {
  2625. pParam[i].iItem = i;
  2626. pParam[i].dwItemData = pT->GetItemData(i);
  2627. pParam[i].pszValue = new TCHAR[pT->m_cchCmpTextMax];
  2628. pT->GetItemText(i, iCol, (LPTSTR)pParam[i].pszValue, pT->m_cchCmpTextMax);
  2629. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2630. }
  2631. bStrValue = true;
  2632. }
  2633. break;
  2634. case LVCOLSORT_LONG:
  2635. {
  2636. pFunc = (PFNLVCOMPARE)pT->LVCompareLong;
  2637. for(int i = 0; i < nCount; i++)
  2638. {
  2639. pParam[i].iItem = i;
  2640. pParam[i].dwItemData = pT->GetItemData(i);
  2641. pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
  2642. pParam[i].lValue = pT->StrToLong(pszTemp);
  2643. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2644. }
  2645. }
  2646. break;
  2647. case LVCOLSORT_DOUBLE:
  2648. {
  2649. pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
  2650. for(int i = 0; i < nCount; i++)
  2651. {
  2652. pParam[i].iItem = i;
  2653. pParam[i].dwItemData = pT->GetItemData(i);
  2654. pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
  2655. pParam[i].dblValue = pT->StrToDouble(pszTemp);
  2656. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2657. }
  2658. }
  2659. break;
  2660. case LVCOLSORT_DECIMAL:
  2661. {
  2662. pFunc = (PFNLVCOMPARE)pT->LVCompareDecimal;
  2663. for(int i = 0; i < nCount; i++)
  2664. {
  2665. pParam[i].iItem = i;
  2666. pParam[i].dwItemData = pT->GetItemData(i);
  2667. pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
  2668. pT->StrToDecimal(pszTemp, &pParam[i].decValue);
  2669. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2670. }
  2671. }
  2672. break;
  2673. case LVCOLSORT_DATETIME:
  2674. case LVCOLSORT_DATE:
  2675. case LVCOLSORT_TIME:
  2676. {
  2677. pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
  2678. DWORD dwFlags = LOCALE_NOUSEROVERRIDE;
  2679. if(wType == LVCOLSORT_DATE)
  2680. dwFlags |= VAR_DATEVALUEONLY;
  2681. else if(wType == LVCOLSORT_TIME)
  2682. dwFlags |= VAR_TIMEVALUEONLY;
  2683. for(int i = 0; i < nCount; i++)
  2684. {
  2685. pParam[i].iItem = i;
  2686. pParam[i].dwItemData = pT->GetItemData(i);
  2687. pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
  2688. pParam[i].dblValue = pT->DateStrToDouble(pszTemp, dwFlags);
  2689. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2690. }
  2691. }
  2692. break;
  2693. default:
  2694. ATLTRACE2(atlTraceUI, 0, _T("Unknown value for sort type in CSortListViewImpl::DoSortItems()\n"));
  2695. break;
  2696. } // switch(wType)
  2697. ATLASSERT(pFunc != NULL);
  2698. LVSortInfo lvsi = { pT, iCol, bDescending };
  2699. bool bRet = ((BOOL)pT->DefWindowProc(LVM_SORTITEMS, (WPARAM)&lvsi, (LPARAM)pFunc) != FALSE);
  2700. for(int i = 0; i < nCount; i++)
  2701. {
  2702. DWORD_PTR dwItemData = pT->GetItemData(i);
  2703. LVCompareParam* p = (LVCompareParam*)dwItemData;
  2704. ATLASSERT(p != NULL);
  2705. if(bStrValue)
  2706. delete [] (TCHAR*)p->pszValue;
  2707. pT->SetItemData(i, p->dwItemData);
  2708. }
  2709. delete [] pParam;
  2710. if(bRet)
  2711. {
  2712. m_bSortDescending = bDescending;
  2713. SetSortColumn(iCol);
  2714. }
  2715. if(m_bUseWaitCursor)
  2716. waitCursor.Restore();
  2717. return bRet;
  2718. }
  2719. void CreateSortBitmaps()
  2720. {
  2721. if((m_dwSortLVExtendedStyle & SORTLV_USESHELLBITMAPS) != 0)
  2722. {
  2723. bool bFree = false;
  2724. LPCTSTR pszModule = _T("shell32.dll");
  2725. HINSTANCE hShell = ::GetModuleHandle(pszModule);
  2726. if (hShell == NULL)
  2727. {
  2728. hShell = ::LoadLibrary(pszModule);
  2729. bFree = true;
  2730. }
  2731. if (hShell != NULL)
  2732. {
  2733. bool bSuccess = true;
  2734. for(int i = m_iSortUp; i <= m_iSortDown; i++)
  2735. {
  2736. if(!m_bmSort[i].IsNull())
  2737. m_bmSort[i].DeleteObject();
  2738. m_bmSort[i] = (HBITMAP)::LoadImage(hShell, MAKEINTRESOURCE(m_nShellSortUpID + i),
  2739. IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
  2740. if(m_bmSort[i].IsNull())
  2741. {
  2742. bSuccess = false;
  2743. break;
  2744. }
  2745. }
  2746. if(bFree)
  2747. ::FreeLibrary(hShell);
  2748. if(bSuccess)
  2749. return;
  2750. }
  2751. }
  2752. T* pT = static_cast<T*>(this);
  2753. for(int i = m_iSortUp; i <= m_iSortDown; i++)
  2754. {
  2755. if(!m_bmSort[i].IsNull())
  2756. m_bmSort[i].DeleteObject();
  2757. CDC dcMem;
  2758. CClientDC dc(::GetDesktopWindow());
  2759. dcMem.CreateCompatibleDC(dc.m_hDC);
  2760. m_bmSort[i].CreateCompatibleBitmap(dc.m_hDC, m_cxSortImage, m_cySortImage);
  2761. HBITMAP hbmOld = dcMem.SelectBitmap(m_bmSort[i]);
  2762. RECT rc = { 0, 0, m_cxSortImage, m_cySortImage };
  2763. pT->DrawSortBitmap(dcMem.m_hDC, i, &rc);
  2764. dcMem.SelectBitmap(hbmOld);
  2765. dcMem.DeleteDC();
  2766. }
  2767. }
  2768. void NotifyParentSortChanged(int iNewSortCol, int iOldSortCol)
  2769. {
  2770. T* pT = static_cast<T*>(this);
  2771. int nID = pT->GetDlgCtrlID();
  2772. NMSORTLISTVIEW nm = { { pT->m_hWnd, (UINT_PTR)nID, SLVN_SORTCHANGED }, iNewSortCol, iOldSortCol };
  2773. ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nm);
  2774. }
  2775. // Overrideables
  2776. int CompareItemsCustom(LVCompareParam* /*pItem1*/, LVCompareParam* /*pItem2*/, int /*iSortCol*/)
  2777. {
  2778. // pItem1 and pItem2 contain valid iItem, dwItemData, and pszValue members.
  2779. // If item1 > item2 return 1, if item1 < item2 return -1, else return 0.
  2780. return 0;
  2781. }
  2782. void DrawSortBitmap(CDCHandle dc, int iBitmap, LPRECT prc)
  2783. {
  2784. dc.FillRect(prc, ::GetSysColorBrush(COLOR_BTNFACE));
  2785. HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW));
  2786. CPen pen;
  2787. pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNSHADOW));
  2788. HPEN hpenOld = dc.SelectPen(pen);
  2789. POINT ptOrg = { (m_cxSortImage - m_cxSortArrow) / 2, (m_cySortImage - m_cySortArrow) / 2 };
  2790. if(iBitmap == m_iSortUp)
  2791. {
  2792. POINT pts[3] =
  2793. {
  2794. { ptOrg.x + m_cxSortArrow / 2, ptOrg.y },
  2795. { ptOrg.x, ptOrg.y + m_cySortArrow - 1 },
  2796. { ptOrg.x + m_cxSortArrow - 1, ptOrg.y + m_cySortArrow - 1 }
  2797. };
  2798. dc.Polygon(pts, 3);
  2799. }
  2800. else
  2801. {
  2802. POINT pts[3] =
  2803. {
  2804. { ptOrg.x, ptOrg.y },
  2805. { ptOrg.x + m_cxSortArrow / 2, ptOrg.y + m_cySortArrow - 1 },
  2806. { ptOrg.x + m_cxSortArrow - 1, ptOrg.y }
  2807. };
  2808. dc.Polygon(pts, 3);
  2809. }
  2810. dc.SelectBrush(hbrOld);
  2811. dc.SelectPen(hpenOld);
  2812. }
  2813. double DateStrToDouble(LPCTSTR lpstr, DWORD dwFlags)
  2814. {
  2815. ATLASSERT(lpstr != NULL);
  2816. if((lpstr == NULL) || (lpstr[0] == _T('\0')))
  2817. return 0;
  2818. USES_CONVERSION;
  2819. HRESULT hRet = E_FAIL;
  2820. DATE dRet = 0;
  2821. if (FAILED(hRet = ::VarDateFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, dwFlags, &dRet)))
  2822. {
  2823. ATLTRACE2(atlTraceUI, 0, _T("VarDateFromStr failed with result of 0x%8.8X\n"), hRet);
  2824. dRet = 0;
  2825. }
  2826. return dRet;
  2827. }
  2828. long StrToLong(LPCTSTR lpstr)
  2829. {
  2830. ATLASSERT(lpstr != NULL);
  2831. if((lpstr == NULL) || (lpstr[0] == _T('\0')))
  2832. return 0;
  2833. USES_CONVERSION;
  2834. HRESULT hRet = E_FAIL;
  2835. long lRet = 0;
  2836. if (FAILED(hRet = ::VarI4FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &lRet)))
  2837. {
  2838. ATLTRACE2(atlTraceUI, 0, _T("VarI4FromStr failed with result of 0x%8.8X\n"), hRet);
  2839. lRet = 0;
  2840. }
  2841. return lRet;
  2842. }
  2843. double StrToDouble(LPCTSTR lpstr)
  2844. {
  2845. ATLASSERT(lpstr != NULL);
  2846. if((lpstr == NULL) || (lpstr[0] == _T('\0')))
  2847. return 0;
  2848. USES_CONVERSION;
  2849. HRESULT hRet = E_FAIL;
  2850. double dblRet = 0;
  2851. if (FAILED(hRet = ::VarR8FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &dblRet)))
  2852. {
  2853. ATLTRACE2(atlTraceUI, 0, _T("VarR8FromStr failed with result of 0x%8.8X\n"), hRet);
  2854. dblRet = 0;
  2855. }
  2856. return dblRet;
  2857. }
  2858. bool StrToDecimal(LPCTSTR lpstr, DECIMAL* pDecimal)
  2859. {
  2860. ATLASSERT(lpstr != NULL);
  2861. ATLASSERT(pDecimal != NULL);
  2862. if((lpstr == NULL) || (pDecimal == NULL))
  2863. return false;
  2864. USES_CONVERSION;
  2865. HRESULT hRet = E_FAIL;
  2866. if (FAILED(hRet = ::VarDecFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, pDecimal)))
  2867. {
  2868. ATLTRACE2(atlTraceUI, 0, _T("VarDecFromStr failed with result of 0x%8.8X\n"), hRet);
  2869. pDecimal->Lo64 = 0;
  2870. pDecimal->Hi32 = 0;
  2871. pDecimal->signscale = 0;
  2872. return false;
  2873. }
  2874. return true;
  2875. }
  2876. // Overrideable PFNLVCOMPARE functions
  2877. static int CALLBACK LVCompareText(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2878. {
  2879. ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
  2880. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2881. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2882. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2883. int nRet = lstrcmp(pParam1->pszValue, pParam2->pszValue);
  2884. return pInfo->bDescending ? -nRet : nRet;
  2885. }
  2886. static int CALLBACK LVCompareTextNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2887. {
  2888. ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
  2889. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2890. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2891. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2892. int nRet = lstrcmpi(pParam1->pszValue, pParam2->pszValue);
  2893. return pInfo->bDescending ? -nRet : nRet;
  2894. }
  2895. static int CALLBACK LVCompareLong(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2896. {
  2897. ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
  2898. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2899. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2900. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2901. int nRet = 0;
  2902. if(pParam1->lValue > pParam2->lValue)
  2903. nRet = 1;
  2904. else if(pParam1->lValue < pParam2->lValue)
  2905. nRet = -1;
  2906. return pInfo->bDescending ? -nRet : nRet;
  2907. }
  2908. static int CALLBACK LVCompareDouble(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2909. {
  2910. ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
  2911. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2912. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2913. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2914. int nRet = 0;
  2915. if(pParam1->dblValue > pParam2->dblValue)
  2916. nRet = 1;
  2917. else if(pParam1->dblValue < pParam2->dblValue)
  2918. nRet = -1;
  2919. return pInfo->bDescending ? -nRet : nRet;
  2920. }
  2921. static int CALLBACK LVCompareCustom(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2922. {
  2923. ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
  2924. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2925. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2926. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2927. int nRet = pInfo->pT->CompareItemsCustom(pParam1, pParam2, pInfo->iSortCol);
  2928. return pInfo->bDescending ? -nRet : nRet;
  2929. }
  2930. static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2931. {
  2932. ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
  2933. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2934. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2935. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2936. int nRet = (int)::VarDecCmp(&pParam1->decValue, &pParam2->decValue);
  2937. nRet--;
  2938. return pInfo->bDescending ? -nRet : nRet;
  2939. }
  2940. BEGIN_MSG_MAP(CSortListViewImpl)
  2941. MESSAGE_HANDLER(LVM_INSERTCOLUMN, OnInsertColumn)
  2942. MESSAGE_HANDLER(LVM_DELETECOLUMN, OnDeleteColumn)
  2943. NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, OnHeaderItemClick)
  2944. NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, OnHeaderItemClick)
  2945. MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
  2946. END_MSG_MAP()
  2947. LRESULT OnInsertColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  2948. {
  2949. T* pT = static_cast<T*>(this);
  2950. LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
  2951. if(lRet == -1)
  2952. return -1;
  2953. WORD wType = 0;
  2954. m_arrColSortType.Add(wType);
  2955. int nCount = m_arrColSortType.GetSize();
  2956. ATLASSERT(nCount == GetColumnCount());
  2957. for(int i = nCount - 1; i > lRet; i--)
  2958. m_arrColSortType[i] = m_arrColSortType[i - 1];
  2959. m_arrColSortType[(int)lRet] = LVCOLSORT_TEXT;
  2960. if(lRet <= m_iSortColumn)
  2961. m_iSortColumn++;
  2962. return lRet;
  2963. }
  2964. LRESULT OnDeleteColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  2965. {
  2966. T* pT = static_cast<T*>(this);
  2967. LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
  2968. if(lRet == 0)
  2969. return 0;
  2970. int iCol = (int)wParam;
  2971. if(m_iSortColumn == iCol)
  2972. m_iSortColumn = -1;
  2973. else if(m_iSortColumn > iCol)
  2974. m_iSortColumn--;
  2975. m_arrColSortType.RemoveAt(iCol);
  2976. return lRet;
  2977. }
  2978. LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
  2979. {
  2980. LPNMHEADER p = (LPNMHEADER)pnmh;
  2981. if(p->iButton == 0)
  2982. {
  2983. int iOld = m_iSortColumn;
  2984. bool bDescending = (m_iSortColumn == p->iItem) ? !m_bSortDescending : false;
  2985. if(DoSortItems(p->iItem, bDescending))
  2986. NotifyParentSortChanged(p->iItem, iOld);
  2987. }
  2988. bHandled = FALSE;
  2989. return 0;
  2990. }
  2991. LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  2992. {
  2993. if(wParam == SPI_SETNONCLIENTMETRICS)
  2994. GetSystemSettings();
  2995. bHandled = FALSE;
  2996. return 0;
  2997. }
  2998. void GetSystemSettings()
  2999. {
  3000. if(!m_bCommCtrl6 && !m_bmSort[m_iSortUp].IsNull())
  3001. {
  3002. T* pT = static_cast<T*>(this);
  3003. pT->CreateSortBitmaps();
  3004. if(m_iSortColumn != -1)
  3005. SetSortColumn(m_iSortColumn);
  3006. }
  3007. }
  3008. };
  3009. typedef ATL::CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_REPORT | LVS_SHOWSELALWAYS , WS_EX_CLIENTEDGE> CSortListViewCtrlTraits;
  3010. template <class T, class TBase = CListViewCtrl, class TWinTraits = CSortListViewCtrlTraits>
  3011. class ATL_NO_VTABLE CSortListViewCtrlImpl: public ATL::CWindowImpl<T, TBase, TWinTraits>, public CSortListViewImpl<T>
  3012. {
  3013. public:
  3014. DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName())
  3015. bool SortItems(int iCol, bool bDescending = false)
  3016. {
  3017. return this->DoSortItems(iCol, bDescending);
  3018. }
  3019. BEGIN_MSG_MAP(CSortListViewCtrlImpl)
  3020. MESSAGE_HANDLER(LVM_INSERTCOLUMN, CSortListViewImpl<T>::OnInsertColumn)
  3021. MESSAGE_HANDLER(LVM_DELETECOLUMN, CSortListViewImpl<T>::OnDeleteColumn)
  3022. NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, CSortListViewImpl<T>::OnHeaderItemClick)
  3023. NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, CSortListViewImpl<T>::OnHeaderItemClick)
  3024. MESSAGE_HANDLER(WM_SETTINGCHANGE, CSortListViewImpl<T>::OnSettingChange)
  3025. END_MSG_MAP()
  3026. };
  3027. class CSortListViewCtrl : public CSortListViewCtrlImpl<CSortListViewCtrl>
  3028. {
  3029. public:
  3030. DECLARE_WND_SUPERCLASS(_T("WTL_SortListViewCtrl"), GetWndClassName())
  3031. };
  3032. ///////////////////////////////////////////////////////////////////////////////
  3033. // CTabView - implements tab view window
  3034. // TabView Notifications
  3035. #define TBVN_PAGEACTIVATED (0U-741)
  3036. #define TBVN_CONTEXTMENU (0U-742)
  3037. // Notification data for TBVN_CONTEXTMENU
  3038. struct TBVCONTEXTMENUINFO
  3039. {
  3040. NMHDR hdr;
  3041. POINT pt;
  3042. };
  3043. typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO;
  3044. template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
  3045. class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
  3046. {
  3047. public:
  3048. DECLARE_WND_CLASS_EX2(NULL, T, 0, COLOR_APPWORKSPACE)
  3049. // Declarations and enums
  3050. struct TABVIEWPAGE
  3051. {
  3052. HWND hWnd;
  3053. LPTSTR lpstrTitle;
  3054. LPVOID pData;
  3055. };
  3056. struct TCITEMEXTRA
  3057. {
  3058. TCITEMHEADER tciheader;
  3059. TABVIEWPAGE tvpage;
  3060. operator LPTCITEM() { return (LPTCITEM)this; }
  3061. };
  3062. enum
  3063. {
  3064. m_nTabID = 1313,
  3065. m_cxMoveMark = 6,
  3066. m_cyMoveMark = 3,
  3067. m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1)
  3068. };
  3069. enum { _nAutoScrollTimerID = 4321 };
  3070. enum AutoScroll
  3071. {
  3072. _AUTOSCROLL_NONE = 0,
  3073. _AUTOSCROLL_LEFT = -1,
  3074. _AUTOSCROLL_RIGHT = 1
  3075. };
  3076. // Data members
  3077. ATL::CContainedWindowT<CTabCtrl> m_tab;
  3078. int m_cyTabHeight;
  3079. int m_nActivePage;
  3080. int m_nInsertItem;
  3081. POINT m_ptStartDrag;
  3082. CMenuHandle m_menu;
  3083. int m_cchTabTextLength;
  3084. int m_nMenuItemsCount;
  3085. ATL::CWindow m_wndTitleBar;
  3086. LPTSTR m_lpstrTitleBarBase;
  3087. int m_cchTitleBarLength;
  3088. CImageList m_ilDrag;
  3089. AutoScroll m_AutoScroll;
  3090. CUpDownCtrl m_ud;
  3091. bool m_bDestroyPageOnRemove:1;
  3092. bool m_bDestroyImageList:1;
  3093. bool m_bActivePageMenuItem:1;
  3094. bool m_bActiveAsDefaultMenuItem:1;
  3095. bool m_bEmptyMenuItem:1;
  3096. bool m_bWindowsMenuItem:1;
  3097. bool m_bNoTabDrag:1;
  3098. bool m_bNoTabDragAutoScroll:1;
  3099. // internal
  3100. bool m_bTabCapture:1;
  3101. bool m_bTabDrag:1;
  3102. bool m_bInternalFont:1;
  3103. // Constructor/destructor
  3104. CTabViewImpl() :
  3105. m_tab(this, 1),
  3106. m_cyTabHeight(0),
  3107. m_nActivePage(-1),
  3108. m_nInsertItem(-1),
  3109. m_cchTabTextLength(30),
  3110. m_nMenuItemsCount(10),
  3111. m_lpstrTitleBarBase(NULL),
  3112. m_cchTitleBarLength(100),
  3113. m_AutoScroll(_AUTOSCROLL_NONE),
  3114. m_bDestroyPageOnRemove(true),
  3115. m_bDestroyImageList(true),
  3116. m_bActivePageMenuItem(true),
  3117. m_bActiveAsDefaultMenuItem(false),
  3118. m_bEmptyMenuItem(false),
  3119. m_bWindowsMenuItem(false),
  3120. m_bNoTabDrag(false),
  3121. m_bNoTabDragAutoScroll(false),
  3122. m_bTabCapture(false),
  3123. m_bTabDrag(false),
  3124. m_bInternalFont(false)
  3125. {
  3126. m_ptStartDrag.x = 0;
  3127. m_ptStartDrag.y = 0;
  3128. }
  3129. ~CTabViewImpl()
  3130. {
  3131. delete [] m_lpstrTitleBarBase;
  3132. }
  3133. // Message filter function - to be called from PreTranslateMessage of the main window
  3134. BOOL PreTranslateMessage(MSG* pMsg)
  3135. {
  3136. if(this->IsWindow() == FALSE)
  3137. return FALSE;
  3138. BOOL bRet = FALSE;
  3139. // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page)
  3140. int nCount = GetPageCount();
  3141. if(nCount > 0)
  3142. {
  3143. bool bControl = (::GetKeyState(VK_CONTROL) < 0);
  3144. if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl)
  3145. {
  3146. if(nCount > 1)
  3147. {
  3148. int nPage = m_nActivePage;
  3149. bool bShift = (::GetKeyState(VK_SHIFT) < 0);
  3150. if(bShift)
  3151. nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1);
  3152. else
  3153. nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0;
  3154. SetActivePage(nPage);
  3155. T* pT = static_cast<T*>(this);
  3156. pT->OnPageActivated(m_nActivePage);
  3157. }
  3158. bRet = TRUE;
  3159. }
  3160. }
  3161. // If we are doing drag-drop, check for Escape key that cancels it
  3162. if(bRet == FALSE)
  3163. {
  3164. if(m_bTabCapture && (pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_ESCAPE))
  3165. {
  3166. ::ReleaseCapture();
  3167. bRet = TRUE;
  3168. }
  3169. }
  3170. // Pass the message to the active page
  3171. if(bRet == FALSE)
  3172. {
  3173. if(m_nActivePage != -1)
  3174. bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg);
  3175. }
  3176. return bRet;
  3177. }
  3178. // Attributes
  3179. int GetPageCount() const
  3180. {
  3181. ATLASSERT(::IsWindow(this->m_hWnd));
  3182. return m_tab.GetItemCount();
  3183. }
  3184. int GetActivePage() const
  3185. {
  3186. return m_nActivePage;
  3187. }
  3188. void SetActivePage(int nPage)
  3189. {
  3190. ATLASSERT(::IsWindow(this->m_hWnd));
  3191. ATLASSERT(IsValidPageIndex(nPage));
  3192. T* pT = static_cast<T*>(this);
  3193. this->SetRedraw(FALSE);
  3194. if(m_nActivePage != -1)
  3195. ::ShowWindow(GetPageHWND(m_nActivePage), SW_HIDE);
  3196. m_nActivePage = nPage;
  3197. m_tab.SetCurSel(m_nActivePage);
  3198. ::ShowWindow(GetPageHWND(m_nActivePage), SW_SHOW);
  3199. pT->UpdateLayout();
  3200. this->SetRedraw(TRUE);
  3201. this->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
  3202. if(::GetFocus() != m_tab.m_hWnd)
  3203. ::SetFocus(GetPageHWND(m_nActivePage));
  3204. pT->UpdateTitleBar();
  3205. pT->UpdateMenu();
  3206. }
  3207. HIMAGELIST GetImageList() const
  3208. {
  3209. ATLASSERT(::IsWindow(this->m_hWnd));
  3210. return m_tab.GetImageList();
  3211. }
  3212. HIMAGELIST SetImageList(HIMAGELIST hImageList)
  3213. {
  3214. ATLASSERT(::IsWindow(this->m_hWnd));
  3215. return m_tab.SetImageList(hImageList);
  3216. }
  3217. void SetWindowMenu(HMENU hMenu)
  3218. {
  3219. ATLASSERT(::IsWindow(this->m_hWnd));
  3220. m_menu = hMenu;
  3221. T* pT = static_cast<T*>(this);
  3222. pT->UpdateMenu();
  3223. }
  3224. void SetTitleBarWindow(HWND hWnd)
  3225. {
  3226. ATLASSERT(::IsWindow(this->m_hWnd));
  3227. delete [] m_lpstrTitleBarBase;
  3228. m_lpstrTitleBarBase = NULL;
  3229. m_wndTitleBar = hWnd;
  3230. if(hWnd == NULL)
  3231. return;
  3232. int cchLen = m_wndTitleBar.GetWindowTextLength() + 1;
  3233. ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]);
  3234. if(m_lpstrTitleBarBase != NULL)
  3235. {
  3236. m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen);
  3237. T* pT = static_cast<T*>(this);
  3238. pT->UpdateTitleBar();
  3239. }
  3240. }
  3241. // Page attributes
  3242. HWND GetPageHWND(int nPage) const
  3243. {
  3244. ATLASSERT(::IsWindow(this->m_hWnd));
  3245. ATLASSERT(IsValidPageIndex(nPage));
  3246. TCITEMEXTRA tcix = {};
  3247. tcix.tciheader.mask = TCIF_PARAM;
  3248. m_tab.GetItem(nPage, tcix);
  3249. return tcix.tvpage.hWnd;
  3250. }
  3251. LPCTSTR GetPageTitle(int nPage) const
  3252. {
  3253. ATLASSERT(::IsWindow(this->m_hWnd));
  3254. ATLASSERT(IsValidPageIndex(nPage));
  3255. TCITEMEXTRA tcix = {};
  3256. tcix.tciheader.mask = TCIF_PARAM;
  3257. if(m_tab.GetItem(nPage, tcix) == FALSE)
  3258. return NULL;
  3259. return tcix.tvpage.lpstrTitle;
  3260. }
  3261. bool SetPageTitle(int nPage, LPCTSTR lpstrTitle)
  3262. {
  3263. ATLASSERT(::IsWindow(this->m_hWnd));
  3264. ATLASSERT(IsValidPageIndex(nPage));
  3265. T* pT = static_cast<T*>(this);
  3266. int cchBuff = lstrlen(lpstrTitle) + 1;
  3267. LPTSTR lpstrBuff = NULL;
  3268. ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
  3269. if(lpstrBuff == NULL)
  3270. return false;
  3271. ATL::Checked::tcscpy_s(lpstrBuff, cchBuff, lpstrTitle);
  3272. TCITEMEXTRA tcix = {};
  3273. tcix.tciheader.mask = TCIF_PARAM;
  3274. if(m_tab.GetItem(nPage, tcix) == FALSE)
  3275. return false;
  3276. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3277. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  3278. if(lpstrTabText == NULL)
  3279. return false;
  3280. delete [] tcix.tvpage.lpstrTitle;
  3281. pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
  3282. tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM;
  3283. tcix.tciheader.pszText = lpstrTabText;
  3284. tcix.tvpage.lpstrTitle = lpstrBuff;
  3285. if(m_tab.SetItem(nPage, tcix) == FALSE)
  3286. return false;
  3287. pT->UpdateTitleBar();
  3288. pT->UpdateMenu();
  3289. return true;
  3290. }
  3291. LPVOID GetPageData(int nPage) const
  3292. {
  3293. ATLASSERT(::IsWindow(this->m_hWnd));
  3294. ATLASSERT(IsValidPageIndex(nPage));
  3295. TCITEMEXTRA tcix = {};
  3296. tcix.tciheader.mask = TCIF_PARAM;
  3297. m_tab.GetItem(nPage, tcix);
  3298. return tcix.tvpage.pData;
  3299. }
  3300. LPVOID SetPageData(int nPage, LPVOID pData)
  3301. {
  3302. ATLASSERT(::IsWindow(this->m_hWnd));
  3303. ATLASSERT(IsValidPageIndex(nPage));
  3304. TCITEMEXTRA tcix = {};
  3305. tcix.tciheader.mask = TCIF_PARAM;
  3306. m_tab.GetItem(nPage, tcix);
  3307. LPVOID pDataOld = tcix.tvpage.pData;
  3308. tcix.tvpage.pData = pData;
  3309. m_tab.SetItem(nPage, tcix);
  3310. return pDataOld;
  3311. }
  3312. int GetPageImage(int nPage) const
  3313. {
  3314. ATLASSERT(::IsWindow(this->m_hWnd));
  3315. ATLASSERT(IsValidPageIndex(nPage));
  3316. TCITEMEXTRA tcix = {};
  3317. tcix.tciheader.mask = TCIF_IMAGE;
  3318. m_tab.GetItem(nPage, tcix);
  3319. return tcix.tciheader.iImage;
  3320. }
  3321. int SetPageImage(int nPage, int nImage)
  3322. {
  3323. ATLASSERT(::IsWindow(this->m_hWnd));
  3324. ATLASSERT(IsValidPageIndex(nPage));
  3325. TCITEMEXTRA tcix = {};
  3326. tcix.tciheader.mask = TCIF_IMAGE;
  3327. m_tab.GetItem(nPage, tcix);
  3328. int nImageOld = tcix.tciheader.iImage;
  3329. tcix.tciheader.iImage = nImage;
  3330. m_tab.SetItem(nPage, tcix);
  3331. return nImageOld;
  3332. }
  3333. // Operations
  3334. bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
  3335. {
  3336. return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
  3337. }
  3338. bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
  3339. {
  3340. ATLASSERT(::IsWindow(this->m_hWnd));
  3341. ATLASSERT((nPage == GetPageCount()) || IsValidPageIndex(nPage));
  3342. T* pT = static_cast<T*>(this);
  3343. int cchBuff = lstrlen(lpstrTitle) + 1;
  3344. LPTSTR lpstrBuff = NULL;
  3345. ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
  3346. if(lpstrBuff == NULL)
  3347. return false;
  3348. ATL::Checked::tcscpy_s(lpstrBuff, cchBuff, lpstrTitle);
  3349. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3350. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  3351. if(lpstrTabText == NULL)
  3352. return false;
  3353. pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
  3354. this->SetRedraw(FALSE);
  3355. TCITEMEXTRA tcix = {};
  3356. tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
  3357. tcix.tciheader.pszText = lpstrTabText;
  3358. tcix.tciheader.iImage = nImage;
  3359. tcix.tvpage.hWnd = hWndView;
  3360. tcix.tvpage.lpstrTitle = lpstrBuff;
  3361. tcix.tvpage.pData = pData;
  3362. int nItem = m_tab.InsertItem(nPage, tcix);
  3363. if(nItem == -1)
  3364. {
  3365. delete [] lpstrBuff;
  3366. this->SetRedraw(TRUE);
  3367. return false;
  3368. }
  3369. // adjust active page index, if inserted before it
  3370. if(nPage <= m_nActivePage)
  3371. m_nActivePage++;
  3372. SetActivePage(nItem);
  3373. pT->OnPageActivated(m_nActivePage);
  3374. if(GetPageCount() == 1)
  3375. pT->ShowTabControl(true);
  3376. pT->UpdateLayout();
  3377. this->SetRedraw(TRUE);
  3378. this->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
  3379. return true;
  3380. }
  3381. void RemovePage(int nPage)
  3382. {
  3383. ATLASSERT(::IsWindow(this->m_hWnd));
  3384. ATLASSERT(IsValidPageIndex(nPage));
  3385. T* pT = static_cast<T*>(this);
  3386. this->SetRedraw(FALSE);
  3387. if(GetPageCount() == 1)
  3388. pT->ShowTabControl(false);
  3389. if(m_bDestroyPageOnRemove)
  3390. ::DestroyWindow(GetPageHWND(nPage));
  3391. else
  3392. ::ShowWindow(GetPageHWND(nPage), SW_HIDE);
  3393. LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage);
  3394. delete [] lpstrTitle;
  3395. ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE);
  3396. if(m_nActivePage == nPage)
  3397. {
  3398. m_nActivePage = -1;
  3399. if(nPage > 0)
  3400. {
  3401. SetActivePage(nPage - 1);
  3402. }
  3403. else if(GetPageCount() > 0)
  3404. {
  3405. SetActivePage(nPage);
  3406. }
  3407. else
  3408. {
  3409. this->SetRedraw(TRUE);
  3410. this->Invalidate();
  3411. this->UpdateWindow();
  3412. pT->UpdateTitleBar();
  3413. pT->UpdateMenu();
  3414. }
  3415. }
  3416. else
  3417. {
  3418. nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage;
  3419. m_nActivePage = -1;
  3420. SetActivePage(nPage);
  3421. }
  3422. pT->OnPageActivated(m_nActivePage);
  3423. }
  3424. void RemoveAllPages()
  3425. {
  3426. ATLASSERT(::IsWindow(this->m_hWnd));
  3427. if(GetPageCount() == 0)
  3428. return;
  3429. T* pT = static_cast<T*>(this);
  3430. this->SetRedraw(FALSE);
  3431. pT->ShowTabControl(false);
  3432. for(int i = 0; i < GetPageCount(); i++)
  3433. {
  3434. if(m_bDestroyPageOnRemove)
  3435. ::DestroyWindow(GetPageHWND(i));
  3436. else
  3437. ::ShowWindow(GetPageHWND(i), SW_HIDE);
  3438. LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i);
  3439. delete [] lpstrTitle;
  3440. }
  3441. m_tab.DeleteAllItems();
  3442. m_nActivePage = -1;
  3443. pT->OnPageActivated(m_nActivePage);
  3444. this->SetRedraw(TRUE);
  3445. this->Invalidate();
  3446. this->UpdateWindow();
  3447. pT->UpdateTitleBar();
  3448. pT->UpdateMenu();
  3449. }
  3450. int PageIndexFromHwnd(HWND hWnd) const
  3451. {
  3452. int nIndex = -1;
  3453. for(int i = 0; i < GetPageCount(); i++)
  3454. {
  3455. if(GetPageHWND(i) == hWnd)
  3456. {
  3457. nIndex = i;
  3458. break;
  3459. }
  3460. }
  3461. return nIndex;
  3462. }
  3463. void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false)
  3464. {
  3465. ATLASSERT(::IsWindow(this->m_hWnd));
  3466. CMenuHandle menu = hMenu;
  3467. T* pT = static_cast<T*>(this);
  3468. (void)pT; // avoid level 4 warning
  3469. int nFirstPos = 0;
  3470. // Find first menu item in our range
  3471. for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++)
  3472. {
  3473. UINT nID = menu.GetMenuItemID(nFirstPos);
  3474. if(((nID >= ID_WINDOW_TABFIRST) && (nID <= ID_WINDOW_TABLAST)) || (nID == ID_WINDOW_SHOWTABLIST))
  3475. break;
  3476. }
  3477. // Remove all menu items for tab pages
  3478. BOOL bRet = TRUE;
  3479. while(bRet != FALSE)
  3480. bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION);
  3481. // Add separator if it's not already there
  3482. int nPageCount = GetPageCount();
  3483. if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0))
  3484. {
  3485. CMenuItemInfo mii;
  3486. mii.fMask = MIIM_TYPE;
  3487. menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
  3488. if((nFirstPos <= 0) || ((mii.fType & MFT_SEPARATOR) == 0))
  3489. {
  3490. menu.AppendMenu(MF_SEPARATOR);
  3491. nFirstPos++;
  3492. }
  3493. }
  3494. // Add menu items for all pages
  3495. if(nPageCount > 0)
  3496. {
  3497. // Append menu items for all pages
  3498. const int cchPrefix = 3; // 2 digits + space
  3499. nMenuItemsCount = __min(__min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax);
  3500. ATLASSERT(nMenuItemsCount < 100); // 2 digits only
  3501. if(nMenuItemsCount >= 100)
  3502. nMenuItemsCount = 99;
  3503. for(int i = 0; i < nMenuItemsCount; i++)
  3504. {
  3505. LPCTSTR lpstrTitle = GetPageTitle(i);
  3506. int nLen = lstrlen(lpstrTitle);
  3507. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3508. LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1);
  3509. ATLASSERT(lpstrText != NULL);
  3510. if(lpstrText != NULL)
  3511. {
  3512. LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s");
  3513. _stprintf_s(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle);
  3514. menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText);
  3515. }
  3516. }
  3517. // Mark active page
  3518. if(bActivePageMenuItem && (m_nActivePage != -1))
  3519. {
  3520. if(bActiveAsDefaultMenuItem)
  3521. {
  3522. menu.SetMenuDefaultItem((UINT)-1, TRUE);
  3523. menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE);
  3524. }
  3525. else
  3526. {
  3527. menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION);
  3528. }
  3529. }
  3530. }
  3531. else
  3532. {
  3533. if(bEmptyMenuItem)
  3534. {
  3535. menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText());
  3536. menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED);
  3537. }
  3538. // Remove separator if nothing else is there
  3539. if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0))
  3540. {
  3541. CMenuItemInfo mii;
  3542. mii.fMask = MIIM_TYPE;
  3543. menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
  3544. if((mii.fType & MFT_SEPARATOR) != 0)
  3545. menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION);
  3546. }
  3547. }
  3548. // Add "Windows..." menu item
  3549. if(bWindowsMenuItem)
  3550. menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText());
  3551. }
  3552. BOOL SubclassWindow(HWND hWnd)
  3553. {
  3554. BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
  3555. if(bRet != FALSE)
  3556. {
  3557. T* pT = static_cast<T*>(this);
  3558. pT->CreateTabControl();
  3559. pT->UpdateLayout();
  3560. }
  3561. return bRet;
  3562. }
  3563. // Message map and handlers
  3564. BEGIN_MSG_MAP(CTabViewImpl)
  3565. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  3566. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  3567. MESSAGE_HANDLER(WM_SIZE, OnSize)
  3568. MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
  3569. MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
  3570. MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
  3571. MESSAGE_HANDLER(WM_TIMER, OnTimer)
  3572. MESSAGE_HANDLER(WM_CONTEXTMENU, OnTabContextMenu)
  3573. NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
  3574. NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)
  3575. NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)
  3576. FORWARD_NOTIFICATIONS()
  3577. ALT_MSG_MAP(1) // tab control
  3578. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)
  3579. MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)
  3580. MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)
  3581. MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)
  3582. END_MSG_MAP()
  3583. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3584. {
  3585. T* pT = static_cast<T*>(this);
  3586. pT->CreateTabControl();
  3587. return 0;
  3588. }
  3589. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3590. {
  3591. RemoveAllPages();
  3592. if(m_bDestroyImageList)
  3593. {
  3594. CImageList il = m_tab.SetImageList(NULL);
  3595. if(il.m_hImageList != NULL)
  3596. il.Destroy();
  3597. }
  3598. if(m_bInternalFont)
  3599. {
  3600. HFONT hFont = m_tab.GetFont();
  3601. m_tab.SetFont(NULL, FALSE);
  3602. ::DeleteObject(hFont);
  3603. m_bInternalFont = false;
  3604. }
  3605. m_ud.m_hWnd = NULL;
  3606. return 0;
  3607. }
  3608. LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3609. {
  3610. T* pT = static_cast<T*>(this);
  3611. pT->UpdateLayout();
  3612. return 0;
  3613. }
  3614. LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3615. {
  3616. if(m_nActivePage != -1)
  3617. ::SetFocus(GetPageHWND(m_nActivePage));
  3618. return 0;
  3619. }
  3620. LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3621. {
  3622. return m_tab.SendMessage(WM_GETFONT);
  3623. }
  3624. LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  3625. {
  3626. if(m_bInternalFont)
  3627. {
  3628. HFONT hFont = m_tab.GetFont();
  3629. m_tab.SetFont(NULL, FALSE);
  3630. ::DeleteObject(hFont);
  3631. m_bInternalFont = false;
  3632. }
  3633. m_tab.SendMessage(WM_SETFONT, wParam, lParam);
  3634. T* pT = static_cast<T*>(this);
  3635. m_cyTabHeight = pT->CalcTabHeight();
  3636. if((BOOL)lParam != FALSE)
  3637. pT->UpdateLayout();
  3638. return 0;
  3639. }
  3640. LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  3641. {
  3642. if(wParam == _nAutoScrollTimerID)
  3643. {
  3644. T* pT = static_cast<T*>(this);
  3645. pT->DoAutoScroll();
  3646. }
  3647. else
  3648. {
  3649. bHandled = FALSE;
  3650. }
  3651. return 0;
  3652. }
  3653. LRESULT OnTabContextMenu(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  3654. {
  3655. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  3656. int nPage = m_nActivePage;
  3657. bool bAction = false;
  3658. if((HWND)wParam == m_tab.m_hWnd)
  3659. {
  3660. if((pt.x == -1) && (pt.y == -1)) // keyboard
  3661. {
  3662. RECT rect = {};
  3663. m_tab.GetItemRect(m_nActivePage, &rect);
  3664. pt.x = rect.left;
  3665. pt.y = rect.bottom;
  3666. m_tab.ClientToScreen(&pt);
  3667. bAction = true;
  3668. }
  3669. else if(::WindowFromPoint(pt) == m_tab.m_hWnd)
  3670. {
  3671. TCHITTESTINFO hti = {};
  3672. hti.pt = pt;
  3673. this->ScreenToClient(&hti.pt);
  3674. nPage = m_tab.HitTest(&hti);
  3675. bAction = true;
  3676. }
  3677. }
  3678. if(bAction)
  3679. {
  3680. T* pT = static_cast<T*>(this);
  3681. pT->OnContextMenu(nPage, pt);
  3682. }
  3683. else
  3684. {
  3685. bHandled = FALSE;
  3686. }
  3687. return 0;
  3688. }
  3689. LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
  3690. {
  3691. SetActivePage(m_tab.GetCurSel());
  3692. T* pT = static_cast<T*>(this);
  3693. pT->OnPageActivated(m_nActivePage);
  3694. return 0;
  3695. }
  3696. LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
  3697. {
  3698. // nothing to do - this just blocks all tab control
  3699. // notifications from being propagated further
  3700. return 0;
  3701. }
  3702. LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
  3703. {
  3704. LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh;
  3705. if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips())
  3706. {
  3707. T* pT = static_cast<T*>(this);
  3708. pT->UpdateTooltipText(pTTDI);
  3709. }
  3710. else
  3711. {
  3712. bHandled = FALSE;
  3713. }
  3714. return 0;
  3715. }
  3716. // Tab control message handlers
  3717. LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3718. {
  3719. if(!m_bNoTabDrag && (m_tab.GetItemCount() > 1))
  3720. {
  3721. m_bTabCapture = true;
  3722. m_tab.SetCapture();
  3723. m_ptStartDrag.x = GET_X_LPARAM(lParam);
  3724. m_ptStartDrag.y = GET_Y_LPARAM(lParam);
  3725. }
  3726. bHandled = FALSE;
  3727. return 0;
  3728. }
  3729. LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3730. {
  3731. if(m_bTabCapture)
  3732. {
  3733. if(m_bTabDrag)
  3734. {
  3735. T* pT = static_cast<T*>(this);
  3736. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  3737. int nItem = pT->DragHitTest(pt);
  3738. if(nItem != -1)
  3739. MovePage(m_nActivePage, nItem);
  3740. }
  3741. ::ReleaseCapture();
  3742. }
  3743. bHandled = FALSE;
  3744. return 0;
  3745. }
  3746. LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  3747. {
  3748. if(m_bTabCapture)
  3749. {
  3750. m_bTabCapture = false;
  3751. if(m_bTabDrag)
  3752. {
  3753. m_bTabDrag = false;
  3754. T* pT = static_cast<T*>(this);
  3755. if(!m_bNoTabDragAutoScroll)
  3756. pT->StartStopAutoScroll(-1);
  3757. pT->DrawMoveMark(-1);
  3758. m_ilDrag.DragLeave(GetDesktopWindow());
  3759. m_ilDrag.EndDrag();
  3760. m_ilDrag.Destroy();
  3761. m_ilDrag.m_hImageList = NULL;
  3762. }
  3763. }
  3764. bHandled = FALSE;
  3765. return 0;
  3766. }
  3767. LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3768. {
  3769. bHandled = FALSE;
  3770. if(m_bTabCapture)
  3771. {
  3772. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  3773. if(!m_bTabDrag)
  3774. {
  3775. if((abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG)) ||
  3776. (abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG)))
  3777. {
  3778. T* pT = static_cast<T*>(this);
  3779. pT->GenerateDragImage(m_nActivePage);
  3780. int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);
  3781. int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);
  3782. m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));
  3783. POINT ptEnter = m_ptStartDrag;
  3784. m_tab.ClientToScreen(&ptEnter);
  3785. m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);
  3786. m_bTabDrag = true;
  3787. }
  3788. }
  3789. if(m_bTabDrag)
  3790. {
  3791. T* pT = static_cast<T*>(this);
  3792. int nItem = pT->DragHitTest(pt);
  3793. pT->SetMoveCursor(nItem != -1);
  3794. if(m_nInsertItem != nItem)
  3795. pT->DrawMoveMark(nItem);
  3796. if(!m_bNoTabDragAutoScroll)
  3797. pT->StartStopAutoScroll(pt.x);
  3798. m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);
  3799. m_tab.ClientToScreen(&pt);
  3800. m_ilDrag.DragMove(pt);
  3801. bHandled = TRUE;
  3802. }
  3803. }
  3804. return 0;
  3805. }
  3806. // Implementation helpers
  3807. bool IsValidPageIndex(int nPage) const
  3808. {
  3809. return ((nPage >= 0) && (nPage < GetPageCount()));
  3810. }
  3811. bool MovePage(int nMovePage, int nInsertBeforePage)
  3812. {
  3813. ATLASSERT(IsValidPageIndex(nMovePage));
  3814. ATLASSERT(IsValidPageIndex(nInsertBeforePage));
  3815. if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage))
  3816. return false;
  3817. if(nMovePage == nInsertBeforePage)
  3818. return true; // nothing to do
  3819. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3820. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  3821. if(lpstrTabText == NULL)
  3822. return false;
  3823. TCITEMEXTRA tcix = {};
  3824. tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
  3825. tcix.tciheader.pszText = lpstrTabText;
  3826. tcix.tciheader.cchTextMax = m_cchTabTextLength + 1;
  3827. BOOL bRet = m_tab.GetItem(nMovePage, tcix);
  3828. ATLASSERT(bRet != FALSE);
  3829. if(bRet == FALSE)
  3830. return false;
  3831. int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage;
  3832. int nNewItem = m_tab.InsertItem(nInsertItem, tcix);
  3833. ATLASSERT(nNewItem == nInsertItem);
  3834. if(nNewItem != nInsertItem)
  3835. {
  3836. ATLVERIFY(m_tab.DeleteItem(nNewItem));
  3837. return false;
  3838. }
  3839. if(nMovePage > nInsertBeforePage)
  3840. ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE);
  3841. else if(nMovePage < nInsertBeforePage)
  3842. ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE);
  3843. SetActivePage(nInsertBeforePage);
  3844. T* pT = static_cast<T*>(this);
  3845. pT->OnPageActivated(m_nActivePage);
  3846. return true;
  3847. }
  3848. // Implementation overrideables
  3849. bool CreateTabControl()
  3850. {
  3851. m_tab.Create(this->m_hWnd, this->rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID);
  3852. ATLASSERT(m_tab.m_hWnd != NULL);
  3853. if(m_tab.m_hWnd == NULL)
  3854. return false;
  3855. m_tab.SetFont(AtlCreateControlFont());
  3856. m_bInternalFont = true;
  3857. m_tab.SetItemExtra(sizeof(TABVIEWPAGE));
  3858. T* pT = static_cast<T*>(this);
  3859. m_cyTabHeight = pT->CalcTabHeight();
  3860. return true;
  3861. }
  3862. int CalcTabHeight()
  3863. {
  3864. int nCount = m_tab.GetItemCount();
  3865. TCHAR szText[] = _T("NS");
  3866. TCITEMEXTRA tcix = {};
  3867. tcix.tciheader.mask = TCIF_TEXT;
  3868. tcix.tciheader.pszText = szText;
  3869. int nIndex = m_tab.InsertItem(nCount, tcix);
  3870. RECT rect = { 0, 0, 1000, 1000 };
  3871. m_tab.AdjustRect(FALSE, &rect);
  3872. RECT rcWnd = { 0, 0, 1000, rect.top };
  3873. ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle());
  3874. int nHeight = rcWnd.bottom - rcWnd.top;
  3875. m_tab.DeleteItem(nIndex);
  3876. return nHeight;
  3877. }
  3878. void ShowTabControl(bool bShow)
  3879. {
  3880. m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
  3881. T* pT = static_cast<T*>(this);
  3882. pT->UpdateLayout();
  3883. }
  3884. void UpdateLayout()
  3885. {
  3886. RECT rect = {};
  3887. this->GetClientRect(&rect);
  3888. int cyOffset = 0;
  3889. if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
  3890. {
  3891. m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);
  3892. cyOffset = m_cyTabHeight;
  3893. }
  3894. if(m_nActivePage != -1)
  3895. ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, cyOffset, rect.right - rect.left, rect.bottom - rect.top - cyOffset, SWP_NOZORDER);
  3896. }
  3897. void UpdateMenu()
  3898. {
  3899. if(m_menu.m_hMenu != NULL)
  3900. BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem);
  3901. }
  3902. void UpdateTitleBar()
  3903. {
  3904. if(!m_wndTitleBar.IsWindow() || (m_lpstrTitleBarBase == NULL))
  3905. return; // nothing to do
  3906. if(m_nActivePage != -1)
  3907. {
  3908. T* pT = static_cast<T*>(this);
  3909. LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage);
  3910. LPCTSTR lpstrDivider = pT->GetTitleDividerText();
  3911. int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1;
  3912. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3913. LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer);
  3914. ATLASSERT(lpstrPageTitle != NULL);
  3915. if(lpstrPageTitle != NULL)
  3916. {
  3917. pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1);
  3918. ATL::Checked::tcscat_s(lpstrPageTitle, cchBuffer, lpstrDivider);
  3919. ATL::Checked::tcscat_s(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase);
  3920. }
  3921. else
  3922. {
  3923. lpstrPageTitle = m_lpstrTitleBarBase;
  3924. }
  3925. m_wndTitleBar.SetWindowText(lpstrPageTitle);
  3926. }
  3927. else
  3928. {
  3929. m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase);
  3930. }
  3931. }
  3932. void DrawMoveMark(int nItem)
  3933. {
  3934. T* pT = static_cast<T*>(this);
  3935. if(m_nInsertItem != -1)
  3936. {
  3937. RECT rect = {};
  3938. pT->GetMoveMarkRect(rect);
  3939. m_tab.InvalidateRect(&rect);
  3940. }
  3941. m_nInsertItem = nItem;
  3942. if(m_nInsertItem != -1)
  3943. {
  3944. CClientDC dc(m_tab.m_hWnd);
  3945. RECT rect = {};
  3946. pT->GetMoveMarkRect(rect);
  3947. CPen pen;
  3948. pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT));
  3949. CBrush brush;
  3950. brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
  3951. HPEN hPenOld = dc.SelectPen(pen);
  3952. HBRUSH hBrushOld = dc.SelectBrush(brush);
  3953. int x = rect.left;
  3954. int y = rect.top;
  3955. POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } };
  3956. dc.Polygon(ptsTop, 3);
  3957. y = rect.bottom - 1;
  3958. POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } };
  3959. dc.Polygon(ptsBottom, 3);
  3960. dc.SelectPen(hPenOld);
  3961. dc.SelectBrush(hBrushOld);
  3962. }
  3963. }
  3964. void GetMoveMarkRect(RECT& rect) const
  3965. {
  3966. m_tab.GetClientRect(&rect);
  3967. RECT rcItem = {};
  3968. m_tab.GetItemRect(m_nInsertItem, &rcItem);
  3969. if(m_nInsertItem <= m_nActivePage)
  3970. {
  3971. rect.left = rcItem.left - m_cxMoveMark / 2 - 1;
  3972. rect.right = rcItem.left + m_cxMoveMark / 2;
  3973. }
  3974. else
  3975. {
  3976. rect.left = rcItem.right - m_cxMoveMark / 2 - 1;
  3977. rect.right = rcItem.right + m_cxMoveMark / 2;
  3978. }
  3979. }
  3980. void SetMoveCursor(bool bCanMove)
  3981. {
  3982. ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO));
  3983. }
  3984. void GenerateDragImage(int nItem)
  3985. {
  3986. ATLASSERT(IsValidPageIndex(nItem));
  3987. RECT rcItem = {};
  3988. m_tab.GetItemRect(nItem, &rcItem);
  3989. ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item
  3990. ATLASSERT(m_ilDrag.m_hImageList == NULL);
  3991. m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);
  3992. CClientDC dc(this->m_hWnd);
  3993. CDC dcMem;
  3994. dcMem.CreateCompatibleDC(dc);
  3995. ATLASSERT(dcMem.m_hDC != NULL);
  3996. dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);
  3997. CBitmap bmp;
  3998. bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);
  3999. ATLASSERT(bmp.m_hBitmap != NULL);
  4000. HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);
  4001. m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);
  4002. dcMem.SelectBitmap(hBmpOld);
  4003. ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);
  4004. }
  4005. void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle)
  4006. {
  4007. if(lstrlen(lpstrTitle) >= cchShortTitle)
  4008. {
  4009. LPCTSTR lpstrEllipsis = _T("...");
  4010. int cchEllipsis = lstrlen(lpstrEllipsis);
  4011. ATL::Checked::tcsncpy_s(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1);
  4012. ATL::Checked::tcscat_s(lpstrShortTitle, cchShortTitle, lpstrEllipsis);
  4013. }
  4014. else
  4015. {
  4016. ATL::Checked::tcscpy_s(lpstrShortTitle, cchShortTitle, lpstrTitle);
  4017. }
  4018. }
  4019. void UpdateTooltipText(LPNMTTDISPINFO pTTDI)
  4020. {
  4021. ATLASSERT(pTTDI != NULL);
  4022. pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom);
  4023. }
  4024. int DragHitTest(POINT pt) const
  4025. {
  4026. RECT rect = {};
  4027. this->GetClientRect(&rect);
  4028. if(::PtInRect(&rect, pt) == FALSE)
  4029. return -1;
  4030. m_tab.GetClientRect(&rect);
  4031. TCHITTESTINFO hti = {};
  4032. hti.pt.x = pt.x;
  4033. hti.pt.y = rect.bottom / 2; // use middle to ignore
  4034. int nItem = m_tab.HitTest(&hti);
  4035. if(nItem == -1)
  4036. {
  4037. int nLast = m_tab.GetItemCount() - 1;
  4038. RECT rcItem = {};
  4039. m_tab.GetItemRect(nLast, &rcItem);
  4040. if(pt.x >= rcItem.right)
  4041. nItem = nLast;
  4042. }
  4043. return nItem;
  4044. }
  4045. void StartStopAutoScroll(int x)
  4046. {
  4047. AutoScroll scroll = _AUTOSCROLL_NONE;
  4048. if(x != -1)
  4049. {
  4050. RECT rect = {};
  4051. m_tab.GetClientRect(&rect);
  4052. int dx = ::GetSystemMetrics(SM_CXVSCROLL);
  4053. if((x >= 0) && (x < dx))
  4054. {
  4055. RECT rcItem = {};
  4056. m_tab.GetItemRect(0, &rcItem);
  4057. if(rcItem.left < rect.left)
  4058. scroll = _AUTOSCROLL_LEFT;
  4059. }
  4060. else if((x >= (rect.right - dx)) && (x < rect.right))
  4061. {
  4062. RECT rcItem = {};
  4063. m_tab.GetItemRect(m_tab.GetItemCount() - 1, &rcItem);
  4064. if(rcItem.right > rect.right)
  4065. scroll = _AUTOSCROLL_RIGHT;
  4066. }
  4067. }
  4068. if(scroll != _AUTOSCROLL_NONE)
  4069. {
  4070. if(m_ud.m_hWnd == NULL)
  4071. m_ud = m_tab.GetWindow(GW_CHILD);
  4072. if(m_AutoScroll != scroll)
  4073. {
  4074. m_AutoScroll = scroll;
  4075. this->SetTimer(_nAutoScrollTimerID, 300);
  4076. }
  4077. }
  4078. else
  4079. {
  4080. this->KillTimer(_nAutoScrollTimerID);
  4081. m_AutoScroll = _AUTOSCROLL_NONE;
  4082. }
  4083. }
  4084. void DoAutoScroll()
  4085. {
  4086. ATLASSERT(m_AutoScroll != _AUTOSCROLL_NONE);
  4087. int nMin = -1, nMax = -1;
  4088. m_ud.GetRange(nMin, nMax);
  4089. int nPos = m_ud.GetPos();
  4090. int nNewPos = -1;
  4091. if((m_AutoScroll == _AUTOSCROLL_LEFT) && (nPos > nMin))
  4092. nNewPos = nPos - 1;
  4093. else if((m_AutoScroll == _AUTOSCROLL_RIGHT) && (nPos < nMax))
  4094. nNewPos = nPos + 1;
  4095. if(nNewPos != -1)
  4096. {
  4097. m_tab.SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, nNewPos));
  4098. m_tab.SendMessage(WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0));
  4099. POINT pt = {};
  4100. ::GetCursorPos(&pt);
  4101. m_tab.ScreenToClient(&pt);
  4102. m_tab.SendMessage(WM_MOUSEMOVE, NULL, MAKELPARAM(pt.x, pt.y));
  4103. }
  4104. }
  4105. // Text for menu items and title bar - override to provide different strings
  4106. static LPCTSTR GetEmptyListText()
  4107. {
  4108. return _T("(Empty)");
  4109. }
  4110. static LPCTSTR GetWindowsMenuItemText()
  4111. {
  4112. return _T("&Windows...");
  4113. }
  4114. static LPCTSTR GetTitleDividerText()
  4115. {
  4116. return _T(" - ");
  4117. }
  4118. // Notifications - override to provide different behavior
  4119. void OnPageActivated(int nPage)
  4120. {
  4121. NMHDR nmhdr = {};
  4122. nmhdr.hwndFrom = this->m_hWnd;
  4123. nmhdr.idFrom = nPage;
  4124. nmhdr.code = TBVN_PAGEACTIVATED;
  4125. this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&nmhdr);
  4126. }
  4127. void OnContextMenu(int nPage, POINT pt)
  4128. {
  4129. TBVCONTEXTMENUINFO cmi = {};
  4130. cmi.hdr.hwndFrom = this->m_hWnd;
  4131. cmi.hdr.idFrom = nPage;
  4132. cmi.hdr.code = TBVN_CONTEXTMENU;
  4133. cmi.pt = pt;
  4134. this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&cmi);
  4135. }
  4136. };
  4137. class CTabView : public CTabViewImpl<CTabView>
  4138. {
  4139. public:
  4140. DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE)
  4141. };
  4142. } // namespace WTL
  4143. #endif // __ATLCTRLX_H__