atlctrlx.h 129 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258
  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. #define TBVN_TABCLOSEBTN (0U-743) // return 0 to close page, 1 to keep open
  3038. // internal
  3039. #define TBVN_CLOSEBTNMOUSELEAVE (0U-744)
  3040. // Notification data for TBVN_CONTEXTMENU
  3041. struct TBVCONTEXTMENUINFO
  3042. {
  3043. NMHDR hdr;
  3044. POINT pt;
  3045. };
  3046. typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO;
  3047. // Helper class for tab item hover close button
  3048. class CTabViewCloseBtn : public ATL::CWindowImpl<CTabViewCloseBtn>
  3049. {
  3050. public:
  3051. DECLARE_WND_CLASS_EX(_T("WTL_TabView_CloseBtn"), 0, -1)
  3052. enum { _xyBtnImageLeftTop = 3, _xyBtnImageRightBottom = 9 };
  3053. bool m_bHover;
  3054. bool m_bPressed;
  3055. CToolTipCtrl m_tip;
  3056. CTabViewCloseBtn() : m_bHover(false), m_bPressed(false)
  3057. { }
  3058. // Message map and handlers
  3059. BEGIN_MSG_MAP(CTabViewCloseBtn)
  3060. MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
  3061. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
  3062. MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
  3063. MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
  3064. MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
  3065. MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
  3066. MESSAGE_HANDLER(WM_PAINT, OnPaint)
  3067. MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
  3068. FORWARD_NOTIFICATIONS()
  3069. END_MSG_MAP()
  3070. LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  3071. {
  3072. MSG msg = { m_hWnd, uMsg, wParam, lParam };
  3073. if(m_tip.IsWindow() != FALSE)
  3074. m_tip.RelayEvent(&msg);
  3075. bHandled = FALSE;
  3076. return 1;
  3077. }
  3078. LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3079. {
  3080. SetCapture();
  3081. m_bHover = false;
  3082. m_bPressed = true;
  3083. Invalidate(FALSE);
  3084. UpdateWindow();
  3085. return 0;
  3086. }
  3087. LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  3088. {
  3089. if(::GetCapture() == m_hWnd)
  3090. {
  3091. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  3092. ClientToScreen(&pt);
  3093. RECT rect = {};
  3094. GetWindowRect(&rect);
  3095. bool bPressed = (::PtInRect(&rect, pt) != FALSE);
  3096. if(m_bPressed != bPressed)
  3097. {
  3098. m_bPressed = bPressed;
  3099. Invalidate(FALSE);
  3100. UpdateWindow();
  3101. }
  3102. }
  3103. else
  3104. {
  3105. if(!m_bHover)
  3106. {
  3107. m_bHover = true;
  3108. Invalidate(FALSE);
  3109. UpdateWindow();
  3110. }
  3111. TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd };
  3112. ::TrackMouseEvent(&tme);
  3113. }
  3114. return 0;
  3115. }
  3116. LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3117. {
  3118. if(m_bHover)
  3119. {
  3120. m_bHover = false;
  3121. Invalidate(FALSE);
  3122. UpdateWindow();
  3123. }
  3124. NMHDR nmhdr = { m_hWnd, (UINT_PTR)GetDlgCtrlID(), TBVN_CLOSEBTNMOUSELEAVE };
  3125. GetParent().SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
  3126. return 0;
  3127. }
  3128. LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3129. {
  3130. if(::GetCapture() == m_hWnd)
  3131. {
  3132. bool bAction = m_bPressed;
  3133. ReleaseCapture();
  3134. if(bAction)
  3135. GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
  3136. }
  3137. return 0;
  3138. }
  3139. LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3140. {
  3141. if(m_bPressed)
  3142. {
  3143. m_bPressed = false;
  3144. Invalidate(FALSE);
  3145. UpdateWindow();
  3146. }
  3147. return 0;
  3148. }
  3149. LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3150. {
  3151. if(wParam != NULL)
  3152. {
  3153. DoPaint((HDC)wParam);
  3154. }
  3155. else
  3156. {
  3157. CPaintDC dc(this->m_hWnd);
  3158. DoPaint(dc.m_hDC);
  3159. }
  3160. return 0;
  3161. }
  3162. // painting helper
  3163. void DoPaint(CDCHandle dc)
  3164. {
  3165. RECT rect = {};
  3166. GetClientRect(&rect);
  3167. RECT rcImage = { _xyBtnImageLeftTop, _xyBtnImageLeftTop, _xyBtnImageRightBottom + 1, _xyBtnImageRightBottom + 1 };
  3168. ::OffsetRect(&rcImage, rect.left, rect.top);
  3169. if(m_bPressed)
  3170. ::OffsetRect(&rcImage, 1, 0);
  3171. // draw button frame and background
  3172. CPen penFrame;
  3173. penFrame.CreatePen(PS_SOLID, 0, ::GetSysColor((m_bHover || m_bPressed) ? COLOR_BTNTEXT : COLOR_BTNSHADOW));
  3174. HPEN hPenOld = dc.SelectPen(penFrame);
  3175. CBrush brush;
  3176. brush.CreateSysColorBrush(m_bPressed ? COLOR_BTNSHADOW : COLOR_WINDOW);
  3177. HBRUSH hBrushOld = dc.SelectBrush(brush);
  3178. dc.Rectangle(&rect);
  3179. // draw button "X"
  3180. CPen penX;
  3181. penX.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
  3182. dc.SelectPen(penX);
  3183. dc.MoveTo(rcImage.left, rcImage.top);
  3184. dc.LineTo(rcImage.right, rcImage.bottom);
  3185. dc.MoveTo(rcImage.left + 1, rcImage.top);
  3186. dc.LineTo(rcImage.right + 1, rcImage.bottom);
  3187. dc.MoveTo(rcImage.left, rcImage.bottom - 1);
  3188. dc.LineTo(rcImage.right, rcImage.top - 1);
  3189. dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
  3190. dc.LineTo(rcImage.right + 1, rcImage.top - 1);
  3191. dc.SelectPen(hPenOld);
  3192. dc.SelectBrush(hBrushOld);
  3193. }
  3194. };
  3195. template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
  3196. class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
  3197. {
  3198. public:
  3199. DECLARE_WND_CLASS_EX2(NULL, T, 0, COLOR_APPWORKSPACE)
  3200. // Declarations and enums
  3201. struct TABVIEWPAGE
  3202. {
  3203. HWND hWnd;
  3204. LPTSTR lpstrTitle;
  3205. LPVOID pData;
  3206. };
  3207. struct TCITEMEXTRA
  3208. {
  3209. TCITEMHEADER tciheader;
  3210. TABVIEWPAGE tvpage;
  3211. operator LPTCITEM() { return (LPTCITEM)this; }
  3212. };
  3213. enum
  3214. {
  3215. m_nTabID = 1313,
  3216. m_cxMoveMark = 6,
  3217. m_cyMoveMark = 3,
  3218. m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1)
  3219. };
  3220. enum { _nAutoScrollTimerID = 4321 };
  3221. enum AutoScroll
  3222. {
  3223. _AUTOSCROLL_NONE = 0,
  3224. _AUTOSCROLL_LEFT = -1,
  3225. _AUTOSCROLL_RIGHT = 1
  3226. };
  3227. enum CloseBtn
  3228. {
  3229. _cxCloseBtn = 14,
  3230. _cyCloseBtn = 13,
  3231. _cxCloseBtnMargin = 4,
  3232. _cxCloseBtnMarginSel = 1,
  3233. _nCloseBtnID = ID_PANE_CLOSE
  3234. };
  3235. // Data members
  3236. ATL::CContainedWindowT<CTabCtrl> m_tab;
  3237. int m_cyTabHeight;
  3238. int m_nActivePage;
  3239. int m_nInsertItem;
  3240. POINT m_ptStartDrag;
  3241. CMenuHandle m_menu;
  3242. int m_cchTabTextLength;
  3243. int m_nMenuItemsCount;
  3244. ATL::CWindow m_wndTitleBar;
  3245. LPTSTR m_lpstrTitleBarBase;
  3246. int m_cchTitleBarLength;
  3247. CImageList m_ilDrag;
  3248. AutoScroll m_AutoScroll;
  3249. CUpDownCtrl m_ud;
  3250. CTabViewCloseBtn m_btnClose;
  3251. int m_nCloseItem;
  3252. bool m_bDestroyPageOnRemove:1;
  3253. bool m_bDestroyImageList:1;
  3254. bool m_bActivePageMenuItem:1;
  3255. bool m_bActiveAsDefaultMenuItem:1;
  3256. bool m_bEmptyMenuItem:1;
  3257. bool m_bWindowsMenuItem:1;
  3258. bool m_bNoTabDrag:1;
  3259. bool m_bNoTabDragAutoScroll:1;
  3260. bool m_bTabCloseButton:1;
  3261. // internal
  3262. bool m_bTabCapture:1;
  3263. bool m_bTabDrag:1;
  3264. bool m_bInternalFont:1;
  3265. // Constructor/destructor
  3266. CTabViewImpl() :
  3267. m_tab(this, 1),
  3268. m_cyTabHeight(0),
  3269. m_nActivePage(-1),
  3270. m_nInsertItem(-1),
  3271. m_cchTabTextLength(30),
  3272. m_nMenuItemsCount(10),
  3273. m_lpstrTitleBarBase(NULL),
  3274. m_cchTitleBarLength(100),
  3275. m_AutoScroll(_AUTOSCROLL_NONE),
  3276. m_nCloseItem(-1),
  3277. m_bDestroyPageOnRemove(true),
  3278. m_bDestroyImageList(true),
  3279. m_bActivePageMenuItem(true),
  3280. m_bActiveAsDefaultMenuItem(false),
  3281. m_bEmptyMenuItem(false),
  3282. m_bWindowsMenuItem(false),
  3283. m_bNoTabDrag(false),
  3284. m_bNoTabDragAutoScroll(false),
  3285. m_bTabCloseButton(true),
  3286. m_bTabCapture(false),
  3287. m_bTabDrag(false),
  3288. m_bInternalFont(false)
  3289. {
  3290. m_ptStartDrag.x = 0;
  3291. m_ptStartDrag.y = 0;
  3292. }
  3293. ~CTabViewImpl()
  3294. {
  3295. delete [] m_lpstrTitleBarBase;
  3296. }
  3297. // Message filter function - to be called from PreTranslateMessage of the main window
  3298. BOOL PreTranslateMessage(MSG* pMsg)
  3299. {
  3300. if(this->IsWindow() == FALSE)
  3301. return FALSE;
  3302. BOOL bRet = FALSE;
  3303. // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page)
  3304. int nCount = GetPageCount();
  3305. if(nCount > 0)
  3306. {
  3307. bool bControl = (::GetKeyState(VK_CONTROL) < 0);
  3308. if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl)
  3309. {
  3310. if(nCount > 1)
  3311. {
  3312. int nPage = m_nActivePage;
  3313. bool bShift = (::GetKeyState(VK_SHIFT) < 0);
  3314. if(bShift)
  3315. nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1);
  3316. else
  3317. nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0;
  3318. SetActivePage(nPage);
  3319. T* pT = static_cast<T*>(this);
  3320. pT->OnPageActivated(m_nActivePage);
  3321. }
  3322. bRet = TRUE;
  3323. }
  3324. }
  3325. // If we are doing drag-drop, check for Escape key that cancels it
  3326. if(bRet == FALSE)
  3327. {
  3328. if(m_bTabCapture && (pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_ESCAPE))
  3329. {
  3330. ::ReleaseCapture();
  3331. bRet = TRUE;
  3332. }
  3333. }
  3334. // Pass the message to the active page
  3335. if(bRet == FALSE)
  3336. {
  3337. if(m_nActivePage != -1)
  3338. bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg);
  3339. }
  3340. return bRet;
  3341. }
  3342. // Attributes
  3343. int GetPageCount() const
  3344. {
  3345. ATLASSERT(::IsWindow(this->m_hWnd));
  3346. return m_tab.GetItemCount();
  3347. }
  3348. int GetActivePage() const
  3349. {
  3350. return m_nActivePage;
  3351. }
  3352. void SetActivePage(int nPage)
  3353. {
  3354. ATLASSERT(::IsWindow(this->m_hWnd));
  3355. ATLASSERT(IsValidPageIndex(nPage));
  3356. T* pT = static_cast<T*>(this);
  3357. this->SetRedraw(FALSE);
  3358. if(m_nActivePage != -1)
  3359. ::ShowWindow(GetPageHWND(m_nActivePage), SW_HIDE);
  3360. m_nActivePage = nPage;
  3361. m_tab.SetCurSel(m_nActivePage);
  3362. ::ShowWindow(GetPageHWND(m_nActivePage), SW_SHOW);
  3363. pT->UpdateLayout();
  3364. this->SetRedraw(TRUE);
  3365. this->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
  3366. HWND hWndFocus = ::GetFocus();
  3367. ATL::CWindow wndTop = this->GetTopLevelWindow();
  3368. if((hWndFocus == wndTop.m_hWnd) || ((wndTop.IsChild(hWndFocus) != FALSE) && (hWndFocus != m_tab.m_hWnd)))
  3369. ::SetFocus(GetPageHWND(m_nActivePage));
  3370. pT->UpdateTitleBar();
  3371. pT->UpdateMenu();
  3372. }
  3373. HIMAGELIST GetImageList() const
  3374. {
  3375. ATLASSERT(::IsWindow(this->m_hWnd));
  3376. return m_tab.GetImageList();
  3377. }
  3378. HIMAGELIST SetImageList(HIMAGELIST hImageList)
  3379. {
  3380. ATLASSERT(::IsWindow(this->m_hWnd));
  3381. return m_tab.SetImageList(hImageList);
  3382. }
  3383. void SetWindowMenu(HMENU hMenu)
  3384. {
  3385. ATLASSERT(::IsWindow(this->m_hWnd));
  3386. m_menu = hMenu;
  3387. T* pT = static_cast<T*>(this);
  3388. pT->UpdateMenu();
  3389. }
  3390. void SetTitleBarWindow(HWND hWnd)
  3391. {
  3392. ATLASSERT(::IsWindow(this->m_hWnd));
  3393. delete [] m_lpstrTitleBarBase;
  3394. m_lpstrTitleBarBase = NULL;
  3395. m_wndTitleBar = hWnd;
  3396. if(hWnd == NULL)
  3397. return;
  3398. int cchLen = m_wndTitleBar.GetWindowTextLength() + 1;
  3399. ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]);
  3400. if(m_lpstrTitleBarBase != NULL)
  3401. {
  3402. m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen);
  3403. T* pT = static_cast<T*>(this);
  3404. pT->UpdateTitleBar();
  3405. }
  3406. }
  3407. // Page attributes
  3408. HWND GetPageHWND(int nPage) const
  3409. {
  3410. ATLASSERT(::IsWindow(this->m_hWnd));
  3411. ATLASSERT(IsValidPageIndex(nPage));
  3412. TCITEMEXTRA tcix = {};
  3413. tcix.tciheader.mask = TCIF_PARAM;
  3414. m_tab.GetItem(nPage, tcix);
  3415. return tcix.tvpage.hWnd;
  3416. }
  3417. LPCTSTR GetPageTitle(int nPage) const
  3418. {
  3419. ATLASSERT(::IsWindow(this->m_hWnd));
  3420. ATLASSERT(IsValidPageIndex(nPage));
  3421. TCITEMEXTRA tcix = {};
  3422. tcix.tciheader.mask = TCIF_PARAM;
  3423. if(m_tab.GetItem(nPage, tcix) == FALSE)
  3424. return NULL;
  3425. return tcix.tvpage.lpstrTitle;
  3426. }
  3427. bool SetPageTitle(int nPage, LPCTSTR lpstrTitle)
  3428. {
  3429. ATLASSERT(::IsWindow(this->m_hWnd));
  3430. ATLASSERT(IsValidPageIndex(nPage));
  3431. T* pT = static_cast<T*>(this);
  3432. int cchBuff = lstrlen(lpstrTitle) + 1;
  3433. LPTSTR lpstrBuff = NULL;
  3434. ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
  3435. if(lpstrBuff == NULL)
  3436. return false;
  3437. ATL::Checked::tcscpy_s(lpstrBuff, cchBuff, lpstrTitle);
  3438. TCITEMEXTRA tcix = {};
  3439. tcix.tciheader.mask = TCIF_PARAM;
  3440. if(m_tab.GetItem(nPage, tcix) == FALSE)
  3441. return false;
  3442. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3443. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  3444. if(lpstrTabText == NULL)
  3445. return false;
  3446. delete [] tcix.tvpage.lpstrTitle;
  3447. pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
  3448. tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM;
  3449. tcix.tciheader.pszText = lpstrTabText;
  3450. tcix.tvpage.lpstrTitle = lpstrBuff;
  3451. if(m_tab.SetItem(nPage, tcix) == FALSE)
  3452. return false;
  3453. pT->UpdateTitleBar();
  3454. pT->UpdateMenu();
  3455. return true;
  3456. }
  3457. LPVOID GetPageData(int nPage) const
  3458. {
  3459. ATLASSERT(::IsWindow(this->m_hWnd));
  3460. ATLASSERT(IsValidPageIndex(nPage));
  3461. TCITEMEXTRA tcix = {};
  3462. tcix.tciheader.mask = TCIF_PARAM;
  3463. m_tab.GetItem(nPage, tcix);
  3464. return tcix.tvpage.pData;
  3465. }
  3466. LPVOID SetPageData(int nPage, LPVOID pData)
  3467. {
  3468. ATLASSERT(::IsWindow(this->m_hWnd));
  3469. ATLASSERT(IsValidPageIndex(nPage));
  3470. TCITEMEXTRA tcix = {};
  3471. tcix.tciheader.mask = TCIF_PARAM;
  3472. m_tab.GetItem(nPage, tcix);
  3473. LPVOID pDataOld = tcix.tvpage.pData;
  3474. tcix.tvpage.pData = pData;
  3475. m_tab.SetItem(nPage, tcix);
  3476. return pDataOld;
  3477. }
  3478. int GetPageImage(int nPage) const
  3479. {
  3480. ATLASSERT(::IsWindow(this->m_hWnd));
  3481. ATLASSERT(IsValidPageIndex(nPage));
  3482. TCITEMEXTRA tcix = {};
  3483. tcix.tciheader.mask = TCIF_IMAGE;
  3484. m_tab.GetItem(nPage, tcix);
  3485. return tcix.tciheader.iImage;
  3486. }
  3487. int SetPageImage(int nPage, int nImage)
  3488. {
  3489. ATLASSERT(::IsWindow(this->m_hWnd));
  3490. ATLASSERT(IsValidPageIndex(nPage));
  3491. TCITEMEXTRA tcix = {};
  3492. tcix.tciheader.mask = TCIF_IMAGE;
  3493. m_tab.GetItem(nPage, tcix);
  3494. int nImageOld = tcix.tciheader.iImage;
  3495. tcix.tciheader.iImage = nImage;
  3496. m_tab.SetItem(nPage, tcix);
  3497. return nImageOld;
  3498. }
  3499. // Operations
  3500. bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
  3501. {
  3502. return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
  3503. }
  3504. bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
  3505. {
  3506. ATLASSERT(::IsWindow(this->m_hWnd));
  3507. ATLASSERT((nPage == GetPageCount()) || IsValidPageIndex(nPage));
  3508. T* pT = static_cast<T*>(this);
  3509. int cchBuff = lstrlen(lpstrTitle) + 1;
  3510. LPTSTR lpstrBuff = NULL;
  3511. ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
  3512. if(lpstrBuff == NULL)
  3513. return false;
  3514. ATL::Checked::tcscpy_s(lpstrBuff, cchBuff, lpstrTitle);
  3515. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3516. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  3517. if(lpstrTabText == NULL)
  3518. return false;
  3519. pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
  3520. this->SetRedraw(FALSE);
  3521. TCITEMEXTRA tcix = {};
  3522. tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
  3523. tcix.tciheader.pszText = lpstrTabText;
  3524. tcix.tciheader.iImage = nImage;
  3525. tcix.tvpage.hWnd = hWndView;
  3526. tcix.tvpage.lpstrTitle = lpstrBuff;
  3527. tcix.tvpage.pData = pData;
  3528. int nItem = m_tab.InsertItem(nPage, tcix);
  3529. if(nItem == -1)
  3530. {
  3531. delete [] lpstrBuff;
  3532. this->SetRedraw(TRUE);
  3533. return false;
  3534. }
  3535. // adjust active page index, if inserted before it
  3536. if(nPage <= m_nActivePage)
  3537. m_nActivePage++;
  3538. SetActivePage(nItem);
  3539. pT->OnPageActivated(m_nActivePage);
  3540. if(GetPageCount() == 1)
  3541. pT->ShowTabControl(true);
  3542. pT->UpdateLayout();
  3543. this->SetRedraw(TRUE);
  3544. this->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
  3545. return true;
  3546. }
  3547. void RemovePage(int nPage)
  3548. {
  3549. ATLASSERT(::IsWindow(this->m_hWnd));
  3550. ATLASSERT(IsValidPageIndex(nPage));
  3551. T* pT = static_cast<T*>(this);
  3552. this->SetRedraw(FALSE);
  3553. if(GetPageCount() == 1)
  3554. pT->ShowTabControl(false);
  3555. if(m_bDestroyPageOnRemove)
  3556. ::DestroyWindow(GetPageHWND(nPage));
  3557. else
  3558. ::ShowWindow(GetPageHWND(nPage), SW_HIDE);
  3559. LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage);
  3560. delete [] lpstrTitle;
  3561. ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE);
  3562. if(m_nActivePage == nPage)
  3563. {
  3564. m_nActivePage = -1;
  3565. if(nPage > 0)
  3566. {
  3567. SetActivePage(nPage - 1);
  3568. }
  3569. else if(GetPageCount() > 0)
  3570. {
  3571. SetActivePage(nPage);
  3572. }
  3573. else
  3574. {
  3575. this->SetRedraw(TRUE);
  3576. this->Invalidate();
  3577. this->UpdateWindow();
  3578. pT->UpdateTitleBar();
  3579. pT->UpdateMenu();
  3580. }
  3581. }
  3582. else
  3583. {
  3584. nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage;
  3585. m_nActivePage = -1;
  3586. SetActivePage(nPage);
  3587. }
  3588. pT->OnPageActivated(m_nActivePage);
  3589. }
  3590. void RemoveAllPages()
  3591. {
  3592. ATLASSERT(::IsWindow(this->m_hWnd));
  3593. if(GetPageCount() == 0)
  3594. return;
  3595. T* pT = static_cast<T*>(this);
  3596. this->SetRedraw(FALSE);
  3597. pT->ShowTabControl(false);
  3598. for(int i = 0; i < GetPageCount(); i++)
  3599. {
  3600. if(m_bDestroyPageOnRemove)
  3601. ::DestroyWindow(GetPageHWND(i));
  3602. else
  3603. ::ShowWindow(GetPageHWND(i), SW_HIDE);
  3604. LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i);
  3605. delete [] lpstrTitle;
  3606. }
  3607. m_tab.DeleteAllItems();
  3608. m_nActivePage = -1;
  3609. pT->OnPageActivated(m_nActivePage);
  3610. this->SetRedraw(TRUE);
  3611. this->Invalidate();
  3612. this->UpdateWindow();
  3613. pT->UpdateTitleBar();
  3614. pT->UpdateMenu();
  3615. }
  3616. int PageIndexFromHwnd(HWND hWnd) const
  3617. {
  3618. int nIndex = -1;
  3619. for(int i = 0; i < GetPageCount(); i++)
  3620. {
  3621. if(GetPageHWND(i) == hWnd)
  3622. {
  3623. nIndex = i;
  3624. break;
  3625. }
  3626. }
  3627. return nIndex;
  3628. }
  3629. void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false)
  3630. {
  3631. ATLASSERT(::IsWindow(this->m_hWnd));
  3632. CMenuHandle menu = hMenu;
  3633. T* pT = static_cast<T*>(this);
  3634. (void)pT; // avoid level 4 warning
  3635. int nFirstPos = 0;
  3636. // Find first menu item in our range
  3637. for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++)
  3638. {
  3639. UINT nID = menu.GetMenuItemID(nFirstPos);
  3640. if(((nID >= ID_WINDOW_TABFIRST) && (nID <= ID_WINDOW_TABLAST)) || (nID == ID_WINDOW_SHOWTABLIST))
  3641. break;
  3642. }
  3643. // Remove all menu items for tab pages
  3644. BOOL bRet = TRUE;
  3645. while(bRet != FALSE)
  3646. bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION);
  3647. // Add separator if it's not already there
  3648. int nPageCount = GetPageCount();
  3649. if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0))
  3650. {
  3651. CMenuItemInfo mii;
  3652. mii.fMask = MIIM_TYPE;
  3653. menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
  3654. if((mii.fType & MFT_SEPARATOR) == 0)
  3655. {
  3656. menu.AppendMenu(MF_SEPARATOR);
  3657. nFirstPos++;
  3658. }
  3659. }
  3660. // Add menu items for all pages
  3661. if(nPageCount > 0)
  3662. {
  3663. // Append menu items for all pages
  3664. const int cchPrefix = 3; // 2 digits + space
  3665. nMenuItemsCount = __min(__min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax);
  3666. ATLASSERT(nMenuItemsCount < 100); // 2 digits only
  3667. if(nMenuItemsCount >= 100)
  3668. nMenuItemsCount = 99;
  3669. for(int i = 0; i < nMenuItemsCount; i++)
  3670. {
  3671. LPCTSTR lpstrTitle = GetPageTitle(i);
  3672. int nLen = lstrlen(lpstrTitle);
  3673. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3674. LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1);
  3675. ATLASSERT(lpstrText != NULL);
  3676. if(lpstrText != NULL)
  3677. {
  3678. LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s");
  3679. _stprintf_s(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle);
  3680. menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText);
  3681. }
  3682. }
  3683. // Mark active page
  3684. if(bActivePageMenuItem && (m_nActivePage != -1))
  3685. {
  3686. if(bActiveAsDefaultMenuItem)
  3687. {
  3688. menu.SetMenuDefaultItem((UINT)-1, TRUE);
  3689. menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE);
  3690. }
  3691. else
  3692. {
  3693. menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION);
  3694. }
  3695. }
  3696. }
  3697. else
  3698. {
  3699. if(bEmptyMenuItem)
  3700. {
  3701. menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText());
  3702. menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED);
  3703. }
  3704. // Remove separator if nothing else is there
  3705. if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0))
  3706. {
  3707. CMenuItemInfo mii;
  3708. mii.fMask = MIIM_TYPE;
  3709. menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
  3710. if((mii.fType & MFT_SEPARATOR) != 0)
  3711. menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION);
  3712. }
  3713. }
  3714. // Add "Windows..." menu item
  3715. if(bWindowsMenuItem)
  3716. menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText());
  3717. }
  3718. BOOL SubclassWindow(HWND hWnd)
  3719. {
  3720. BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
  3721. if(bRet != FALSE)
  3722. {
  3723. T* pT = static_cast<T*>(this);
  3724. pT->CreateTabControl();
  3725. pT->UpdateLayout();
  3726. }
  3727. return bRet;
  3728. }
  3729. // Message map and handlers
  3730. BEGIN_MSG_MAP(CTabViewImpl)
  3731. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  3732. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  3733. MESSAGE_HANDLER(WM_SIZE, OnSize)
  3734. MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
  3735. MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
  3736. MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
  3737. MESSAGE_HANDLER(WM_TIMER, OnTimer)
  3738. MESSAGE_HANDLER(WM_CONTEXTMENU, OnTabContextMenu)
  3739. NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
  3740. NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)
  3741. NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)
  3742. FORWARD_NOTIFICATIONS()
  3743. ALT_MSG_MAP(1) // tab control
  3744. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)
  3745. MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)
  3746. MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)
  3747. MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)
  3748. MESSAGE_HANDLER(WM_MOUSELEAVE, OnTabMouseLeave)
  3749. NOTIFY_HANDLER(T::_nCloseBtnID, TBVN_CLOSEBTNMOUSELEAVE, OnTabCloseBtnMouseLeave)
  3750. COMMAND_HANDLER(T::_nCloseBtnID, BN_CLICKED, OnTabCloseBtnClicked)
  3751. END_MSG_MAP()
  3752. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3753. {
  3754. T* pT = static_cast<T*>(this);
  3755. pT->CreateTabControl();
  3756. return 0;
  3757. }
  3758. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3759. {
  3760. RemoveAllPages();
  3761. if(m_bDestroyImageList)
  3762. {
  3763. CImageList il = m_tab.SetImageList(NULL);
  3764. if(il.m_hImageList != NULL)
  3765. il.Destroy();
  3766. }
  3767. if(m_bInternalFont)
  3768. {
  3769. HFONT hFont = m_tab.GetFont();
  3770. m_tab.SetFont(NULL, FALSE);
  3771. ::DeleteObject(hFont);
  3772. m_bInternalFont = false;
  3773. }
  3774. m_ud.m_hWnd = NULL;
  3775. return 0;
  3776. }
  3777. LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3778. {
  3779. T* pT = static_cast<T*>(this);
  3780. pT->UpdateLayout();
  3781. return 0;
  3782. }
  3783. LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3784. {
  3785. if(m_nActivePage != -1)
  3786. ::SetFocus(GetPageHWND(m_nActivePage));
  3787. return 0;
  3788. }
  3789. LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3790. {
  3791. return m_tab.SendMessage(WM_GETFONT);
  3792. }
  3793. LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  3794. {
  3795. if(m_bInternalFont)
  3796. {
  3797. HFONT hFont = m_tab.GetFont();
  3798. m_tab.SetFont(NULL, FALSE);
  3799. ::DeleteObject(hFont);
  3800. m_bInternalFont = false;
  3801. }
  3802. m_tab.SendMessage(WM_SETFONT, wParam, lParam);
  3803. T* pT = static_cast<T*>(this);
  3804. m_cyTabHeight = pT->CalcTabHeight();
  3805. if((BOOL)lParam != FALSE)
  3806. pT->UpdateLayout();
  3807. return 0;
  3808. }
  3809. LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  3810. {
  3811. if(wParam == _nAutoScrollTimerID)
  3812. {
  3813. T* pT = static_cast<T*>(this);
  3814. pT->DoAutoScroll();
  3815. }
  3816. else
  3817. {
  3818. bHandled = FALSE;
  3819. }
  3820. return 0;
  3821. }
  3822. LRESULT OnTabContextMenu(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  3823. {
  3824. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  3825. int nPage = m_nActivePage;
  3826. bool bAction = false;
  3827. if((HWND)wParam == m_tab.m_hWnd)
  3828. {
  3829. if((pt.x == -1) && (pt.y == -1)) // keyboard
  3830. {
  3831. RECT rect = {};
  3832. m_tab.GetItemRect(m_nActivePage, &rect);
  3833. pt.x = rect.left;
  3834. pt.y = rect.bottom;
  3835. m_tab.ClientToScreen(&pt);
  3836. bAction = true;
  3837. }
  3838. else if(::WindowFromPoint(pt) == m_tab.m_hWnd)
  3839. {
  3840. TCHITTESTINFO hti = {};
  3841. hti.pt = pt;
  3842. this->ScreenToClient(&hti.pt);
  3843. nPage = m_tab.HitTest(&hti);
  3844. bAction = true;
  3845. }
  3846. }
  3847. if(bAction)
  3848. {
  3849. T* pT = static_cast<T*>(this);
  3850. pT->OnContextMenu(nPage, pt);
  3851. }
  3852. else
  3853. {
  3854. bHandled = FALSE;
  3855. }
  3856. return 0;
  3857. }
  3858. LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
  3859. {
  3860. if(m_bTabCloseButton && (m_btnClose.m_hWnd != NULL))
  3861. {
  3862. T* pT = static_cast<T*>(this);
  3863. RECT rcClose = {};
  3864. pT->CalcCloseButtonRect(m_nCloseItem, rcClose);
  3865. m_btnClose.SetWindowPos(NULL, &rcClose, SWP_NOZORDER | SWP_NOACTIVATE);
  3866. }
  3867. SetActivePage(m_tab.GetCurSel());
  3868. T* pT = static_cast<T*>(this);
  3869. pT->OnPageActivated(m_nActivePage);
  3870. return 0;
  3871. }
  3872. LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
  3873. {
  3874. // nothing to do - this just blocks all tab control
  3875. // notifications from being propagated further
  3876. return 0;
  3877. }
  3878. LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
  3879. {
  3880. LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh;
  3881. if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips())
  3882. {
  3883. T* pT = static_cast<T*>(this);
  3884. pT->UpdateTooltipText(pTTDI);
  3885. }
  3886. else
  3887. {
  3888. bHandled = FALSE;
  3889. }
  3890. return 0;
  3891. }
  3892. // Tab control message handlers
  3893. LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3894. {
  3895. if(!m_bNoTabDrag && (m_tab.GetItemCount() > 1))
  3896. {
  3897. m_bTabCapture = true;
  3898. m_tab.SetCapture();
  3899. m_ptStartDrag.x = GET_X_LPARAM(lParam);
  3900. m_ptStartDrag.y = GET_Y_LPARAM(lParam);
  3901. }
  3902. bHandled = FALSE;
  3903. return 0;
  3904. }
  3905. LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3906. {
  3907. if(m_bTabCapture)
  3908. {
  3909. if(m_bTabDrag)
  3910. {
  3911. T* pT = static_cast<T*>(this);
  3912. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  3913. int nItem = pT->DragHitTest(pt);
  3914. if(nItem != -1)
  3915. MovePage(m_nActivePage, nItem);
  3916. }
  3917. ::ReleaseCapture();
  3918. }
  3919. bHandled = FALSE;
  3920. return 0;
  3921. }
  3922. LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  3923. {
  3924. if(m_bTabCapture)
  3925. {
  3926. m_bTabCapture = false;
  3927. if(m_bTabDrag)
  3928. {
  3929. m_bTabDrag = false;
  3930. T* pT = static_cast<T*>(this);
  3931. if(!m_bNoTabDragAutoScroll)
  3932. pT->StartStopAutoScroll(-1);
  3933. pT->DrawMoveMark(-1);
  3934. m_ilDrag.DragLeave(GetDesktopWindow());
  3935. m_ilDrag.EndDrag();
  3936. m_ilDrag.Destroy();
  3937. m_ilDrag.m_hImageList = NULL;
  3938. }
  3939. }
  3940. bHandled = FALSE;
  3941. return 0;
  3942. }
  3943. LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3944. {
  3945. bHandled = FALSE;
  3946. if(m_bTabCapture)
  3947. {
  3948. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  3949. if(!m_bTabDrag)
  3950. {
  3951. if((abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG)) ||
  3952. (abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG)))
  3953. {
  3954. T* pT = static_cast<T*>(this);
  3955. pT->GenerateDragImage(m_nActivePage);
  3956. int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);
  3957. int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);
  3958. m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));
  3959. POINT ptEnter = m_ptStartDrag;
  3960. m_tab.ClientToScreen(&ptEnter);
  3961. m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);
  3962. m_bTabDrag = true;
  3963. }
  3964. }
  3965. if(m_bTabDrag)
  3966. {
  3967. T* pT = static_cast<T*>(this);
  3968. int nItem = pT->DragHitTest(pt);
  3969. pT->SetMoveCursor(nItem != -1);
  3970. if(m_nInsertItem != nItem)
  3971. pT->DrawMoveMark(nItem);
  3972. if(!m_bNoTabDragAutoScroll)
  3973. pT->StartStopAutoScroll(pt.x);
  3974. m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);
  3975. m_tab.ClientToScreen(&pt);
  3976. m_ilDrag.DragMove(pt);
  3977. bHandled = TRUE;
  3978. }
  3979. }
  3980. else if(m_bTabCloseButton)
  3981. {
  3982. TCHITTESTINFO thti = {};
  3983. thti.pt.x = GET_X_LPARAM(lParam);
  3984. thti.pt.y = GET_Y_LPARAM(lParam);
  3985. int nItem = m_tab.HitTest(&thti);
  3986. if(nItem >= 0)
  3987. {
  3988. ATLTRACE(_T("+++++ item = %i\n"), nItem);
  3989. T* pT = static_cast<T*>(this);
  3990. if(m_btnClose.m_hWnd == NULL)
  3991. {
  3992. pT->CreateCloseButton(nItem);
  3993. m_nCloseItem = nItem;
  3994. }
  3995. else if(m_nCloseItem != nItem)
  3996. {
  3997. RECT rcClose = {};
  3998. pT->CalcCloseButtonRect(nItem, rcClose);
  3999. m_btnClose.SetWindowPos(NULL, &rcClose, SWP_NOZORDER | SWP_NOACTIVATE);
  4000. m_nCloseItem = nItem;
  4001. }
  4002. TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_tab.m_hWnd };
  4003. ::TrackMouseEvent(&tme);
  4004. }
  4005. }
  4006. return 0;
  4007. }
  4008. LRESULT OnTabMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  4009. {
  4010. bHandled = FALSE;
  4011. if(m_btnClose.m_hWnd != NULL)
  4012. {
  4013. POINT pt = {};
  4014. ::GetCursorPos(&pt);
  4015. RECT rect = {};
  4016. m_btnClose.GetWindowRect(&rect);
  4017. if(::PtInRect(&rect, pt) == FALSE)
  4018. {
  4019. m_nCloseItem = -1;
  4020. T* pT = static_cast<T*>(this);
  4021. pT->DestroyCloseButton();
  4022. }
  4023. else
  4024. {
  4025. bHandled = TRUE;
  4026. }
  4027. }
  4028. return 0;
  4029. }
  4030. LRESULT OnTabCloseBtnMouseLeave(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
  4031. {
  4032. TCHITTESTINFO thti = {};
  4033. ::GetCursorPos(&thti.pt);
  4034. m_tab.ScreenToClient(&thti.pt);
  4035. int nItem = m_tab.HitTest(&thti);
  4036. if(nItem == -1)
  4037. m_tab.SendMessage(WM_MOUSELEAVE);
  4038. return 0;
  4039. }
  4040. LRESULT OnTabCloseBtnClicked(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
  4041. {
  4042. T* pT = static_cast<T*>(this);
  4043. pT->OnTabCloseBtn(m_nCloseItem);
  4044. return 0;
  4045. }
  4046. // Implementation helpers
  4047. bool IsValidPageIndex(int nPage) const
  4048. {
  4049. return ((nPage >= 0) && (nPage < GetPageCount()));
  4050. }
  4051. bool MovePage(int nMovePage, int nInsertBeforePage)
  4052. {
  4053. ATLASSERT(IsValidPageIndex(nMovePage));
  4054. ATLASSERT(IsValidPageIndex(nInsertBeforePage));
  4055. if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage))
  4056. return false;
  4057. if(nMovePage == nInsertBeforePage)
  4058. return true; // nothing to do
  4059. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  4060. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  4061. if(lpstrTabText == NULL)
  4062. return false;
  4063. TCITEMEXTRA tcix = {};
  4064. tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
  4065. tcix.tciheader.pszText = lpstrTabText;
  4066. tcix.tciheader.cchTextMax = m_cchTabTextLength + 1;
  4067. BOOL bRet = m_tab.GetItem(nMovePage, tcix);
  4068. ATLASSERT(bRet != FALSE);
  4069. if(bRet == FALSE)
  4070. return false;
  4071. int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage;
  4072. int nNewItem = m_tab.InsertItem(nInsertItem, tcix);
  4073. ATLASSERT(nNewItem == nInsertItem);
  4074. if(nNewItem != nInsertItem)
  4075. {
  4076. ATLVERIFY(m_tab.DeleteItem(nNewItem));
  4077. return false;
  4078. }
  4079. if(nMovePage > nInsertBeforePage)
  4080. ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE);
  4081. else if(nMovePage < nInsertBeforePage)
  4082. ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE);
  4083. SetActivePage(nInsertBeforePage);
  4084. T* pT = static_cast<T*>(this);
  4085. pT->OnPageActivated(m_nActivePage);
  4086. return true;
  4087. }
  4088. // Implementation overrideables
  4089. bool CreateTabControl()
  4090. {
  4091. m_tab.Create(this->m_hWnd, this->rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID);
  4092. ATLASSERT(m_tab.m_hWnd != NULL);
  4093. if(m_tab.m_hWnd == NULL)
  4094. return false;
  4095. m_tab.SetFont(AtlCreateControlFont());
  4096. m_bInternalFont = true;
  4097. m_tab.SetItemExtra(sizeof(TABVIEWPAGE));
  4098. T* pT = static_cast<T*>(this);
  4099. m_cyTabHeight = pT->CalcTabHeight();
  4100. return true;
  4101. }
  4102. int CalcTabHeight()
  4103. {
  4104. int nCount = m_tab.GetItemCount();
  4105. TCHAR szText[] = _T("NS");
  4106. TCITEMEXTRA tcix = {};
  4107. tcix.tciheader.mask = TCIF_TEXT;
  4108. tcix.tciheader.pszText = szText;
  4109. int nIndex = m_tab.InsertItem(nCount, tcix);
  4110. RECT rect = { 0, 0, 1000, 1000 };
  4111. m_tab.AdjustRect(FALSE, &rect);
  4112. RECT rcWnd = { 0, 0, 1000, rect.top };
  4113. ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle());
  4114. int nHeight = rcWnd.bottom - rcWnd.top;
  4115. m_tab.DeleteItem(nIndex);
  4116. return nHeight;
  4117. }
  4118. void ShowTabControl(bool bShow)
  4119. {
  4120. m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
  4121. T* pT = static_cast<T*>(this);
  4122. pT->UpdateLayout();
  4123. }
  4124. void UpdateLayout()
  4125. {
  4126. RECT rect = {};
  4127. this->GetClientRect(&rect);
  4128. int cyOffset = 0;
  4129. if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
  4130. {
  4131. m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);
  4132. cyOffset = m_cyTabHeight;
  4133. }
  4134. if(m_nActivePage != -1)
  4135. ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, cyOffset, rect.right - rect.left, rect.bottom - rect.top - cyOffset, SWP_NOZORDER);
  4136. }
  4137. void UpdateMenu()
  4138. {
  4139. if(m_menu.m_hMenu != NULL)
  4140. BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem);
  4141. }
  4142. void UpdateTitleBar()
  4143. {
  4144. if(!m_wndTitleBar.IsWindow() || (m_lpstrTitleBarBase == NULL))
  4145. return; // nothing to do
  4146. if(m_nActivePage != -1)
  4147. {
  4148. T* pT = static_cast<T*>(this);
  4149. LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage);
  4150. LPCTSTR lpstrDivider = pT->GetTitleDividerText();
  4151. int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1;
  4152. ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  4153. LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer);
  4154. ATLASSERT(lpstrPageTitle != NULL);
  4155. if(lpstrPageTitle != NULL)
  4156. {
  4157. pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1);
  4158. ATL::Checked::tcscat_s(lpstrPageTitle, cchBuffer, lpstrDivider);
  4159. ATL::Checked::tcscat_s(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase);
  4160. }
  4161. else
  4162. {
  4163. lpstrPageTitle = m_lpstrTitleBarBase;
  4164. }
  4165. m_wndTitleBar.SetWindowText(lpstrPageTitle);
  4166. }
  4167. else
  4168. {
  4169. m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase);
  4170. }
  4171. }
  4172. void DrawMoveMark(int nItem)
  4173. {
  4174. T* pT = static_cast<T*>(this);
  4175. if(m_nInsertItem != -1)
  4176. {
  4177. RECT rect = {};
  4178. pT->GetMoveMarkRect(rect);
  4179. m_tab.InvalidateRect(&rect);
  4180. }
  4181. m_nInsertItem = nItem;
  4182. if(m_nInsertItem != -1)
  4183. {
  4184. CClientDC dc(m_tab.m_hWnd);
  4185. RECT rect = {};
  4186. pT->GetMoveMarkRect(rect);
  4187. CPen pen;
  4188. pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT));
  4189. CBrush brush;
  4190. brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
  4191. HPEN hPenOld = dc.SelectPen(pen);
  4192. HBRUSH hBrushOld = dc.SelectBrush(brush);
  4193. int x = rect.left;
  4194. int y = rect.top;
  4195. POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } };
  4196. dc.Polygon(ptsTop, 3);
  4197. y = rect.bottom - 1;
  4198. POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } };
  4199. dc.Polygon(ptsBottom, 3);
  4200. dc.SelectPen(hPenOld);
  4201. dc.SelectBrush(hBrushOld);
  4202. }
  4203. }
  4204. void GetMoveMarkRect(RECT& rect) const
  4205. {
  4206. m_tab.GetClientRect(&rect);
  4207. RECT rcItem = {};
  4208. m_tab.GetItemRect(m_nInsertItem, &rcItem);
  4209. if(m_nInsertItem <= m_nActivePage)
  4210. {
  4211. rect.left = rcItem.left - m_cxMoveMark / 2 - 1;
  4212. rect.right = rcItem.left + m_cxMoveMark / 2;
  4213. }
  4214. else
  4215. {
  4216. rect.left = rcItem.right - m_cxMoveMark / 2 - 1;
  4217. rect.right = rcItem.right + m_cxMoveMark / 2;
  4218. }
  4219. }
  4220. void SetMoveCursor(bool bCanMove)
  4221. {
  4222. ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO));
  4223. }
  4224. void GenerateDragImage(int nItem)
  4225. {
  4226. ATLASSERT(IsValidPageIndex(nItem));
  4227. RECT rcItem = {};
  4228. m_tab.GetItemRect(nItem, &rcItem);
  4229. ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item
  4230. ATLASSERT(m_ilDrag.m_hImageList == NULL);
  4231. m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);
  4232. CClientDC dc(this->m_hWnd);
  4233. CDC dcMem;
  4234. dcMem.CreateCompatibleDC(dc);
  4235. ATLASSERT(dcMem.m_hDC != NULL);
  4236. dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);
  4237. CBitmap bmp;
  4238. bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);
  4239. ATLASSERT(bmp.m_hBitmap != NULL);
  4240. HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);
  4241. m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);
  4242. dcMem.SelectBitmap(hBmpOld);
  4243. ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);
  4244. }
  4245. void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle)
  4246. {
  4247. if(lstrlen(lpstrTitle) >= cchShortTitle)
  4248. {
  4249. LPCTSTR lpstrEllipsis = _T("...");
  4250. int cchEllipsis = lstrlen(lpstrEllipsis);
  4251. ATL::Checked::tcsncpy_s(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1);
  4252. ATL::Checked::tcscat_s(lpstrShortTitle, cchShortTitle, lpstrEllipsis);
  4253. }
  4254. else
  4255. {
  4256. ATL::Checked::tcscpy_s(lpstrShortTitle, cchShortTitle, lpstrTitle);
  4257. }
  4258. }
  4259. void UpdateTooltipText(LPNMTTDISPINFO pTTDI)
  4260. {
  4261. ATLASSERT(pTTDI != NULL);
  4262. pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom);
  4263. }
  4264. int DragHitTest(POINT pt) const
  4265. {
  4266. RECT rect = {};
  4267. this->GetClientRect(&rect);
  4268. if(::PtInRect(&rect, pt) == FALSE)
  4269. return -1;
  4270. m_tab.GetClientRect(&rect);
  4271. TCHITTESTINFO hti = {};
  4272. hti.pt.x = pt.x;
  4273. hti.pt.y = rect.bottom / 2; // use middle to ignore
  4274. int nItem = m_tab.HitTest(&hti);
  4275. if(nItem == -1)
  4276. {
  4277. int nLast = m_tab.GetItemCount() - 1;
  4278. RECT rcItem = {};
  4279. m_tab.GetItemRect(nLast, &rcItem);
  4280. if(pt.x >= rcItem.right)
  4281. nItem = nLast;
  4282. }
  4283. return nItem;
  4284. }
  4285. void StartStopAutoScroll(int x)
  4286. {
  4287. AutoScroll scroll = _AUTOSCROLL_NONE;
  4288. if(x != -1)
  4289. {
  4290. RECT rect = {};
  4291. m_tab.GetClientRect(&rect);
  4292. int dx = ::GetSystemMetrics(SM_CXVSCROLL);
  4293. if((x >= 0) && (x < dx))
  4294. {
  4295. RECT rcItem = {};
  4296. m_tab.GetItemRect(0, &rcItem);
  4297. if(rcItem.left < rect.left)
  4298. scroll = _AUTOSCROLL_LEFT;
  4299. }
  4300. else if((x >= (rect.right - dx)) && (x < rect.right))
  4301. {
  4302. RECT rcItem = {};
  4303. m_tab.GetItemRect(m_tab.GetItemCount() - 1, &rcItem);
  4304. if(rcItem.right > rect.right)
  4305. scroll = _AUTOSCROLL_RIGHT;
  4306. }
  4307. }
  4308. if(scroll != _AUTOSCROLL_NONE)
  4309. {
  4310. if(m_ud.m_hWnd == NULL)
  4311. m_ud = m_tab.GetWindow(GW_CHILD);
  4312. if(m_AutoScroll != scroll)
  4313. {
  4314. m_AutoScroll = scroll;
  4315. this->SetTimer(_nAutoScrollTimerID, 300);
  4316. }
  4317. }
  4318. else
  4319. {
  4320. this->KillTimer(_nAutoScrollTimerID);
  4321. m_AutoScroll = _AUTOSCROLL_NONE;
  4322. }
  4323. }
  4324. void DoAutoScroll()
  4325. {
  4326. ATLASSERT(m_AutoScroll != _AUTOSCROLL_NONE);
  4327. int nMin = -1, nMax = -1;
  4328. m_ud.GetRange(nMin, nMax);
  4329. int nPos = m_ud.GetPos();
  4330. int nNewPos = -1;
  4331. if((m_AutoScroll == _AUTOSCROLL_LEFT) && (nPos > nMin))
  4332. nNewPos = nPos - 1;
  4333. else if((m_AutoScroll == _AUTOSCROLL_RIGHT) && (nPos < nMax))
  4334. nNewPos = nPos + 1;
  4335. if(nNewPos != -1)
  4336. {
  4337. m_tab.SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, nNewPos));
  4338. m_tab.SendMessage(WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0));
  4339. POINT pt = {};
  4340. ::GetCursorPos(&pt);
  4341. m_tab.ScreenToClient(&pt);
  4342. m_tab.SendMessage(WM_MOUSEMOVE, NULL, MAKELPARAM(pt.x, pt.y));
  4343. }
  4344. }
  4345. // Text for menu items and title bar - override to provide different strings
  4346. static LPCTSTR GetEmptyListText()
  4347. {
  4348. return _T("(Empty)");
  4349. }
  4350. static LPCTSTR GetWindowsMenuItemText()
  4351. {
  4352. return _T("&Windows...");
  4353. }
  4354. static LPCTSTR GetTitleDividerText()
  4355. {
  4356. return _T(" - ");
  4357. }
  4358. // Notifications - override to provide different behavior
  4359. void OnPageActivated(int nPage)
  4360. {
  4361. NMHDR nmhdr = {};
  4362. nmhdr.hwndFrom = this->m_hWnd;
  4363. nmhdr.idFrom = nPage;
  4364. nmhdr.code = TBVN_PAGEACTIVATED;
  4365. this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&nmhdr);
  4366. }
  4367. void OnContextMenu(int nPage, POINT pt)
  4368. {
  4369. TBVCONTEXTMENUINFO cmi = {};
  4370. cmi.hdr.hwndFrom = this->m_hWnd;
  4371. cmi.hdr.idFrom = nPage;
  4372. cmi.hdr.code = TBVN_CONTEXTMENU;
  4373. cmi.pt = pt;
  4374. this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&cmi);
  4375. }
  4376. void OnTabCloseBtn(int nPage)
  4377. {
  4378. NMHDR nmhdr = {};
  4379. nmhdr.hwndFrom = this->m_hWnd;
  4380. nmhdr.idFrom = nPage;
  4381. nmhdr.code = TBVN_TABCLOSEBTN;
  4382. LRESULT lRet = this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&nmhdr);
  4383. if(lRet == 0) // default - close page
  4384. {
  4385. T* pT = static_cast<T*>(this);
  4386. pT->RemovePage(m_nCloseItem);
  4387. m_nCloseItem = -1;
  4388. pT->DestroyCloseButton();
  4389. }
  4390. else
  4391. {
  4392. m_tab.SendMessage(WM_MOUSELEAVE);
  4393. }
  4394. }
  4395. // Close button overrideables
  4396. void CreateCloseButton(int nItem)
  4397. {
  4398. ATLASSERT(m_btnClose.m_hWnd == NULL);
  4399. m_btnClose.m_bPressed = false;
  4400. T* pT = static_cast<T*>(this);
  4401. RECT rcClose = {};
  4402. pT->CalcCloseButtonRect(nItem, rcClose);
  4403. m_btnClose.Create(m_tab.m_hWnd, rcClose, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, T::_nCloseBtnID);
  4404. ATLASSERT(m_btnClose.IsWindow());
  4405. if(m_btnClose.m_hWnd != NULL)
  4406. {
  4407. // create a tool tip
  4408. ATLASSERT(m_btnClose.m_tip.m_hWnd == NULL);
  4409. m_btnClose.m_tip.Create(m_btnClose.m_hWnd);
  4410. ATLASSERT(m_btnClose.m_tip.IsWindow());
  4411. if(m_btnClose.m_tip.IsWindow())
  4412. {
  4413. m_btnClose.m_tip.Activate(TRUE);
  4414. RECT rect = {};
  4415. m_btnClose.GetClientRect(&rect);
  4416. m_btnClose.m_tip.AddTool(m_btnClose.m_hWnd, LPSTR_TEXTCALLBACK, &rect, T::_nCloseBtnID);
  4417. }
  4418. }
  4419. }
  4420. void DestroyCloseButton()
  4421. {
  4422. ATLASSERT(m_btnClose.m_hWnd != NULL);
  4423. if(m_btnClose.m_hWnd != NULL)
  4424. {
  4425. if(m_btnClose.m_tip.IsWindow())
  4426. {
  4427. m_btnClose.m_tip.DestroyWindow();
  4428. m_btnClose.m_tip.m_hWnd = NULL;
  4429. }
  4430. m_btnClose.DestroyWindow();
  4431. }
  4432. }
  4433. void CalcCloseButtonRect(int nItem, RECT& rcClose)
  4434. {
  4435. RECT rcItem = {};
  4436. m_tab.GetItemRect(nItem, &rcItem);
  4437. int cy = (rcItem.bottom - rcItem.top - _cyCloseBtn) / 2;
  4438. int cx = (nItem == m_tab.GetCurSel()) ? _cxCloseBtnMarginSel : _cxCloseBtnMargin;
  4439. ::SetRect(&rcClose, rcItem.right - cx - _cxCloseBtn, rcItem.top + cy,
  4440. rcItem.right - cx, rcItem.top + cy + _cyCloseBtn);
  4441. }
  4442. };
  4443. class CTabView : public CTabViewImpl<CTabView>
  4444. {
  4445. public:
  4446. DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE)
  4447. };
  4448. } // namespace WTL
  4449. #endif // __ATLCTRLX_H__