ThemeMgr.pas 109 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156
  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. begin
  539. // First try the cached data to speed up retrieval.
  540. if Control = FLastControl then
  541. begin
  542. Result := True;
  543. Index := FLastIndex;
  544. end
  545. else
  546. begin
  547. if FDirty and (Count > 1) then
  548. begin
  549. Sort(Compare);
  550. FDirty := False;
  551. end;
  552. Result := False;
  553. Dummy.Control := Control;
  554. L := 0;
  555. H := Count - 1;
  556. while L <= H do
  557. begin
  558. I := (L + H) shr 1;
  559. C := Compare(Items[I], @Dummy);
  560. if C < 0 then
  561. L := I + 1
  562. else
  563. begin
  564. H := I - 1;
  565. if C = 0 then
  566. begin
  567. Result := True;
  568. L := I;
  569. end;
  570. end;
  571. end;
  572. Index := L;
  573. if Result then
  574. begin
  575. FLastControl := Control;
  576. FLastIndex := L;
  577. end;
  578. end;
  579. end;
  580. //----------------------------------------------------------------------------------------------------------------------
  581. procedure TWindowProcList.Remove(Control: TControl);
  582. var
  583. I: Integer;
  584. Entry: PWindowProcEntry;
  585. begin
  586. if Find(Control, I) then
  587. begin
  588. Entry := Items[I];
  589. Delete(I);
  590. Entry.Control.WindowProc := Entry.OldWndProc;
  591. // Implicitly release all child subclassing.
  592. if Entry.Control is TWinControl then
  593. FOwner.RemoveChildSubclassing(Entry.Control as TWinControl);
  594. Dispose(Entry);
  595. {$ifdef Debug}
  596. Lock.Enter;
  597. try
  598. Dec(SubclassCount);
  599. finally
  600. Lock.Leave;
  601. end;
  602. {$endif Debug}
  603. end;
  604. if I <= FLastIndex then
  605. begin
  606. FLastControl := nil;
  607. FLastIndex := -1;
  608. end;
  609. MainManager.RemoveRecreationCandidate(Control);
  610. end;
  611. //----------------- TThemeManager --------------------------------------------------------------------------------------
  612. constructor TThemeManager.Create(AOwner: TComponent);
  613. begin
  614. inherited;
  615. FListeners := TList.Create;
  616. FOptions := DefaultThemeOptions;
  617. FPendingFormsList := TList.Create;
  618. FPendingRecreationList := TList.Create;
  619. FListViewList := TWindowProcList.Create(Self, PreListviewWindowProc, TCustomListView);
  620. FTabSheetList := TWindowProcList.Create(Self, PreTabSheetWindowProc, TTabSheet);
  621. FGroupBoxList := TWindowProcList.Create(Self, PreGroupBoxWindowProc, TCustomGroupBox);
  622. FButtonControlList := TWindowProcList.Create(Self, PreButtonControlWindowProc, TButtonControl);
  623. // MP BEGIN
  624. FCheckBoxList := TWindowProcList.Create(Self, PreCheckBoxWindowProc, TCheckBox);
  625. FButtonList := TWindowProcList.Create(Self, PreButtonWindowProc, TButton);
  626. // MP END
  627. FSpeedButtonList := TWindowProcList.Create(Self, PreSpeedButtonWindowProc, TSpeedButton);
  628. FSplitterList := TWindowProcList.Create(Self, PreSplitterWindowProc, TSplitter);
  629. FTrackBarList := TWindowProcList.Create(Self, PreTrackBarWindowProc, TTrackBar);
  630. FAnimateList := TWindowProcList.Create(Self, PreAnimateWindowProc, TAnimate);
  631. FStatusBarList := TWindowProcList.Create(Self, PreStatusBarWindowProc, TCustomStatusBar);
  632. {$ifdef CheckListSupport}
  633. FCheckListBoxList := TWindowProcList.Create(Self, PreCheckListBoxWindowProc, TCheckListBox);
  634. {$endif CheckListSupport}
  635. FFormList := TWindowProcList.Create(Self, PreFormWindowProc, TCustomForm);
  636. {$ifdef COMPILER_5_UP}
  637. FFrameList := TWindowProcList.Create(Self, PreFrameWindowProc, TCustomFrame);
  638. {$endif COMPILER_5_UP}
  639. FPanelList := TWindowProcList.Create(Self, PrePanelWindowProc, TCustomPanel);
  640. FWinControlList := TWindowProcList.Create(Self, PreWinControlWindowProc, TWinControl);
  641. if csDesigning in ComponentState then
  642. FSubclassingDisabled := True
  643. else
  644. begin
  645. if ThemeServices.ThemesEnabled then
  646. begin
  647. Application.HookMainWindow(MainWindowHook);
  648. FHookWasInstalled := True;
  649. end
  650. else
  651. FHookWasInstalled := False;
  652. // Keep the reference of this instance if it is the first one created in the application.
  653. Lock.Enter;
  654. try
  655. // If this is not the first instance then disable subclassing.
  656. if MainManager = nil then
  657. MainManager := Self
  658. else
  659. begin
  660. FSubclassingDisabled := True;
  661. FOptions := MainManager.FOptions;
  662. end;
  663. finally
  664. Lock.Leave;
  665. end;
  666. end;
  667. end;
  668. //----------------------------------------------------------------------------------------------------------------------
  669. destructor TThemeManager.Destroy;
  670. begin
  671. FWinControlList.Free;
  672. FPanelList.Free;
  673. {$ifdef COMPILER_5_UP}
  674. FFrameList.Free;
  675. {$endif COMPILER_5_UP}
  676. FFormList.Free;
  677. {$ifdef CheckListSupport}
  678. FCheckListBoxList.Free;
  679. {$endif CheckListSupport}
  680. FStatusBarList.Free;
  681. FAnimateList.Free;
  682. FTrackBarList.Free;
  683. FSpeedButtonList.Free;
  684. FSplitterList.Free;
  685. // MP BEGIN
  686. FButtonList.Free;
  687. FCheckBoxList.Free;
  688. // MP END
  689. FButtonControlList.Free;
  690. FListViewList.Free;
  691. FTabSheetList.Free;
  692. FGroupBoxList.Free;
  693. // Reset first manager reference if it is set to this instance.
  694. if not (csDesigning in ComponentState) then
  695. begin
  696. if FHookWasInstalled then
  697. Application.UnhookMainWindow(MainWindowHook);
  698. // We have to check the critical section here because it can happen that it is already freed (finalization section)
  699. // but there is still a theme manager instance lurking around, due to the finalization order.
  700. // If there is no lock anymore then the app. is being terminated and we don't need to set a new main manager.
  701. if Assigned(Lock) then
  702. begin
  703. Lock.Enter;
  704. try
  705. if MainManager = Self then
  706. begin
  707. MainManager := nil;
  708. if Application.Handle <> 0 then
  709. SendAppMessage(WM_MAINMANAGERRELEASED, 0, 0);
  710. end;
  711. finally
  712. Lock.Leave;
  713. end;
  714. end;
  715. end;
  716. FPendingFormsList.Free;
  717. FPendingRecreationList.Free;
  718. FListeners.Free;
  719. inherited;
  720. end;
  721. //----------------------------------------------------------------------------------------------------------------------
  722. type
  723. // Used to access protected methods and properties.
  724. TWinControlCast = class(TWinControl);
  725. procedure TThemeManager.AnimateWindowProc(Control: TControl; var Message: TMessage);
  726. begin
  727. if not DoControlMessage(Control, Message) then
  728. begin
  729. if ThemeServices.ThemesEnabled then
  730. begin
  731. case Message.Msg of
  732. WM_ERASEBKGND:
  733. Message.Result := 1;
  734. CN_CTLCOLORSTATIC:
  735. if TAnimate(Control).Transparent then
  736. with TWMCtlColorStatic(Message) do
  737. begin
  738. // Return a brush corresponding to the control's fixed background color.
  739. // The animation control insists on always erasing its background.
  740. Result := GetSysColorBrush(TWinControlCast(Control).Color and not $80000000);
  741. {ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
  742. SetBkMode(ChildDC, TRANSPARENT);
  743. // Return an empty brush to prevent Windows from overpainting we just have created.
  744. Result := GetStockObject(NULL_BRUSH);}
  745. end
  746. else
  747. FAnimateList.DispatchMessage(Control, Message);
  748. else
  749. FAnimateList.DispatchMessage(Control, Message);
  750. end;
  751. end
  752. else
  753. FAnimateList.DispatchMessage(Control, Message);
  754. end;
  755. end;
  756. //----------------------------------------------------------------------------------------------------------------------
  757. procedure TThemeManager.ButtonControlWindowProc(Control: TControl; var Message: TMessage;
  758. { MP } List: TWindowProcList);
  759. var
  760. Details: TThemedElementDetails;
  761. begin
  762. if not DoControlMessage(Control, Message) then
  763. begin
  764. if ThemeServices.ThemesEnabled then
  765. begin
  766. case Message.Msg of
  767. CN_KEYDOWN,
  768. WM_SYSKEYDOWN,
  769. WM_KEYDOWN:
  770. begin
  771. UpdateUIState(Control, TWMKey(Message).CharCode);
  772. // MP
  773. List.DispatchMessage(Control, Message);
  774. end;
  775. WM_ERASEBKGND:
  776. Message.Result := 1;
  777. CN_CTLCOLORBTN: // TButton background erasing. Necessary for some themes (like EclipseOSX).
  778. with TWMCtlColorBtn(Message) do
  779. begin
  780. if TWinControl(Control.Parent).DoubleBuffered then
  781. PerformEraseBackground(Control, ChildDC)
  782. else
  783. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
  784. // Return an empty brush to prevent Windows from overpainting we just have created.
  785. Result := GetStockObject(NULL_BRUSH);
  786. end;
  787. CN_CTLCOLORSTATIC: // Background erasing for check boxes and radio buttons.
  788. with TWMCtlColorStatic(Message) do
  789. begin
  790. if TWinControl(Control.Parent).DoubleBuffered then
  791. PerformEraseBackground(Control, ChildDC)
  792. else
  793. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
  794. // Return an empty brush to prevent Windows from overpainting we just have created.
  795. Result := GetStockObject(NULL_BRUSH);
  796. end;
  797. CM_MOUSEENTER,
  798. CM_MOUSELEAVE:
  799. begin
  800. // Hot tracking for owner drawn buttons seems to be unsupported by Windows. So we have to work around that.
  801. if Control is TBitBtn then
  802. Control.Invalidate;
  803. // MP
  804. List.DispatchMessage(Control, Message);
  805. end;
  806. CN_DRAWITEM: // Painting for owner drawn buttons.
  807. with TWMDrawItem(Message) do
  808. begin
  809. // This message is sent for bit buttons (TBitBtn) when they must be drawn. Since a bit button is a normal
  810. // Windows button (but with custom draw enabled) it is handled here too.
  811. // TSpeedButton is a TGraphicControl descentant and handled separately.
  812. Details := ThemeServices.GetElementDetails(tbPushButtonNormal);
  813. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DrawItemStruct.hDC, @Details, True);
  814. // CN_DRAWITEM can also come in when the control is a subclassed button with enabled custom draw.
  815. // In this case the content of the control is fully controlled by the original source. So let it do
  816. // whatever it wants to do.
  817. if (Control is TBitBtn) or (Control is TSpeedButton) then
  818. DrawBitBtn(TBitBtn(Control), DrawItemStruct^)
  819. else
  820. // MP
  821. List.DispatchMessage(Control, Message);
  822. end;
  823. else
  824. // MP
  825. List.DispatchMessage(Control, Message);
  826. end;
  827. end
  828. else
  829. // MP
  830. List.DispatchMessage(Control, Message);
  831. end;
  832. end;
  833. //----------------------------------------------------------------------------------------------------------------------
  834. {$ifdef CheckListSupport}
  835. type
  836. TCheckListBoxCast = class(TCheckListBox);
  837. procedure TThemeManager.CheckListBoxWindowProc(Control: TControl; var Message: TMessage);
  838. var
  839. DrawState: TOwnerDrawState;
  840. ListBox: TCheckListBoxCast;
  841. //--------------- local functions -------------------------------------------
  842. procedure DrawCheck(R: TRect; AState: TCheckBoxState; Enabled: Boolean);
  843. var
  844. DrawRect: TRect;
  845. Button: TThemedButton;
  846. Details: TThemedElementDetails;
  847. begin
  848. DrawRect.Left := R.Left + (R.Right - R.Left - GlobalCheckWidth) div 2;
  849. DrawRect.Top := R.Top + (R.Bottom - R.Top - GlobalCheckWidth) div 2;
  850. DrawRect.Right := DrawRect.Left + GlobalCheckWidth;
  851. DrawRect.Bottom := DrawRect.Top + GlobalCheckHeight;
  852. case AState of
  853. cbChecked:
  854. if Enabled then
  855. Button := tbCheckBoxCheckedNormal
  856. else
  857. Button := tbCheckBoxCheckedDisabled;
  858. cbUnchecked:
  859. if Enabled then
  860. Button := tbCheckBoxUncheckedNormal
  861. else
  862. Button := tbCheckBoxUncheckedDisabled;
  863. else // cbGrayed
  864. if Enabled then
  865. Button := tbCheckBoxMixedNormal
  866. else
  867. Button := tbCheckBoxMixedDisabled;
  868. end;
  869. Details := ThemeServices.GetElementDetails(Button);
  870. ThemeServices.DrawElement(ListBox.Canvas.Handle, Details, DrawRect, @DrawRect);
  871. end;
  872. //---------------------------------------------------------------------------
  873. procedure NewDrawItem(Index: Integer; Rect: TRect; DrawState: TOwnerDrawState);
  874. var
  875. Flags: Integer;
  876. Data: string;
  877. R: TRect;
  878. ACheckWidth: Integer;
  879. Enable: Boolean;
  880. begin
  881. with ListBox do
  882. begin
  883. // The checkbox is always drawn, regardless of the owner draw style.
  884. ACheckWidth := GetCheckWidth;
  885. if Index < Items.Count then
  886. begin
  887. R := Rect;
  888. // Delphi 4 has neither an enabled state nor a header state for items.
  889. Enable := Enabled {$ifdef COMPILER_6_UP} and ItemEnabled[Index] {$endif COMPILER_6_UP};
  890. if {$ifdef COMPILER_6_UP} not Header[Index] {$else} True {$endif COMPILER_6_UP} then
  891. begin
  892. if not UseRightToLeftAlignment then
  893. begin
  894. R.Right := Rect.Left;
  895. R.Left := R.Right - ACheckWidth;
  896. end
  897. else
  898. begin
  899. R.Left := Rect.Right;
  900. R.Right := R.Left + ACheckWidth;
  901. end;
  902. DrawCheck(R, State[Index], Enable);
  903. end
  904. else
  905. begin
  906. {$ifdef COMPILER_6_UP}
  907. Canvas.Font.Color := HeaderColor;
  908. Canvas.Brush.Color := HeaderBackgroundColor;
  909. {$endif COMPILER_6_UP}
  910. end;
  911. if not Enable then
  912. Canvas.Font.Color := clGrayText;
  913. end;
  914. if Assigned(OnDrawItem) and (Style <> lbStandard)then
  915. OnDrawItem(ListBox, Index, Rect, DrawState)
  916. else
  917. begin
  918. Canvas.FillRect(Rect);
  919. if Index < {$ifdef COMPILER_6_UP} Count {$else} Items.Count {$endif COMPILER_6_UP}then
  920. begin
  921. Flags := DrawTextBiDiModeFlags(DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
  922. if not UseRightToLeftAlignment then
  923. Inc(Rect.Left, 2)
  924. else
  925. Dec(Rect.Right, 2);
  926. Data := '';
  927. {$ifdef COMPILER_6_UP}
  928. if (Style in [lbVirtual, lbVirtualOwnerDraw]) then
  929. Data := DoGetData(Index)
  930. else
  931. {$endif COMPILER_6_UP}
  932. Data := Items[Index];
  933. DrawText(Canvas.Handle, PChar(Data), Length(Data), Rect, Flags);
  934. end;
  935. end;
  936. end;
  937. end;
  938. //--------------- end local function ----------------------------------------
  939. begin
  940. if not DoControlMessage(Control, Message) then
  941. begin
  942. if ThemeServices.ThemesEnabled then
  943. begin
  944. ListBox := TCheckListBoxCast(Control);
  945. case Message.Msg of
  946. CN_DRAWITEM:
  947. if {$ifdef COMPILER_6_UP} ListBox.Count > 0 {$else} ListBox.Items.Count > 0 {$endif COMPILER_6_UP} then
  948. with TWMDrawItem(Message).DrawItemStruct^, ListBox do
  949. begin
  950. if {$ifdef COMPILER_6_UP} not Header[itemID] {$else} True {$endif COMPILER_6_UP} then
  951. if not UseRightToLeftAlignment then
  952. rcItem.Left := rcItem.Left + GetCheckWidth
  953. else
  954. rcItem.Right := rcItem.Right - GetCheckWidth;
  955. {$ifdef COMPILER_5_UP}
  956. DrawState := TOwnerDrawState(LongRec(itemState).Lo);
  957. {$else}
  958. DrawState := TOwnerDrawState(Byte(LongRec(itemState).Lo));
  959. {$endif COMPILER_5_UP}
  960. Canvas.Handle := hDC;
  961. Canvas.Font := Font;
  962. Canvas.Brush := Brush;
  963. if (Integer(itemID) >= 0) and (odSelected in DrawState) then
  964. begin
  965. Canvas.Brush.Color := clHighlight;
  966. Canvas.Font.Color := clHighlightText
  967. end;
  968. if Integer(itemID) >= 0 then
  969. NewDrawItem(itemID, rcItem, DrawState)
  970. else
  971. Canvas.FillRect(rcItem);
  972. if odFocused in DrawState then
  973. DrawFocusRect(hDC, rcItem);
  974. Canvas.Handle := 0;
  975. end;
  976. else
  977. FCheckListBoxList.DispatchMessage(Control, Message);
  978. end;
  979. end
  980. else
  981. FCheckListBoxList.DispatchMessage(Control, Message);
  982. end
  983. else
  984. FCheckListBoxList.DispatchMessage(Control, Message);
  985. end;
  986. {$endif CheckListSupport}
  987. //----------------------------------------------------------------------------------------------------------------------
  988. procedure TThemeManager.FormWindowProc(Control: TControl; var Message: TMessage);
  989. var
  990. DC: HDC;
  991. begin
  992. case Message.Msg of
  993. CM_CONTROLLISTCHANGE: // Single control addition or removal.
  994. with TCMControlListChange(Message) do
  995. HandleControlChange(Control, Inserting);
  996. end;
  997. if not DoControlMessage(Control, Message) then
  998. begin
  999. if ThemeServices.ThemesEnabled then
  1000. begin
  1001. case Message.Msg of
  1002. WM_PRINTCLIENT,
  1003. WM_ERASEBKGND:
  1004. begin
  1005. if (Message.Msg=WM_PRINTCLIENT) then
  1006. DC := TWMPrintClient(Message).DC
  1007. else
  1008. DC := TWMEraseBkGnd(Message).DC;
  1009. // Get the parent to draw its background into the form's background.
  1010. if not (Control.Parent is TWinControl) then
  1011. FFormList.DispatchMessage(Control, Message)
  1012. else
  1013. if TWinControl(Control.Parent).DoubleBuffered then
  1014. PerformEraseBackground(Control, DC)
  1015. else
  1016. if TWinControl(Control).DoubleBuffered then
  1017. begin
  1018. if (Message.Msg <> WM_ERASEBKGND) or (Longint(DC) = TWMEraseBkGnd(Message).Unused) then
  1019. // VCL mark for second pass, this time into the offscreen bitmap
  1020. PerformEraseBackground(Control, DC);
  1021. end
  1022. else
  1023. DrawThemeParentBackground(TWinControl(Control).Handle, DC, nil);
  1024. Message.Result := 1;
  1025. end;
  1026. else
  1027. FFormList.DispatchMessage(Control, Message);
  1028. end;
  1029. end
  1030. else
  1031. FFormList.DispatchMessage(Control, Message);
  1032. end;
  1033. end;
  1034. //----------------------------------------------------------------------------------------------------------------------
  1035. {$ifdef COMPILER_5_UP}
  1036. type
  1037. // Used to access protected properties.
  1038. TFrameCast = class(TCustomFrame);
  1039. procedure TThemeManager.FrameWindowProc(Control: TControl; var Message: TMessage);
  1040. var
  1041. PS: TPaintStruct;
  1042. Details: TThemedElementDetails;
  1043. begin
  1044. if not DoControlMessage(Control, Message) then
  1045. begin
  1046. if ThemeServices.ThemesEnabled then
  1047. begin
  1048. case Message.Msg of
  1049. WM_ERASEBKGND:
  1050. // MP BEGIN
  1051. if TFrameCast(Control).Color <> clBtnFace then
  1052. FFrameList.DispatchMessage(Control, Message)
  1053. else
  1054. // MP END
  1055. with TWMEraseBkGnd(Message) do
  1056. begin
  1057. // Get the parent to draw its background into the control's background.
  1058. if TWinControl(Control.Parent).DoubleBuffered then
  1059. PerformEraseBackground(Control, DC)
  1060. else
  1061. begin
  1062. Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
  1063. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, @Details, False);
  1064. end;
  1065. Result := 1;
  1066. end;
  1067. WM_PAINT:
  1068. begin
  1069. BeginPaint(TFrameCast(Control).Handle, PS);
  1070. TFrameCast(Control).PaintControls(PS.hdc, nil);
  1071. EndPaint(TFrameCast(Control).Handle, PS);
  1072. Message.Result := 0;
  1073. end;
  1074. else
  1075. FFrameList.DispatchMessage(Control, Message);
  1076. end;
  1077. end
  1078. else
  1079. FFrameList.DispatchMessage(Control, Message);
  1080. end;
  1081. end;
  1082. {$endif COMPILER_5_UP}
  1083. //----------------------------------------------------------------------------------------------------------------------
  1084. function TThemeManager.GetIsMainManager: Boolean;
  1085. begin
  1086. Result := MainManager = Self;
  1087. end;
  1088. //----------------------------------------------------------------------------------------------------------------------
  1089. type
  1090. // Used to access protected properties.
  1091. TGroupBoxCast = class(TCustomGroupBox);
  1092. procedure TThemeManager.GroupBoxWindowProc(Control: TControl; var Message: TMessage);
  1093. //--------------- local function --------------------------------------------
  1094. procedure NewPaint(DC: HDC);
  1095. var
  1096. CaptionRect,
  1097. OuterRect: TRect;
  1098. Size: TSize;
  1099. LastFont: HFONT;
  1100. Box: TThemedButton;
  1101. Details: TThemedElementDetails;
  1102. begin
  1103. with TGroupBoxCast(Control) do
  1104. begin
  1105. LastFont := SelectObject(DC, Font.Handle);
  1106. if Text <> '' then
  1107. begin
  1108. SetTextColor(DC, Graphics.ColorToRGB(Font.Color));
  1109. // Determine size and position of text rectangle.
  1110. // This must be clipped out before painting the frame.
  1111. GetTextExtentPoint32(DC, PChar(Text), Length(Text), Size);
  1112. CaptionRect := Rect(0, 0, Size.cx, Size.cy);
  1113. if not UseRightToLeftAlignment then
  1114. OffsetRect(CaptionRect, 8, 0)
  1115. else
  1116. OffsetRect(CaptionRect, Width - 8 - CaptionRect.Right, 0);
  1117. end
  1118. else
  1119. CaptionRect := Rect(0, 0, 0, 0);
  1120. OuterRect := ClientRect;
  1121. OuterRect.Top := (CaptionRect.Bottom - CaptionRect.Top) div 2;
  1122. with CaptionRect do
  1123. ExcludeClipRect(DC, Left, Top, Right, Bottom);
  1124. if Control.Enabled then
  1125. Box := tbGroupBoxNormal
  1126. else
  1127. Box := tbGroupBoxDisabled;
  1128. Details := ThemeServices.GetElementDetails(Box);
  1129. ThemeServices.DrawElement(DC, Details, OuterRect);
  1130. SelectClipRgn(DC, 0);
  1131. if Text <> '' then
  1132. ThemeServices.DrawText(DC, Details, Text, CaptionRect, DT_LEFT, 0);
  1133. SelectObject(DC, LastFont);
  1134. end;
  1135. end;
  1136. //--------------- local function --------------------------------------------
  1137. var
  1138. PS: TPaintStruct;
  1139. Details: TThemedElementDetails;
  1140. begin
  1141. if not DoControlMessage(Control, Message) then
  1142. begin
  1143. if ThemeServices.ThemesEnabled then
  1144. begin
  1145. case Message.Msg of
  1146. WM_SYSKEYDOWN,
  1147. CN_KEYDOWN,
  1148. WM_KEYDOWN:
  1149. begin
  1150. UpdateUIState(Control, TWMKey(Message).CharCode);
  1151. FGroupBoxList.DispatchMessage(Control, Message);
  1152. end;
  1153. WM_ERASEBKGND:
  1154. with TWMEraseBkGnd(Message) do
  1155. begin
  1156. // Get the parent to draw its background into the control's background.
  1157. if TWinControl(Control.Parent).DoubleBuffered then
  1158. PerformEraseBackground(Control, DC)
  1159. else
  1160. begin
  1161. Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
  1162. ThemeServices.DrawParentBackground(TGroupBoxCast(Control).Handle, DC, @Details, True);
  1163. end;
  1164. Result := 1;
  1165. end;
  1166. WM_PAINT:
  1167. begin
  1168. BeginPaint(TGroupBoxCast(Control).Handle, PS);
  1169. NewPaint(PS.hdc);
  1170. TGroupBoxCast(Control).PaintControls(PS.hdc, nil);
  1171. EndPaint(TGroupBoxCast(Control).Handle, PS);
  1172. Message.Result := 0;
  1173. end;
  1174. else
  1175. FGroupBoxList.DispatchMessage(Control, Message);
  1176. end;
  1177. end
  1178. else
  1179. FGroupBoxList.DispatchMessage(Control, Message);
  1180. end;
  1181. end;
  1182. //----------------------------------------------------------------------------------------------------------------------
  1183. procedure TThemeManager.ListviewWindowProc(Control: TControl; var Message: TMessage);
  1184. begin
  1185. if not DoControlMessage(Control, Message) then
  1186. begin
  1187. // MP BEGIN
  1188. if ThemeServices.ThemesEnabled then
  1189. begin
  1190. case Message.Msg of
  1191. WM_SYSKEYDOWN,
  1192. CN_KEYDOWN,
  1193. WM_KEYDOWN:
  1194. begin
  1195. UpdateUIState(Control, TWMKey(Message).CharCode);
  1196. FGroupBoxList.DispatchMessage(Control, Message);
  1197. end;
  1198. end;
  1199. end;
  1200. // MP END
  1201. // In opposition to the other window procedures we should always apply the fix for TListView,
  1202. // regardless of whether themes are enabled or not.
  1203. if (Message.Msg = LVM_SETCOLUMN) or (Message.Msg = LVM_INSERTCOLUMN) then
  1204. begin
  1205. with PLVColumn(Message.LParam)^ do
  1206. begin
  1207. // Fix TListView report mode bug.
  1208. if iImage = - 1 then
  1209. Mask := Mask and not LVCF_IMAGE;
  1210. end;
  1211. end;
  1212. // This special notification message is not handled in the VCL and creates an access violation when
  1213. // passed to the default window procedure. Ignoring it does not seem to have any negative impact.
  1214. if not ((Message.Msg = WM_NOTIFY) and (TWMNotify(Message).NMHdr.code = HDN_GETDISPINFOW)) then
  1215. FListViewList.DispatchMessage(Control, Message);
  1216. end;
  1217. end;
  1218. //----------------------------------------------------------------------------------------------------------------------
  1219. function TThemeManager.MainWindowHook(var Message: TMessage): Boolean;
  1220. // Listens to messages sent to the application to know when a theme change occured.
  1221. var
  1222. Form: TCustomForm;
  1223. begin
  1224. Result := False;
  1225. // workaround for so far unknown bug on vista (bug 140)
  1226. if Message.Msg = WM_GETICON then
  1227. begin
  1228. Exit;
  1229. end;
  1230. // If the main manager was destroyed then it posted this message to the application so all still existing
  1231. // theme managers know a new election is due. Well, it is not purely democratic. The earlier a manager was created
  1232. // the higher is the probability to get this message first and become the new main manager.
  1233. if Message.Msg = WM_MAINMANAGERRELEASED then
  1234. begin
  1235. Lock.Enter;
  1236. try
  1237. // Check if the main manager role is still vacant.
  1238. if MainManager = nil then
  1239. begin
  1240. MainManager := Self;
  1241. FSubclassingDisabled := False;
  1242. CollectForms;
  1243. end;
  1244. finally
  1245. Lock.Leave;
  1246. end;
  1247. end;
  1248. // Check first if there are still forms to subclass.
  1249. while FPendingFormsList.Count > 0 do
  1250. begin
  1251. Form := TCustomForm(FPendingFormsList[0]);
  1252. FPendingFormsList.Delete(0);
  1253. FFormList.Add(Form);
  1254. // Since we don't know how many controls on this form already have been created we better collect everything
  1255. // which is already there. The window proc lists will take care not to add a control twice.
  1256. if MainManager = Self then
  1257. CollectControls(Form);
  1258. if [toResetMouseCapture, toSetTransparency] * FOptions <> [] then
  1259. FixControls(Form);
  1260. // Sometimes not all controls are visually updated. Force it to be correct.
  1261. RedrawWindow(Form.Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN or RDW_VALIDATE);
  1262. end;
  1263. while FPendingRecreationList.Count > 0 do
  1264. begin
  1265. TWinControl(FPendingRecreationList[0]).HandleNeeded;
  1266. CollectControls(TWinControl(FPendingRecreationList[0]));
  1267. FPendingRecreationList.Delete(0);
  1268. end;
  1269. if Message.Msg = WM_THEMECHANGED then
  1270. begin
  1271. UpdateThemes;
  1272. DoOnThemeChange;
  1273. end;
  1274. end;
  1275. //----------------------------------------------------------------------------------------------------------------------
  1276. type
  1277. // Used to access protected properties.
  1278. TPanelCast = class(TCustomPanel);
  1279. procedure TThemeManager.PanelWindowProc(Control: TControl; var Message: TMessage);
  1280. var
  1281. DrawRect: TRect;
  1282. DC: HDC;
  1283. OldFont: HFONT;
  1284. PS: TPaintStruct;
  1285. Details: TThemedElementDetails;
  1286. //--------------- local function --------------------------------------------
  1287. procedure NewPaint;
  1288. // This is an adapted version of the actual TCustomPanel.Paint procedure
  1289. const
  1290. Alignments: array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
  1291. var
  1292. Rect: TRect;
  1293. TopColor, BottomColor: TColor;
  1294. FontHeight: Integer;
  1295. Flags: Longint;
  1296. //------------- local functions -------------------------------------------
  1297. procedure AdjustColors(Bevel: TPanelBevel);
  1298. begin
  1299. TopColor := clBtnHighlight;
  1300. if Bevel = bvLowered then
  1301. TopColor := clBtnShadow;
  1302. BottomColor := clBtnShadow;
  1303. if Bevel = bvLowered then
  1304. BottomColor := clBtnHighlight;
  1305. end;
  1306. //------------- end local functions ---------------------------------------
  1307. begin
  1308. with TPanelCast(Control) do
  1309. begin
  1310. Canvas.Handle := DC;
  1311. try
  1312. Canvas.Font := Font;
  1313. Rect := GetClientRect;
  1314. if BevelOuter <> bvNone then
  1315. begin
  1316. AdjustColors(BevelOuter);
  1317. Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
  1318. end;
  1319. InflateRect(Rect, -BorderWidth, -BorderWidth);
  1320. if BevelInner <> bvNone then
  1321. begin
  1322. AdjustColors(BevelInner);
  1323. Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
  1324. end;
  1325. if ParentColor or ((Control.Parent <> nil) and (Control.Parent.Brush.Color = Color)) then
  1326. begin
  1327. if TWinControl(Control.Parent).DoubleBuffered then
  1328. PerformEraseBackground(Control, PS.hdc)
  1329. else
  1330. begin
  1331. Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
  1332. ThemeServices.DrawParentBackground(Handle, DC, @Details, False, @Rect);
  1333. end
  1334. end
  1335. else
  1336. begin
  1337. Canvas.Brush.Style := bsSolid;
  1338. Canvas.Brush.Color := Color;
  1339. FillRect(PS.hdc, Rect, Canvas.Brush.Handle);
  1340. end;
  1341. FontHeight := Canvas.TextHeight('W');
  1342. with Rect do
  1343. begin
  1344. Top := ((Bottom + Top) - FontHeight) div 2;
  1345. Bottom := Top + FontHeight;
  1346. end;
  1347. Flags := DT_EXPANDTABS or DT_VCENTER or Alignments[Alignment];
  1348. Flags := DrawTextBiDiModeFlags(Flags);
  1349. OldFont := SelectObject(DC, Font.Handle);
  1350. SetBKMode(DC, TRANSPARENT);
  1351. SetTextColor(DC, ColorToRGB(Font.Color));
  1352. DrawText(DC, PChar(Caption), -1, Rect, Flags);
  1353. SelectObject(DC, OldFont);
  1354. finally
  1355. Canvas.Handle := 0;
  1356. end;
  1357. end;
  1358. end;
  1359. //--------------- end local function ----------------------------------------
  1360. begin
  1361. if not DoControlMessage(Control, Message) then
  1362. begin
  1363. if ThemeServices.ThemesEnabled and TPanelCast(Control).ParentColor or
  1364. (Assigned(Control.Parent) and (Control.Parent.Brush.Color = TPanelCast(Control).Color)) then
  1365. begin
  1366. case Message.Msg of
  1367. WM_ERASEBKGND:
  1368. with TPanelCast(Control) do
  1369. begin
  1370. DC := TWMEraseBkGnd(Message).DC;
  1371. // Get the parent to draw its background into the control's background.
  1372. if TWinControl(Control.Parent).DoubleBuffered then
  1373. PerformEraseBackground(Control, DC)
  1374. else
  1375. begin
  1376. Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
  1377. ThemeServices.DrawParentBackground(Handle, DC, @Details, False);
  1378. end;
  1379. Message.Result := 1;
  1380. end;
  1381. WM_NCPAINT:
  1382. with TPanelCast(Control) do
  1383. begin
  1384. FPanelList.DispatchMessage(Control, Message);
  1385. if BorderStyle <> bsNone then
  1386. begin
  1387. DrawRect := BoundsRect;
  1388. OffsetRect(DrawRect, -Left, -Top);
  1389. DC := GetWindowDC(Handle);
  1390. try
  1391. Details := ThemeServices.GetElementDetails(trBandNormal);
  1392. ThemeServices.DrawEdge(DC, Details, DrawRect, EDGE_SUNKEN, BF_RECT);
  1393. finally
  1394. ReleaseDC(Handle, DC);
  1395. end;
  1396. end;
  1397. Message.Result := 0;
  1398. end;
  1399. WM_PAINT:
  1400. with TPanelCast(Control) do
  1401. begin
  1402. DC := BeginPaint(Handle, PS);
  1403. NewPaint;
  1404. PaintControls(DC, nil);
  1405. EndPaint(Handle, PS);
  1406. Message.Result := 0;
  1407. end;
  1408. WM_PRINTCLIENT:
  1409. with TPanelCast(Control) do
  1410. begin
  1411. DC := TWMPrintClient(Message).DC;
  1412. NewPaint;
  1413. PaintControls(DC, nil);
  1414. Message.Result := 0;
  1415. end;
  1416. else
  1417. FPanelList.DispatchMessage(Control, Message);
  1418. end;
  1419. end
  1420. else
  1421. FPanelList.DispatchMessage(Control, Message);
  1422. end;
  1423. end;
  1424. //----------------------------------------------------------------------------------------------------------------------
  1425. procedure TThemeManager.SetThemeOptions(const Value: TThemeOptions);
  1426. var
  1427. Flags: Cardinal;
  1428. I: Integer;
  1429. begin
  1430. // If this instance is the main manager then apply the options directly. Otherwise let the current main manager do it.
  1431. Lock.Enter;
  1432. try
  1433. if Assigned(MainManager) and (MainManager <> Self) then
  1434. MainManager.Options := Value
  1435. else
  1436. begin
  1437. if FOptions <> Value then
  1438. begin
  1439. FOptions := Value;
  1440. if ThemeServices.ThemesAvailable and not FSubclassingDisabled and not (csDesigning in ComponentState) then
  1441. begin
  1442. Flags := 0;
  1443. if toAllowNonClientArea in FOptions then
  1444. Flags := Flags or STAP_ALLOW_NONCLIENT;
  1445. if toAllowControls in FOptions then
  1446. Flags := Flags or STAP_ALLOW_CONTROLS;
  1447. if toAllowWebContent in FOptions then
  1448. Flags := Flags or STAP_ALLOW_WEBCONTENT;
  1449. SetThemeAppProperties(Flags);
  1450. if ComponentState * [csLoading, csReading] = [] then
  1451. begin
  1452. UpdateThemes;
  1453. // Tell the application that we changed the options.
  1454. BroadcastThemeChange;
  1455. // Notify all theme manager instances about the change.
  1456. SendAppMessage(WM_THEMECHANGED, 0, 0);
  1457. for I := 0 to Screen.FormCount - 1 do
  1458. RedrawWindow(Screen.Forms[I].Handle, nil, 0, RDW_ERASE or RDW_FRAME or RDW_INVALIDATE or RDW_INTERNALPAINT or
  1459. RDW_ERASENOW or RDW_UPDATENOW or RDW_ALLCHILDREN);
  1460. end;
  1461. end;
  1462. end;
  1463. end;
  1464. finally
  1465. Lock.Leave;
  1466. end;
  1467. end;
  1468. //----------------------------------------------------------------------------------------------------------------------
  1469. type
  1470. TSpeedButtonCast = class(TSpeedButton);
  1471. procedure TThemeManager.SpeedButtonWindowProc(Control: TControl; var Message: TMessage);
  1472. var
  1473. Button: TThemedButton;
  1474. P: TPoint;
  1475. begin
  1476. if not DoControlMessage(Control, Message) then
  1477. begin
  1478. if ThemeServices.ThemesEnabled then
  1479. begin
  1480. case Message.Msg of
  1481. WM_PAINT:
  1482. with TWMPaint(Message) do
  1483. begin
  1484. // We cannot use the theme parent paint for the background of general speed buttons (because they are not
  1485. // window controls).
  1486. PerformEraseBackground(Control, DC);
  1487. // Speed buttons are not window controls and are painted by a call of their parent with a given DC.
  1488. if not Control.Enabled then
  1489. Button := tbPushButtonDisabled
  1490. else
  1491. if TSpeedButtonCast(Control).FState in [bsDown, bsExclusive] then
  1492. Button := tbPushButtonPressed
  1493. else
  1494. with TSpeedButtonCast(Control) do
  1495. begin
  1496. // Check the hot style here. If the button has a flat style then this check is easy. Otherwise
  1497. // some more work is necessary.
  1498. Button := tbPushButtonNormal;
  1499. if Flat then
  1500. begin
  1501. if MouseInControl then
  1502. Button := tbPushButtonHot;
  1503. end
  1504. else
  1505. begin
  1506. GetCursorPos(P);
  1507. if FindDragTarget(P, True) = Control then
  1508. Button := tbPushButtonHot;
  1509. end;
  1510. end;
  1511. DrawButton(Control, Button, DC, Control.ClientRect, False);
  1512. Message.Result := 0;
  1513. end;
  1514. CM_MOUSEENTER,
  1515. CM_MOUSELEAVE:
  1516. begin
  1517. // Non-flat speed buttons don't have a hot-tracking style. We have to emulate this.
  1518. if not TSpeedButtonCast(Control).Flat and Control.Enabled then
  1519. Control.Invalidate;
  1520. FSpeedButtonList.DispatchMessage(Control, Message);
  1521. end;
  1522. else
  1523. FSpeedButtonList.DispatchMessage(Control, Message);
  1524. end;
  1525. end
  1526. else
  1527. FSpeedButtonList.DispatchMessage(Control, Message);
  1528. end;
  1529. end;
  1530. //----------------------------------------------------------------------------------------------------------------------
  1531. procedure TThemeManager.SplitterWindowProc(Control: TControl; var Message: TMessage);
  1532. begin
  1533. if not DoControlMessage(Control, Message) then
  1534. begin
  1535. if ThemeServices.ThemesEnabled then
  1536. begin
  1537. case Message.Msg of
  1538. WM_PAINT:
  1539. with TWMPaint(Message) do
  1540. begin
  1541. PerformEraseBackground(Control, DC);
  1542. Message.Result := 0;
  1543. end;
  1544. else
  1545. FSplitterList.DispatchMessage(Control, Message);
  1546. end;
  1547. end
  1548. else
  1549. FSplitterList.DispatchMessage(Control, Message);
  1550. end;
  1551. end;
  1552. //----------------------------------------------------------------------------------------------------------------------
  1553. type
  1554. TCustomStatusBarCast = class(TCustomStatusBar);
  1555. procedure TThemeManager.StatusBarWindowProc(Control: TControl; var Message: TMessage);
  1556. var
  1557. Details: TThemedElementDetails;
  1558. begin
  1559. if not DoControlMessage(Control, Message) then
  1560. begin
  1561. if ThemeServices.ThemesEnabled then
  1562. begin
  1563. case Message.Msg of
  1564. WM_NCCALCSIZE:
  1565. with TWMNCCalcSize(Message) do
  1566. begin
  1567. FStatusBarList.DispatchMessage(Control, Message);
  1568. // We cannot simply override the window class' CS_HREDRAW and CS_VREDRAW styles but the following
  1569. // does the job very well too.
  1570. // Note: this may produce trouble with embedded controls (e.g. progress bars).
  1571. if CalcValidRects then
  1572. Result := Result or WVR_REDRAW;
  1573. end;
  1574. WM_ERASEBKGND:
  1575. with TWMEraseBkGnd(Message) do
  1576. begin
  1577. Details := ThemeServices.GetElementDetails(tsStatusRoot);
  1578. ThemeServices.DrawElement(DC, Details, Control.ClientRect);
  1579. Message.Result := 1;
  1580. end;
  1581. else
  1582. FStatusBarList.DispatchMessage(Control, Message);
  1583. end;
  1584. end
  1585. else
  1586. FStatusBarList.DispatchMessage(Control, Message);
  1587. end;
  1588. end;
  1589. //----------------------------------------------------------------------------------------------------------------------
  1590. procedure TThemeManager.TabSheetWindowProc(Control: TControl; var Message: TMessage);
  1591. var
  1592. DrawRect: TRect;
  1593. Details: TThemedElementDetails;
  1594. DC: HDC;
  1595. begin
  1596. if not DoControlMessage(Control, Message) then
  1597. begin
  1598. if ThemeServices.ThemesEnabled then
  1599. begin
  1600. case Message.Msg of
  1601. // Paint the border (and erase the background)
  1602. WM_NCPAINT:
  1603. with TTabSheet(Control) do
  1604. begin
  1605. DC := GetWindowDC(Handle);
  1606. try
  1607. // Exclude the client area from painting. We only want to erase the non-client area.
  1608. DrawRect := ClientRect;
  1609. OffsetRect(DrawRect, BorderWidth, BorderWidth);
  1610. with DrawRect do
  1611. ExcludeClipRect(DC, Left, Top, Right, Bottom);
  1612. // The parent paints relative to the control's client area. We have to compensate for this by
  1613. // shifting the dc's window origin.
  1614. SetWindowOrgEx(DC, -BorderWidth, -BorderWidth, nil);
  1615. Details := ThemeServices.GetElementDetails(ttBody);
  1616. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, @Details, False);
  1617. finally
  1618. ReleaseDC(Handle, DC);
  1619. end;
  1620. Message.Result := 0;
  1621. end;
  1622. WM_PRINTCLIENT,
  1623. WM_ERASEBKGND:
  1624. begin
  1625. if Message.Msg = WM_PRINTCLIENT then
  1626. DC := TWMPrintClient(Message).DC
  1627. else
  1628. DC := TWMEraseBkGnd(Message).DC;
  1629. // Using the parent's background here does not always work. Particularly, it does not work in cases
  1630. // where the parent (pane) background does not include the body background. One way to solve this problem
  1631. // would be to paint the body background here. However this produces a lot of problems all caused by
  1632. // the fact that these backgrounds might be tiled or might otherwise have special drawing style.
  1633. // Due to the near-to-non-existing documentation on all the themes APIs I use the lesser evil by default and
  1634. // paint the parent background, which works in most cases very well.
  1635. // However you may want to enable the other way, if needed.
  1636. if toAlternateTabSheetDraw in FOptions then
  1637. begin
  1638. Details := ThemeServices.GetElementDetails(ttBody);
  1639. DrawRect := Control.ClientRect;
  1640. ThemeServices.DrawElement(DC, Details, DrawRect);
  1641. end
  1642. else
  1643. ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, nil, False);
  1644. Message.Result := 1;
  1645. end;
  1646. else
  1647. FTabSheetList.DispatchMessage(Control, Message);
  1648. end;
  1649. end
  1650. else
  1651. FTabSheetList.DispatchMessage(Control, Message);
  1652. end;
  1653. end;
  1654. //----------------------------------------------------------------------------------------------------------------------
  1655. procedure TThemeManager.TrackBarWindowProc(Control: TControl; var Message: TMessage);
  1656. var
  1657. Info: PNMCustomDraw;
  1658. R: TRect;
  1659. Rgn: HRGN;
  1660. Details: TThemedElementDetails;
  1661. Offset: Integer;
  1662. FocusBorderWidth,
  1663. FocusBorderHeight: Integer;
  1664. begin
  1665. if not DoControlMessage(Control, Message) then
  1666. begin
  1667. if ThemeServices.ThemesEnabled then
  1668. begin
  1669. case Message.Msg of
  1670. CN_NOTIFY:
  1671. with TWMNotify(Message) do
  1672. if NMHdr.code = NM_CUSTOMDRAW then
  1673. begin
  1674. Info := Pointer(NMHdr);
  1675. case Info.dwDrawStage of
  1676. CDDS_PREPAINT:
  1677. Result := CDRF_NOTIFYITEMDRAW;
  1678. CDDS_ITEMPREPAINT:
  1679. with Control as TTrackBar do
  1680. begin
  1681. // Take action based on which item is about to be painted.
  1682. case Info.dwItemSpec of
  1683. TBCD_TICS: // Before re-painting ticks redo whole background.
  1684. begin
  1685. R := ClientRect;
  1686. // Leave room for the focus rectangle if there is one.
  1687. if Focused and ((Perform(WM_QUERYUISTATE, 0, 0) and UISF_HIDEFOCUS) = 0) then
  1688. begin
  1689. SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH, 0, @FocusBorderWidth, 0);
  1690. SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, @FocusBorderHeight, 0);
  1691. InflateRect(R, -FocusBorderWidth, -FocusBorderHeight);
  1692. end;
  1693. ThemeServices.DrawParentBackground(Handle, Info.hDC, nil, False, @R);
  1694. end;
  1695. TBCD_CHANNEL: // Before re-painting channel just redo strip of background overlapped.
  1696. begin
  1697. // Retrieve the bounding box for the thumb.
  1698. SendMessage(Handle, TBM_GETTHUMBRECT, 0, Integer(@R));
  1699. // Extend this rectangle to the top/bottom or left/right border, respectively.
  1700. Offset := 0;
  1701. if Orientation = trHorizontal then
  1702. begin
  1703. // Leave room for the focus rectangle if there is one.
  1704. if Focused then
  1705. begin
  1706. SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH, 0, @FocusBorderWidth, 0);
  1707. Inc(Offset, FocusBorderWidth);
  1708. end;
  1709. R.Left := ClientRect.Left + Offset;
  1710. R.Right := ClientRect.Right - Offset;
  1711. end
  1712. else
  1713. begin
  1714. // Leave room for the focus rectangle if there is one.
  1715. if Focused then
  1716. begin
  1717. SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, @FocusBorderHeight, 0);
  1718. Inc(Offset, FocusBorderWidth);
  1719. end;
  1720. R.Top := ClientRect.Top + Offset;
  1721. R.Bottom := ClientRect.Bottom - Offset;
  1722. end;
  1723. with R do
  1724. Rgn := CreateRectRgn(Left, Top, Right, Bottom);
  1725. SelectClipRgn(Info.hDC, Rgn);
  1726. Details := ThemeServices.GetElementDetails(ttbThumbTics);
  1727. ThemeServices.DrawParentBackground(Handle, Info.hDC, @Details, False);
  1728. DeleteObject(Rgn);
  1729. SelectClipRgn(Info.hDC, 0);
  1730. end;
  1731. end;
  1732. Result := CDRF_DODEFAULT;
  1733. end;
  1734. else
  1735. Result := CDRF_DODEFAULT;
  1736. end;
  1737. end;
  1738. else
  1739. FTrackBarList.DispatchMessage(Control, Message);
  1740. end;
  1741. end
  1742. else
  1743. FTrackBarList.DispatchMessage(Control, Message);
  1744. end;
  1745. end;
  1746. //----------------------------------------------------------------------------------------------------------------------
  1747. procedure TThemeManager.WinControlWindowProc(Control: TControl; var Message: TMessage);
  1748. var
  1749. DC: HDC;
  1750. SavedDC: Integer;
  1751. begin
  1752. if not DoControlMessage(Control, Message) then
  1753. begin
  1754. if ThemeServices.ThemesEnabled then
  1755. begin
  1756. case Message.Msg of
  1757. CN_KEYDOWN,
  1758. WM_SYSKEYDOWN,
  1759. WM_KEYDOWN:
  1760. begin
  1761. UpdateUIState(Control, TWMKey(Message).CharCode);
  1762. FWinControlList.DispatchMessage(Control, Message);
  1763. end;
  1764. WM_ERASEBKGND:
  1765. begin
  1766. if Control is TScrollingWinControl then
  1767. with Control as TWinControl do
  1768. begin
  1769. DC := TWMEraseBkGnd(Message).DC;
  1770. if DoubleBuffered then
  1771. PerformEraseBackground(Control, DC)
  1772. else
  1773. ThemeServices.DrawParentBackground(Handle, DC, nil, False);
  1774. Message.Result := 1;
  1775. end
  1776. else
  1777. FWinControlList.DispatchMessage(Control, Message);
  1778. end;
  1779. WM_NCPAINT:
  1780. begin
  1781. FWinControlList.DispatchMessage(Control, Message);
  1782. ThemeServices.PaintBorder(Control as TWinControl, Control is TCustomGrid);
  1783. end;
  1784. CN_CTLCOLORSTATIC:
  1785. if Control is TCustomStaticText then
  1786. with TWMCtlColorStatic(Message), { MP } TWinControlCast(Control as TWinControl) do
  1787. begin
  1788. SetBkMode(ChildDC, Windows.TRANSPARENT);
  1789. // MP BEGIN
  1790. SetTextColor(ChildDC, ColorToRGB(Font.Color));
  1791. SetBkColor(ChildDC, ColorToRGB(Brush.Color));
  1792. // MP END
  1793. SavedDC := SaveDC(ChildDC);
  1794. ThemeServices.DrawParentBackground(Handle, ChildDC, nil, False);
  1795. FWinControlList.DispatchMessage(Control, Message);
  1796. RestoreDC(ChildDC, SavedDC);
  1797. // Return an empty brush to prevent Windows from overpainting what we just have created.
  1798. Result := GetStockObject(NULL_BRUSH);
  1799. end
  1800. else
  1801. FWinControlList.DispatchMessage(Control, Message);
  1802. else
  1803. FWinControlList.DispatchMessage(Control, Message);
  1804. end;
  1805. end
  1806. else
  1807. FWinControlList.DispatchMessage(Control, Message);
  1808. end;
  1809. end;
  1810. //----------------------------------------------------------------------------------------------------------------------
  1811. procedure TThemeManager.PreAnimateWindowProc(var Message: TMessage);
  1812. // This and the other proxy window procs do an important step to make the entire subclassing work here.
  1813. // Because we have only one window procedure for each class of subclassed controls (many to 1 relation), it is necessary
  1814. // to know to which control the message was sent originally (read: whose WindowProc property had been called). This is
  1815. // important because we have to forward the message to the original window procedure once we are finished with our own
  1816. // processing and sometimes properties of the control are needed too.
  1817. // When this method is called the hidden self parameter is not the actual theme manager instance but the
  1818. // control reference to which the message was sent originally. This is the result from the explicit Data member
  1819. // assignment done in TWindowProcList.Add. This is very helpful but has the side effect that we don't have the theme
  1820. // manager instance anymore (since the self param is the control). Thus we need another reference, which we have
  1821. // in the form of the main manager. Since only the main manager will subclass controls it is guaranteed that
  1822. // there is a valid reference when we arrive here (and in the other proxy methods).
  1823. begin
  1824. Assert(Assigned(MainManager));
  1825. MainManager.AnimateWindowProc(TControl(Self), Message);
  1826. end;
  1827. //----------------------------------------------------------------------------------------------------------------------
  1828. procedure TThemeManager.PreButtonControlWindowProc(var Message: TMessage);
  1829. // Read more about this code in PreAnimateWindowProc.
  1830. begin
  1831. Assert(Assigned(MainManager));
  1832. MainManager.ButtonControlWindowProc(TControl(Self), Message, { MP }MainManager.FButtonControlList);
  1833. end;
  1834. //----------------------------------------------------------------------------------------------------------------------
  1835. // MP BEGIN
  1836. procedure TThemeManager.PreCheckBoxWindowProc(var Message: TMessage);
  1837. // Read more about this code in PreAnimateWindowProc.
  1838. begin
  1839. Assert(Assigned(MainManager));
  1840. MainManager.ButtonControlWindowProc(TControl(Self), Message, MainManager.FCheckBoxList);
  1841. end;
  1842. //----------------------------------------------------------------------------------------------------------------------
  1843. procedure TThemeManager.PreButtonWindowProc(var Message: TMessage);
  1844. // Read more about this code in PreAnimateWindowProc.
  1845. begin
  1846. Assert(Assigned(MainManager));
  1847. MainManager.ButtonControlWindowProc(TControl(Self), Message, MainManager.FButtonList);
  1848. end;
  1849. // MP END
  1850. //----------------------------------------------------------------------------------------------------------------------
  1851. {$ifdef CheckListSupport}
  1852. procedure TThemeManager.PreCheckListBoxWindowProc(var Message: TMessage);
  1853. // Read more about this code in PreAnimateWindowProc.
  1854. begin
  1855. Assert(Assigned(MainManager));
  1856. MainManager.CheckListBoxWindowProc(TControl(Self), Message);
  1857. end;
  1858. {$endif CheckListSupport}
  1859. //----------------------------------------------------------------------------------------------------------------------
  1860. procedure TThemeManager.PreFormWindowProc(var Message: TMessage);
  1861. // Read more about this code in PreAnimateWindowProc.
  1862. begin
  1863. Assert(Assigned(MainManager));
  1864. MainManager.FormWindowProc(TControl(Self), Message);
  1865. end;
  1866. //----------------------------------------------------------------------------------------------------------------------
  1867. {$ifdef COMPILER_5_UP}
  1868. procedure TThemeManager.PreFrameWindowProc(var Message: TMessage);
  1869. // Read more about this code in PreAnimateWindowProc.
  1870. begin
  1871. Assert(Assigned(MainManager));
  1872. MainManager.FrameWindowProc(TControl(Self), Message);
  1873. end;
  1874. {$endif COMPILER_5_UP}
  1875. //----------------------------------------------------------------------------------------------------------------------
  1876. procedure TThemeManager.PreGroupBoxWindowProc(var Message: TMessage);
  1877. // Read more about this code in PreAnimateWindowProc.
  1878. begin
  1879. Assert(Assigned(MainManager));
  1880. MainManager.GroupBoxWindowProc(TControl(Self), Message);
  1881. end;
  1882. //----------------------------------------------------------------------------------------------------------------------
  1883. procedure TThemeManager.PreListviewWindowProc(var Message: TMessage);
  1884. // Read more about this code in PreAnimateWindowProc.
  1885. begin
  1886. Assert(Assigned(MainManager));
  1887. MainManager.ListviewWindowProc(TControl(Self), Message);
  1888. end;
  1889. //----------------------------------------------------------------------------------------------------------------------
  1890. procedure TThemeManager.PrePanelWindowProc(var Message: TMessage);
  1891. // Read more about this code in PreAnimateWindowProc.
  1892. begin
  1893. Assert(Assigned(MainManager));
  1894. MainManager.PanelWindowProc(TControl(Self), Message);
  1895. end;
  1896. //----------------------------------------------------------------------------------------------------------------------
  1897. procedure TThemeManager.PreSpeedButtonWindowProc(var Message: TMessage);
  1898. // Read more about this code in PreAnimateWindowProc.
  1899. begin
  1900. Assert(Assigned(MainManager));
  1901. MainManager.SpeedButtonWindowProc(TControl(Self), Message);
  1902. end;
  1903. //----------------------------------------------------------------------------------------------------------------------
  1904. procedure TThemeManager.PreSplitterWindowProc(var Message: TMessage);
  1905. // Read more about this code in PreAnimateWindowProc.
  1906. begin
  1907. Assert(Assigned(MainManager));
  1908. MainManager.SplitterWindowProc(TControl(Self), Message);
  1909. end;
  1910. //----------------------------------------------------------------------------------------------------------------------
  1911. procedure TThemeManager.PreStatusBarWindowProc(var Message: TMessage);
  1912. // Read more about this code in PreAnimateWindowProc.
  1913. begin
  1914. Assert(Assigned(MainManager));
  1915. MainManager.StatusBarWindowProc(TControl(Self), Message);
  1916. end;
  1917. //----------------------------------------------------------------------------------------------------------------------
  1918. procedure TThemeManager.PreTabSheetWindowProc(var Message: TMessage);
  1919. // Read more about this code in PreAnimateWindowProc.
  1920. begin
  1921. Assert(Assigned(MainManager));
  1922. MainManager.TabSheetWindowProc(TControl(Self), Message);
  1923. end;
  1924. //----------------------------------------------------------------------------------------------------------------------
  1925. procedure TThemeManager.PreTrackBarWindowProc(var Message: TMessage);
  1926. // Read more about this code in PreAnimateWindowProc.
  1927. begin
  1928. Assert(Assigned(MainManager));
  1929. MainManager.TrackBarWindowProc(TControl(Self), Message);
  1930. end;
  1931. //----------------------------------------------------------------------------------------------------------------------
  1932. procedure TThemeManager.PreWinControlWindowProc(var Message: TMessage);
  1933. // Read more about this code in PreAnimateWindowProc.
  1934. begin
  1935. Assert(Assigned(MainManager));
  1936. MainManager.WinControlWindowProc(TControl(Self), Message);
  1937. end;
  1938. //----------------------------------------------------------------------------------------------------------------------
  1939. procedure TThemeManager.AddRecreationCandidate(Control: TControl);
  1940. begin
  1941. if FPendingRecreationList.IndexOf(Control) = -1 then
  1942. FPendingRecreationList.Add(Control);
  1943. end;
  1944. //----------------------------------------------------------------------------------------------------------------------
  1945. procedure TThemeManager.BroadcastThemeChange;
  1946. //--------------- local function --------------------------------------------
  1947. procedure BroadcastChildren(Control: TWinControl);
  1948. var
  1949. I: Integer;
  1950. ChildControl: TWinControl;
  1951. begin
  1952. for I := 0 to Control.ControlCount - 1 do
  1953. if Control.Controls[I] is TWinControl then
  1954. begin
  1955. ChildControl := TWinControl(Control.Controls[I]);
  1956. if ChildControl.HandleAllocated then
  1957. ChildControl.Perform(WM_THEMECHANGED, 0, 0);
  1958. // We must force recreation of some window handles (to reapply all the control settings).
  1959. if (ChildControl is TCustomListView) or (ChildControl is TCoolBar) then
  1960. TWinControlCast(ChildControl).RecreateWnd
  1961. else
  1962. BroadcastChildren(ChildControl);
  1963. end;
  1964. end;
  1965. //--------------- local function --------------------------------------------
  1966. var
  1967. I: Integer;
  1968. Form: TCustomForm;
  1969. begin
  1970. for I := 0 to Screen.FormCount - 1 do
  1971. begin
  1972. Form := Screen.Forms[I];
  1973. Form.Perform(WM_THEMECHANGED, 0, 0);
  1974. BroadcastChildren(Form);
  1975. end;
  1976. end;
  1977. //----------------------------------------------------------------------------------------------------------------------
  1978. class function TThemeManager.CurrentThemeManager: TThemeManager;
  1979. begin
  1980. Result := MainManager;
  1981. end;
  1982. //----------------------------------------------------------------------------------------------------------------------
  1983. function TThemeManager.DoAllowSubclassing(Control: TControl): Boolean;
  1984. begin
  1985. Result := True;
  1986. if Assigned(FOnAllowSubclassing) then
  1987. FOnAllowSubclassing(Self,Control,Result);
  1988. end;
  1989. //----------------------------------------------------------------------------------------------------------------------
  1990. function TThemeManager.DoControlMessage(Control: TControl; var Message: TMessage): Boolean;
  1991. var
  1992. I: Integer;
  1993. Event: PControlMessageEvent;
  1994. begin
  1995. Result := False;
  1996. if Assigned(FOnControlMessage) then
  1997. FOnControlMessage(Self, Control, Message, Result);
  1998. if not Result then
  1999. begin
  2000. I := 0;
  2001. while I < FListeners.Count do
  2002. begin
  2003. Event := FListeners[I];
  2004. try
  2005. Event^(Self, Control, Message, Result);
  2006. if Result then
  2007. Break;
  2008. Inc(I);
  2009. except
  2010. // Raised an exception, so delete the registration
  2011. UnregisterListener(Event^);
  2012. end;
  2013. end;
  2014. end;
  2015. end;
  2016. //----------------------------------------------------------------------------------------------------------------------
  2017. procedure TThemeManager.DoOnThemeChange;
  2018. begin
  2019. if Assigned(FOnThemeChange) then
  2020. FOnThemeChange(Self);
  2021. end;
  2022. //----------------------------------------------------------------------------------------------------------------------
  2023. procedure TThemeManager.DrawBitBtn(Control: TBitBtn; var DrawItemStruct: TDrawItemStruct);
  2024. var
  2025. Button: TThemedButton;
  2026. R: TRect;
  2027. Wnd: HWND;
  2028. P: TPoint;
  2029. begin
  2030. with DrawItemStruct do
  2031. begin
  2032. // For owner drawn buttons we will never get the ODS_HIGHLIGHT flag. This makes it necessary to
  2033. // check ourselves if the button is "hot".
  2034. GetCursorPos(P);
  2035. Wnd := WindowFromPoint(P);
  2036. if Wnd = TWinControl(Control).Handle then
  2037. itemState := itemState or ODS_HOTLIGHT;
  2038. R := rcItem;
  2039. if not Control.Enabled then
  2040. Button := tbPushButtonDisabled
  2041. else
  2042. if (itemState and ODS_SELECTED) <> 0 then
  2043. Button := tbPushButtonPressed
  2044. else
  2045. if (itemState and ODS_HOTLIGHT) <> 0 then
  2046. Button := tbPushButtonHot
  2047. else
  2048. // It seems ODS_DEFAULT is never set, so we have to check the control's properties.
  2049. if Control.Default or ((itemState and ODS_FOCUS) <> 0) then
  2050. Button := tbPushButtonDefaulted
  2051. else
  2052. Button := tbPushButtonNormal;
  2053. DrawButton(Control, Button, hDC, R, itemState and ODS_FOCUS <> 0);
  2054. end;
  2055. end;
  2056. //----------------------------------------------------------------------------------------------------------------------
  2057. procedure TThemeManager.DrawButton(Control: TControl; Button: TThemedButton; DC: HDC; R: TRect; Focused: Boolean);
  2058. // Common paint routine for TBitBtn and TSpeedButton.
  2059. var
  2060. TextBounds: TRect;
  2061. LastFont: HFONT;
  2062. Glyph: TBitmap;
  2063. GlyphPos: TPoint;
  2064. GlyphWidth: Integer;
  2065. GlyphSourceX: Integer;
  2066. GlyphMask: TBitmap;
  2067. Offset: TPoint;
  2068. ToolButton: TThemedToolBar;
  2069. Details: TThemedElementDetails;
  2070. begin
  2071. GlyphSourceX := 0;
  2072. GlyphWidth := 0;
  2073. ToolButton := ttbToolbarDontCare;
  2074. if Control is TBitBtn then
  2075. begin
  2076. Glyph := TBitBtn(Control).Glyph;
  2077. // Determine which image to use (if there is more than one in the glyph).
  2078. with TBitBtn(Control), Glyph do
  2079. begin
  2080. if not Empty then
  2081. begin
  2082. GlyphWidth := Width div NumGlyphs;
  2083. if not Enabled and (NumGlyphs > 1) then
  2084. GlyphSourceX := GlyphWidth
  2085. else
  2086. if (Button = tbPushButtonPressed) and (NumGlyphs > 2) then
  2087. GlyphSourceX := 2 * GlyphWidth;
  2088. end;
  2089. end;
  2090. end
  2091. else
  2092. begin
  2093. Glyph := TSpeedButton(Control).Glyph;
  2094. with TSpeedButtonCast(Control) do
  2095. begin
  2096. // Determine which image to use (if there is more than one in the glyph).
  2097. with Glyph do
  2098. if not Empty then
  2099. begin
  2100. GlyphWidth := Width div NumGlyphs;
  2101. if not Enabled and (NumGlyphs > 1) then
  2102. GlyphSourceX := GlyphWidth
  2103. else
  2104. case FState of
  2105. bsDown:
  2106. if NumGlyphs > 2 then
  2107. GlyphSourceX := 2 * GlyphWidth;
  2108. bsExclusive:
  2109. if NumGlyphs > 3 then
  2110. GlyphSourceX := 3 * GlyphWidth;
  2111. end;
  2112. end;
  2113. // If the speed button is flat then we use toolbutton images for drawing.
  2114. if Flat then
  2115. begin
  2116. case Button of
  2117. tbPushButtonDisabled:
  2118. Toolbutton := ttbButtonDisabled;
  2119. tbPushButtonPressed:
  2120. Toolbutton := ttbButtonPressed;
  2121. tbPushButtonHot:
  2122. Toolbutton := ttbButtonHot;
  2123. tbPushButtonNormal:
  2124. Toolbutton := ttbButtonNormal;
  2125. end;
  2126. end;
  2127. end;
  2128. end;
  2129. if ToolButton = ttbToolbarDontCare then
  2130. begin
  2131. Details := ThemeServices.GetElementDetails(Button);
  2132. ThemeServices.DrawElement(DC, Details, R);
  2133. R := ThemeServices.ContentRect(DC, Details, R);
  2134. end
  2135. else
  2136. begin
  2137. Details := ThemeServices.GetElementDetails(ToolButton);
  2138. ThemeServices.DrawElement(DC, Details, R);
  2139. R := ThemeServices.ContentRect(DC, Details, R);
  2140. end;
  2141. // The XP style does no longer indicate pressed buttons by moving the caption one pixel down and right.
  2142. Offset := Point(0, 0);
  2143. with TControlCast(Control) do
  2144. begin
  2145. LastFont := SelectObject(DC, Font.Handle);
  2146. CalcButtonLayout(Control, DC, R, Offset, GlyphPos, TextBounds, DrawTextBidiModeFlags(0));
  2147. // Note: Currently we cannot do text output via the themes services because the second flags parameter (which is
  2148. // used for graying out strings) is ignored (bug in XP themes implementation?).
  2149. // Hence we have to do it the "usual" way.
  2150. if Button = tbPushButtonDisabled then
  2151. SetTextColor(DC, ColorToRGB(clGrayText))
  2152. else
  2153. SetTextColor(DC, ColorToRGB(Font.Color));
  2154. SetBkMode(DC, TRANSPARENT);
  2155. DrawText(DC, PChar(Caption), Length(Caption), TextBounds, DT_CENTER or DT_VCENTER);
  2156. with Glyph do
  2157. if not Empty then
  2158. begin
  2159. GlyphMask := TBitmap.Create;
  2160. GlyphMask.Assign(Glyph);
  2161. GlyphMask.Mask(Glyph.TransparentColor);
  2162. TransparentStretchBlt(DC, GlyphPos.X, GlyphPos.Y, GlyphWidth, Height, Canvas.Handle, GlyphSourceX, 0,
  2163. GlyphWidth, Height, GlyphMask.Canvas.Handle, GlyphSourceX, 0);
  2164. GlyphMask.Free;
  2165. end;
  2166. SelectObject(DC, LastFont);
  2167. end;
  2168. if Focused then
  2169. begin
  2170. SetTextColor(DC, 0);
  2171. DrawFocusRect(DC, R);
  2172. end;
  2173. end;
  2174. //----------------------------------------------------------------------------------------------------------------------
  2175. function TThemeManager.FindListener(AControlMessage: TControlMessageEvent; var Index: Integer): Boolean;
  2176. var
  2177. I: Integer;
  2178. begin
  2179. Result := False;
  2180. for I := 0 to FListeners.Count - 1 do
  2181. if @PControlMessageEvent(FListeners[I])^ = @AControlMessage then
  2182. begin
  2183. Result := True;
  2184. Index := I;
  2185. Break;
  2186. end;
  2187. end;
  2188. //----------------------------------------------------------------------------------------------------------------------
  2189. type
  2190. // Cast to access the Transparent property in TCustomLabel which is protected there.
  2191. TLabelCast = class(TCustomLabel);
  2192. procedure TThemeManager.FixControls(Form: TCustomForm);
  2193. // Iterates through all existing controls in all forms which are registered with Screen and checks for TToolBar and
  2194. // TCustomLabel. Both controls will get their Transparent property set to True.
  2195. var
  2196. MakeTransparent: Boolean;
  2197. RemoveMouseCapture: Boolean;
  2198. //--------------- local function --------------------------------------------
  2199. procedure IterateControls(Parent: TWinControl);
  2200. var
  2201. I, J: Integer;
  2202. ToolBar: TToolBar;
  2203. Control: TControl;
  2204. begin
  2205. for I := 0 to Parent.ControlCount - 1 do
  2206. begin
  2207. Control := Parent.Controls[I];
  2208. // MP
  2209. if not DoAllowSubClassing(Control) then Continue;
  2210. // Allow all window controls to use themed background if they are placed on a tab sheet. This works only for controls
  2211. // whose background is drawn by Windows and which can be transparent. There aren't many which qualify, though.
  2212. if (Control is TWinControl) and ThemeServices.ThemesEnabled then
  2213. begin
  2214. // MP BEGIN
  2215. try
  2216. TWinControl(Control).HandleNeeded;
  2217. except
  2218. // if allocating handle fails, just do not fix the control and continue
  2219. // strangelly happens irregularly for buttons
  2220. Continue;
  2221. end;
  2222. // MP END
  2223. EnableThemeDialogTexture(TWinControl(Control).Handle, ETDT_ENABLETAB);
  2224. end;
  2225. if Control is TToolBar then
  2226. begin
  2227. ToolBar := TToolBar(Control);
  2228. if MakeTransparent then
  2229. ToolBar.Transparent := True;
  2230. if RemoveMouseCapture then
  2231. begin
  2232. for J := 0 to ToolBar.ButtonCount - 1 do
  2233. if ToolBar.Buttons[J].Style <> tbsDropDown then
  2234. ToolBar.Buttons[J].ControlStyle := ToolBar.Buttons[J].ControlStyle - [csCaptureMouse];
  2235. end;
  2236. end
  2237. else
  2238. if Control is TCustomLabel then
  2239. begin
  2240. if MakeTransparent then
  2241. TLabelCast(Control).Transparent := True;
  2242. end
  2243. else
  2244. if (Control is TWinControl) and (TWinControl(Control).ControlCount > 0) then
  2245. IterateControls(Control as TWinControl);
  2246. end;
  2247. end;
  2248. //--------------- end local function ----------------------------------------
  2249. var
  2250. I: Integer;
  2251. begin
  2252. MakeTransparent := toSetTransparency in FOptions;
  2253. RemoveMouseCapture := toResetMouseCapture in FOptions;
  2254. if Form = nil then
  2255. begin
  2256. for I := 0 to Screen.FormCount - 1 do
  2257. begin
  2258. Form := Screen.Forms[I];
  2259. IterateControls(Form);
  2260. end;
  2261. end
  2262. else
  2263. IterateControls(Form);
  2264. end;
  2265. //----------------------------------------------------------------------------------------------------------------------
  2266. procedure TThemeManager.ForceAsMainManager;
  2267. // Forces this instance to become the main manager. This is useful for descentants to provide additional functionality.
  2268. begin
  2269. if MainManager <> Self then
  2270. begin
  2271. Lock.Enter;
  2272. try
  2273. if Assigned(MainManager) then
  2274. begin
  2275. MainManager.FSubclassingDisabled := True;
  2276. MainManager.ClearLists;
  2277. end;
  2278. MainManager := Self;
  2279. FSubclassingDisabled := False;
  2280. CollectForms;
  2281. finally
  2282. Lock.Release;
  2283. end;
  2284. end;
  2285. end;
  2286. //----------------------------------------------------------------------------------------------------------------------
  2287. procedure TThemeManager.HandleControlChange(Control: TControl; Inserting: Boolean);
  2288. var
  2289. List: TWindowProcList;
  2290. Index: Integer;
  2291. WinControl: TWinControl;
  2292. begin
  2293. List := nil;
  2294. // Do subclassing work only on Windows XP or higher.
  2295. if IsWindowsXP then
  2296. begin
  2297. if not ThemeServices.ThemesEnabled then
  2298. begin
  2299. // TCustomListview always must be subclassed.
  2300. if Control is TCustomListView then
  2301. begin
  2302. if (toSubclassListView in FOptions) or not Inserting then
  2303. begin
  2304. List := FListViewList;
  2305. // We have to force the listview to recreate its window handle (to reapply all the control settings).
  2306. // However if it is already in our list then don't touch the window anymore.
  2307. WinControl := Control as TWinControl;
  2308. if Inserting and not List.Find(Control, Index) and WinControl.HandleAllocated then
  2309. PostMessage(WinControl.Handle, CM_RECREATEWND, 0, 0);
  2310. end;
  2311. end;
  2312. end
  2313. else
  2314. begin
  2315. // MP BEGIN
  2316. // Including checkboxes and buttons to button-control list makes it strangely fail
  2317. // for some dialogs (irregularly). Introducing separate list for
  2318. // them solves the problem
  2319. if Control is TCheckBox then
  2320. begin
  2321. if (toSubclassButtons in FOptions) or not Inserting then
  2322. List := FCheckBoxList;
  2323. end
  2324. else
  2325. if Control is TButton then
  2326. begin
  2327. if (toSubclassButtons in FOptions) or not Inserting then
  2328. List := FButtonList;
  2329. end
  2330. else
  2331. // MP END
  2332. if Control is TButtonControl then
  2333. begin
  2334. if (toSubclassButtons in FOptions) or not Inserting then
  2335. List := FButtonControlList;
  2336. end
  2337. else
  2338. if Control is TSpeedButton then
  2339. begin
  2340. if (toSubclassSpeedButtons in FOptions) or not Inserting then
  2341. List := FSpeedButtonList;
  2342. end
  2343. else
  2344. if Control is TCustomGroupBox then
  2345. begin
  2346. if (toSubclassGroupBox in FOptions) or not Inserting then
  2347. List := FGroupBoxList;
  2348. end
  2349. else
  2350. if Control is TTabSheet then
  2351. begin
  2352. if (toSubclassTabSheet in FOptions) or not Inserting then
  2353. List := FTabSheetList;
  2354. end
  2355. else
  2356. if Control is TCustomPanel then
  2357. begin
  2358. if (toSubclassPanel in FOptions) or not Inserting then
  2359. List := FPanelList;
  2360. end
  2361. else
  2362. {$ifdef COMPILER_5_UP}
  2363. if Control is TCustomFrame then
  2364. begin
  2365. if (toSubclassFrame in FOptions) or not Inserting then
  2366. List := FFrameList;
  2367. end
  2368. else
  2369. {$endif COMPILER_5_UP}
  2370. if Control is TCustomListView then
  2371. begin
  2372. if (toSubclassListView in FOptions) or not Inserting then
  2373. begin
  2374. List := FListViewList;
  2375. // We have to force the listview to recreate its window handle (to reapply all the control settings).
  2376. // However if it is already in our list then don't touch the window anymore.
  2377. WinControl := Control as TWinControl;
  2378. if Inserting and not List.Find(Control, Index) and WinControl.HandleAllocated then
  2379. PostMessage(WinControl.Handle, CM_RECREATEWND, 0, 0);
  2380. end;
  2381. end
  2382. else
  2383. if Control is TTrackBar then
  2384. begin
  2385. if (toSubclassTrackBar in FOptions) or not Inserting then
  2386. List := FTrackBarList;
  2387. end
  2388. else
  2389. {$ifdef CheckListSupport}
  2390. if Control is TCheckListBox then
  2391. begin
  2392. if (toSubclassCheckListBox in FOptions) or not Inserting then
  2393. List := FCheckListBoxList;
  2394. end
  2395. else
  2396. {$endif CheckListSupport}
  2397. if Control is TCustomStatusBar then
  2398. begin
  2399. if (toSubclassStatusBar in FOptions) or not Inserting then
  2400. List := FStatusBarList;
  2401. end
  2402. else
  2403. if Control is TSplitter then
  2404. begin
  2405. if (toSubclassSplitter in FOptions) or not Inserting then
  2406. List := FSplitterList;
  2407. end
  2408. else
  2409. if Control is TAnimate then
  2410. begin
  2411. if (toSubclassAnimate in FOptions) or not Inserting then
  2412. List := FAnimateList;
  2413. end
  2414. else
  2415. if Control is TCustomForm then
  2416. begin
  2417. List := FFormList;
  2418. if Inserting then
  2419. FPendingFormsList.Remove(Control);
  2420. end
  2421. else
  2422. if Control is TWinControl then
  2423. begin
  2424. if (toSubclassWinControl in FOptions) or not Inserting then
  2425. List := FWinControlList;
  2426. end;
  2427. end;
  2428. if Assigned(List) then
  2429. begin
  2430. if Inserting and (DoAllowSubClassing(Control) and (Control.Perform(CM_DENYSUBCLASSING, 0, 0) = 0)) then
  2431. begin
  2432. List.Add(Control);
  2433. // We need a notification for this control about its destruction.
  2434. Control.FreeNotification(Self);
  2435. // Automatically collect the child controls when a TWinControl is added.
  2436. if (Control is TWinControl) and (TWinControl(Control).ControlCount > 0) then
  2437. CollectControls(TWinControl(Control));
  2438. end
  2439. else
  2440. List.Remove(Control);
  2441. end;
  2442. end;
  2443. end;
  2444. //----------------------------------------------------------------------------------------------------------------------
  2445. function TThemeManager.IsRecreationCandidate(Control: TControl): Boolean;
  2446. // Tells the caller whether the given controls is being recreated.
  2447. begin
  2448. Result := FPendingRecreationList.IndexOf(Control) > -1;
  2449. end;
  2450. //----------------------------------------------------------------------------------------------------------------------
  2451. procedure TThemeManager.Loaded;
  2452. begin
  2453. // Collect all controls which already exist. Those controls, which are later added/removed are handled by the
  2454. // subclassing of their old/new parent.
  2455. if (MainManager = Self) and not (csDesigning in ComponentState) then
  2456. CollectForms;
  2457. inherited;
  2458. end;
  2459. //----------------------------------------------------------------------------------------------------------------------
  2460. function TThemeManager.NeedsBorderPaint(Control: TControl): Boolean;
  2461. // Some controls need their frame (non-client area with 3D border) explicitely painted in a themed fashion.
  2462. // This method determines, which controls need this.
  2463. begin
  2464. Result := (Control is TScrollingWinControl) or (Control is TCustomGrid) or (Control is TCustomRichEdit);
  2465. end;
  2466. //----------------------------------------------------------------------------------------------------------------------
  2467. procedure TThemeManager.Notification(AComponent: TComponent; Operation: TOperation);
  2468. begin
  2469. if not (csDesigning in ComponentState) then
  2470. begin
  2471. case Operation of
  2472. opInsert:
  2473. // At this place we cannot subclass the control because it did not yet get its initial window procedure.
  2474. // So we add it to an intermediate list and subclass it at a later moment.
  2475. if (AComponent is TCustomForm) and (FPendingFormsList.IndexOf(AComponent) < 0) then
  2476. begin
  2477. if (MainManager = Self) then
  2478. begin
  2479. FPendingFormsList.Add(AComponent);
  2480. // Under some circumstances (e.g. when a MDI child is created) there is no application message, which we
  2481. // need to subclass the form. By posting a dummy message this problem is circumvented.
  2482. PostMessage(Application.Handle, WM_NULL, 0, 0);
  2483. end
  2484. else
  2485. MainManager.Notification(AComponent, Operation);
  2486. end;
  2487. opRemove:
  2488. if (MainManager = Self) and (AComponent is TControl) then
  2489. begin
  2490. if AComponent is TCustomForm then
  2491. // A form is being destroyed. Remove it from the pending forms list if it is still there.
  2492. FPendingFormsList.Remove(AComponent);
  2493. HandleControlChange(AComponent as TControl, False);
  2494. end;
  2495. end;
  2496. end;
  2497. inherited;
  2498. end;
  2499. //----------------------------------------------------------------------------------------------------------------------
  2500. procedure TThemeManager.RemoveChildSubclassing(Control: TWinControl);
  2501. // Child controls may be released without further notice if their parent control is destroyed.
  2502. // One can use the WM_DESTORY message to get notified but if the control haven't even created their window handle
  2503. // then also this possibility does not exist anymore.
  2504. // Hence when we get notice of a control which is being destroyed then we implicitely remove all subclassed child
  2505. // controls from our lists too.
  2506. var
  2507. I: Integer;
  2508. begin
  2509. for I := 0 to Control.ControlCount - 1 do
  2510. if Control.Controls[I] is TWinControl then
  2511. begin
  2512. RemoveChildSubclassing(TWinControl(Control.Controls[I]));
  2513. HandleControlChange(Control.Controls[I], False);
  2514. end;
  2515. end;
  2516. //----------------------------------------------------------------------------------------------------------------------
  2517. procedure TThemeManager.RemoveRecreationCandidate(Control: TControl);
  2518. begin
  2519. FPendingRecreationList.Remove(Control);
  2520. end;
  2521. //----------------------------------------------------------------------------------------------------------------------
  2522. procedure TThemeManager.UpdateThemes;
  2523. var
  2524. Flags: Cardinal;
  2525. begin
  2526. ThemeServices.UpdateThemes;
  2527. if ThemeServices.ThemesAvailable and not (csDesigning in ComponentState) then
  2528. begin
  2529. Flags := GetThemeAppProperties;
  2530. if (Flags and STAP_ALLOW_NONCLIENT) <> 0 then
  2531. Include(FOptions, toAllowNonClientArea)
  2532. else
  2533. Exclude(FOptions, toAllowNonClientArea);
  2534. if (Flags and STAP_ALLOW_CONTROLS) <> 0 then
  2535. Include(FOptions, toAllowControls)
  2536. else
  2537. Exclude(FOptions, toAllowControls);
  2538. if (Flags and STAP_ALLOW_WEBCONTENT) <> 0 then
  2539. Include(FOptions, toAllowWebContent)
  2540. else
  2541. Exclude(FOptions, toAllowWebContent);
  2542. end;
  2543. end;
  2544. //----------------------------------------------------------------------------------------------------------------------
  2545. procedure TThemeManager.UpdateUIState(Control: TControl; CharCode: Word);
  2546. // Beginning with Windows 2000 the UI in an application may hide focus rectangles and accelerator key indication.
  2547. // We have to take care to show them if the user starts navigating using the keyboard.
  2548. var
  2549. Form: TCustomForm;
  2550. //--------------- Local functions --------------------------------------------
  2551. procedure InvalidateStaticText(Control: TWinControl);
  2552. var
  2553. I: Integer;
  2554. begin
  2555. if Control is TCustomStaticText then
  2556. Control.Invalidate;
  2557. for I := 0 to Control.ControlCount - 1 do
  2558. if Control.Controls[I] is TWinControl then
  2559. InvalidateStaticText(Control.Controls[I] as TWinControl);
  2560. end;
  2561. //--------------- End local functions ----------------------------------------
  2562. begin
  2563. Form := GetParentForm(Control);
  2564. if Assigned(Form) then
  2565. case CharCode of
  2566. VK_LEFT..VK_DOWN,
  2567. VK_TAB:
  2568. Form.Perform(WM_CHANGEUISTATE, MakeLong(UIS_CLEAR, UISF_HIDEFOCUS), 0);
  2569. VK_MENU:
  2570. begin
  2571. Form.Perform(WM_CHANGEUISTATE, MakeLong(UIS_CLEAR, UISF_HIDEACCEL), 0);
  2572. // For no appearent reason does TCustomStaticText not correctly redraw when the accelerator underline
  2573. // is enabled. So we have manually invalide all instances of TCustomStaticText.
  2574. InvalidateStaticText(Form);
  2575. end;
  2576. end;
  2577. end;
  2578. //----------------------------------------------------------------------------------------------------------------------
  2579. procedure TThemeManager.ClearLists;
  2580. begin
  2581. // Listview controls must always be subclassed, otherwise they produce trouble on XP with
  2582. // classic themes.
  2583. FListViewList.Clear;
  2584. if ThemeServices.ThemesEnabled then
  2585. begin
  2586. {$ifdef CheckListSupport}
  2587. FCheckListBoxList.Clear;
  2588. {$endif CheckListSupport}
  2589. FStatusBarList.Clear;
  2590. FAnimateList.Clear;
  2591. FTrackBarList.Clear;
  2592. FSpeedButtonList.Clear;
  2593. // MP BEGIN
  2594. FCheckBoxList.Clear;
  2595. FButtonList.Clear;
  2596. // MP END
  2597. FButtonControlList.Clear;
  2598. FTabSheetList.Clear;
  2599. FWinControlList.Clear;
  2600. FGroupBoxList.Clear;
  2601. FFormList.Clear;
  2602. FPanelList.Clear;
  2603. {$ifdef COMPILER_5_UP}
  2604. FFrameList.Clear;
  2605. {$endif COMPILER_5_UP}
  2606. end;
  2607. end;
  2608. //----------------------------------------------------------------------------------------------------------------------
  2609. procedure TThemeManager.CollectForms(Form: TCustomForm = nil);
  2610. // (Re)initiates collecting all controls which need to be subclassed to fixed one or more problems.
  2611. var
  2612. I: Integer;
  2613. begin
  2614. if not FSubclassingDisabled and not (csDesigning in ComponentState) then
  2615. begin
  2616. if Form = nil then
  2617. begin
  2618. ClearLists;
  2619. for I := 0 to Screen.FormCount - 1 do
  2620. begin
  2621. FFormList.Add(Screen.Forms[I]);
  2622. CollectControls(Screen.Forms[I]);
  2623. end;
  2624. end
  2625. else
  2626. begin
  2627. FFormList.Add(Form);
  2628. CollectControls(Form);
  2629. end;
  2630. if ([toResetMouseCapture, toSetTransparency] * FOptions) <> [] then
  2631. FixControls(Form);
  2632. end;
  2633. end;
  2634. //----------------------------------------------------------------------------------------------------------------------
  2635. procedure TThemeManager.CollectControls(Parent: TWinControl);
  2636. var
  2637. I: Integer;
  2638. begin
  2639. Assert(Assigned(Parent), 'Parent of controls to be collected must be valid.');
  2640. if not FSubclassingDisabled and not (csDesigning in ComponentState) then
  2641. begin
  2642. for I := 0 to Parent.ControlCount - 1 do
  2643. begin
  2644. HandleControlChange(Parent.Controls[I], True);
  2645. if (Parent.Controls[I] is TWinControl) and (TWinControl(Parent.Controls[I]).ControlCount > 0) then
  2646. CollectControls(Parent.Controls[I] as TWinControl);
  2647. end;
  2648. end;
  2649. end;
  2650. //----------------------------------------------------------------------------------------------------------------------
  2651. procedure TThemeManager.PerformEraseBackground(Control: TControl; DC: HDC);
  2652. // Repainting the background of a control using theme services relies on the ability of the parent to handle
  2653. // WM_PRINT messages. Usually the default behavior of a window is enough to make this possible. However
  2654. // double buffered and non-windowed controls are quite different and need so special handling.
  2655. // This method uses the WM_ERASEBKGND message to achieve the same effect.
  2656. var
  2657. LastOrigin: TPoint;
  2658. begin
  2659. GetWindowOrgEx(DC, LastOrigin);
  2660. SetWindowOrgEx(DC, LastOrigin.X + Control.Left, LastOrigin.Y + Control.Top, nil);
  2661. Control.Parent.Perform(WM_ERASEBKGND, Integer(DC), Integer(DC));
  2662. SetWindowOrgEx(DC, LastOrigin.X, LastOrigin.Y, nil);
  2663. end;
  2664. //----------------------------------------------------------------------------------------------------------------------
  2665. procedure TThemeManager.RegisterListener(AControlMessage: TControlMessageEvent);
  2666. var
  2667. I: Integer;
  2668. Ptr: PControlMessageEvent;
  2669. begin
  2670. if not FindListener(AControlMessage, I) then
  2671. begin
  2672. New(Ptr);
  2673. Ptr^ := AControlMessage;
  2674. FListeners.Add(Ptr);
  2675. end;
  2676. end;
  2677. //----------------------------------------------------------------------------------------------------------------------
  2678. procedure TThemeManager.UnregisterListener(AControlMessage: TControlMessageEvent);
  2679. var
  2680. I: Integer;
  2681. begin
  2682. if FindListener(AControlMessage, I) then
  2683. begin
  2684. Dispose(PControlMessageEvent(FListeners[I]));
  2685. FListeners.Delete(I);
  2686. end;
  2687. end;
  2688. //----------------------------------------------------------------------------------------------------------------------
  2689. // MP BEGIN
  2690. function TThemeManager.GetColor(Element: TThemedElement; PartId: Integer;
  2691. StateId: Integer; PropId: Integer): TColor;
  2692. begin
  2693. Result := ThemeServices.GetColor(Element, PartId, StateId, PropId);
  2694. end;
  2695. //----------------------------------------------------------------------------------------------------------------------
  2696. function TThemeManager.GetThemesEnabled: Boolean;
  2697. begin
  2698. Result := ThemeServices.ThemesEnabled;
  2699. end;
  2700. // MP END
  2701. //----------------------------------------------------------------------------------------------------------------------
  2702. initialization
  2703. Lock := TCriticalSection.Create;
  2704. GetCheckSize;
  2705. IsWindowsXP := (Win32MajorVersion > 5) or ((Win32MajorVersion = 5) and (Win32MinorVersion >= 1));
  2706. finalization
  2707. Lock.Free;
  2708. Lock := nil;
  2709. end.