ThemeMgr.pas 110 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168
  1. unit ThemeMgr;
  2. //----------------------------------------------------------------------------------------------------------------------
  3. // Version 1.10.1
  4. //
  5. // Windows XP Theme Manager is freeware. You may freely use it in any software, including commercial software, provided
  6. // you accept the following conditions:
  7. //
  8. // 1) The software may not be included into component collections and similar compilations which are sold. If you want
  9. // to distribute this software for money then contact me first and ask for my permission.
  10. // 2) My copyright notices in the source code may not be removed or modified.
  11. // 3) If you modify and/or distribute the code to any third party then you must not veil the original author. It must
  12. // always be clearly identifiable that I, Mike Lischke, am the original author.
  13. // Although it is not required it would be a nice move to recognize my work by adding a citation to the application's
  14. // about box or a similar place.
  15. //
  16. // The original code is ThemeMgr.pas, released 01. January 2002.
  17. //
  18. // The initial developer of the original code is:
  19. // Mike Lischke ([email protected], www.soft-gems.net).
  20. //
  21. // Portions created by Mike Lischke are
  22. // (C) 2001-2005 Mike Lischke. All Rights Reserved.
  23. //----------------------------------------------------------------------------------------------------------------------
  24. //
  25. // This unit contains the implementation of TThemeManager which is designed to fix certain VCL components to enable
  26. // XP theme support in Delphi and BCB applications (version 6 and lower).
  27. //
  28. // TThemeManager uses global theming (all windows in the application use the same theme). Hence you don't
  29. // need more than one instance in an application (except for DLLs). Having more than one instance in the same module
  30. // (application, DLL) will disable subclassing of controls by all other but the first instance.
  31. //
  32. // Note: If you are using a Theme Manager in a DLL then make sure the handle of the application object in the DLL (which
  33. // is usually not allocated) is set to that of the main application, e.g. by passing it via an exported function.
  34. //----------------------------------------------------------------------------------------------------------------------
  35. //
  36. // January 2005
  37. // - Bug fix: Test for Windows XP was wrong.
  38. //
  39. // For full development history see help file.
  40. //
  41. // Credits for their valuable help go to:
  42. // Bert Moorthaemer, Rob Schoenaker, John W. Long, Vassiliev V.V., Steve Moss, Torsten Detsch, Milan Vandrovec
  43. //----------------------------------------------------------------------------------------------------------------------
  44. interface
  45. {$I Compilers.inc}
  46. {$ifdef COMPILER_7_UP}
  47. ATTENTION! Theme support is already included in this Borland product.
  48. Remove the Delphi Gems Theme Manager from your project to compile it correctly!
  49. {$endif COMPILER_7_UP}
  50. // The CheckListSupport switch is used to remove support for TCheckListBox. The main reason for this
  51. // is that TCheckListBox is in a special package (VCLX??.dpk), which you may not want to have included
  52. // (particularly when using runtime packages). Disable the switch to remove the link to the package
  53. // and remove the package reference from the ThemeManagerX.dpk file).
  54. {$define CheckListSupport}
  55. uses
  56. Windows, Classes, Messages, Graphics, Controls, StdCtrls, Buttons, Forms,
  57. ThemeSrv;
  58. const
  59. TMVersion = '1.10.1';
  60. // Sent to any control to give it a chance to deny its subclassing. This is mainly useful for controls
  61. // which are derived from classes which are usually subclassed by the Theme Manager but do their own
  62. // painting. A control should return a value <> 0 if subclassing should not be done.
  63. CM_DENYSUBCLASSING = CM_BASE + 2000;
  64. {$ifndef COMPILER_5_UP}
  65. {$EXTERNALSYM WM_CHANGEUISTATE}
  66. WM_CHANGEUISTATE = $0127;
  67. {$EXTERNALSYM WM_UPDATEUISTATE}
  68. WM_UPDATEUISTATE = $0128;
  69. {$EXTERNALSYM WM_QUERYUISTATE}
  70. WM_QUERYUISTATE = $0129;
  71. UIS_CLEAR = 2;
  72. UISF_HIDEFOCUS = 1;
  73. UISF_HIDEACCEL = 2;
  74. {$endif COMPILER_5_UP}
  75. // These constants are not defined in Delphi/BCB 6 or lower.
  76. SPI_GETFOCUSBORDERWIDTH = $200E;
  77. SPI_SETFOCUSBORDERWIDTH = $200F;
  78. SPI_GETFOCUSBORDERHEIGHT = $2010;
  79. SPI_SETFOCUSBORDERHEIGHT = $2011;
  80. type
  81. TThemeOption = (
  82. toAllowNonClientArea, // Specifies that the nonclient areas of application windows will have visual styles applied.
  83. toAllowControls, // Specifies that the controls used in an application will have visual styles applied.
  84. toAllowWebContent, // Specifies that Web content displayed in an application will have visual styles applied.
  85. toSubclassAnimate, // Enables subclassing of TAnimate controls (themed painting does not correctly work).
  86. toSubclassButtons, // Enables subclassing of button controls (also checkbox, radio button).
  87. toSubclassCheckListbox, // Enables subclassing of TCheckListBox.
  88. toSubclassDBLookup, // Enables subclassing of TDBLookupControl. Only used in TThemeManagerDB.
  89. toSubclassFrame, // Enables subclassing of frames (only available in Delphi 5 or higher).
  90. toSubclassGroupBox, // Enables subclassing of group box controls.
  91. toSubclassListView, // Enables subclassing of listview controls (including report mode bug fix).
  92. toSubclassPanel, // Enables subclassing of panels.
  93. toSubclassTabSheet, // Enables subclassing of tab sheet controls.
  94. toSubclassSpeedButtons, // Enables subclassing of speed button controls.
  95. toSubclassSplitter, // Enables subclassing of splitter controls.
  96. toSubclassStatusBar, // Enables subclassing of status bar controls.
  97. toSubclassTrackBar, // Enables subclassing of track bar controls (slight paint problems, though).
  98. toSubclassWinControl, // Enables subclassing of all window controls not belonging to any of the other classes.
  99. toResetMouseCapture, // If set then TToolButtons get their csCaptureMouse flag removed to properly show
  100. // their pressed state.
  101. toSetTransparency, // If set then TCustomLabel and TToolBar controls are automatically set to transparent.
  102. toAlternateTabSheetDraw // If set then use alternate drawing for TTabSheet body.
  103. );
  104. TThemeOptions = set of TThemeOption;
  105. const
  106. DefaultThemeOptions = [toAllowNonClientArea..toAllowWebContent, toSubclassButtons..toSetTransparency];
  107. type
  108. // These message records are not declared in Delphi 6 and lower.
  109. TWMPrint = packed record
  110. Msg: Cardinal;
  111. DC: HDC;
  112. Flags: Cardinal;
  113. Result: Integer;
  114. end;
  115. TWMPrintClient = TWMPrint;
  116. TThemeManager = class;
  117. TAllowSubclassingEvent = procedure(Sender: TThemeManager; Control: TControl; var Allow: Boolean) of object;
  118. TControlMessageEvent = procedure(Sender: TThemeManager; Control: TControl; var Message: TMessage;
  119. var Handled: Boolean) of object;
  120. PControlMessageEvent = ^TControlMessageEvent;
  121. // The window procedure list maintains the connections between control instances and their old window procedures.
  122. TWindowProcList = class(TList)
  123. private
  124. FDirty: Boolean;
  125. FLastControl: TControl;
  126. FLastIndex: Integer;
  127. FOwner: TThemeManager;
  128. FNewWindowProc: TWndMethod; // The new window procedure which handles the corrections for the control class.
  129. FControlClass: TControlClass; // The class for which this list is responsible.
  130. public
  131. constructor Create(Owner: TThemeManager; WindowProc: TWndMethod; ControlClass: TControlClass);
  132. destructor Destroy; override;
  133. function Add(Control: TControl): Integer;
  134. procedure Clear; override;
  135. procedure DispatchMessage(Control: TControl; var Message: TMessage);
  136. function Find(Control: TControl; out Index: Integer): Boolean;
  137. procedure Remove(Control: TControl);
  138. end;
  139. // TThemeManager is a class whose primary task is to fix various issues which show up when an application
  140. // is themed.
  141. TThemeManager = class(TComponent)
  142. private
  143. FOptions: TThemeOptions; // Determines which parts are allowed to be themed.
  144. FPanelList,
  145. {$ifdef COMPILER_5_UP}
  146. FFrameList, // Frames are first available in Delphi 5.
  147. {$endif COMPILER_5_UP}
  148. FListViewList,
  149. FTabSheetList,
  150. FWinControlList,
  151. FGroupBoxList,
  152. FButtonControlList,
  153. // MP
  154. FCheckBoxList,
  155. FButtonList,
  156. FSpeedButtonList,
  157. FSplitterList,
  158. FTrackBarList,
  159. FAnimateList,
  160. FStatusBarList,
  161. {$ifdef CheckListSupport}
  162. FCheckListBoxList,
  163. {$endif CheckListSupport}
  164. FFormList: TWindowProcList;
  165. FListeners: TList;
  166. FPendingFormsList: TList;
  167. FPendingRecreationList: TList;
  168. FSubclassingDisabled: Boolean; // Disable subclassing generally (e.g. for multi instancing).
  169. FHookWasInstalled: Boolean;
  170. FOnThemeChange: TNotifyEvent; // Called when the Windows theme or an application option has changed.
  171. FOnControlMessage: TControlMessageEvent;
  172. FOnAllowSubclassing: TAllowSubclassingEvent;
  173. procedure AnimateWindowProc(Control: TControl; var Message: TMessage);
  174. procedure ButtonControlWindowProc(Control: TControl; var Message: TMessage; { MP } List: TWindowProcList);
  175. {$ifdef CheckListSupport}
  176. procedure CheckListBoxWindowProc(Control: TControl; var Message: TMessage);
  177. {$endif CheckListSupport}
  178. procedure FormWindowProc(Control: TControl; var Message: TMessage);
  179. {$ifdef COMPILER_5_UP}
  180. procedure FrameWindowProc(Control: TControl; var Message: TMessage);
  181. {$endif COMPILER_5_UP}
  182. function GetIsMainManager: Boolean;
  183. procedure GroupBoxWindowProc(Control: TControl; var Message: TMessage);
  184. procedure ListviewWindowProc(Control: TControl; var Message: TMessage);
  185. function MainWindowHook(var Message: TMessage): Boolean;
  186. procedure PanelWindowProc(Control: TControl; var Message: TMessage);
  187. procedure SetThemeOptions(const Value: TThemeOptions);
  188. procedure SpeedButtonWindowProc(Control: TControl; var Message: TMessage);
  189. procedure SplitterWindowProc(Control: TControl; var Message: TMessage);
  190. procedure StatusBarWindowProc(Control: TControl; var Message: TMessage);
  191. procedure TabSheetWindowProc(Control: TControl; var Message: TMessage);
  192. procedure TrackBarWindowProc(Control: TControl; var Message: TMessage);
  193. procedure WinControlWindowProc(Control: TControl; var Message: TMessage);
  194. procedure PreAnimateWindowProc(var Message: TMessage);
  195. procedure PreButtonControlWindowProc(var Message: TMessage);
  196. // MP BEGIN
  197. procedure PreCheckBoxWindowProc(var Message: TMessage);
  198. procedure PreButtonWindowProc(var Message: TMessage);
  199. // MP END
  200. {$ifdef CheckListSupport}
  201. procedure PreCheckListBoxWindowProc(var Message: TMessage);
  202. {$endif CheckListSupport}
  203. procedure PreFormWindowProc(var Message: TMessage);
  204. {$ifdef COMPILER_5_UP}
  205. procedure PreFrameWindowProc(var Message: TMessage);
  206. {$endif COMPILER_5_UP}
  207. procedure PreGroupBoxWindowProc(var Message: TMessage);
  208. procedure PreListviewWindowProc(var Message: TMessage);
  209. procedure PrePanelWindowProc(var Message: TMessage);
  210. procedure PreSpeedButtonWindowProc(var Message: TMessage);
  211. procedure PreSplitterWindowProc(var Message: TMessage);
  212. procedure PreStatusBarWindowProc(var Message: TMessage);
  213. procedure PreTabSheetWindowProc(var Message: TMessage);
  214. procedure PreTrackBarWindowProc(var Message: TMessage);
  215. procedure PreWinControlWindowProc(var Message: TMessage);
  216. // MP
  217. function GetThemesEnabled: Boolean;
  218. protected
  219. procedure AddRecreationCandidate(Control: TControl); virtual;
  220. procedure BroadcastThemeChange;
  221. class function CurrentThemeManager: TThemeManager;
  222. function DoAllowSubclassing(Control: TControl): Boolean; virtual;
  223. function DoControlMessage(Control: TControl; var Message: TMessage): Boolean; virtual;
  224. procedure DoOnThemeChange; virtual;
  225. procedure DrawBitBtn(Control: TBitBtn; var DrawItemStruct: TDrawItemStruct);
  226. procedure DrawButton(Control: TControl; Button: TThemedButton; DC: HDC; R: TRect; Focused: Boolean);
  227. function FindListener(AControlMessage: TControlMessageEvent; var Index: Integer): Boolean;
  228. procedure FixControls(Form: TCustomForm = nil);
  229. procedure ForceAsMainManager; virtual;
  230. procedure HandleControlChange(Control: TControl; Inserting: Boolean); virtual;
  231. function IsRecreationCandidate(Control: TControl): Boolean;
  232. procedure Loaded; override;
  233. function NeedsBorderPaint(Control: TControl): Boolean; virtual;
  234. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  235. procedure RemoveChildSubclassing(Control: TWinControl);
  236. procedure RemoveRecreationCandidate(Control: TControl);
  237. procedure UpdateThemes;
  238. procedure UpdateUIState(Control: TControl; CharCode: Word);
  239. public
  240. constructor Create(AOwner: TComponent); override;
  241. destructor Destroy; override;
  242. // MP BEGIN
  243. function GetColor(Element: TThemedElement; PartId: Integer; StateId: Integer;
  244. PropId: Integer): TColor;
  245. property ThemesEnabled: Boolean read GetThemesEnabled;
  246. // MP END
  247. procedure ClearLists;
  248. procedure CollectForms(Form: TCustomForm = nil);
  249. procedure CollectControls(Parent: TWinControl);
  250. procedure PerformEraseBackground(Control: TControl; DC: HDC);
  251. procedure RegisterListener(AControlMessage: TControlMessageEvent);
  252. procedure UnregisterListener(AControlMessage: TControlMessageEvent);
  253. property IsMainManager: Boolean read GetIsMainManager;
  254. published
  255. property Options: TThemeOptions read FOptions write SetThemeOptions default DefaultThemeOptions;
  256. property OnAllowSubclassing: TAllowSubclassingEvent read FOnAllowSubclassing write FOnAllowSubclassing;
  257. property OnControlMessage: TControlMessageEvent read FOnControlMessage write FOnControlMessage;
  258. property OnThemeChange: TNotifyEvent read FOnThemeChange write FOnThemeChange;
  259. end;
  260. var
  261. IsWindowsXP: Boolean;
  262. //----------------------------------------------------------------------------------------------------------------------
  263. implementation
  264. uses
  265. SysUtils, ComCtrls, CommCtrl, SyncObjs, ExtCtrls, Grids, UxTheme
  266. {$ifdef CheckListSupport}
  267. , CheckLst
  268. {$endif CheckListSupport}
  269. ;
  270. const
  271. WM_MAINMANAGERRELEASED = CN_NOTIFY + 100;
  272. type
  273. {$ifndef COMPILER_6_UP}
  274. // TCustomStatusBar does not exist prior Delphi/BCB 6.
  275. TCustomStatusBar = TStatusBar;
  276. {$endif COMPILER_6_UP}
  277. PWindowProcEntry = ^TWindowProcEntry;
  278. TWindowProcEntry = record
  279. Control: TControl;
  280. OldWndProc: TWndMethod;
  281. end;
  282. var
  283. Lock: TCriticalSection;
  284. {$ifdef Debug}
  285. SubclassCount: Integer;
  286. {$endif}
  287. var
  288. MainManager: TThemeManager;
  289. GlobalCheckWidth,
  290. GlobalCheckHeight: Integer;
  291. //----------------- Drawing helper routines ----------------------------------------------------------------------------
  292. procedure GetCheckSize;
  293. begin
  294. with TBitmap.Create do
  295. try
  296. Handle := LoadBitmap(0, PChar(32759));
  297. GlobalCheckWidth := Width div 4;
  298. GlobalCheckHeight := Height div 3;
  299. finally
  300. Free;
  301. end;
  302. end;
  303. //----------------------------------------------------------------------------------------------------------------------
  304. type
  305. // Used to access protected properties.
  306. TControlCast = class(TControl);
  307. procedure CalcButtonLayout(Control: TControl; DC: HDC; const Client: TRect; const Offset: TPoint; var GlyphPos: TPoint;
  308. var TextBounds: TRect; BiDiFlags: Integer);
  309. // This routine is nearly the same as the same named version in TButtonGlyph. The inclusion here is necessary
  310. // because we need the same layout as in the VCL but the implementation of TButtonGlyph is hidden in Buttons and
  311. // cannot be made accessible from here.
  312. var
  313. TextPos: TPoint;
  314. ClientSize,
  315. GlyphSize,
  316. TextSize: TPoint;
  317. TotalSize: TPoint;
  318. Layout: TButtonLayout;
  319. Spacing: Integer;
  320. Margin: Integer;
  321. Glyph: TBitmap;
  322. NumGlyphs: Integer;
  323. Caption: TCaption;
  324. begin
  325. if Control is TBitBtn then
  326. begin
  327. Layout := TBitBtn(Control).Layout;
  328. Spacing := TBitBtn(Control).Spacing;
  329. Margin := TBitBtn(Control).Margin;
  330. Glyph := TBitBtn(Control).Glyph;
  331. NumGlyphs := TBitBtn(Control).NumGlyphs;
  332. Caption := TBitBtn(Control).Caption;
  333. end
  334. else
  335. begin
  336. Layout := TSpeedButton(Control).Layout;
  337. Spacing := TSpeedButton(Control).Spacing;
  338. Margin := TSpeedButton(Control).Margin;
  339. Glyph := TSpeedButton(Control).Glyph;
  340. NumGlyphs := TSpeedButton(Control).NumGlyphs;
  341. Caption := TSpeedButton(Control).Caption;
  342. end;
  343. if (BiDiFlags and DT_RIGHT) = DT_RIGHT then
  344. if Layout = blGlyphLeft then
  345. Layout := blGlyphRight
  346. else
  347. if Layout = blGlyphRight then
  348. Layout := blGlyphLeft;
  349. // Calculate the item sizes.
  350. ClientSize := Point(Client.Right - Client.Left, Client.Bottom - Client.Top);
  351. if Assigned(Glyph) then
  352. GlyphSize := Point(Glyph.Width div NumGlyphs, Glyph.Height)
  353. else
  354. GlyphSize := Point(0, 0);
  355. if Length(Caption) > 0 then
  356. begin
  357. TextBounds := Rect(0, 0, Client.Right - Client.Left, 0);
  358. Windows.DrawText(DC, PChar(Caption), Length(Caption), TextBounds, DT_CALCRECT or BiDiFlags);
  359. TextSize := Point(TextBounds.Right - TextBounds.Left, TextBounds.Bottom - TextBounds.Top);
  360. end
  361. else
  362. begin
  363. TextBounds := Rect(0, 0, 0, 0);
  364. TextSize := Point(0,0);
  365. end;
  366. // If the layout has the glyph on the right or the left, then both the text and the glyph are centered vertically.
  367. // If the glyph is on the top or the bottom, then both the text and the glyph are centered horizontally.
  368. if Layout in [blGlyphLeft, blGlyphRight] then
  369. begin
  370. GlyphPos.Y := (ClientSize.Y - GlyphSize.Y + 1) div 2;
  371. TextPos.Y := (ClientSize.Y - TextSize.Y + 1) div 2;
  372. end
  373. else
  374. begin
  375. GlyphPos.X := (ClientSize.X - GlyphSize.X + 1) div 2;
  376. TextPos.X := (ClientSize.X - TextSize.X + 1) div 2;
  377. end;
  378. // If there is no text or no bitmap, then Spacing is irrelevant.
  379. if (TextSize.X = 0) or (GlyphSize.X = 0) then
  380. Spacing := 0;
  381. // Adjust Margin and Spacing.
  382. if Margin = -1 then
  383. begin
  384. if Spacing = -1 then
  385. begin
  386. TotalSize := Point(GlyphSize.X + TextSize.X, GlyphSize.Y + TextSize.Y);
  387. if Layout in [blGlyphLeft, blGlyphRight] then
  388. Margin := (ClientSize.X - TotalSize.X) div 3
  389. else
  390. Margin := (ClientSize.Y - TotalSize.Y) div 3;
  391. Spacing := Margin;
  392. end
  393. else
  394. begin
  395. TotalSize := Point(GlyphSize.X + Spacing + TextSize.X, GlyphSize.Y + Spacing + TextSize.Y);
  396. if Layout in [blGlyphLeft, blGlyphRight] then
  397. Margin := (ClientSize.X - TotalSize.X + 1) div 2
  398. else
  399. Margin := (ClientSize.Y - TotalSize.Y + 1) div 2;
  400. end;
  401. end
  402. else
  403. begin
  404. if Spacing = -1 then
  405. begin
  406. TotalSize := Point(ClientSize.X - (Margin + GlyphSize.X), ClientSize.Y - (Margin + GlyphSize.Y));
  407. if Layout in [blGlyphLeft, blGlyphRight] then
  408. Spacing := (TotalSize.X - TextSize.X) div 2
  409. else
  410. Spacing := (TotalSize.Y - TextSize.Y) div 2;
  411. end;
  412. end;
  413. case Layout of
  414. blGlyphLeft:
  415. begin
  416. GlyphPos.X := Margin;
  417. TextPos.X := GlyphPos.X + GlyphSize.X + Spacing;
  418. end;
  419. blGlyphRight:
  420. begin
  421. GlyphPos.X := ClientSize.X - Margin - GlyphSize.X;
  422. TextPos.X := GlyphPos.X - Spacing - TextSize.X;
  423. end;
  424. blGlyphTop:
  425. begin
  426. GlyphPos.Y := Margin;
  427. TextPos.Y := GlyphPos.Y + GlyphSize.Y + Spacing;
  428. end;
  429. blGlyphBottom:
  430. begin
  431. GlyphPos.Y := ClientSize.Y - Margin - GlyphSize.Y;
  432. TextPos.Y := GlyphPos.Y - Spacing - TextSize.Y;
  433. end;
  434. end;
  435. // Fixup the result variables.
  436. with GlyphPos do
  437. begin
  438. Inc(X, Client.Left + Offset.X);
  439. Inc(Y, Client.Top + Offset.Y);
  440. end;
  441. OffsetRect(TextBounds, TextPos.X + Client.Left + Offset.X, TextPos.Y + Client.Top + Offset.X);
  442. end;
  443. //----------------- TWindowProcList ------------------------------------------------------------------------------------
  444. // For fixing various things in the VCL we have to subclass some of the VCL controls. For each class of control
  445. // one instance of the TWindowProcList is used.
  446. constructor TWindowProcList.Create(Owner: TThemeManager; WindowProc: TWndMethod; ControlClass: TControlClass);
  447. begin
  448. inherited Create;
  449. FOwner := Owner;
  450. FNewWindowProc := WindowProc;
  451. FControlClass := ControlClass;
  452. end;
  453. //----------------------------------------------------------------------------------------------------------------------
  454. destructor TWindowProcList.Destroy;
  455. begin
  456. Clear;
  457. inherited;
  458. end;
  459. //----------------------------------------------------------------------------------------------------------------------
  460. function Compare(Item1, Item2: Pointer): Integer;
  461. // Helper function for sort and find in window proc lists. They are sorted by control reference.
  462. begin
  463. Result := Integer(PWindowProcEntry(Item1).Control) - Integer(PWindowProcEntry(Item2).Control);
  464. end;
  465. //----------------------------------------------------------------------------------------------------------------------
  466. function TWindowProcList.Add(Control: TControl): Integer;
  467. var
  468. I: Integer;
  469. Entry: PWindowProcEntry;
  470. ControlWndProc: TWndMethod;
  471. begin
  472. Result := -1;
  473. if (Control is FControlClass) and not Find(Control, I) then
  474. begin
  475. {$ifdef Debug}
  476. Lock.Enter;
  477. try
  478. Inc(SubclassCount);
  479. finally
  480. Lock.Leave;
  481. end;
  482. {$endif Debug}
  483. New(Entry);
  484. Entry.Control := Control;
  485. Entry.OldWndProc := Control.WindowProc;
  486. // The following two lines make sure we get the original control, to which a message is sent, in our
  487. // proxy window procedures. This works because the Data member of the window proc does not get the reference to
  488. // the theme manager (as it would happen with ControlWindowProc := FNewWindowProc) but instead we explicitly
  489. // set the control's reference there (see also first proxy method implementation below).
  490. TMethod(ControlWndProc).Code := TMethod(FNewWindowProc).Code;
  491. TMethod(ControlWndProc).Data := Control;
  492. Control.WindowProc := ControlWndProc;
  493. Result := inherited Add(Entry);
  494. FDirty := True;
  495. end;
  496. end;
  497. //----------------------------------------------------------------------------------------------------------------------
  498. procedure TWindowProcList.Clear;
  499. begin
  500. while Count > 0 do
  501. Remove(PWindowProcEntry(Items[0]).Control);
  502. inherited;
  503. end;
  504. //----------------------------------------------------------------------------------------------------------------------
  505. procedure TWindowProcList.DispatchMessage(Control: TControl; var Message: TMessage);
  506. var
  507. I: Integer;
  508. Entry: PWindowProcEntry;
  509. begin
  510. if Find(Control, I) then
  511. begin
  512. // If a window handle is being recreated then we must ensure the handle is really recreated not only destroyed
  513. // (this might happen when a hidden window's handle is recreated). Otherwise we will not get notified again about
  514. // the window's real destruction.
  515. if Message.Msg = CM_RECREATEWND then
  516. MainManager.AddRecreationCandidate(Control);
  517. Entry := Items[I];
  518. Entry.OldWndProc(Message);
  519. // If a control is being destroyed then we have to revert the subclassing.
  520. // We don't get any other opportunity to clean up since TComponent.Notification comes too late and is also not
  521. // called for controls, which are implicitely freed because their parent is freed.
  522. if Message.Msg = WM_DESTROY then
  523. begin
  524. // Remove any control, which is permanently destroyed, but take care for window recreations.
  525. if (csDestroying in Control.ComponentState) or not (MainManager.IsRecreationCandidate(Control)) then
  526. // This call will also remove any child subclassing.
  527. Remove(Control);
  528. end;
  529. end;
  530. end;
  531. //----------------------------------------------------------------------------------------------------------------------
  532. function TWindowProcList.Find(Control: TControl; out Index: Integer): Boolean;
  533. // Binary search implementation to quickly find a control in the list.
  534. var
  535. L, H,
  536. I, C: Integer;
  537. Dummy: TWindowProcEntry;
  538. NeedSort: Boolean;
  539. begin
  540. // First try the cached data to speed up retrieval.
  541. if Control = FLastControl then
  542. begin
  543. Result := True;
  544. Index := FLastIndex;
  545. end
  546. else
  547. begin
  548. NeedSort := FDirty and (Count > 1);
  549. if NeedSort then
  550. begin
  551. Sort(Compare);
  552. FDirty := False;
  553. end;
  554. Result := False;
  555. Dummy.Control := Control;
  556. L := 0;
  557. H := Count - 1;
  558. while L <= H do
  559. begin
  560. I := (L + H) shr 1;
  561. C := Compare(Items[I], @Dummy);
  562. if C < 0 then
  563. L := I + 1
  564. else
  565. begin
  566. H := I - 1;
  567. if C = 0 then
  568. begin
  569. Result := True;
  570. L := I;
  571. end;
  572. end;
  573. end;
  574. Index := L;
  575. if Result then
  576. begin
  577. FLastControl := Control;
  578. FLastIndex := L;
  579. end
  580. else
  581. begin
  582. // even if lookup failed, we need to clear cache as we were forced to resort
  583. if NeedSort then
  584. begin
  585. FLastControl := nil;
  586. FLastIndex := -1;
  587. end;
  588. end;
  589. end;
  590. end;
  591. //----------------------------------------------------------------------------------------------------------------------
  592. procedure TWindowProcList.Remove(Control: TControl);
  593. var
  594. I: Integer;
  595. Entry: PWindowProcEntry;
  596. begin
  597. if Find(Control, I) then
  598. begin
  599. Entry := Items[I];
  600. Delete(I);
  601. Entry.Control.WindowProc := Entry.OldWndProc;
  602. // Implicitly release all child subclassing.
  603. if Entry.Control is TWinControl then
  604. FOwner.RemoveChildSubclassing(Entry.Control as TWinControl);
  605. Dispose(Entry);
  606. {$ifdef Debug}
  607. Lock.Enter;
  608. try
  609. Dec(SubclassCount);
  610. finally
  611. Lock.Leave;
  612. end;
  613. {$endif Debug}
  614. end;
  615. if I <= FLastIndex then
  616. begin
  617. FLastControl := nil;
  618. FLastIndex := -1;
  619. end;
  620. MainManager.RemoveRecreationCandidate(Control);
  621. end;
  622. //----------------- TThemeManager --------------------------------------------------------------------------------------
  623. constructor TThemeManager.Create(AOwner: TComponent);
  624. begin
  625. inherited;
  626. FListeners := TList.Create;
  627. FOptions := DefaultThemeOptions;
  628. FPendingFormsList := TList.Create;
  629. FPendingRecreationList := TList.Create;
  630. FListViewList := TWindowProcList.Create(Self, PreListviewWindowProc, TCustomListView);
  631. FTabSheetList := TWindowProcList.Create(Self, PreTabSheetWindowProc, TTabSheet);
  632. FGroupBoxList := TWindowProcList.Create(Self, PreGroupBoxWindowProc, TCustomGroupBox);
  633. FButtonControlList := TWindowProcList.Create(Self, PreButtonControlWindowProc, TButtonControl);
  634. // MP BEGIN
  635. FCheckBoxList := TWindowProcList.Create(Self, PreCheckBoxWindowProc, TCheckBox);
  636. FButtonList := TWindowProcList.Create(Self, PreButtonWindowProc, TButton);
  637. // MP END
  638. FSpeedButtonList := TWindowProcList.Create(Self, PreSpeedButtonWindowProc, TSpeedButton);
  639. FSplitterList := TWindowProcList.Create(Self, PreSplitterWindowProc, TSplitter);
  640. FTrackBarList := TWindowProcList.Create(Self, PreTrackBarWindowProc, TTrackBar);
  641. FAnimateList := TWindowProcList.Create(Self, PreAnimateWindowProc, TAnimate);
  642. FStatusBarList := TWindowProcList.Create(Self, PreStatusBarWindowProc, TCustomStatusBar);
  643. {$ifdef CheckListSupport}
  644. FCheckListBoxList := TWindowProcList.Create(Self, PreCheckListBoxWindowProc, TCheckListBox);
  645. {$endif CheckListSupport}
  646. FFormList := TWindowProcList.Create(Self, PreFormWindowProc, TCustomForm);
  647. {$ifdef COMPILER_5_UP}
  648. FFrameList := TWindowProcList.Create(Self, PreFrameWindowProc, TCustomFrame);
  649. {$endif COMPILER_5_UP}
  650. FPanelList := TWindowProcList.Create(Self, PrePanelWindowProc, TCustomPanel);
  651. FWinControlList := TWindowProcList.Create(Self, PreWinControlWindowProc, TWinControl);
  652. if csDesigning in ComponentState then
  653. FSubclassingDisabled := True
  654. else
  655. begin
  656. if ThemeServices.ThemesEnabled then
  657. begin
  658. Application.HookMainWindow(MainWindowHook);
  659. FHookWasInstalled := True;
  660. end
  661. else
  662. FHookWasInstalled := False;
  663. // Keep the reference of this instance if it is the first one created in the application.
  664. Lock.Enter;
  665. try
  666. // If this is not the first instance then disable subclassing.
  667. if MainManager = nil then
  668. MainManager := Self
  669. else
  670. begin
  671. FSubclassingDisabled := True;
  672. FOptions := MainManager.FOptions;
  673. end;
  674. finally
  675. Lock.Leave;
  676. end;
  677. end;
  678. end;
  679. //----------------------------------------------------------------------------------------------------------------------
  680. destructor TThemeManager.Destroy;
  681. begin
  682. FWinControlList.Free;
  683. FPanelList.Free;
  684. {$ifdef COMPILER_5_UP}
  685. FFrameList.Free;
  686. {$endif COMPILER_5_UP}
  687. FFormList.Free;
  688. {$ifdef CheckListSupport}
  689. FCheckListBoxList.Free;
  690. {$endif CheckListSupport}
  691. FStatusBarList.Free;
  692. FAnimateList.Free;
  693. FTrackBarList.Free;
  694. FSpeedButtonList.Free;
  695. FSplitterList.Free;
  696. // MP BEGIN
  697. FButtonList.Free;
  698. FCheckBoxList.Free;
  699. // MP END
  700. FButtonControlList.Free;
  701. FListViewList.Free;
  702. FTabSheetList.Free;
  703. FGroupBoxList.Free;
  704. // Reset first manager reference if it is set to this instance.
  705. if not (csDesigning in ComponentState) then
  706. begin
  707. if FHookWasInstalled then
  708. Application.UnhookMainWindow(MainWindowHook);
  709. // We have to check the critical section here because it can happen that it is already freed (finalization section)
  710. // but there is still a theme manager instance lurking around, due to the finalization order.
  711. // If there is no lock anymore then the app. is being terminated and we don't need to set a new main manager.
  712. if Assigned(Lock) then
  713. begin
  714. Lock.Enter;
  715. try
  716. if MainManager = Self then
  717. begin
  718. MainManager := nil;
  719. if Application.Handle <> 0 then
  720. SendAppMessage(WM_MAINMANAGERRELEASED, 0, 0);
  721. end;
  722. finally
  723. Lock.Leave;
  724. end;
  725. end;
  726. end;
  727. FPendingFormsList.Free;
  728. FPendingRecreationList.Free;
  729. FListeners.Free;
  730. inherited;
  731. end;
  732. //----------------------------------------------------------------------------------------------------------------------
  733. type
  734. // Used to access protected methods and properties.
  735. TWinControlCast = class(TWinControl);
  736. procedure TThemeManager.AnimateWindowProc(Control: TControl; var Message: TMessage);
  737. begin
  738. if not DoControlMessage(Control, Message) then
  739. begin
  740. if ThemeServices.ThemesEnabled then
  741. begin
  742. case Message.Msg of
  743. WM_ERASEBKGND:
  744. Message.Result := 1;
  745. CN_CTLCOLORSTATIC:
  746. if TAnimate(Control).Transparent then
  747. with TWMCtlColorStatic(Message) do
  748. begin
  749. // Return a brush corresponding to the control's fixed background color.
  750. // The animation control insists on always erasing its background.
  751. Result := GetSysColorBrush(TWinControlCast(Control).Color and not $80000000);
  752. {ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
  753. SetBkMode(ChildDC, TRANSPARENT);
  754. // Return an empty brush to prevent Windows from overpainting we just have created.
  755. Result := GetStockObject(NULL_BRUSH);}
  756. end
  757. else
  758. FAnimateList.DispatchMessage(Control, Message);
  759. else
  760. FAnimateList.DispatchMessage(Control, Message);
  761. end;
  762. end
  763. else
  764. FAnimateList.DispatchMessage(Control, Message);
  765. end;
  766. end;
  767. //----------------------------------------------------------------------------------------------------------------------
  768. procedure TThemeManager.ButtonControlWindowProc(Control: TControl; var Message: TMessage;
  769. { MP } List: TWindowProcList);
  770. var
  771. Details: TThemedElementDetails;
  772. begin
  773. if not DoControlMessage(Control, Message) then
  774. begin
  775. if ThemeServices.ThemesEnabled then
  776. begin
  777. case Message.Msg of
  778. CN_KEYDOWN,
  779. WM_SYSKEYDOWN,
  780. WM_KEYDOWN:
  781. begin
  782. UpdateUIState(Control, TWMKey(Message).CharCode);
  783. // MP
  784. List.DispatchMessage(Control, Message);
  785. end;
  786. WM_ERASEBKGND:
  787. Message.Result := 1;
  788. CN_CTLCOLORBTN: // TButton background erasing. Necessary for some themes (like EclipseOSX).
  789. with TWMCtlColorBtn(Message) do
  790. begin
  791. if TWinControl(Control.Parent).DoubleBuffered then
  792. PerformEraseBackground(Control, ChildDC)
  793. else
  794. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
  795. // Return an empty brush to prevent Windows from overpainting we just have created.
  796. Result := GetStockObject(NULL_BRUSH);
  797. end;
  798. CN_CTLCOLORSTATIC: // Background erasing for check boxes and radio buttons.
  799. with TWMCtlColorStatic(Message) do
  800. begin
  801. if TWinControl(Control.Parent).DoubleBuffered then
  802. PerformEraseBackground(Control, ChildDC)
  803. else
  804. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
  805. // Return an empty brush to prevent Windows from overpainting we just have created.
  806. Result := GetStockObject(NULL_BRUSH);
  807. end;
  808. CM_MOUSEENTER,
  809. CM_MOUSELEAVE:
  810. begin
  811. // Hot tracking for owner drawn buttons seems to be unsupported by Windows. So we have to work around that.
  812. if Control is TBitBtn then
  813. Control.Invalidate;
  814. // MP
  815. List.DispatchMessage(Control, Message);
  816. end;
  817. CN_DRAWITEM: // Painting for owner drawn buttons.
  818. with TWMDrawItem(Message) do
  819. begin
  820. // This message is sent for bit buttons (TBitBtn) when they must be drawn. Since a bit button is a normal
  821. // Windows button (but with custom draw enabled) it is handled here too.
  822. // TSpeedButton is a TGraphicControl descentant and handled separately.
  823. Details := ThemeServices.GetElementDetails(tbPushButtonNormal);
  824. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DrawItemStruct.hDC, @Details, True);
  825. // CN_DRAWITEM can also come in when the control is a subclassed button with enabled custom draw.
  826. // In this case the content of the control is fully controlled by the original source. So let it do
  827. // whatever it wants to do.
  828. if (Control is TBitBtn) or (Control is TSpeedButton) then
  829. DrawBitBtn(TBitBtn(Control), DrawItemStruct^)
  830. else
  831. // MP
  832. List.DispatchMessage(Control, Message);
  833. end;
  834. else
  835. // MP
  836. List.DispatchMessage(Control, Message);
  837. end;
  838. end
  839. else
  840. // MP
  841. List.DispatchMessage(Control, Message);
  842. end;
  843. end;
  844. //----------------------------------------------------------------------------------------------------------------------
  845. {$ifdef CheckListSupport}
  846. type
  847. TCheckListBoxCast = class(TCheckListBox);
  848. procedure TThemeManager.CheckListBoxWindowProc(Control: TControl; var Message: TMessage);
  849. var
  850. DrawState: TOwnerDrawState;
  851. ListBox: TCheckListBoxCast;
  852. //--------------- local functions -------------------------------------------
  853. procedure DrawCheck(R: TRect; AState: TCheckBoxState; Enabled: Boolean);
  854. var
  855. DrawRect: TRect;
  856. Button: TThemedButton;
  857. Details: TThemedElementDetails;
  858. begin
  859. DrawRect.Left := R.Left + (R.Right - R.Left - GlobalCheckWidth) div 2;
  860. DrawRect.Top := R.Top + (R.Bottom - R.Top - GlobalCheckWidth) div 2;
  861. DrawRect.Right := DrawRect.Left + GlobalCheckWidth;
  862. DrawRect.Bottom := DrawRect.Top + GlobalCheckHeight;
  863. case AState of
  864. cbChecked:
  865. if Enabled then
  866. Button := tbCheckBoxCheckedNormal
  867. else
  868. Button := tbCheckBoxCheckedDisabled;
  869. cbUnchecked:
  870. if Enabled then
  871. Button := tbCheckBoxUncheckedNormal
  872. else
  873. Button := tbCheckBoxUncheckedDisabled;
  874. else // cbGrayed
  875. if Enabled then
  876. Button := tbCheckBoxMixedNormal
  877. else
  878. Button := tbCheckBoxMixedDisabled;
  879. end;
  880. Details := ThemeServices.GetElementDetails(Button);
  881. ThemeServices.DrawElement(ListBox.Canvas.Handle, Details, DrawRect, @DrawRect);
  882. end;
  883. //---------------------------------------------------------------------------
  884. procedure NewDrawItem(Index: Integer; Rect: TRect; DrawState: TOwnerDrawState);
  885. var
  886. Flags: Integer;
  887. Data: string;
  888. R: TRect;
  889. ACheckWidth: Integer;
  890. Enable: Boolean;
  891. begin
  892. with ListBox do
  893. begin
  894. // The checkbox is always drawn, regardless of the owner draw style.
  895. ACheckWidth := GetCheckWidth;
  896. if Index < Items.Count then
  897. begin
  898. R := Rect;
  899. // Delphi 4 has neither an enabled state nor a header state for items.
  900. Enable := Enabled {$ifdef COMPILER_6_UP} and ItemEnabled[Index] {$endif COMPILER_6_UP};
  901. if {$ifdef COMPILER_6_UP} not Header[Index] {$else} True {$endif COMPILER_6_UP} then
  902. begin
  903. if not UseRightToLeftAlignment then
  904. begin
  905. R.Right := Rect.Left;
  906. R.Left := R.Right - ACheckWidth;
  907. end
  908. else
  909. begin
  910. R.Left := Rect.Right;
  911. R.Right := R.Left + ACheckWidth;
  912. end;
  913. DrawCheck(R, State[Index], Enable);
  914. end
  915. else
  916. begin
  917. {$ifdef COMPILER_6_UP}
  918. Canvas.Font.Color := HeaderColor;
  919. Canvas.Brush.Color := HeaderBackgroundColor;
  920. {$endif COMPILER_6_UP}
  921. end;
  922. if not Enable then
  923. Canvas.Font.Color := clGrayText;
  924. end;
  925. if Assigned(OnDrawItem) and (Style <> lbStandard)then
  926. OnDrawItem(ListBox, Index, Rect, DrawState)
  927. else
  928. begin
  929. Canvas.FillRect(Rect);
  930. if Index < {$ifdef COMPILER_6_UP} Count {$else} Items.Count {$endif COMPILER_6_UP}then
  931. begin
  932. Flags := DrawTextBiDiModeFlags(DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
  933. if not UseRightToLeftAlignment then
  934. Inc(Rect.Left, 2)
  935. else
  936. Dec(Rect.Right, 2);
  937. Data := '';
  938. {$ifdef COMPILER_6_UP}
  939. if (Style in [lbVirtual, lbVirtualOwnerDraw]) then
  940. Data := DoGetData(Index)
  941. else
  942. {$endif COMPILER_6_UP}
  943. Data := Items[Index];
  944. DrawText(Canvas.Handle, PChar(Data), Length(Data), Rect, Flags);
  945. end;
  946. end;
  947. end;
  948. end;
  949. //--------------- end local function ----------------------------------------
  950. begin
  951. if not DoControlMessage(Control, Message) then
  952. begin
  953. if ThemeServices.ThemesEnabled then
  954. begin
  955. ListBox := TCheckListBoxCast(Control);
  956. case Message.Msg of
  957. CN_DRAWITEM:
  958. if {$ifdef COMPILER_6_UP} ListBox.Count > 0 {$else} ListBox.Items.Count > 0 {$endif COMPILER_6_UP} then
  959. with TWMDrawItem(Message).DrawItemStruct^, ListBox do
  960. begin
  961. if {$ifdef COMPILER_6_UP} not Header[itemID] {$else} True {$endif COMPILER_6_UP} then
  962. if not UseRightToLeftAlignment then
  963. rcItem.Left := rcItem.Left + GetCheckWidth
  964. else
  965. rcItem.Right := rcItem.Right - GetCheckWidth;
  966. {$ifdef COMPILER_5_UP}
  967. DrawState := TOwnerDrawState(LongRec(itemState).Lo);
  968. {$else}
  969. DrawState := TOwnerDrawState(Byte(LongRec(itemState).Lo));
  970. {$endif COMPILER_5_UP}
  971. Canvas.Handle := hDC;
  972. Canvas.Font := Font;
  973. Canvas.Brush := Brush;
  974. if (Integer(itemID) >= 0) and (odSelected in DrawState) then
  975. begin
  976. Canvas.Brush.Color := clHighlight;
  977. Canvas.Font.Color := clHighlightText
  978. end;
  979. if Integer(itemID) >= 0 then
  980. NewDrawItem(itemID, rcItem, DrawState)
  981. else
  982. Canvas.FillRect(rcItem);
  983. if odFocused in DrawState then
  984. DrawFocusRect(hDC, rcItem);
  985. Canvas.Handle := 0;
  986. end;
  987. else
  988. FCheckListBoxList.DispatchMessage(Control, Message);
  989. end;
  990. end
  991. else
  992. FCheckListBoxList.DispatchMessage(Control, Message);
  993. end
  994. else
  995. FCheckListBoxList.DispatchMessage(Control, Message);
  996. end;
  997. {$endif CheckListSupport}
  998. //----------------------------------------------------------------------------------------------------------------------
  999. procedure TThemeManager.FormWindowProc(Control: TControl; var Message: TMessage);
  1000. var
  1001. DC: HDC;
  1002. begin
  1003. case Message.Msg of
  1004. CM_CONTROLLISTCHANGE: // Single control addition or removal.
  1005. with TCMControlListChange(Message) do
  1006. HandleControlChange(Control, Inserting);
  1007. end;
  1008. if not DoControlMessage(Control, Message) then
  1009. begin
  1010. if ThemeServices.ThemesEnabled then
  1011. begin
  1012. case Message.Msg of
  1013. WM_PRINTCLIENT,
  1014. WM_ERASEBKGND:
  1015. begin
  1016. if (Message.Msg=WM_PRINTCLIENT) then
  1017. DC := TWMPrintClient(Message).DC
  1018. else
  1019. DC := TWMEraseBkGnd(Message).DC;
  1020. // Get the parent to draw its background into the form's background.
  1021. if not (Control.Parent is TWinControl) then
  1022. FFormList.DispatchMessage(Control, Message)
  1023. else
  1024. if TWinControl(Control.Parent).DoubleBuffered then
  1025. PerformEraseBackground(Control, DC)
  1026. else
  1027. if TWinControl(Control).DoubleBuffered then
  1028. begin
  1029. if (Message.Msg <> WM_ERASEBKGND) or (Longint(DC) = TWMEraseBkGnd(Message).Unused) then
  1030. // VCL mark for second pass, this time into the offscreen bitmap
  1031. PerformEraseBackground(Control, DC);
  1032. end
  1033. else
  1034. DrawThemeParentBackground(TWinControl(Control).Handle, DC, nil);
  1035. Message.Result := 1;
  1036. end;
  1037. else
  1038. FFormList.DispatchMessage(Control, Message);
  1039. end;
  1040. end
  1041. else
  1042. FFormList.DispatchMessage(Control, Message);
  1043. end;
  1044. end;
  1045. //----------------------------------------------------------------------------------------------------------------------
  1046. {$ifdef COMPILER_5_UP}
  1047. type
  1048. // Used to access protected properties.
  1049. TFrameCast = class(TCustomFrame);
  1050. procedure TThemeManager.FrameWindowProc(Control: TControl; var Message: TMessage);
  1051. var
  1052. PS: TPaintStruct;
  1053. Details: TThemedElementDetails;
  1054. begin
  1055. if not DoControlMessage(Control, Message) then
  1056. begin
  1057. if ThemeServices.ThemesEnabled then
  1058. begin
  1059. case Message.Msg of
  1060. WM_ERASEBKGND:
  1061. // MP BEGIN
  1062. if TFrameCast(Control).Color <> clBtnFace then
  1063. FFrameList.DispatchMessage(Control, Message)
  1064. else
  1065. // MP END
  1066. with TWMEraseBkGnd(Message) do
  1067. begin
  1068. // Get the parent to draw its background into the control's background.
  1069. if TWinControl(Control.Parent).DoubleBuffered then
  1070. PerformEraseBackground(Control, DC)
  1071. else
  1072. begin
  1073. Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
  1074. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, @Details, False);
  1075. end;
  1076. Result := 1;
  1077. end;
  1078. WM_PAINT:
  1079. begin
  1080. BeginPaint(TFrameCast(Control).Handle, PS);
  1081. TFrameCast(Control).PaintControls(PS.hdc, nil);
  1082. EndPaint(TFrameCast(Control).Handle, PS);
  1083. Message.Result := 0;
  1084. end;
  1085. else
  1086. FFrameList.DispatchMessage(Control, Message);
  1087. end;
  1088. end
  1089. else
  1090. FFrameList.DispatchMessage(Control, Message);
  1091. end;
  1092. end;
  1093. {$endif COMPILER_5_UP}
  1094. //----------------------------------------------------------------------------------------------------------------------
  1095. function TThemeManager.GetIsMainManager: Boolean;
  1096. begin
  1097. Result := MainManager = Self;
  1098. end;
  1099. //----------------------------------------------------------------------------------------------------------------------
  1100. type
  1101. // Used to access protected properties.
  1102. TGroupBoxCast = class(TCustomGroupBox);
  1103. procedure TThemeManager.GroupBoxWindowProc(Control: TControl; var Message: TMessage);
  1104. //--------------- local function --------------------------------------------
  1105. procedure NewPaint(DC: HDC);
  1106. var
  1107. CaptionRect,
  1108. OuterRect: TRect;
  1109. Size: TSize;
  1110. LastFont: HFONT;
  1111. Box: TThemedButton;
  1112. Details: TThemedElementDetails;
  1113. begin
  1114. with TGroupBoxCast(Control) do
  1115. begin
  1116. LastFont := SelectObject(DC, Font.Handle);
  1117. if Text <> '' then
  1118. begin
  1119. SetTextColor(DC, Graphics.ColorToRGB(Font.Color));
  1120. // Determine size and position of text rectangle.
  1121. // This must be clipped out before painting the frame.
  1122. GetTextExtentPoint32(DC, PChar(Text), Length(Text), Size);
  1123. CaptionRect := Rect(0, 0, Size.cx, Size.cy);
  1124. if not UseRightToLeftAlignment then
  1125. OffsetRect(CaptionRect, 8, 0)
  1126. else
  1127. OffsetRect(CaptionRect, Width - 8 - CaptionRect.Right, 0);
  1128. end
  1129. else
  1130. CaptionRect := Rect(0, 0, 0, 0);
  1131. OuterRect := ClientRect;
  1132. OuterRect.Top := (CaptionRect.Bottom - CaptionRect.Top) div 2;
  1133. with CaptionRect do
  1134. ExcludeClipRect(DC, Left, Top, Right, Bottom);
  1135. if Control.Enabled then
  1136. Box := tbGroupBoxNormal
  1137. else
  1138. Box := tbGroupBoxDisabled;
  1139. Details := ThemeServices.GetElementDetails(Box);
  1140. ThemeServices.DrawElement(DC, Details, OuterRect);
  1141. SelectClipRgn(DC, 0);
  1142. if Text <> '' then
  1143. ThemeServices.DrawText(DC, Details, Text, CaptionRect, DT_LEFT, 0);
  1144. SelectObject(DC, LastFont);
  1145. end;
  1146. end;
  1147. //--------------- local function --------------------------------------------
  1148. var
  1149. PS: TPaintStruct;
  1150. Details: TThemedElementDetails;
  1151. begin
  1152. if not DoControlMessage(Control, Message) then
  1153. begin
  1154. if ThemeServices.ThemesEnabled then
  1155. begin
  1156. case Message.Msg of
  1157. WM_SYSKEYDOWN,
  1158. CN_KEYDOWN,
  1159. WM_KEYDOWN:
  1160. begin
  1161. UpdateUIState(Control, TWMKey(Message).CharCode);
  1162. FGroupBoxList.DispatchMessage(Control, Message);
  1163. end;
  1164. WM_ERASEBKGND:
  1165. with TWMEraseBkGnd(Message) do
  1166. begin
  1167. // Get the parent to draw its background into the control's background.
  1168. if TWinControl(Control.Parent).DoubleBuffered then
  1169. PerformEraseBackground(Control, DC)
  1170. else
  1171. begin
  1172. Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
  1173. ThemeServices.DrawParentBackground(TGroupBoxCast(Control).Handle, DC, @Details, True);
  1174. end;
  1175. Result := 1;
  1176. end;
  1177. WM_PAINT:
  1178. begin
  1179. BeginPaint(TGroupBoxCast(Control).Handle, PS);
  1180. NewPaint(PS.hdc);
  1181. TGroupBoxCast(Control).PaintControls(PS.hdc, nil);
  1182. EndPaint(TGroupBoxCast(Control).Handle, PS);
  1183. Message.Result := 0;
  1184. end;
  1185. else
  1186. FGroupBoxList.DispatchMessage(Control, Message);
  1187. end;
  1188. end
  1189. else
  1190. FGroupBoxList.DispatchMessage(Control, Message);
  1191. end;
  1192. end;
  1193. //----------------------------------------------------------------------------------------------------------------------
  1194. procedure TThemeManager.ListviewWindowProc(Control: TControl; var Message: TMessage);
  1195. begin
  1196. if not DoControlMessage(Control, Message) then
  1197. begin
  1198. // MP BEGIN
  1199. if ThemeServices.ThemesEnabled then
  1200. begin
  1201. case Message.Msg of
  1202. WM_SYSKEYDOWN,
  1203. CN_KEYDOWN,
  1204. WM_KEYDOWN:
  1205. begin
  1206. UpdateUIState(Control, TWMKey(Message).CharCode);
  1207. FGroupBoxList.DispatchMessage(Control, Message);
  1208. end;
  1209. end;
  1210. end;
  1211. // MP END
  1212. // In opposition to the other window procedures we should always apply the fix for TListView,
  1213. // regardless of whether themes are enabled or not.
  1214. if (Message.Msg = LVM_SETCOLUMN) or (Message.Msg = LVM_INSERTCOLUMN) then
  1215. begin
  1216. with PLVColumn(Message.LParam)^ do
  1217. begin
  1218. // Fix TListView report mode bug.
  1219. if iImage = - 1 then
  1220. Mask := Mask and not LVCF_IMAGE;
  1221. end;
  1222. end;
  1223. // This special notification message is not handled in the VCL and creates an access violation when
  1224. // passed to the default window procedure. Ignoring it does not seem to have any negative impact.
  1225. if not ((Message.Msg = WM_NOTIFY) and (TWMNotify(Message).NMHdr.code = HDN_GETDISPINFOW)) then
  1226. FListViewList.DispatchMessage(Control, Message);
  1227. end;
  1228. end;
  1229. //----------------------------------------------------------------------------------------------------------------------
  1230. function TThemeManager.MainWindowHook(var Message: TMessage): Boolean;
  1231. // Listens to messages sent to the application to know when a theme change occured.
  1232. var
  1233. Form: TCustomForm;
  1234. I: Integer;
  1235. begin
  1236. Result := False;
  1237. // If the main manager was destroyed then it posted this message to the application so all still existing
  1238. // theme managers know a new election is due. Well, it is not purely democratic. The earlier a manager was created
  1239. // the higher is the probability to get this message first and become the new main manager.
  1240. if Message.Msg = WM_MAINMANAGERRELEASED then
  1241. begin
  1242. Lock.Enter;
  1243. try
  1244. // Check if the main manager role is still vacant.
  1245. if MainManager = nil then
  1246. begin
  1247. MainManager := Self;
  1248. FSubclassingDisabled := False;
  1249. CollectForms;
  1250. end;
  1251. finally
  1252. Lock.Leave;
  1253. end;
  1254. end;
  1255. // Check first if there are still forms to subclass.
  1256. I := 0;
  1257. while I < FPendingFormsList.Count do
  1258. begin
  1259. Form := TCustomForm(FPendingFormsList[I]);
  1260. // workaround for so far unknown bug on vista (bug 140)
  1261. // solves similar problem on WinXP
  1262. if not Form.HandleAllocated then Inc(I)
  1263. else
  1264. begin
  1265. FPendingFormsList.Delete(I);
  1266. FFormList.Add(Form);
  1267. // Since we don't know how many controls on this form already have been created we better collect everything
  1268. // which is already there. The window proc lists will take care not to add a control twice.
  1269. if MainManager = Self then
  1270. CollectControls(Form);
  1271. if [toResetMouseCapture, toSetTransparency] * FOptions <> [] then
  1272. FixControls(Form);
  1273. // Sometimes not all controls are visually updated. Force it to be correct.
  1274. RedrawWindow(Form.Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN or RDW_VALIDATE);
  1275. end;
  1276. end;
  1277. while FPendingRecreationList.Count > 0 do
  1278. begin
  1279. TWinControl(FPendingRecreationList[0]).HandleNeeded;
  1280. CollectControls(TWinControl(FPendingRecreationList[0]));
  1281. FPendingRecreationList.Delete(0);
  1282. end;
  1283. if Message.Msg = WM_THEMECHANGED then
  1284. begin
  1285. UpdateThemes;
  1286. DoOnThemeChange;
  1287. end;
  1288. end;
  1289. //----------------------------------------------------------------------------------------------------------------------
  1290. type
  1291. // Used to access protected properties.
  1292. TPanelCast = class(TCustomPanel);
  1293. procedure TThemeManager.PanelWindowProc(Control: TControl; var Message: TMessage);
  1294. var
  1295. DrawRect: TRect;
  1296. DC: HDC;
  1297. OldFont: HFONT;
  1298. PS: TPaintStruct;
  1299. Details: TThemedElementDetails;
  1300. //--------------- local function --------------------------------------------
  1301. procedure NewPaint;
  1302. // This is an adapted version of the actual TCustomPanel.Paint procedure
  1303. const
  1304. Alignments: array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
  1305. var
  1306. Rect: TRect;
  1307. TopColor, BottomColor: TColor;
  1308. FontHeight: Integer;
  1309. Flags: Longint;
  1310. //------------- local functions -------------------------------------------
  1311. procedure AdjustColors(Bevel: TPanelBevel);
  1312. begin
  1313. TopColor := clBtnHighlight;
  1314. if Bevel = bvLowered then
  1315. TopColor := clBtnShadow;
  1316. BottomColor := clBtnShadow;
  1317. if Bevel = bvLowered then
  1318. BottomColor := clBtnHighlight;
  1319. end;
  1320. //------------- end local functions ---------------------------------------
  1321. begin
  1322. with TPanelCast(Control) do
  1323. begin
  1324. Canvas.Handle := DC;
  1325. try
  1326. Canvas.Font := Font;
  1327. Rect := GetClientRect;
  1328. if BevelOuter <> bvNone then
  1329. begin
  1330. AdjustColors(BevelOuter);
  1331. Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
  1332. end;
  1333. InflateRect(Rect, -BorderWidth, -BorderWidth);
  1334. if BevelInner <> bvNone then
  1335. begin
  1336. AdjustColors(BevelInner);
  1337. Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
  1338. end;
  1339. if ParentColor or ((Control.Parent <> nil) and (Control.Parent.Brush.Color = Color)) then
  1340. begin
  1341. if TWinControl(Control.Parent).DoubleBuffered then
  1342. PerformEraseBackground(Control, PS.hdc)
  1343. else
  1344. begin
  1345. Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
  1346. ThemeServices.DrawParentBackground(Handle, DC, @Details, False, @Rect);
  1347. end
  1348. end
  1349. else
  1350. begin
  1351. Canvas.Brush.Style := bsSolid;
  1352. Canvas.Brush.Color := Color;
  1353. FillRect(PS.hdc, Rect, Canvas.Brush.Handle);
  1354. end;
  1355. FontHeight := Canvas.TextHeight('W');
  1356. with Rect do
  1357. begin
  1358. Top := ((Bottom + Top) - FontHeight) div 2;
  1359. Bottom := Top + FontHeight;
  1360. end;
  1361. Flags := DT_EXPANDTABS or DT_VCENTER or Alignments[Alignment];
  1362. Flags := DrawTextBiDiModeFlags(Flags);
  1363. OldFont := SelectObject(DC, Font.Handle);
  1364. SetBKMode(DC, TRANSPARENT);
  1365. SetTextColor(DC, ColorToRGB(Font.Color));
  1366. DrawText(DC, PChar(Caption), -1, Rect, Flags);
  1367. SelectObject(DC, OldFont);
  1368. finally
  1369. Canvas.Handle := 0;
  1370. end;
  1371. end;
  1372. end;
  1373. //--------------- end local function ----------------------------------------
  1374. begin
  1375. if not DoControlMessage(Control, Message) then
  1376. begin
  1377. if ThemeServices.ThemesEnabled and TPanelCast(Control).ParentColor or
  1378. (Assigned(Control.Parent) and (Control.Parent.Brush.Color = TPanelCast(Control).Color)) then
  1379. begin
  1380. case Message.Msg of
  1381. WM_ERASEBKGND:
  1382. with TPanelCast(Control) do
  1383. begin
  1384. DC := TWMEraseBkGnd(Message).DC;
  1385. // Get the parent to draw its background into the control's background.
  1386. if TWinControl(Control.Parent).DoubleBuffered then
  1387. PerformEraseBackground(Control, DC)
  1388. else
  1389. begin
  1390. Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
  1391. ThemeServices.DrawParentBackground(Handle, DC, @Details, False);
  1392. end;
  1393. Message.Result := 1;
  1394. end;
  1395. WM_NCPAINT:
  1396. with TPanelCast(Control) do
  1397. begin
  1398. FPanelList.DispatchMessage(Control, Message);
  1399. if BorderStyle <> bsNone then
  1400. begin
  1401. DrawRect := BoundsRect;
  1402. OffsetRect(DrawRect, -Left, -Top);
  1403. DC := GetWindowDC(Handle);
  1404. try
  1405. Details := ThemeServices.GetElementDetails(trBandNormal);
  1406. ThemeServices.DrawEdge(DC, Details, DrawRect, EDGE_SUNKEN, BF_RECT);
  1407. finally
  1408. ReleaseDC(Handle, DC);
  1409. end;
  1410. end;
  1411. Message.Result := 0;
  1412. end;
  1413. WM_PAINT:
  1414. with TPanelCast(Control) do
  1415. begin
  1416. DC := BeginPaint(Handle, PS);
  1417. NewPaint;
  1418. PaintControls(DC, nil);
  1419. EndPaint(Handle, PS);
  1420. Message.Result := 0;
  1421. end;
  1422. WM_PRINTCLIENT:
  1423. with TPanelCast(Control) do
  1424. begin
  1425. DC := TWMPrintClient(Message).DC;
  1426. NewPaint;
  1427. PaintControls(DC, nil);
  1428. Message.Result := 0;
  1429. end;
  1430. else
  1431. FPanelList.DispatchMessage(Control, Message);
  1432. end;
  1433. end
  1434. else
  1435. FPanelList.DispatchMessage(Control, Message);
  1436. end;
  1437. end;
  1438. //----------------------------------------------------------------------------------------------------------------------
  1439. procedure TThemeManager.SetThemeOptions(const Value: TThemeOptions);
  1440. var
  1441. Flags: Cardinal;
  1442. I: Integer;
  1443. begin
  1444. // If this instance is the main manager then apply the options directly. Otherwise let the current main manager do it.
  1445. Lock.Enter;
  1446. try
  1447. if Assigned(MainManager) and (MainManager <> Self) then
  1448. MainManager.Options := Value
  1449. else
  1450. begin
  1451. if FOptions <> Value then
  1452. begin
  1453. FOptions := Value;
  1454. if ThemeServices.ThemesAvailable and not FSubclassingDisabled and not (csDesigning in ComponentState) then
  1455. begin
  1456. Flags := 0;
  1457. if toAllowNonClientArea in FOptions then
  1458. Flags := Flags or STAP_ALLOW_NONCLIENT;
  1459. if toAllowControls in FOptions then
  1460. Flags := Flags or STAP_ALLOW_CONTROLS;
  1461. if toAllowWebContent in FOptions then
  1462. Flags := Flags or STAP_ALLOW_WEBCONTENT;
  1463. SetThemeAppProperties(Flags);
  1464. if ComponentState * [csLoading, csReading] = [] then
  1465. begin
  1466. UpdateThemes;
  1467. // Tell the application that we changed the options.
  1468. BroadcastThemeChange;
  1469. // Notify all theme manager instances about the change.
  1470. SendAppMessage(WM_THEMECHANGED, 0, 0);
  1471. for I := 0 to Screen.FormCount - 1 do
  1472. RedrawWindow(Screen.Forms[I].Handle, nil, 0, RDW_ERASE or RDW_FRAME or RDW_INVALIDATE or RDW_INTERNALPAINT or
  1473. RDW_ERASENOW or RDW_UPDATENOW or RDW_ALLCHILDREN);
  1474. end;
  1475. end;
  1476. end;
  1477. end;
  1478. finally
  1479. Lock.Leave;
  1480. end;
  1481. end;
  1482. //----------------------------------------------------------------------------------------------------------------------
  1483. type
  1484. TSpeedButtonCast = class(TSpeedButton);
  1485. procedure TThemeManager.SpeedButtonWindowProc(Control: TControl; var Message: TMessage);
  1486. var
  1487. Button: TThemedButton;
  1488. P: TPoint;
  1489. begin
  1490. if not DoControlMessage(Control, Message) then
  1491. begin
  1492. if ThemeServices.ThemesEnabled then
  1493. begin
  1494. case Message.Msg of
  1495. WM_PAINT:
  1496. with TWMPaint(Message) do
  1497. begin
  1498. // We cannot use the theme parent paint for the background of general speed buttons (because they are not
  1499. // window controls).
  1500. PerformEraseBackground(Control, DC);
  1501. // Speed buttons are not window controls and are painted by a call of their parent with a given DC.
  1502. if not Control.Enabled then
  1503. Button := tbPushButtonDisabled
  1504. else
  1505. if TSpeedButtonCast(Control).FState in [bsDown, bsExclusive] then
  1506. Button := tbPushButtonPressed
  1507. else
  1508. with TSpeedButtonCast(Control) do
  1509. begin
  1510. // Check the hot style here. If the button has a flat style then this check is easy. Otherwise
  1511. // some more work is necessary.
  1512. Button := tbPushButtonNormal;
  1513. if Flat then
  1514. begin
  1515. if MouseInControl then
  1516. Button := tbPushButtonHot;
  1517. end
  1518. else
  1519. begin
  1520. GetCursorPos(P);
  1521. if FindDragTarget(P, True) = Control then
  1522. Button := tbPushButtonHot;
  1523. end;
  1524. end;
  1525. DrawButton(Control, Button, DC, Control.ClientRect, False);
  1526. Message.Result := 0;
  1527. end;
  1528. CM_MOUSEENTER,
  1529. CM_MOUSELEAVE:
  1530. begin
  1531. // Non-flat speed buttons don't have a hot-tracking style. We have to emulate this.
  1532. if not TSpeedButtonCast(Control).Flat and Control.Enabled then
  1533. Control.Invalidate;
  1534. FSpeedButtonList.DispatchMessage(Control, Message);
  1535. end;
  1536. else
  1537. FSpeedButtonList.DispatchMessage(Control, Message);
  1538. end;
  1539. end
  1540. else
  1541. FSpeedButtonList.DispatchMessage(Control, Message);
  1542. end;
  1543. end;
  1544. //----------------------------------------------------------------------------------------------------------------------
  1545. procedure TThemeManager.SplitterWindowProc(Control: TControl; var Message: TMessage);
  1546. begin
  1547. if not DoControlMessage(Control, Message) then
  1548. begin
  1549. if ThemeServices.ThemesEnabled then
  1550. begin
  1551. case Message.Msg of
  1552. WM_PAINT:
  1553. with TWMPaint(Message) do
  1554. begin
  1555. PerformEraseBackground(Control, DC);
  1556. Message.Result := 0;
  1557. end;
  1558. else
  1559. FSplitterList.DispatchMessage(Control, Message);
  1560. end;
  1561. end
  1562. else
  1563. FSplitterList.DispatchMessage(Control, Message);
  1564. end;
  1565. end;
  1566. //----------------------------------------------------------------------------------------------------------------------
  1567. type
  1568. TCustomStatusBarCast = class(TCustomStatusBar);
  1569. procedure TThemeManager.StatusBarWindowProc(Control: TControl; var Message: TMessage);
  1570. var
  1571. Details: TThemedElementDetails;
  1572. begin
  1573. if not DoControlMessage(Control, Message) then
  1574. begin
  1575. if ThemeServices.ThemesEnabled then
  1576. begin
  1577. case Message.Msg of
  1578. WM_NCCALCSIZE:
  1579. with TWMNCCalcSize(Message) do
  1580. begin
  1581. FStatusBarList.DispatchMessage(Control, Message);
  1582. // We cannot simply override the window class' CS_HREDRAW and CS_VREDRAW styles but the following
  1583. // does the job very well too.
  1584. // Note: this may produce trouble with embedded controls (e.g. progress bars).
  1585. if CalcValidRects then
  1586. Result := Result or WVR_REDRAW;
  1587. end;
  1588. WM_ERASEBKGND:
  1589. with TWMEraseBkGnd(Message) do
  1590. begin
  1591. Details := ThemeServices.GetElementDetails(tsStatusRoot);
  1592. ThemeServices.DrawElement(DC, Details, Control.ClientRect);
  1593. Message.Result := 1;
  1594. end;
  1595. else
  1596. FStatusBarList.DispatchMessage(Control, Message);
  1597. end;
  1598. end
  1599. else
  1600. FStatusBarList.DispatchMessage(Control, Message);
  1601. end;
  1602. end;
  1603. //----------------------------------------------------------------------------------------------------------------------
  1604. procedure TThemeManager.TabSheetWindowProc(Control: TControl; var Message: TMessage);
  1605. var
  1606. DrawRect: TRect;
  1607. Details: TThemedElementDetails;
  1608. DC: HDC;
  1609. begin
  1610. if not DoControlMessage(Control, Message) then
  1611. begin
  1612. if ThemeServices.ThemesEnabled then
  1613. begin
  1614. case Message.Msg of
  1615. // Paint the border (and erase the background)
  1616. WM_NCPAINT:
  1617. with TTabSheet(Control) do
  1618. begin
  1619. DC := GetWindowDC(Handle);
  1620. try
  1621. // Exclude the client area from painting. We only want to erase the non-client area.
  1622. DrawRect := ClientRect;
  1623. OffsetRect(DrawRect, BorderWidth, BorderWidth);
  1624. with DrawRect do
  1625. ExcludeClipRect(DC, Left, Top, Right, Bottom);
  1626. // The parent paints relative to the control's client area. We have to compensate for this by
  1627. // shifting the dc's window origin.
  1628. SetWindowOrgEx(DC, -BorderWidth, -BorderWidth, nil);
  1629. Details := ThemeServices.GetElementDetails(ttBody);
  1630. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, @Details, False);
  1631. finally
  1632. ReleaseDC(Handle, DC);
  1633. end;
  1634. Message.Result := 0;
  1635. end;
  1636. WM_PRINTCLIENT,
  1637. WM_ERASEBKGND:
  1638. begin
  1639. if Message.Msg = WM_PRINTCLIENT then
  1640. DC := TWMPrintClient(Message).DC
  1641. else
  1642. DC := TWMEraseBkGnd(Message).DC;
  1643. // Using the parent's background here does not always work. Particularly, it does not work in cases
  1644. // where the parent (pane) background does not include the body background. One way to solve this problem
  1645. // would be to paint the body background here. However this produces a lot of problems all caused by
  1646. // the fact that these backgrounds might be tiled or might otherwise have special drawing style.
  1647. // Due to the near-to-non-existing documentation on all the themes APIs I use the lesser evil by default and
  1648. // paint the parent background, which works in most cases very well.
  1649. // However you may want to enable the other way, if needed.
  1650. if toAlternateTabSheetDraw in FOptions then
  1651. begin
  1652. Details := ThemeServices.GetElementDetails(ttBody);
  1653. DrawRect := Control.ClientRect;
  1654. ThemeServices.DrawElement(DC, Details, DrawRect);
  1655. end
  1656. else
  1657. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, nil, False);
  1658. Message.Result := 1;
  1659. end;
  1660. else
  1661. FTabSheetList.DispatchMessage(Control, Message);
  1662. end;
  1663. end
  1664. else
  1665. FTabSheetList.DispatchMessage(Control, Message);
  1666. end;
  1667. end;
  1668. //----------------------------------------------------------------------------------------------------------------------
  1669. procedure TThemeManager.TrackBarWindowProc(Control: TControl; var Message: TMessage);
  1670. var
  1671. Info: PNMCustomDraw;
  1672. R: TRect;
  1673. Rgn: HRGN;
  1674. Details: TThemedElementDetails;
  1675. Offset: Integer;
  1676. FocusBorderWidth,
  1677. FocusBorderHeight: Integer;
  1678. begin
  1679. if not DoControlMessage(Control, Message) then
  1680. begin
  1681. if ThemeServices.ThemesEnabled then
  1682. begin
  1683. case Message.Msg of
  1684. CN_NOTIFY:
  1685. with TWMNotify(Message) do
  1686. if NMHdr.code = NM_CUSTOMDRAW then
  1687. begin
  1688. Info := Pointer(NMHdr);
  1689. case Info.dwDrawStage of
  1690. CDDS_PREPAINT:
  1691. Result := CDRF_NOTIFYITEMDRAW;
  1692. CDDS_ITEMPREPAINT:
  1693. with Control as TTrackBar do
  1694. begin
  1695. // Take action based on which item is about to be painted.
  1696. case Info.dwItemSpec of
  1697. TBCD_TICS: // Before re-painting ticks redo whole background.
  1698. begin
  1699. R := ClientRect;
  1700. // Leave room for the focus rectangle if there is one.
  1701. if Focused and ((Perform(WM_QUERYUISTATE, 0, 0) and UISF_HIDEFOCUS) = 0) then
  1702. begin
  1703. SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH, 0, @FocusBorderWidth, 0);
  1704. SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, @FocusBorderHeight, 0);
  1705. InflateRect(R, -FocusBorderWidth, -FocusBorderHeight);
  1706. end;
  1707. ThemeServices.DrawParentBackground(Handle, Info.hDC, nil, False, @R);
  1708. end;
  1709. TBCD_CHANNEL: // Before re-painting channel just redo strip of background overlapped.
  1710. begin
  1711. // Retrieve the bounding box for the thumb.
  1712. SendMessage(Handle, TBM_GETTHUMBRECT, 0, Integer(@R));
  1713. // Extend this rectangle to the top/bottom or left/right border, respectively.
  1714. Offset := 0;
  1715. if Orientation = trHorizontal then
  1716. begin
  1717. // Leave room for the focus rectangle if there is one.
  1718. if Focused then
  1719. begin
  1720. SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH, 0, @FocusBorderWidth, 0);
  1721. Inc(Offset, FocusBorderWidth);
  1722. end;
  1723. R.Left := ClientRect.Left + Offset;
  1724. R.Right := ClientRect.Right - Offset;
  1725. end
  1726. else
  1727. begin
  1728. // Leave room for the focus rectangle if there is one.
  1729. if Focused then
  1730. begin
  1731. SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, @FocusBorderHeight, 0);
  1732. Inc(Offset, FocusBorderWidth);
  1733. end;
  1734. R.Top := ClientRect.Top + Offset;
  1735. R.Bottom := ClientRect.Bottom - Offset;
  1736. end;
  1737. with R do
  1738. Rgn := CreateRectRgn(Left, Top, Right, Bottom);
  1739. SelectClipRgn(Info.hDC, Rgn);
  1740. Details := ThemeServices.GetElementDetails(ttbThumbTics);
  1741. ThemeServices.DrawParentBackground(Handle, Info.hDC, @Details, False);
  1742. DeleteObject(Rgn);
  1743. SelectClipRgn(Info.hDC, 0);
  1744. end;
  1745. end;
  1746. Result := CDRF_DODEFAULT;
  1747. end;
  1748. else
  1749. Result := CDRF_DODEFAULT;
  1750. end;
  1751. end;
  1752. else
  1753. FTrackBarList.DispatchMessage(Control, Message);
  1754. end;
  1755. end
  1756. else
  1757. FTrackBarList.DispatchMessage(Control, Message);
  1758. end;
  1759. end;
  1760. //----------------------------------------------------------------------------------------------------------------------
  1761. procedure TThemeManager.WinControlWindowProc(Control: TControl; var Message: TMessage);
  1762. var
  1763. DC: HDC;
  1764. SavedDC: Integer;
  1765. begin
  1766. if not DoControlMessage(Control, Message) then
  1767. begin
  1768. if ThemeServices.ThemesEnabled then
  1769. begin
  1770. case Message.Msg of
  1771. CN_KEYDOWN,
  1772. WM_SYSKEYDOWN,
  1773. WM_KEYDOWN:
  1774. begin
  1775. UpdateUIState(Control, TWMKey(Message).CharCode);
  1776. FWinControlList.DispatchMessage(Control, Message);
  1777. end;
  1778. WM_ERASEBKGND:
  1779. begin
  1780. if Control is TScrollingWinControl then
  1781. with Control as TWinControl do
  1782. begin
  1783. DC := TWMEraseBkGnd(Message).DC;
  1784. if DoubleBuffered then
  1785. PerformEraseBackground(Control, DC)
  1786. else
  1787. ThemeServices.DrawParentBackground(Handle, DC, nil, False);
  1788. Message.Result := 1;
  1789. end
  1790. else
  1791. FWinControlList.DispatchMessage(Control, Message);
  1792. end;
  1793. WM_NCPAINT:
  1794. begin
  1795. FWinControlList.DispatchMessage(Control, Message);
  1796. ThemeServices.PaintBorder(Control as TWinControl, Control is TCustomGrid);
  1797. end;
  1798. CN_CTLCOLORSTATIC:
  1799. if Control is TCustomStaticText then
  1800. with TWMCtlColorStatic(Message), { MP } TWinControlCast(Control as TWinControl) do
  1801. begin
  1802. SetBkMode(ChildDC, Windows.TRANSPARENT);
  1803. // MP BEGIN
  1804. SetTextColor(ChildDC, ColorToRGB(Font.Color));
  1805. SetBkColor(ChildDC, ColorToRGB(Brush.Color));
  1806. // MP END
  1807. SavedDC := SaveDC(ChildDC);
  1808. ThemeServices.DrawParentBackground(Handle, ChildDC, nil, False);
  1809. FWinControlList.DispatchMessage(Control, Message);
  1810. RestoreDC(ChildDC, SavedDC);
  1811. // Return an empty brush to prevent Windows from overpainting what we just have created.
  1812. Result := GetStockObject(NULL_BRUSH);
  1813. end
  1814. else
  1815. FWinControlList.DispatchMessage(Control, Message);
  1816. else
  1817. FWinControlList.DispatchMessage(Control, Message);
  1818. end;
  1819. end
  1820. else
  1821. FWinControlList.DispatchMessage(Control, Message);
  1822. end;
  1823. end;
  1824. //----------------------------------------------------------------------------------------------------------------------
  1825. procedure TThemeManager.PreAnimateWindowProc(var Message: TMessage);
  1826. // This and the other proxy window procs do an important step to make the entire subclassing work here.
  1827. // Because we have only one window procedure for each class of subclassed controls (many to 1 relation), it is necessary
  1828. // to know to which control the message was sent originally (read: whose WindowProc property had been called). This is
  1829. // important because we have to forward the message to the original window procedure once we are finished with our own
  1830. // processing and sometimes properties of the control are needed too.
  1831. // When this method is called the hidden self parameter is not the actual theme manager instance but the
  1832. // control reference to which the message was sent originally. This is the result from the explicit Data member
  1833. // assignment done in TWindowProcList.Add. This is very helpful but has the side effect that we don't have the theme
  1834. // manager instance anymore (since the self param is the control). Thus we need another reference, which we have
  1835. // in the form of the main manager. Since only the main manager will subclass controls it is guaranteed that
  1836. // there is a valid reference when we arrive here (and in the other proxy methods).
  1837. begin
  1838. Assert(Assigned(MainManager));
  1839. MainManager.AnimateWindowProc(TControl(Self), Message);
  1840. end;
  1841. //----------------------------------------------------------------------------------------------------------------------
  1842. procedure TThemeManager.PreButtonControlWindowProc(var Message: TMessage);
  1843. // Read more about this code in PreAnimateWindowProc.
  1844. begin
  1845. Assert(Assigned(MainManager));
  1846. MainManager.ButtonControlWindowProc(TControl(Self), Message, { MP }MainManager.FButtonControlList);
  1847. end;
  1848. //----------------------------------------------------------------------------------------------------------------------
  1849. // MP BEGIN
  1850. procedure TThemeManager.PreCheckBoxWindowProc(var Message: TMessage);
  1851. // Read more about this code in PreAnimateWindowProc.
  1852. begin
  1853. Assert(Assigned(MainManager));
  1854. MainManager.ButtonControlWindowProc(TControl(Self), Message, MainManager.FCheckBoxList);
  1855. end;
  1856. //----------------------------------------------------------------------------------------------------------------------
  1857. procedure TThemeManager.PreButtonWindowProc(var Message: TMessage);
  1858. // Read more about this code in PreAnimateWindowProc.
  1859. begin
  1860. Assert(Assigned(MainManager));
  1861. MainManager.ButtonControlWindowProc(TControl(Self), Message, MainManager.FButtonList);
  1862. end;
  1863. // MP END
  1864. //----------------------------------------------------------------------------------------------------------------------
  1865. {$ifdef CheckListSupport}
  1866. procedure TThemeManager.PreCheckListBoxWindowProc(var Message: TMessage);
  1867. // Read more about this code in PreAnimateWindowProc.
  1868. begin
  1869. Assert(Assigned(MainManager));
  1870. MainManager.CheckListBoxWindowProc(TControl(Self), Message);
  1871. end;
  1872. {$endif CheckListSupport}
  1873. //----------------------------------------------------------------------------------------------------------------------
  1874. procedure TThemeManager.PreFormWindowProc(var Message: TMessage);
  1875. // Read more about this code in PreAnimateWindowProc.
  1876. begin
  1877. Assert(Assigned(MainManager));
  1878. MainManager.FormWindowProc(TControl(Self), Message);
  1879. end;
  1880. //----------------------------------------------------------------------------------------------------------------------
  1881. {$ifdef COMPILER_5_UP}
  1882. procedure TThemeManager.PreFrameWindowProc(var Message: TMessage);
  1883. // Read more about this code in PreAnimateWindowProc.
  1884. begin
  1885. Assert(Assigned(MainManager));
  1886. MainManager.FrameWindowProc(TControl(Self), Message);
  1887. end;
  1888. {$endif COMPILER_5_UP}
  1889. //----------------------------------------------------------------------------------------------------------------------
  1890. procedure TThemeManager.PreGroupBoxWindowProc(var Message: TMessage);
  1891. // Read more about this code in PreAnimateWindowProc.
  1892. begin
  1893. Assert(Assigned(MainManager));
  1894. MainManager.GroupBoxWindowProc(TControl(Self), Message);
  1895. end;
  1896. //----------------------------------------------------------------------------------------------------------------------
  1897. procedure TThemeManager.PreListviewWindowProc(var Message: TMessage);
  1898. // Read more about this code in PreAnimateWindowProc.
  1899. begin
  1900. Assert(Assigned(MainManager));
  1901. MainManager.ListviewWindowProc(TControl(Self), Message);
  1902. end;
  1903. //----------------------------------------------------------------------------------------------------------------------
  1904. procedure TThemeManager.PrePanelWindowProc(var Message: TMessage);
  1905. // Read more about this code in PreAnimateWindowProc.
  1906. begin
  1907. Assert(Assigned(MainManager));
  1908. MainManager.PanelWindowProc(TControl(Self), Message);
  1909. end;
  1910. //----------------------------------------------------------------------------------------------------------------------
  1911. procedure TThemeManager.PreSpeedButtonWindowProc(var Message: TMessage);
  1912. // Read more about this code in PreAnimateWindowProc.
  1913. begin
  1914. Assert(Assigned(MainManager));
  1915. MainManager.SpeedButtonWindowProc(TControl(Self), Message);
  1916. end;
  1917. //----------------------------------------------------------------------------------------------------------------------
  1918. procedure TThemeManager.PreSplitterWindowProc(var Message: TMessage);
  1919. // Read more about this code in PreAnimateWindowProc.
  1920. begin
  1921. Assert(Assigned(MainManager));
  1922. MainManager.SplitterWindowProc(TControl(Self), Message);
  1923. end;
  1924. //----------------------------------------------------------------------------------------------------------------------
  1925. procedure TThemeManager.PreStatusBarWindowProc(var Message: TMessage);
  1926. // Read more about this code in PreAnimateWindowProc.
  1927. begin
  1928. Assert(Assigned(MainManager));
  1929. MainManager.StatusBarWindowProc(TControl(Self), Message);
  1930. end;
  1931. //----------------------------------------------------------------------------------------------------------------------
  1932. procedure TThemeManager.PreTabSheetWindowProc(var Message: TMessage);
  1933. // Read more about this code in PreAnimateWindowProc.
  1934. begin
  1935. Assert(Assigned(MainManager));
  1936. MainManager.TabSheetWindowProc(TControl(Self), Message);
  1937. end;
  1938. //----------------------------------------------------------------------------------------------------------------------
  1939. procedure TThemeManager.PreTrackBarWindowProc(var Message: TMessage);
  1940. // Read more about this code in PreAnimateWindowProc.
  1941. begin
  1942. Assert(Assigned(MainManager));
  1943. MainManager.TrackBarWindowProc(TControl(Self), Message);
  1944. end;
  1945. //----------------------------------------------------------------------------------------------------------------------
  1946. procedure TThemeManager.PreWinControlWindowProc(var Message: TMessage);
  1947. // Read more about this code in PreAnimateWindowProc.
  1948. begin
  1949. Assert(Assigned(MainManager));
  1950. MainManager.WinControlWindowProc(TControl(Self), Message);
  1951. end;
  1952. //----------------------------------------------------------------------------------------------------------------------
  1953. procedure TThemeManager.AddRecreationCandidate(Control: TControl);
  1954. begin
  1955. if FPendingRecreationList.IndexOf(Control) = -1 then
  1956. FPendingRecreationList.Add(Control);
  1957. end;
  1958. //----------------------------------------------------------------------------------------------------------------------
  1959. procedure TThemeManager.BroadcastThemeChange;
  1960. //--------------- local function --------------------------------------------
  1961. procedure BroadcastChildren(Control: TWinControl);
  1962. var
  1963. I: Integer;
  1964. ChildControl: TWinControl;
  1965. begin
  1966. for I := 0 to Control.ControlCount - 1 do
  1967. if Control.Controls[I] is TWinControl then
  1968. begin
  1969. ChildControl := TWinControl(Control.Controls[I]);
  1970. if ChildControl.HandleAllocated then
  1971. ChildControl.Perform(WM_THEMECHANGED, 0, 0);
  1972. // We must force recreation of some window handles (to reapply all the control settings).
  1973. if (ChildControl is TCustomListView) or (ChildControl is TCoolBar) then
  1974. TWinControlCast(ChildControl).RecreateWnd
  1975. else
  1976. BroadcastChildren(ChildControl);
  1977. end;
  1978. end;
  1979. //--------------- local function --------------------------------------------
  1980. var
  1981. I: Integer;
  1982. Form: TCustomForm;
  1983. begin
  1984. for I := 0 to Screen.FormCount - 1 do
  1985. begin
  1986. Form := Screen.Forms[I];
  1987. Form.Perform(WM_THEMECHANGED, 0, 0);
  1988. BroadcastChildren(Form);
  1989. end;
  1990. end;
  1991. //----------------------------------------------------------------------------------------------------------------------
  1992. class function TThemeManager.CurrentThemeManager: TThemeManager;
  1993. begin
  1994. Result := MainManager;
  1995. end;
  1996. //----------------------------------------------------------------------------------------------------------------------
  1997. function TThemeManager.DoAllowSubclassing(Control: TControl): Boolean;
  1998. begin
  1999. Result := True;
  2000. if Assigned(FOnAllowSubclassing) then
  2001. FOnAllowSubclassing(Self,Control,Result);
  2002. end;
  2003. //----------------------------------------------------------------------------------------------------------------------
  2004. function TThemeManager.DoControlMessage(Control: TControl; var Message: TMessage): Boolean;
  2005. var
  2006. I: Integer;
  2007. Event: PControlMessageEvent;
  2008. begin
  2009. Result := False;
  2010. if Assigned(FOnControlMessage) then
  2011. FOnControlMessage(Self, Control, Message, Result);
  2012. if not Result then
  2013. begin
  2014. I := 0;
  2015. while I < FListeners.Count do
  2016. begin
  2017. Event := FListeners[I];
  2018. try
  2019. Event^(Self, Control, Message, Result);
  2020. if Result then
  2021. Break;
  2022. Inc(I);
  2023. except
  2024. // Raised an exception, so delete the registration
  2025. UnregisterListener(Event^);
  2026. end;
  2027. end;
  2028. end;
  2029. end;
  2030. //----------------------------------------------------------------------------------------------------------------------
  2031. procedure TThemeManager.DoOnThemeChange;
  2032. begin
  2033. if Assigned(FOnThemeChange) then
  2034. FOnThemeChange(Self);
  2035. end;
  2036. //----------------------------------------------------------------------------------------------------------------------
  2037. procedure TThemeManager.DrawBitBtn(Control: TBitBtn; var DrawItemStruct: TDrawItemStruct);
  2038. var
  2039. Button: TThemedButton;
  2040. R: TRect;
  2041. Wnd: HWND;
  2042. P: TPoint;
  2043. begin
  2044. with DrawItemStruct do
  2045. begin
  2046. // For owner drawn buttons we will never get the ODS_HIGHLIGHT flag. This makes it necessary to
  2047. // check ourselves if the button is "hot".
  2048. GetCursorPos(P);
  2049. Wnd := WindowFromPoint(P);
  2050. if Wnd = TWinControl(Control).Handle then
  2051. itemState := itemState or ODS_HOTLIGHT;
  2052. R := rcItem;
  2053. if not Control.Enabled then
  2054. Button := tbPushButtonDisabled
  2055. else
  2056. if (itemState and ODS_SELECTED) <> 0 then
  2057. Button := tbPushButtonPressed
  2058. else
  2059. if (itemState and ODS_HOTLIGHT) <> 0 then
  2060. Button := tbPushButtonHot
  2061. else
  2062. // It seems ODS_DEFAULT is never set, so we have to check the control's properties.
  2063. if Control.Default or ((itemState and ODS_FOCUS) <> 0) then
  2064. Button := tbPushButtonDefaulted
  2065. else
  2066. Button := tbPushButtonNormal;
  2067. DrawButton(Control, Button, hDC, R, itemState and ODS_FOCUS <> 0);
  2068. end;
  2069. end;
  2070. //----------------------------------------------------------------------------------------------------------------------
  2071. procedure TThemeManager.DrawButton(Control: TControl; Button: TThemedButton; DC: HDC; R: TRect; Focused: Boolean);
  2072. // Common paint routine for TBitBtn and TSpeedButton.
  2073. var
  2074. TextBounds: TRect;
  2075. LastFont: HFONT;
  2076. Glyph: TBitmap;
  2077. GlyphPos: TPoint;
  2078. GlyphWidth: Integer;
  2079. GlyphSourceX: Integer;
  2080. GlyphMask: TBitmap;
  2081. Offset: TPoint;
  2082. ToolButton: TThemedToolBar;
  2083. Details: TThemedElementDetails;
  2084. begin
  2085. GlyphSourceX := 0;
  2086. GlyphWidth := 0;
  2087. ToolButton := ttbToolbarDontCare;
  2088. if Control is TBitBtn then
  2089. begin
  2090. Glyph := TBitBtn(Control).Glyph;
  2091. // Determine which image to use (if there is more than one in the glyph).
  2092. with TBitBtn(Control), Glyph do
  2093. begin
  2094. if not Empty then
  2095. begin
  2096. GlyphWidth := Width div NumGlyphs;
  2097. if not Enabled and (NumGlyphs > 1) then
  2098. GlyphSourceX := GlyphWidth
  2099. else
  2100. if (Button = tbPushButtonPressed) and (NumGlyphs > 2) then
  2101. GlyphSourceX := 2 * GlyphWidth;
  2102. end;
  2103. end;
  2104. end
  2105. else
  2106. begin
  2107. Glyph := TSpeedButton(Control).Glyph;
  2108. with TSpeedButtonCast(Control) do
  2109. begin
  2110. // Determine which image to use (if there is more than one in the glyph).
  2111. with Glyph do
  2112. if not Empty then
  2113. begin
  2114. GlyphWidth := Width div NumGlyphs;
  2115. if not Enabled and (NumGlyphs > 1) then
  2116. GlyphSourceX := GlyphWidth
  2117. else
  2118. case FState of
  2119. bsDown:
  2120. if NumGlyphs > 2 then
  2121. GlyphSourceX := 2 * GlyphWidth;
  2122. bsExclusive:
  2123. if NumGlyphs > 3 then
  2124. GlyphSourceX := 3 * GlyphWidth;
  2125. end;
  2126. end;
  2127. // If the speed button is flat then we use toolbutton images for drawing.
  2128. if Flat then
  2129. begin
  2130. case Button of
  2131. tbPushButtonDisabled:
  2132. Toolbutton := ttbButtonDisabled;
  2133. tbPushButtonPressed:
  2134. Toolbutton := ttbButtonPressed;
  2135. tbPushButtonHot:
  2136. Toolbutton := ttbButtonHot;
  2137. tbPushButtonNormal:
  2138. Toolbutton := ttbButtonNormal;
  2139. end;
  2140. end;
  2141. end;
  2142. end;
  2143. if ToolButton = ttbToolbarDontCare then
  2144. begin
  2145. Details := ThemeServices.GetElementDetails(Button);
  2146. ThemeServices.DrawElement(DC, Details, R);
  2147. R := ThemeServices.ContentRect(DC, Details, R);
  2148. end
  2149. else
  2150. begin
  2151. Details := ThemeServices.GetElementDetails(ToolButton);
  2152. ThemeServices.DrawElement(DC, Details, R);
  2153. R := ThemeServices.ContentRect(DC, Details, R);
  2154. end;
  2155. // The XP style does no longer indicate pressed buttons by moving the caption one pixel down and right.
  2156. Offset := Point(0, 0);
  2157. with TControlCast(Control) do
  2158. begin
  2159. LastFont := SelectObject(DC, Font.Handle);
  2160. CalcButtonLayout(Control, DC, R, Offset, GlyphPos, TextBounds, DrawTextBidiModeFlags(0));
  2161. // Note: Currently we cannot do text output via the themes services because the second flags parameter (which is
  2162. // used for graying out strings) is ignored (bug in XP themes implementation?).
  2163. // Hence we have to do it the "usual" way.
  2164. if Button = tbPushButtonDisabled then
  2165. SetTextColor(DC, ColorToRGB(clGrayText))
  2166. else
  2167. SetTextColor(DC, ColorToRGB(Font.Color));
  2168. SetBkMode(DC, TRANSPARENT);
  2169. DrawText(DC, PChar(Caption), Length(Caption), TextBounds, DT_CENTER or DT_VCENTER);
  2170. with Glyph do
  2171. if not Empty then
  2172. begin
  2173. GlyphMask := TBitmap.Create;
  2174. GlyphMask.Assign(Glyph);
  2175. GlyphMask.Mask(Glyph.TransparentColor);
  2176. TransparentStretchBlt(DC, GlyphPos.X, GlyphPos.Y, GlyphWidth, Height, Canvas.Handle, GlyphSourceX, 0,
  2177. GlyphWidth, Height, GlyphMask.Canvas.Handle, GlyphSourceX, 0);
  2178. GlyphMask.Free;
  2179. end;
  2180. SelectObject(DC, LastFont);
  2181. end;
  2182. if Focused then
  2183. begin
  2184. SetTextColor(DC, 0);
  2185. DrawFocusRect(DC, R);
  2186. end;
  2187. end;
  2188. //----------------------------------------------------------------------------------------------------------------------
  2189. function TThemeManager.FindListener(AControlMessage: TControlMessageEvent; var Index: Integer): Boolean;
  2190. var
  2191. I: Integer;
  2192. begin
  2193. Result := False;
  2194. for I := 0 to FListeners.Count - 1 do
  2195. if @PControlMessageEvent(FListeners[I])^ = @AControlMessage then
  2196. begin
  2197. Result := True;
  2198. Index := I;
  2199. Break;
  2200. end;
  2201. end;
  2202. //----------------------------------------------------------------------------------------------------------------------
  2203. type
  2204. // Cast to access the Transparent property in TCustomLabel which is protected there.
  2205. TLabelCast = class(TCustomLabel);
  2206. procedure TThemeManager.FixControls(Form: TCustomForm);
  2207. // Iterates through all existing controls in all forms which are registered with Screen and checks for TToolBar and
  2208. // TCustomLabel. Both controls will get their Transparent property set to True.
  2209. var
  2210. MakeTransparent: Boolean;
  2211. RemoveMouseCapture: Boolean;
  2212. //--------------- local function --------------------------------------------
  2213. procedure IterateControls(Parent: TWinControl);
  2214. var
  2215. I, J: Integer;
  2216. ToolBar: TToolBar;
  2217. Control: TControl;
  2218. begin
  2219. for I := 0 to Parent.ControlCount - 1 do
  2220. begin
  2221. Control := Parent.Controls[I];
  2222. // MP
  2223. if not DoAllowSubClassing(Control) then Continue;
  2224. // Allow all window controls to use themed background if they are placed on a tab sheet. This works only for controls
  2225. // whose background is drawn by Windows and which can be transparent. There aren't many which qualify, though.
  2226. if (Control is TWinControl) and ThemeServices.ThemesEnabled then
  2227. begin
  2228. // MP BEGIN
  2229. try
  2230. TWinControl(Control).HandleNeeded;
  2231. except
  2232. // if allocating handle fails, just do not fix the control and continue
  2233. // strangelly happens irregularly for buttons
  2234. Continue;
  2235. end;
  2236. // MP END
  2237. EnableThemeDialogTexture(TWinControl(Control).Handle, ETDT_ENABLETAB);
  2238. end;
  2239. if Control is TToolBar then
  2240. begin
  2241. ToolBar := TToolBar(Control);
  2242. if MakeTransparent then
  2243. ToolBar.Transparent := True;
  2244. if RemoveMouseCapture then
  2245. begin
  2246. for J := 0 to ToolBar.ButtonCount - 1 do
  2247. if ToolBar.Buttons[J].Style <> tbsDropDown then
  2248. ToolBar.Buttons[J].ControlStyle := ToolBar.Buttons[J].ControlStyle - [csCaptureMouse];
  2249. end;
  2250. end
  2251. else
  2252. if Control is TCustomLabel then
  2253. begin
  2254. if MakeTransparent then
  2255. TLabelCast(Control).Transparent := True;
  2256. end
  2257. else
  2258. if (Control is TWinControl) and (TWinControl(Control).ControlCount > 0) then
  2259. IterateControls(Control as TWinControl);
  2260. end;
  2261. end;
  2262. //--------------- end local function ----------------------------------------
  2263. var
  2264. I: Integer;
  2265. begin
  2266. MakeTransparent := toSetTransparency in FOptions;
  2267. RemoveMouseCapture := toResetMouseCapture in FOptions;
  2268. if Form = nil then
  2269. begin
  2270. for I := 0 to Screen.FormCount - 1 do
  2271. begin
  2272. Form := Screen.Forms[I];
  2273. IterateControls(Form);
  2274. end;
  2275. end
  2276. else
  2277. IterateControls(Form);
  2278. end;
  2279. //----------------------------------------------------------------------------------------------------------------------
  2280. procedure TThemeManager.ForceAsMainManager;
  2281. // Forces this instance to become the main manager. This is useful for descentants to provide additional functionality.
  2282. begin
  2283. if MainManager <> Self then
  2284. begin
  2285. Lock.Enter;
  2286. try
  2287. if Assigned(MainManager) then
  2288. begin
  2289. MainManager.FSubclassingDisabled := True;
  2290. MainManager.ClearLists;
  2291. end;
  2292. MainManager := Self;
  2293. FSubclassingDisabled := False;
  2294. CollectForms;
  2295. finally
  2296. Lock.Release;
  2297. end;
  2298. end;
  2299. end;
  2300. //----------------------------------------------------------------------------------------------------------------------
  2301. procedure TThemeManager.HandleControlChange(Control: TControl; Inserting: Boolean);
  2302. var
  2303. List: TWindowProcList;
  2304. Index: Integer;
  2305. WinControl: TWinControl;
  2306. begin
  2307. List := nil;
  2308. // Do subclassing work only on Windows XP or higher.
  2309. if IsWindowsXP then
  2310. begin
  2311. if not ThemeServices.ThemesEnabled then
  2312. begin
  2313. // TCustomListview always must be subclassed.
  2314. if Control is TCustomListView then
  2315. begin
  2316. if (toSubclassListView in FOptions) or not Inserting then
  2317. begin
  2318. List := FListViewList;
  2319. // We have to force the listview to recreate its window handle (to reapply all the control settings).
  2320. // However if it is already in our list then don't touch the window anymore.
  2321. WinControl := Control as TWinControl;
  2322. if Inserting and not List.Find(Control, Index) and WinControl.HandleAllocated then
  2323. PostMessage(WinControl.Handle, CM_RECREATEWND, 0, 0);
  2324. end;
  2325. end;
  2326. end
  2327. else
  2328. begin
  2329. // MP BEGIN
  2330. // Including checkboxes and buttons to button-control list makes it strangely fail
  2331. // for some dialogs (irregularly). Introducing separate list for
  2332. // them solves the problem
  2333. if Control is TCheckBox then
  2334. begin
  2335. if (toSubclassButtons in FOptions) or not Inserting then
  2336. List := FCheckBoxList;
  2337. end
  2338. else
  2339. if Control is TButton then
  2340. begin
  2341. if (toSubclassButtons in FOptions) or not Inserting then
  2342. List := FButtonList;
  2343. end
  2344. else
  2345. // MP END
  2346. if Control is TButtonControl then
  2347. begin
  2348. if (toSubclassButtons in FOptions) or not Inserting then
  2349. List := FButtonControlList;
  2350. end
  2351. else
  2352. if Control is TSpeedButton then
  2353. begin
  2354. if (toSubclassSpeedButtons in FOptions) or not Inserting then
  2355. List := FSpeedButtonList;
  2356. end
  2357. else
  2358. if Control is TCustomGroupBox then
  2359. begin
  2360. if (toSubclassGroupBox in FOptions) or not Inserting then
  2361. List := FGroupBoxList;
  2362. end
  2363. else
  2364. if Control is TTabSheet then
  2365. begin
  2366. if (toSubclassTabSheet in FOptions) or not Inserting then
  2367. List := FTabSheetList;
  2368. end
  2369. else
  2370. if Control is TCustomPanel then
  2371. begin
  2372. if (toSubclassPanel in FOptions) or not Inserting then
  2373. List := FPanelList;
  2374. end
  2375. else
  2376. {$ifdef COMPILER_5_UP}
  2377. if Control is TCustomFrame then
  2378. begin
  2379. if (toSubclassFrame in FOptions) or not Inserting then
  2380. List := FFrameList;
  2381. end
  2382. else
  2383. {$endif COMPILER_5_UP}
  2384. if Control is TCustomListView then
  2385. begin
  2386. if (toSubclassListView in FOptions) or not Inserting then
  2387. begin
  2388. List := FListViewList;
  2389. // We have to force the listview to recreate its window handle (to reapply all the control settings).
  2390. // However if it is already in our list then don't touch the window anymore.
  2391. WinControl := Control as TWinControl;
  2392. if Inserting and not List.Find(Control, Index) and WinControl.HandleAllocated then
  2393. PostMessage(WinControl.Handle, CM_RECREATEWND, 0, 0);
  2394. end;
  2395. end
  2396. else
  2397. if Control is TTrackBar then
  2398. begin
  2399. if (toSubclassTrackBar in FOptions) or not Inserting then
  2400. List := FTrackBarList;
  2401. end
  2402. else
  2403. {$ifdef CheckListSupport}
  2404. if Control is TCheckListBox then
  2405. begin
  2406. if (toSubclassCheckListBox in FOptions) or not Inserting then
  2407. List := FCheckListBoxList;
  2408. end
  2409. else
  2410. {$endif CheckListSupport}
  2411. if Control is TCustomStatusBar then
  2412. begin
  2413. if (toSubclassStatusBar in FOptions) or not Inserting then
  2414. List := FStatusBarList;
  2415. end
  2416. else
  2417. if Control is TSplitter then
  2418. begin
  2419. if (toSubclassSplitter in FOptions) or not Inserting then
  2420. List := FSplitterList;
  2421. end
  2422. else
  2423. if Control is TAnimate then
  2424. begin
  2425. if (toSubclassAnimate in FOptions) or not Inserting then
  2426. List := FAnimateList;
  2427. end
  2428. else
  2429. if Control is TCustomForm then
  2430. begin
  2431. List := FFormList;
  2432. if Inserting then
  2433. FPendingFormsList.Remove(Control);
  2434. end
  2435. else
  2436. if Control is TWinControl then
  2437. begin
  2438. if (toSubclassWinControl in FOptions) or not Inserting then
  2439. List := FWinControlList;
  2440. end;
  2441. end;
  2442. if Assigned(List) then
  2443. begin
  2444. if Inserting and (DoAllowSubClassing(Control) and (Control.Perform(CM_DENYSUBCLASSING, 0, 0) = 0)) then
  2445. begin
  2446. List.Add(Control);
  2447. // We need a notification for this control about its destruction.
  2448. Control.FreeNotification(Self);
  2449. // Automatically collect the child controls when a TWinControl is added.
  2450. if (Control is TWinControl) and (TWinControl(Control).ControlCount > 0) then
  2451. CollectControls(TWinControl(Control));
  2452. end
  2453. else
  2454. List.Remove(Control);
  2455. end;
  2456. end;
  2457. end;
  2458. //----------------------------------------------------------------------------------------------------------------------
  2459. function TThemeManager.IsRecreationCandidate(Control: TControl): Boolean;
  2460. // Tells the caller whether the given controls is being recreated.
  2461. begin
  2462. Result := FPendingRecreationList.IndexOf(Control) > -1;
  2463. end;
  2464. //----------------------------------------------------------------------------------------------------------------------
  2465. procedure TThemeManager.Loaded;
  2466. begin
  2467. // Collect all controls which already exist. Those controls, which are later added/removed are handled by the
  2468. // subclassing of their old/new parent.
  2469. if (MainManager = Self) and not (csDesigning in ComponentState) then
  2470. CollectForms;
  2471. inherited;
  2472. end;
  2473. //----------------------------------------------------------------------------------------------------------------------
  2474. function TThemeManager.NeedsBorderPaint(Control: TControl): Boolean;
  2475. // Some controls need their frame (non-client area with 3D border) explicitely painted in a themed fashion.
  2476. // This method determines, which controls need this.
  2477. begin
  2478. Result := (Control is TScrollingWinControl) or (Control is TCustomGrid) or (Control is TCustomRichEdit);
  2479. end;
  2480. //----------------------------------------------------------------------------------------------------------------------
  2481. procedure TThemeManager.Notification(AComponent: TComponent; Operation: TOperation);
  2482. begin
  2483. if not (csDesigning in ComponentState) then
  2484. begin
  2485. case Operation of
  2486. opInsert:
  2487. // At this place we cannot subclass the control because it did not yet get its initial window procedure.
  2488. // So we add it to an intermediate list and subclass it at a later moment.
  2489. if (AComponent is TCustomForm) and (FPendingFormsList.IndexOf(AComponent) < 0) then
  2490. begin
  2491. if (MainManager = Self) then
  2492. begin
  2493. FPendingFormsList.Add(AComponent);
  2494. // Under some circumstances (e.g. when a MDI child is created) there is no application message, which we
  2495. // need to subclass the form. By posting a dummy message this problem is circumvented.
  2496. PostMessage(Application.Handle, WM_NULL, 0, 0);
  2497. end
  2498. else
  2499. MainManager.Notification(AComponent, Operation);
  2500. end;
  2501. opRemove:
  2502. if (MainManager = Self) and (AComponent is TControl) then
  2503. begin
  2504. if AComponent is TCustomForm then
  2505. // A form is being destroyed. Remove it from the pending forms list if it is still there.
  2506. FPendingFormsList.Remove(AComponent);
  2507. HandleControlChange(AComponent as TControl, False);
  2508. end;
  2509. end;
  2510. end;
  2511. inherited;
  2512. end;
  2513. //----------------------------------------------------------------------------------------------------------------------
  2514. procedure TThemeManager.RemoveChildSubclassing(Control: TWinControl);
  2515. // Child controls may be released without further notice if their parent control is destroyed.
  2516. // One can use the WM_DESTORY message to get notified but if the control haven't even created their window handle
  2517. // then also this possibility does not exist anymore.
  2518. // Hence when we get notice of a control which is being destroyed then we implicitely remove all subclassed child
  2519. // controls from our lists too.
  2520. var
  2521. I: Integer;
  2522. begin
  2523. for I := 0 to Control.ControlCount - 1 do
  2524. if Control.Controls[I] is TWinControl then
  2525. begin
  2526. RemoveChildSubclassing(TWinControl(Control.Controls[I]));
  2527. HandleControlChange(Control.Controls[I], False);
  2528. end;
  2529. end;
  2530. //----------------------------------------------------------------------------------------------------------------------
  2531. procedure TThemeManager.RemoveRecreationCandidate(Control: TControl);
  2532. begin
  2533. FPendingRecreationList.Remove(Control);
  2534. end;
  2535. //----------------------------------------------------------------------------------------------------------------------
  2536. procedure TThemeManager.UpdateThemes;
  2537. var
  2538. Flags: Cardinal;
  2539. begin
  2540. ThemeServices.UpdateThemes;
  2541. if ThemeServices.ThemesAvailable and not (csDesigning in ComponentState) then
  2542. begin
  2543. Flags := GetThemeAppProperties;
  2544. if (Flags and STAP_ALLOW_NONCLIENT) <> 0 then
  2545. Include(FOptions, toAllowNonClientArea)
  2546. else
  2547. Exclude(FOptions, toAllowNonClientArea);
  2548. if (Flags and STAP_ALLOW_CONTROLS) <> 0 then
  2549. Include(FOptions, toAllowControls)
  2550. else
  2551. Exclude(FOptions, toAllowControls);
  2552. if (Flags and STAP_ALLOW_WEBCONTENT) <> 0 then
  2553. Include(FOptions, toAllowWebContent)
  2554. else
  2555. Exclude(FOptions, toAllowWebContent);
  2556. end;
  2557. end;
  2558. //----------------------------------------------------------------------------------------------------------------------
  2559. procedure TThemeManager.UpdateUIState(Control: TControl; CharCode: Word);
  2560. // Beginning with Windows 2000 the UI in an application may hide focus rectangles and accelerator key indication.
  2561. // We have to take care to show them if the user starts navigating using the keyboard.
  2562. var
  2563. Form: TCustomForm;
  2564. //--------------- Local functions --------------------------------------------
  2565. procedure InvalidateStaticText(Control: TWinControl);
  2566. var
  2567. I: Integer;
  2568. begin
  2569. if Control is TCustomStaticText then
  2570. Control.Invalidate;
  2571. for I := 0 to Control.ControlCount - 1 do
  2572. if Control.Controls[I] is TWinControl then
  2573. InvalidateStaticText(Control.Controls[I] as TWinControl);
  2574. end;
  2575. //--------------- End local functions ----------------------------------------
  2576. begin
  2577. Form := GetParentForm(Control);
  2578. if Assigned(Form) then
  2579. case CharCode of
  2580. VK_LEFT..VK_DOWN,
  2581. VK_TAB:
  2582. Form.Perform(WM_CHANGEUISTATE, MakeLong(UIS_CLEAR, UISF_HIDEFOCUS), 0);
  2583. VK_MENU:
  2584. begin
  2585. Form.Perform(WM_CHANGEUISTATE, MakeLong(UIS_CLEAR, UISF_HIDEACCEL), 0);
  2586. // For no appearent reason does TCustomStaticText not correctly redraw when the accelerator underline
  2587. // is enabled. So we have manually invalide all instances of TCustomStaticText.
  2588. InvalidateStaticText(Form);
  2589. end;
  2590. end;
  2591. end;
  2592. //----------------------------------------------------------------------------------------------------------------------
  2593. procedure TThemeManager.ClearLists;
  2594. begin
  2595. // Listview controls must always be subclassed, otherwise they produce trouble on XP with
  2596. // classic themes.
  2597. FListViewList.Clear;
  2598. if ThemeServices.ThemesEnabled then
  2599. begin
  2600. {$ifdef CheckListSupport}
  2601. FCheckListBoxList.Clear;
  2602. {$endif CheckListSupport}
  2603. FStatusBarList.Clear;
  2604. FAnimateList.Clear;
  2605. FTrackBarList.Clear;
  2606. FSpeedButtonList.Clear;
  2607. // MP BEGIN
  2608. FCheckBoxList.Clear;
  2609. FButtonList.Clear;
  2610. // MP END
  2611. FButtonControlList.Clear;
  2612. FTabSheetList.Clear;
  2613. FWinControlList.Clear;
  2614. FGroupBoxList.Clear;
  2615. FFormList.Clear;
  2616. FPanelList.Clear;
  2617. {$ifdef COMPILER_5_UP}
  2618. FFrameList.Clear;
  2619. {$endif COMPILER_5_UP}
  2620. end;
  2621. end;
  2622. //----------------------------------------------------------------------------------------------------------------------
  2623. procedure TThemeManager.CollectForms(Form: TCustomForm = nil);
  2624. // (Re)initiates collecting all controls which need to be subclassed to fixed one or more problems.
  2625. var
  2626. I: Integer;
  2627. begin
  2628. if not FSubclassingDisabled and not (csDesigning in ComponentState) then
  2629. begin
  2630. if Form = nil then
  2631. begin
  2632. ClearLists;
  2633. for I := 0 to Screen.FormCount - 1 do
  2634. begin
  2635. FFormList.Add(Screen.Forms[I]);
  2636. CollectControls(Screen.Forms[I]);
  2637. end;
  2638. end
  2639. else
  2640. begin
  2641. FFormList.Add(Form);
  2642. CollectControls(Form);
  2643. end;
  2644. if ([toResetMouseCapture, toSetTransparency] * FOptions) <> [] then
  2645. FixControls(Form);
  2646. end;
  2647. end;
  2648. //----------------------------------------------------------------------------------------------------------------------
  2649. procedure TThemeManager.CollectControls(Parent: TWinControl);
  2650. var
  2651. I: Integer;
  2652. begin
  2653. Assert(Assigned(Parent), 'Parent of controls to be collected must be valid.');
  2654. if not FSubclassingDisabled and not (csDesigning in ComponentState) then
  2655. begin
  2656. for I := 0 to Parent.ControlCount - 1 do
  2657. begin
  2658. HandleControlChange(Parent.Controls[I], True);
  2659. if (Parent.Controls[I] is TWinControl) and (TWinControl(Parent.Controls[I]).ControlCount > 0) then
  2660. CollectControls(Parent.Controls[I] as TWinControl);
  2661. end;
  2662. end;
  2663. end;
  2664. //----------------------------------------------------------------------------------------------------------------------
  2665. procedure TThemeManager.PerformEraseBackground(Control: TControl; DC: HDC);
  2666. // Repainting the background of a control using theme services relies on the ability of the parent to handle
  2667. // WM_PRINT messages. Usually the default behavior of a window is enough to make this possible. However
  2668. // double buffered and non-windowed controls are quite different and need so special handling.
  2669. // This method uses the WM_ERASEBKGND message to achieve the same effect.
  2670. var
  2671. LastOrigin: TPoint;
  2672. begin
  2673. GetWindowOrgEx(DC, LastOrigin);
  2674. SetWindowOrgEx(DC, LastOrigin.X + Control.Left, LastOrigin.Y + Control.Top, nil);
  2675. Control.Parent.Perform(WM_ERASEBKGND, Integer(DC), Integer(DC));
  2676. SetWindowOrgEx(DC, LastOrigin.X, LastOrigin.Y, nil);
  2677. end;
  2678. //----------------------------------------------------------------------------------------------------------------------
  2679. procedure TThemeManager.RegisterListener(AControlMessage: TControlMessageEvent);
  2680. var
  2681. I: Integer;
  2682. Ptr: PControlMessageEvent;
  2683. begin
  2684. if not FindListener(AControlMessage, I) then
  2685. begin
  2686. New(Ptr);
  2687. Ptr^ := AControlMessage;
  2688. FListeners.Add(Ptr);
  2689. end;
  2690. end;
  2691. //----------------------------------------------------------------------------------------------------------------------
  2692. procedure TThemeManager.UnregisterListener(AControlMessage: TControlMessageEvent);
  2693. var
  2694. I: Integer;
  2695. begin
  2696. if FindListener(AControlMessage, I) then
  2697. begin
  2698. Dispose(PControlMessageEvent(FListeners[I]));
  2699. FListeners.Delete(I);
  2700. end;
  2701. end;
  2702. //----------------------------------------------------------------------------------------------------------------------
  2703. // MP BEGIN
  2704. function TThemeManager.GetColor(Element: TThemedElement; PartId: Integer;
  2705. StateId: Integer; PropId: Integer): TColor;
  2706. begin
  2707. Result := ThemeServices.GetColor(Element, PartId, StateId, PropId);
  2708. end;
  2709. //----------------------------------------------------------------------------------------------------------------------
  2710. function TThemeManager.GetThemesEnabled: Boolean;
  2711. begin
  2712. Result := ThemeServices.ThemesEnabled;
  2713. end;
  2714. // MP END
  2715. //----------------------------------------------------------------------------------------------------------------------
  2716. initialization
  2717. Lock := TCriticalSection.Create;
  2718. GetCheckSize;
  2719. IsWindowsXP := (Win32MajorVersion > 5) or ((Win32MajorVersion = 5) and (Win32MinorVersion >= 1));
  2720. finalization
  2721. Lock.Free;
  2722. Lock := nil;
  2723. end.