| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156 |
- unit ThemeMgr;
- //----------------------------------------------------------------------------------------------------------------------
- // Version 1.10.1
- //
- // Windows XP Theme Manager is freeware. You may freely use it in any software, including commercial software, provided
- // you accept the following conditions:
- //
- // 1) The software may not be included into component collections and similar compilations which are sold. If you want
- // to distribute this software for money then contact me first and ask for my permission.
- // 2) My copyright notices in the source code may not be removed or modified.
- // 3) If you modify and/or distribute the code to any third party then you must not veil the original author. It must
- // always be clearly identifiable that I, Mike Lischke, am the original author.
- // Although it is not required it would be a nice move to recognize my work by adding a citation to the application's
- // about box or a similar place.
- //
- // The original code is ThemeMgr.pas, released 01. January 2002.
- //
- // The initial developer of the original code is:
- // Mike Lischke ([email protected], www.soft-gems.net).
- //
- // Portions created by Mike Lischke are
- // (C) 2001-2005 Mike Lischke. All Rights Reserved.
- //----------------------------------------------------------------------------------------------------------------------
- //
- // This unit contains the implementation of TThemeManager which is designed to fix certain VCL components to enable
- // XP theme support in Delphi and BCB applications (version 6 and lower).
- //
- // TThemeManager uses global theming (all windows in the application use the same theme). Hence you don't
- // need more than one instance in an application (except for DLLs). Having more than one instance in the same module
- // (application, DLL) will disable subclassing of controls by all other but the first instance.
- //
- // Note: If you are using a Theme Manager in a DLL then make sure the handle of the application object in the DLL (which
- // is usually not allocated) is set to that of the main application, e.g. by passing it via an exported function.
- //----------------------------------------------------------------------------------------------------------------------
- //
- // January 2005
- // - Bug fix: Test for Windows XP was wrong.
- //
- // For full development history see help file.
- //
- // Credits for their valuable help go to:
- // Bert Moorthaemer, Rob Schoenaker, John W. Long, Vassiliev V.V., Steve Moss, Torsten Detsch, Milan Vandrovec
- //----------------------------------------------------------------------------------------------------------------------
- interface
- {$I Compilers.inc}
- {$ifdef COMPILER_7_UP}
- ATTENTION! Theme support is already included in this Borland product.
- Remove the Delphi Gems Theme Manager from your project to compile it correctly!
- {$endif COMPILER_7_UP}
- // The CheckListSupport switch is used to remove support for TCheckListBox. The main reason for this
- // is that TCheckListBox is in a special package (VCLX??.dpk), which you may not want to have included
- // (particularly when using runtime packages). Disable the switch to remove the link to the package
- // and remove the package reference from the ThemeManagerX.dpk file).
- {$define CheckListSupport}
- uses
- Windows, Classes, Messages, Graphics, Controls, StdCtrls, Buttons, Forms,
- ThemeSrv;
- const
- TMVersion = '1.10.1';
- // Sent to any control to give it a chance to deny its subclassing. This is mainly useful for controls
- // which are derived from classes which are usually subclassed by the Theme Manager but do their own
- // painting. A control should return a value <> 0 if subclassing should not be done.
- CM_DENYSUBCLASSING = CM_BASE + 2000;
- {$ifndef COMPILER_5_UP}
- {$EXTERNALSYM WM_CHANGEUISTATE}
- WM_CHANGEUISTATE = $0127;
- {$EXTERNALSYM WM_UPDATEUISTATE}
- WM_UPDATEUISTATE = $0128;
- {$EXTERNALSYM WM_QUERYUISTATE}
- WM_QUERYUISTATE = $0129;
- UIS_CLEAR = 2;
- UISF_HIDEFOCUS = 1;
- UISF_HIDEACCEL = 2;
- {$endif COMPILER_5_UP}
- // These constants are not defined in Delphi/BCB 6 or lower.
- SPI_GETFOCUSBORDERWIDTH = $200E;
- SPI_SETFOCUSBORDERWIDTH = $200F;
- SPI_GETFOCUSBORDERHEIGHT = $2010;
- SPI_SETFOCUSBORDERHEIGHT = $2011;
- type
- TThemeOption = (
- toAllowNonClientArea, // Specifies that the nonclient areas of application windows will have visual styles applied.
- toAllowControls, // Specifies that the controls used in an application will have visual styles applied.
- toAllowWebContent, // Specifies that Web content displayed in an application will have visual styles applied.
- toSubclassAnimate, // Enables subclassing of TAnimate controls (themed painting does not correctly work).
- toSubclassButtons, // Enables subclassing of button controls (also checkbox, radio button).
- toSubclassCheckListbox, // Enables subclassing of TCheckListBox.
- toSubclassDBLookup, // Enables subclassing of TDBLookupControl. Only used in TThemeManagerDB.
- toSubclassFrame, // Enables subclassing of frames (only available in Delphi 5 or higher).
- toSubclassGroupBox, // Enables subclassing of group box controls.
- toSubclassListView, // Enables subclassing of listview controls (including report mode bug fix).
- toSubclassPanel, // Enables subclassing of panels.
- toSubclassTabSheet, // Enables subclassing of tab sheet controls.
- toSubclassSpeedButtons, // Enables subclassing of speed button controls.
- toSubclassSplitter, // Enables subclassing of splitter controls.
- toSubclassStatusBar, // Enables subclassing of status bar controls.
- toSubclassTrackBar, // Enables subclassing of track bar controls (slight paint problems, though).
- toSubclassWinControl, // Enables subclassing of all window controls not belonging to any of the other classes.
- toResetMouseCapture, // If set then TToolButtons get their csCaptureMouse flag removed to properly show
- // their pressed state.
- toSetTransparency, // If set then TCustomLabel and TToolBar controls are automatically set to transparent.
- toAlternateTabSheetDraw // If set then use alternate drawing for TTabSheet body.
- );
- TThemeOptions = set of TThemeOption;
- const
- DefaultThemeOptions = [toAllowNonClientArea..toAllowWebContent, toSubclassButtons..toSetTransparency];
- type
- // These message records are not declared in Delphi 6 and lower.
- TWMPrint = packed record
- Msg: Cardinal;
- DC: HDC;
- Flags: Cardinal;
- Result: Integer;
- end;
- TWMPrintClient = TWMPrint;
- TThemeManager = class;
- TAllowSubclassingEvent = procedure(Sender: TThemeManager; Control: TControl; var Allow: Boolean) of object;
- TControlMessageEvent = procedure(Sender: TThemeManager; Control: TControl; var Message: TMessage;
- var Handled: Boolean) of object;
- PControlMessageEvent = ^TControlMessageEvent;
- // The window procedure list maintains the connections between control instances and their old window procedures.
- TWindowProcList = class(TList)
- private
- FDirty: Boolean;
- FLastControl: TControl;
- FLastIndex: Integer;
- FOwner: TThemeManager;
- FNewWindowProc: TWndMethod; // The new window procedure which handles the corrections for the control class.
- FControlClass: TControlClass; // The class for which this list is responsible.
- public
- constructor Create(Owner: TThemeManager; WindowProc: TWndMethod; ControlClass: TControlClass);
- destructor Destroy; override;
- function Add(Control: TControl): Integer;
- procedure Clear; override;
- procedure DispatchMessage(Control: TControl; var Message: TMessage);
- function Find(Control: TControl; out Index: Integer): Boolean;
- procedure Remove(Control: TControl);
- end;
- // TThemeManager is a class whose primary task is to fix various issues which show up when an application
- // is themed.
- TThemeManager = class(TComponent)
- private
- FOptions: TThemeOptions; // Determines which parts are allowed to be themed.
- FPanelList,
- {$ifdef COMPILER_5_UP}
- FFrameList, // Frames are first available in Delphi 5.
- {$endif COMPILER_5_UP}
- FListViewList,
- FTabSheetList,
- FWinControlList,
- FGroupBoxList,
- FButtonControlList,
- // MP
- FCheckBoxList,
- FButtonList,
- FSpeedButtonList,
- FSplitterList,
- FTrackBarList,
- FAnimateList,
- FStatusBarList,
- {$ifdef CheckListSupport}
- FCheckListBoxList,
- {$endif CheckListSupport}
- FFormList: TWindowProcList;
- FListeners: TList;
- FPendingFormsList: TList;
- FPendingRecreationList: TList;
- FSubclassingDisabled: Boolean; // Disable subclassing generally (e.g. for multi instancing).
- FHookWasInstalled: Boolean;
- FOnThemeChange: TNotifyEvent; // Called when the Windows theme or an application option has changed.
- FOnControlMessage: TControlMessageEvent;
- FOnAllowSubclassing: TAllowSubclassingEvent;
- procedure AnimateWindowProc(Control: TControl; var Message: TMessage);
- procedure ButtonControlWindowProc(Control: TControl; var Message: TMessage; { MP } List: TWindowProcList);
- {$ifdef CheckListSupport}
- procedure CheckListBoxWindowProc(Control: TControl; var Message: TMessage);
- {$endif CheckListSupport}
- procedure FormWindowProc(Control: TControl; var Message: TMessage);
- {$ifdef COMPILER_5_UP}
- procedure FrameWindowProc(Control: TControl; var Message: TMessage);
- {$endif COMPILER_5_UP}
- function GetIsMainManager: Boolean;
- procedure GroupBoxWindowProc(Control: TControl; var Message: TMessage);
- procedure ListviewWindowProc(Control: TControl; var Message: TMessage);
- function MainWindowHook(var Message: TMessage): Boolean;
- procedure PanelWindowProc(Control: TControl; var Message: TMessage);
- procedure SetThemeOptions(const Value: TThemeOptions);
- procedure SpeedButtonWindowProc(Control: TControl; var Message: TMessage);
- procedure SplitterWindowProc(Control: TControl; var Message: TMessage);
- procedure StatusBarWindowProc(Control: TControl; var Message: TMessage);
- procedure TabSheetWindowProc(Control: TControl; var Message: TMessage);
- procedure TrackBarWindowProc(Control: TControl; var Message: TMessage);
- procedure WinControlWindowProc(Control: TControl; var Message: TMessage);
- procedure PreAnimateWindowProc(var Message: TMessage);
- procedure PreButtonControlWindowProc(var Message: TMessage);
- // MP BEGIN
- procedure PreCheckBoxWindowProc(var Message: TMessage);
- procedure PreButtonWindowProc(var Message: TMessage);
- // MP END
- {$ifdef CheckListSupport}
- procedure PreCheckListBoxWindowProc(var Message: TMessage);
- {$endif CheckListSupport}
- procedure PreFormWindowProc(var Message: TMessage);
- {$ifdef COMPILER_5_UP}
- procedure PreFrameWindowProc(var Message: TMessage);
- {$endif COMPILER_5_UP}
- procedure PreGroupBoxWindowProc(var Message: TMessage);
- procedure PreListviewWindowProc(var Message: TMessage);
- procedure PrePanelWindowProc(var Message: TMessage);
- procedure PreSpeedButtonWindowProc(var Message: TMessage);
- procedure PreSplitterWindowProc(var Message: TMessage);
- procedure PreStatusBarWindowProc(var Message: TMessage);
- procedure PreTabSheetWindowProc(var Message: TMessage);
- procedure PreTrackBarWindowProc(var Message: TMessage);
- procedure PreWinControlWindowProc(var Message: TMessage);
- // MP
- function GetThemesEnabled: Boolean;
- protected
- procedure AddRecreationCandidate(Control: TControl); virtual;
- procedure BroadcastThemeChange;
- class function CurrentThemeManager: TThemeManager;
- function DoAllowSubclassing(Control: TControl): Boolean; virtual;
- function DoControlMessage(Control: TControl; var Message: TMessage): Boolean; virtual;
- procedure DoOnThemeChange; virtual;
- procedure DrawBitBtn(Control: TBitBtn; var DrawItemStruct: TDrawItemStruct);
- procedure DrawButton(Control: TControl; Button: TThemedButton; DC: HDC; R: TRect; Focused: Boolean);
- function FindListener(AControlMessage: TControlMessageEvent; var Index: Integer): Boolean;
- procedure FixControls(Form: TCustomForm = nil);
- procedure ForceAsMainManager; virtual;
- procedure HandleControlChange(Control: TControl; Inserting: Boolean); virtual;
- function IsRecreationCandidate(Control: TControl): Boolean;
- procedure Loaded; override;
- function NeedsBorderPaint(Control: TControl): Boolean; virtual;
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure RemoveChildSubclassing(Control: TWinControl);
- procedure RemoveRecreationCandidate(Control: TControl);
- procedure UpdateThemes;
- procedure UpdateUIState(Control: TControl; CharCode: Word);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- // MP BEGIN
- function GetColor(Element: TThemedElement; PartId: Integer; StateId: Integer;
- PropId: Integer): TColor;
- property ThemesEnabled: Boolean read GetThemesEnabled;
- // MP END
- procedure ClearLists;
- procedure CollectForms(Form: TCustomForm = nil);
- procedure CollectControls(Parent: TWinControl);
- procedure PerformEraseBackground(Control: TControl; DC: HDC);
- procedure RegisterListener(AControlMessage: TControlMessageEvent);
- procedure UnregisterListener(AControlMessage: TControlMessageEvent);
- property IsMainManager: Boolean read GetIsMainManager;
- published
- property Options: TThemeOptions read FOptions write SetThemeOptions default DefaultThemeOptions;
- property OnAllowSubclassing: TAllowSubclassingEvent read FOnAllowSubclassing write FOnAllowSubclassing;
- property OnControlMessage: TControlMessageEvent read FOnControlMessage write FOnControlMessage;
- property OnThemeChange: TNotifyEvent read FOnThemeChange write FOnThemeChange;
- end;
- var
- IsWindowsXP: Boolean;
- //----------------------------------------------------------------------------------------------------------------------
- implementation
- uses
- SysUtils, ComCtrls, CommCtrl, SyncObjs, ExtCtrls, Grids, UxTheme
- {$ifdef CheckListSupport}
- , CheckLst
- {$endif CheckListSupport}
- ;
- const
- WM_MAINMANAGERRELEASED = CN_NOTIFY + 100;
- type
- {$ifndef COMPILER_6_UP}
- // TCustomStatusBar does not exist prior Delphi/BCB 6.
- TCustomStatusBar = TStatusBar;
- {$endif COMPILER_6_UP}
- PWindowProcEntry = ^TWindowProcEntry;
- TWindowProcEntry = record
- Control: TControl;
- OldWndProc: TWndMethod;
- end;
- var
- Lock: TCriticalSection;
- {$ifdef Debug}
- SubclassCount: Integer;
- {$endif}
- var
- MainManager: TThemeManager;
- GlobalCheckWidth,
- GlobalCheckHeight: Integer;
- //----------------- Drawing helper routines ----------------------------------------------------------------------------
- procedure GetCheckSize;
- begin
- with TBitmap.Create do
- try
- Handle := LoadBitmap(0, PChar(32759));
- GlobalCheckWidth := Width div 4;
- GlobalCheckHeight := Height div 3;
- finally
- Free;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- type
- // Used to access protected properties.
- TControlCast = class(TControl);
- procedure CalcButtonLayout(Control: TControl; DC: HDC; const Client: TRect; const Offset: TPoint; var GlyphPos: TPoint;
- var TextBounds: TRect; BiDiFlags: Integer);
- // This routine is nearly the same as the same named version in TButtonGlyph. The inclusion here is necessary
- // because we need the same layout as in the VCL but the implementation of TButtonGlyph is hidden in Buttons and
- // cannot be made accessible from here.
- var
- TextPos: TPoint;
- ClientSize,
- GlyphSize,
- TextSize: TPoint;
- TotalSize: TPoint;
- Layout: TButtonLayout;
- Spacing: Integer;
- Margin: Integer;
- Glyph: TBitmap;
- NumGlyphs: Integer;
- Caption: TCaption;
- begin
- if Control is TBitBtn then
- begin
- Layout := TBitBtn(Control).Layout;
- Spacing := TBitBtn(Control).Spacing;
- Margin := TBitBtn(Control).Margin;
- Glyph := TBitBtn(Control).Glyph;
- NumGlyphs := TBitBtn(Control).NumGlyphs;
- Caption := TBitBtn(Control).Caption;
- end
- else
- begin
- Layout := TSpeedButton(Control).Layout;
- Spacing := TSpeedButton(Control).Spacing;
- Margin := TSpeedButton(Control).Margin;
- Glyph := TSpeedButton(Control).Glyph;
- NumGlyphs := TSpeedButton(Control).NumGlyphs;
- Caption := TSpeedButton(Control).Caption;
- end;
- if (BiDiFlags and DT_RIGHT) = DT_RIGHT then
- if Layout = blGlyphLeft then
- Layout := blGlyphRight
- else
- if Layout = blGlyphRight then
- Layout := blGlyphLeft;
- // Calculate the item sizes.
- ClientSize := Point(Client.Right - Client.Left, Client.Bottom - Client.Top);
- if Assigned(Glyph) then
- GlyphSize := Point(Glyph.Width div NumGlyphs, Glyph.Height)
- else
- GlyphSize := Point(0, 0);
- if Length(Caption) > 0 then
- begin
- TextBounds := Rect(0, 0, Client.Right - Client.Left, 0);
- Windows.DrawText(DC, PChar(Caption), Length(Caption), TextBounds, DT_CALCRECT or BiDiFlags);
- TextSize := Point(TextBounds.Right - TextBounds.Left, TextBounds.Bottom - TextBounds.Top);
- end
- else
- begin
- TextBounds := Rect(0, 0, 0, 0);
- TextSize := Point(0,0);
- end;
- // If the layout has the glyph on the right or the left, then both the text and the glyph are centered vertically.
- // If the glyph is on the top or the bottom, then both the text and the glyph are centered horizontally.
- if Layout in [blGlyphLeft, blGlyphRight] then
- begin
- GlyphPos.Y := (ClientSize.Y - GlyphSize.Y + 1) div 2;
- TextPos.Y := (ClientSize.Y - TextSize.Y + 1) div 2;
- end
- else
- begin
- GlyphPos.X := (ClientSize.X - GlyphSize.X + 1) div 2;
- TextPos.X := (ClientSize.X - TextSize.X + 1) div 2;
- end;
- // If there is no text or no bitmap, then Spacing is irrelevant.
- if (TextSize.X = 0) or (GlyphSize.X = 0) then
- Spacing := 0;
- // Adjust Margin and Spacing.
- if Margin = -1 then
- begin
- if Spacing = -1 then
- begin
- TotalSize := Point(GlyphSize.X + TextSize.X, GlyphSize.Y + TextSize.Y);
- if Layout in [blGlyphLeft, blGlyphRight] then
- Margin := (ClientSize.X - TotalSize.X) div 3
- else
- Margin := (ClientSize.Y - TotalSize.Y) div 3;
- Spacing := Margin;
- end
- else
- begin
- TotalSize := Point(GlyphSize.X + Spacing + TextSize.X, GlyphSize.Y + Spacing + TextSize.Y);
- if Layout in [blGlyphLeft, blGlyphRight] then
- Margin := (ClientSize.X - TotalSize.X + 1) div 2
- else
- Margin := (ClientSize.Y - TotalSize.Y + 1) div 2;
- end;
- end
- else
- begin
- if Spacing = -1 then
- begin
- TotalSize := Point(ClientSize.X - (Margin + GlyphSize.X), ClientSize.Y - (Margin + GlyphSize.Y));
- if Layout in [blGlyphLeft, blGlyphRight] then
- Spacing := (TotalSize.X - TextSize.X) div 2
- else
- Spacing := (TotalSize.Y - TextSize.Y) div 2;
- end;
- end;
- case Layout of
- blGlyphLeft:
- begin
- GlyphPos.X := Margin;
- TextPos.X := GlyphPos.X + GlyphSize.X + Spacing;
- end;
- blGlyphRight:
- begin
- GlyphPos.X := ClientSize.X - Margin - GlyphSize.X;
- TextPos.X := GlyphPos.X - Spacing - TextSize.X;
- end;
- blGlyphTop:
- begin
- GlyphPos.Y := Margin;
- TextPos.Y := GlyphPos.Y + GlyphSize.Y + Spacing;
- end;
- blGlyphBottom:
- begin
- GlyphPos.Y := ClientSize.Y - Margin - GlyphSize.Y;
- TextPos.Y := GlyphPos.Y - Spacing - TextSize.Y;
- end;
- end;
- // Fixup the result variables.
- with GlyphPos do
- begin
- Inc(X, Client.Left + Offset.X);
- Inc(Y, Client.Top + Offset.Y);
- end;
- OffsetRect(TextBounds, TextPos.X + Client.Left + Offset.X, TextPos.Y + Client.Top + Offset.X);
- end;
- //----------------- TWindowProcList ------------------------------------------------------------------------------------
- // For fixing various things in the VCL we have to subclass some of the VCL controls. For each class of control
- // one instance of the TWindowProcList is used.
- constructor TWindowProcList.Create(Owner: TThemeManager; WindowProc: TWndMethod; ControlClass: TControlClass);
- begin
- inherited Create;
- FOwner := Owner;
- FNewWindowProc := WindowProc;
- FControlClass := ControlClass;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- destructor TWindowProcList.Destroy;
- begin
- Clear;
- inherited;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function Compare(Item1, Item2: Pointer): Integer;
- // Helper function for sort and find in window proc lists. They are sorted by control reference.
- begin
- Result := Integer(PWindowProcEntry(Item1).Control) - Integer(PWindowProcEntry(Item2).Control);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TWindowProcList.Add(Control: TControl): Integer;
- var
- I: Integer;
- Entry: PWindowProcEntry;
- ControlWndProc: TWndMethod;
- begin
- Result := -1;
- if (Control is FControlClass) and not Find(Control, I) then
- begin
- {$ifdef Debug}
- Lock.Enter;
- try
- Inc(SubclassCount);
- finally
- Lock.Leave;
- end;
- {$endif Debug}
- New(Entry);
- Entry.Control := Control;
- Entry.OldWndProc := Control.WindowProc;
- // The following two lines make sure we get the original control, to which a message is sent, in our
- // proxy window procedures. This works because the Data member of the window proc does not get the reference to
- // the theme manager (as it would happen with ControlWindowProc := FNewWindowProc) but instead we explicitly
- // set the control's reference there (see also first proxy method implementation below).
- TMethod(ControlWndProc).Code := TMethod(FNewWindowProc).Code;
- TMethod(ControlWndProc).Data := Control;
- Control.WindowProc := ControlWndProc;
- Result := inherited Add(Entry);
- FDirty := True;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TWindowProcList.Clear;
- begin
- while Count > 0 do
- Remove(PWindowProcEntry(Items[0]).Control);
- inherited;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TWindowProcList.DispatchMessage(Control: TControl; var Message: TMessage);
- var
- I: Integer;
- Entry: PWindowProcEntry;
- begin
- if Find(Control, I) then
- begin
- // If a window handle is being recreated then we must ensure the handle is really recreated not only destroyed
- // (this might happen when a hidden window's handle is recreated). Otherwise we will not get notified again about
- // the window's real destruction.
- if Message.Msg = CM_RECREATEWND then
- MainManager.AddRecreationCandidate(Control);
- Entry := Items[I];
- Entry.OldWndProc(Message);
- // If a control is being destroyed then we have to revert the subclassing.
- // We don't get any other opportunity to clean up since TComponent.Notification comes too late and is also not
- // called for controls, which are implicitely freed because their parent is freed.
- if Message.Msg = WM_DESTROY then
- begin
- // Remove any control, which is permanently destroyed, but take care for window recreations.
- if (csDestroying in Control.ComponentState) or not (MainManager.IsRecreationCandidate(Control)) then
- // This call will also remove any child subclassing.
- Remove(Control);
- end;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TWindowProcList.Find(Control: TControl; out Index: Integer): Boolean;
- // Binary search implementation to quickly find a control in the list.
- var
- L, H,
- I, C: Integer;
- Dummy: TWindowProcEntry;
- begin
- // First try the cached data to speed up retrieval.
- if Control = FLastControl then
- begin
- Result := True;
- Index := FLastIndex;
- end
- else
- begin
- if FDirty and (Count > 1) then
- begin
- Sort(Compare);
- FDirty := False;
- end;
- Result := False;
- Dummy.Control := Control;
- L := 0;
- H := Count - 1;
- while L <= H do
- begin
- I := (L + H) shr 1;
- C := Compare(Items[I], @Dummy);
- if C < 0 then
- L := I + 1
- else
- begin
- H := I - 1;
- if C = 0 then
- begin
- Result := True;
- L := I;
- end;
- end;
- end;
- Index := L;
- if Result then
- begin
- FLastControl := Control;
- FLastIndex := L;
- end;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TWindowProcList.Remove(Control: TControl);
- var
- I: Integer;
- Entry: PWindowProcEntry;
- begin
- if Find(Control, I) then
- begin
- Entry := Items[I];
- Delete(I);
- Entry.Control.WindowProc := Entry.OldWndProc;
- // Implicitly release all child subclassing.
- if Entry.Control is TWinControl then
- FOwner.RemoveChildSubclassing(Entry.Control as TWinControl);
- Dispose(Entry);
- {$ifdef Debug}
- Lock.Enter;
- try
- Dec(SubclassCount);
- finally
- Lock.Leave;
- end;
- {$endif Debug}
- end;
- if I <= FLastIndex then
- begin
- FLastControl := nil;
- FLastIndex := -1;
- end;
- MainManager.RemoveRecreationCandidate(Control);
- end;
- //----------------- TThemeManager --------------------------------------------------------------------------------------
- constructor TThemeManager.Create(AOwner: TComponent);
- begin
- inherited;
- FListeners := TList.Create;
- FOptions := DefaultThemeOptions;
- FPendingFormsList := TList.Create;
- FPendingRecreationList := TList.Create;
- FListViewList := TWindowProcList.Create(Self, PreListviewWindowProc, TCustomListView);
- FTabSheetList := TWindowProcList.Create(Self, PreTabSheetWindowProc, TTabSheet);
- FGroupBoxList := TWindowProcList.Create(Self, PreGroupBoxWindowProc, TCustomGroupBox);
- FButtonControlList := TWindowProcList.Create(Self, PreButtonControlWindowProc, TButtonControl);
- // MP BEGIN
- FCheckBoxList := TWindowProcList.Create(Self, PreCheckBoxWindowProc, TCheckBox);
- FButtonList := TWindowProcList.Create(Self, PreButtonWindowProc, TButton);
- // MP END
- FSpeedButtonList := TWindowProcList.Create(Self, PreSpeedButtonWindowProc, TSpeedButton);
- FSplitterList := TWindowProcList.Create(Self, PreSplitterWindowProc, TSplitter);
- FTrackBarList := TWindowProcList.Create(Self, PreTrackBarWindowProc, TTrackBar);
- FAnimateList := TWindowProcList.Create(Self, PreAnimateWindowProc, TAnimate);
- FStatusBarList := TWindowProcList.Create(Self, PreStatusBarWindowProc, TCustomStatusBar);
- {$ifdef CheckListSupport}
- FCheckListBoxList := TWindowProcList.Create(Self, PreCheckListBoxWindowProc, TCheckListBox);
- {$endif CheckListSupport}
- FFormList := TWindowProcList.Create(Self, PreFormWindowProc, TCustomForm);
- {$ifdef COMPILER_5_UP}
- FFrameList := TWindowProcList.Create(Self, PreFrameWindowProc, TCustomFrame);
- {$endif COMPILER_5_UP}
- FPanelList := TWindowProcList.Create(Self, PrePanelWindowProc, TCustomPanel);
- FWinControlList := TWindowProcList.Create(Self, PreWinControlWindowProc, TWinControl);
- if csDesigning in ComponentState then
- FSubclassingDisabled := True
- else
- begin
- if ThemeServices.ThemesEnabled then
- begin
- Application.HookMainWindow(MainWindowHook);
- FHookWasInstalled := True;
- end
- else
- FHookWasInstalled := False;
- // Keep the reference of this instance if it is the first one created in the application.
- Lock.Enter;
- try
- // If this is not the first instance then disable subclassing.
- if MainManager = nil then
- MainManager := Self
- else
- begin
- FSubclassingDisabled := True;
- FOptions := MainManager.FOptions;
- end;
- finally
- Lock.Leave;
- end;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- destructor TThemeManager.Destroy;
- begin
- FWinControlList.Free;
- FPanelList.Free;
- {$ifdef COMPILER_5_UP}
- FFrameList.Free;
- {$endif COMPILER_5_UP}
- FFormList.Free;
- {$ifdef CheckListSupport}
- FCheckListBoxList.Free;
- {$endif CheckListSupport}
- FStatusBarList.Free;
- FAnimateList.Free;
- FTrackBarList.Free;
- FSpeedButtonList.Free;
- FSplitterList.Free;
- // MP BEGIN
- FButtonList.Free;
- FCheckBoxList.Free;
- // MP END
- FButtonControlList.Free;
- FListViewList.Free;
- FTabSheetList.Free;
- FGroupBoxList.Free;
- // Reset first manager reference if it is set to this instance.
- if not (csDesigning in ComponentState) then
- begin
- if FHookWasInstalled then
- Application.UnhookMainWindow(MainWindowHook);
- // We have to check the critical section here because it can happen that it is already freed (finalization section)
- // but there is still a theme manager instance lurking around, due to the finalization order.
- // If there is no lock anymore then the app. is being terminated and we don't need to set a new main manager.
- if Assigned(Lock) then
- begin
- Lock.Enter;
- try
- if MainManager = Self then
- begin
- MainManager := nil;
- if Application.Handle <> 0 then
- SendAppMessage(WM_MAINMANAGERRELEASED, 0, 0);
- end;
- finally
- Lock.Leave;
- end;
- end;
- end;
- FPendingFormsList.Free;
- FPendingRecreationList.Free;
- FListeners.Free;
- inherited;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- type
- // Used to access protected methods and properties.
- TWinControlCast = class(TWinControl);
- procedure TThemeManager.AnimateWindowProc(Control: TControl; var Message: TMessage);
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- WM_ERASEBKGND:
- Message.Result := 1;
- CN_CTLCOLORSTATIC:
- if TAnimate(Control).Transparent then
- with TWMCtlColorStatic(Message) do
- begin
- // Return a brush corresponding to the control's fixed background color.
- // The animation control insists on always erasing its background.
- Result := GetSysColorBrush(TWinControlCast(Control).Color and not $80000000);
- {ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
- SetBkMode(ChildDC, TRANSPARENT);
- // Return an empty brush to prevent Windows from overpainting we just have created.
- Result := GetStockObject(NULL_BRUSH);}
- end
- else
- FAnimateList.DispatchMessage(Control, Message);
- else
- FAnimateList.DispatchMessage(Control, Message);
- end;
- end
- else
- FAnimateList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.ButtonControlWindowProc(Control: TControl; var Message: TMessage;
- { MP } List: TWindowProcList);
- var
- Details: TThemedElementDetails;
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- CN_KEYDOWN,
- WM_SYSKEYDOWN,
- WM_KEYDOWN:
- begin
- UpdateUIState(Control, TWMKey(Message).CharCode);
- // MP
- List.DispatchMessage(Control, Message);
- end;
- WM_ERASEBKGND:
- Message.Result := 1;
- CN_CTLCOLORBTN: // TButton background erasing. Necessary for some themes (like EclipseOSX).
- with TWMCtlColorBtn(Message) do
- begin
- if TWinControl(Control.Parent).DoubleBuffered then
- PerformEraseBackground(Control, ChildDC)
- else
- ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
- // Return an empty brush to prevent Windows from overpainting we just have created.
- Result := GetStockObject(NULL_BRUSH);
- end;
- CN_CTLCOLORSTATIC: // Background erasing for check boxes and radio buttons.
- with TWMCtlColorStatic(Message) do
- begin
- if TWinControl(Control.Parent).DoubleBuffered then
- PerformEraseBackground(Control, ChildDC)
- else
- ThemeServices.DrawParentBackground(TWinControl(Control).Handle, ChildDC, nil, False);
- // Return an empty brush to prevent Windows from overpainting we just have created.
- Result := GetStockObject(NULL_BRUSH);
- end;
- CM_MOUSEENTER,
- CM_MOUSELEAVE:
- begin
- // Hot tracking for owner drawn buttons seems to be unsupported by Windows. So we have to work around that.
- if Control is TBitBtn then
- Control.Invalidate;
- // MP
- List.DispatchMessage(Control, Message);
- end;
- CN_DRAWITEM: // Painting for owner drawn buttons.
- with TWMDrawItem(Message) do
- begin
- // This message is sent for bit buttons (TBitBtn) when they must be drawn. Since a bit button is a normal
- // Windows button (but with custom draw enabled) it is handled here too.
- // TSpeedButton is a TGraphicControl descentant and handled separately.
- Details := ThemeServices.GetElementDetails(tbPushButtonNormal);
- ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DrawItemStruct.hDC, @Details, True);
- // CN_DRAWITEM can also come in when the control is a subclassed button with enabled custom draw.
- // In this case the content of the control is fully controlled by the original source. So let it do
- // whatever it wants to do.
- if (Control is TBitBtn) or (Control is TSpeedButton) then
- DrawBitBtn(TBitBtn(Control), DrawItemStruct^)
- else
- // MP
- List.DispatchMessage(Control, Message);
- end;
- else
- // MP
- List.DispatchMessage(Control, Message);
- end;
- end
- else
- // MP
- List.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- {$ifdef CheckListSupport}
- type
- TCheckListBoxCast = class(TCheckListBox);
- procedure TThemeManager.CheckListBoxWindowProc(Control: TControl; var Message: TMessage);
- var
- DrawState: TOwnerDrawState;
- ListBox: TCheckListBoxCast;
- //--------------- local functions -------------------------------------------
- procedure DrawCheck(R: TRect; AState: TCheckBoxState; Enabled: Boolean);
- var
- DrawRect: TRect;
- Button: TThemedButton;
- Details: TThemedElementDetails;
- begin
- DrawRect.Left := R.Left + (R.Right - R.Left - GlobalCheckWidth) div 2;
- DrawRect.Top := R.Top + (R.Bottom - R.Top - GlobalCheckWidth) div 2;
- DrawRect.Right := DrawRect.Left + GlobalCheckWidth;
- DrawRect.Bottom := DrawRect.Top + GlobalCheckHeight;
- case AState of
- cbChecked:
- if Enabled then
- Button := tbCheckBoxCheckedNormal
- else
- Button := tbCheckBoxCheckedDisabled;
- cbUnchecked:
- if Enabled then
- Button := tbCheckBoxUncheckedNormal
- else
- Button := tbCheckBoxUncheckedDisabled;
- else // cbGrayed
- if Enabled then
- Button := tbCheckBoxMixedNormal
- else
- Button := tbCheckBoxMixedDisabled;
- end;
- Details := ThemeServices.GetElementDetails(Button);
- ThemeServices.DrawElement(ListBox.Canvas.Handle, Details, DrawRect, @DrawRect);
- end;
- //---------------------------------------------------------------------------
- procedure NewDrawItem(Index: Integer; Rect: TRect; DrawState: TOwnerDrawState);
- var
- Flags: Integer;
- Data: string;
- R: TRect;
- ACheckWidth: Integer;
- Enable: Boolean;
- begin
- with ListBox do
- begin
- // The checkbox is always drawn, regardless of the owner draw style.
- ACheckWidth := GetCheckWidth;
- if Index < Items.Count then
- begin
- R := Rect;
- // Delphi 4 has neither an enabled state nor a header state for items.
- Enable := Enabled {$ifdef COMPILER_6_UP} and ItemEnabled[Index] {$endif COMPILER_6_UP};
- if {$ifdef COMPILER_6_UP} not Header[Index] {$else} True {$endif COMPILER_6_UP} then
- begin
- if not UseRightToLeftAlignment then
- begin
- R.Right := Rect.Left;
- R.Left := R.Right - ACheckWidth;
- end
- else
- begin
- R.Left := Rect.Right;
- R.Right := R.Left + ACheckWidth;
- end;
- DrawCheck(R, State[Index], Enable);
- end
- else
- begin
- {$ifdef COMPILER_6_UP}
- Canvas.Font.Color := HeaderColor;
- Canvas.Brush.Color := HeaderBackgroundColor;
- {$endif COMPILER_6_UP}
- end;
- if not Enable then
- Canvas.Font.Color := clGrayText;
- end;
- if Assigned(OnDrawItem) and (Style <> lbStandard)then
- OnDrawItem(ListBox, Index, Rect, DrawState)
- else
- begin
- Canvas.FillRect(Rect);
- if Index < {$ifdef COMPILER_6_UP} Count {$else} Items.Count {$endif COMPILER_6_UP}then
- begin
- Flags := DrawTextBiDiModeFlags(DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
- if not UseRightToLeftAlignment then
- Inc(Rect.Left, 2)
- else
- Dec(Rect.Right, 2);
- Data := '';
- {$ifdef COMPILER_6_UP}
- if (Style in [lbVirtual, lbVirtualOwnerDraw]) then
- Data := DoGetData(Index)
- else
- {$endif COMPILER_6_UP}
- Data := Items[Index];
- DrawText(Canvas.Handle, PChar(Data), Length(Data), Rect, Flags);
- end;
- end;
- end;
- end;
- //--------------- end local function ----------------------------------------
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- ListBox := TCheckListBoxCast(Control);
- case Message.Msg of
- CN_DRAWITEM:
- if {$ifdef COMPILER_6_UP} ListBox.Count > 0 {$else} ListBox.Items.Count > 0 {$endif COMPILER_6_UP} then
- with TWMDrawItem(Message).DrawItemStruct^, ListBox do
- begin
- if {$ifdef COMPILER_6_UP} not Header[itemID] {$else} True {$endif COMPILER_6_UP} then
- if not UseRightToLeftAlignment then
- rcItem.Left := rcItem.Left + GetCheckWidth
- else
- rcItem.Right := rcItem.Right - GetCheckWidth;
- {$ifdef COMPILER_5_UP}
- DrawState := TOwnerDrawState(LongRec(itemState).Lo);
- {$else}
- DrawState := TOwnerDrawState(Byte(LongRec(itemState).Lo));
- {$endif COMPILER_5_UP}
- Canvas.Handle := hDC;
- Canvas.Font := Font;
- Canvas.Brush := Brush;
- if (Integer(itemID) >= 0) and (odSelected in DrawState) then
- begin
- Canvas.Brush.Color := clHighlight;
- Canvas.Font.Color := clHighlightText
- end;
- if Integer(itemID) >= 0 then
- NewDrawItem(itemID, rcItem, DrawState)
- else
- Canvas.FillRect(rcItem);
- if odFocused in DrawState then
- DrawFocusRect(hDC, rcItem);
- Canvas.Handle := 0;
- end;
- else
- FCheckListBoxList.DispatchMessage(Control, Message);
- end;
- end
- else
- FCheckListBoxList.DispatchMessage(Control, Message);
- end
- else
- FCheckListBoxList.DispatchMessage(Control, Message);
- end;
- {$endif CheckListSupport}
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.FormWindowProc(Control: TControl; var Message: TMessage);
- var
- DC: HDC;
- begin
- case Message.Msg of
- CM_CONTROLLISTCHANGE: // Single control addition or removal.
- with TCMControlListChange(Message) do
- HandleControlChange(Control, Inserting);
- end;
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- WM_PRINTCLIENT,
- WM_ERASEBKGND:
- begin
- if (Message.Msg=WM_PRINTCLIENT) then
- DC := TWMPrintClient(Message).DC
- else
- DC := TWMEraseBkGnd(Message).DC;
- // Get the parent to draw its background into the form's background.
- if not (Control.Parent is TWinControl) then
- FFormList.DispatchMessage(Control, Message)
- else
- if TWinControl(Control.Parent).DoubleBuffered then
- PerformEraseBackground(Control, DC)
- else
- if TWinControl(Control).DoubleBuffered then
- begin
- if (Message.Msg <> WM_ERASEBKGND) or (Longint(DC) = TWMEraseBkGnd(Message).Unused) then
- // VCL mark for second pass, this time into the offscreen bitmap
- PerformEraseBackground(Control, DC);
- end
- else
- DrawThemeParentBackground(TWinControl(Control).Handle, DC, nil);
- Message.Result := 1;
- end;
- else
- FFormList.DispatchMessage(Control, Message);
- end;
- end
- else
- FFormList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- {$ifdef COMPILER_5_UP}
- type
- // Used to access protected properties.
- TFrameCast = class(TCustomFrame);
- procedure TThemeManager.FrameWindowProc(Control: TControl; var Message: TMessage);
- var
- PS: TPaintStruct;
- Details: TThemedElementDetails;
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- WM_ERASEBKGND:
- // MP BEGIN
- if TFrameCast(Control).Color <> clBtnFace then
- FFrameList.DispatchMessage(Control, Message)
- else
- // MP END
- with TWMEraseBkGnd(Message) do
- begin
- // Get the parent to draw its background into the control's background.
- if TWinControl(Control.Parent).DoubleBuffered then
- PerformEraseBackground(Control, DC)
- else
- begin
- Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
- ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, @Details, False);
- end;
- Result := 1;
- end;
- WM_PAINT:
- begin
- BeginPaint(TFrameCast(Control).Handle, PS);
- TFrameCast(Control).PaintControls(PS.hdc, nil);
- EndPaint(TFrameCast(Control).Handle, PS);
- Message.Result := 0;
- end;
- else
- FFrameList.DispatchMessage(Control, Message);
- end;
- end
- else
- FFrameList.DispatchMessage(Control, Message);
- end;
- end;
- {$endif COMPILER_5_UP}
- //----------------------------------------------------------------------------------------------------------------------
- function TThemeManager.GetIsMainManager: Boolean;
- begin
- Result := MainManager = Self;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- type
- // Used to access protected properties.
- TGroupBoxCast = class(TCustomGroupBox);
- procedure TThemeManager.GroupBoxWindowProc(Control: TControl; var Message: TMessage);
- //--------------- local function --------------------------------------------
- procedure NewPaint(DC: HDC);
- var
- CaptionRect,
- OuterRect: TRect;
- Size: TSize;
- LastFont: HFONT;
- Box: TThemedButton;
- Details: TThemedElementDetails;
- begin
- with TGroupBoxCast(Control) do
- begin
- LastFont := SelectObject(DC, Font.Handle);
- if Text <> '' then
- begin
- SetTextColor(DC, Graphics.ColorToRGB(Font.Color));
- // Determine size and position of text rectangle.
- // This must be clipped out before painting the frame.
- GetTextExtentPoint32(DC, PChar(Text), Length(Text), Size);
- CaptionRect := Rect(0, 0, Size.cx, Size.cy);
- if not UseRightToLeftAlignment then
- OffsetRect(CaptionRect, 8, 0)
- else
- OffsetRect(CaptionRect, Width - 8 - CaptionRect.Right, 0);
- end
- else
- CaptionRect := Rect(0, 0, 0, 0);
- OuterRect := ClientRect;
- OuterRect.Top := (CaptionRect.Bottom - CaptionRect.Top) div 2;
- with CaptionRect do
- ExcludeClipRect(DC, Left, Top, Right, Bottom);
- if Control.Enabled then
- Box := tbGroupBoxNormal
- else
- Box := tbGroupBoxDisabled;
- Details := ThemeServices.GetElementDetails(Box);
- ThemeServices.DrawElement(DC, Details, OuterRect);
- SelectClipRgn(DC, 0);
- if Text <> '' then
- ThemeServices.DrawText(DC, Details, Text, CaptionRect, DT_LEFT, 0);
- SelectObject(DC, LastFont);
- end;
- end;
- //--------------- local function --------------------------------------------
- var
- PS: TPaintStruct;
- Details: TThemedElementDetails;
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- WM_SYSKEYDOWN,
- CN_KEYDOWN,
- WM_KEYDOWN:
- begin
- UpdateUIState(Control, TWMKey(Message).CharCode);
- FGroupBoxList.DispatchMessage(Control, Message);
- end;
- WM_ERASEBKGND:
- with TWMEraseBkGnd(Message) do
- begin
- // Get the parent to draw its background into the control's background.
- if TWinControl(Control.Parent).DoubleBuffered then
- PerformEraseBackground(Control, DC)
- else
- begin
- Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
- ThemeServices.DrawParentBackground(TGroupBoxCast(Control).Handle, DC, @Details, True);
- end;
- Result := 1;
- end;
- WM_PAINT:
- begin
- BeginPaint(TGroupBoxCast(Control).Handle, PS);
- NewPaint(PS.hdc);
- TGroupBoxCast(Control).PaintControls(PS.hdc, nil);
- EndPaint(TGroupBoxCast(Control).Handle, PS);
- Message.Result := 0;
- end;
- else
- FGroupBoxList.DispatchMessage(Control, Message);
- end;
- end
- else
- FGroupBoxList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.ListviewWindowProc(Control: TControl; var Message: TMessage);
- begin
- if not DoControlMessage(Control, Message) then
- begin
- // MP BEGIN
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- WM_SYSKEYDOWN,
- CN_KEYDOWN,
- WM_KEYDOWN:
- begin
- UpdateUIState(Control, TWMKey(Message).CharCode);
- FGroupBoxList.DispatchMessage(Control, Message);
- end;
- end;
- end;
- // MP END
- // In opposition to the other window procedures we should always apply the fix for TListView,
- // regardless of whether themes are enabled or not.
- if (Message.Msg = LVM_SETCOLUMN) or (Message.Msg = LVM_INSERTCOLUMN) then
- begin
- with PLVColumn(Message.LParam)^ do
- begin
- // Fix TListView report mode bug.
- if iImage = - 1 then
- Mask := Mask and not LVCF_IMAGE;
- end;
- end;
- // This special notification message is not handled in the VCL and creates an access violation when
- // passed to the default window procedure. Ignoring it does not seem to have any negative impact.
- if not ((Message.Msg = WM_NOTIFY) and (TWMNotify(Message).NMHdr.code = HDN_GETDISPINFOW)) then
- FListViewList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TThemeManager.MainWindowHook(var Message: TMessage): Boolean;
- // Listens to messages sent to the application to know when a theme change occured.
- var
- Form: TCustomForm;
- begin
- Result := False;
- // workaround for so far unknown bug on vista (bug 140)
- if Message.Msg = WM_GETICON then
- begin
- Exit;
- end;
- // If the main manager was destroyed then it posted this message to the application so all still existing
- // theme managers know a new election is due. Well, it is not purely democratic. The earlier a manager was created
- // the higher is the probability to get this message first and become the new main manager.
- if Message.Msg = WM_MAINMANAGERRELEASED then
- begin
- Lock.Enter;
- try
- // Check if the main manager role is still vacant.
- if MainManager = nil then
- begin
- MainManager := Self;
- FSubclassingDisabled := False;
- CollectForms;
- end;
- finally
- Lock.Leave;
- end;
- end;
- // Check first if there are still forms to subclass.
- while FPendingFormsList.Count > 0 do
- begin
- Form := TCustomForm(FPendingFormsList[0]);
- FPendingFormsList.Delete(0);
- FFormList.Add(Form);
- // Since we don't know how many controls on this form already have been created we better collect everything
- // which is already there. The window proc lists will take care not to add a control twice.
- if MainManager = Self then
- CollectControls(Form);
- if [toResetMouseCapture, toSetTransparency] * FOptions <> [] then
- FixControls(Form);
- // Sometimes not all controls are visually updated. Force it to be correct.
- RedrawWindow(Form.Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN or RDW_VALIDATE);
- end;
- while FPendingRecreationList.Count > 0 do
- begin
- TWinControl(FPendingRecreationList[0]).HandleNeeded;
- CollectControls(TWinControl(FPendingRecreationList[0]));
- FPendingRecreationList.Delete(0);
- end;
- if Message.Msg = WM_THEMECHANGED then
- begin
- UpdateThemes;
- DoOnThemeChange;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- type
- // Used to access protected properties.
- TPanelCast = class(TCustomPanel);
- procedure TThemeManager.PanelWindowProc(Control: TControl; var Message: TMessage);
- var
- DrawRect: TRect;
- DC: HDC;
- OldFont: HFONT;
- PS: TPaintStruct;
- Details: TThemedElementDetails;
- //--------------- local function --------------------------------------------
- procedure NewPaint;
- // This is an adapted version of the actual TCustomPanel.Paint procedure
- const
- Alignments: array[TAlignment] of Longint = (DT_LEFT, DT_RIGHT, DT_CENTER);
- var
- Rect: TRect;
- TopColor, BottomColor: TColor;
- FontHeight: Integer;
- Flags: Longint;
- //------------- local functions -------------------------------------------
- procedure AdjustColors(Bevel: TPanelBevel);
- begin
- TopColor := clBtnHighlight;
- if Bevel = bvLowered then
- TopColor := clBtnShadow;
- BottomColor := clBtnShadow;
- if Bevel = bvLowered then
- BottomColor := clBtnHighlight;
- end;
- //------------- end local functions ---------------------------------------
- begin
- with TPanelCast(Control) do
- begin
- Canvas.Handle := DC;
- try
- Canvas.Font := Font;
- Rect := GetClientRect;
- if BevelOuter <> bvNone then
- begin
- AdjustColors(BevelOuter);
- Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
- end;
- InflateRect(Rect, -BorderWidth, -BorderWidth);
- if BevelInner <> bvNone then
- begin
- AdjustColors(BevelInner);
- Frame3D(Canvas, Rect, TopColor, BottomColor, BevelWidth);
- end;
- if ParentColor or ((Control.Parent <> nil) and (Control.Parent.Brush.Color = Color)) then
- begin
- if TWinControl(Control.Parent).DoubleBuffered then
- PerformEraseBackground(Control, PS.hdc)
- else
- begin
- Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
- ThemeServices.DrawParentBackground(Handle, DC, @Details, False, @Rect);
- end
- end
- else
- begin
- Canvas.Brush.Style := bsSolid;
- Canvas.Brush.Color := Color;
- FillRect(PS.hdc, Rect, Canvas.Brush.Handle);
- end;
- FontHeight := Canvas.TextHeight('W');
- with Rect do
- begin
- Top := ((Bottom + Top) - FontHeight) div 2;
- Bottom := Top + FontHeight;
- end;
- Flags := DT_EXPANDTABS or DT_VCENTER or Alignments[Alignment];
- Flags := DrawTextBiDiModeFlags(Flags);
- OldFont := SelectObject(DC, Font.Handle);
- SetBKMode(DC, TRANSPARENT);
- SetTextColor(DC, ColorToRGB(Font.Color));
- DrawText(DC, PChar(Caption), -1, Rect, Flags);
- SelectObject(DC, OldFont);
- finally
- Canvas.Handle := 0;
- end;
- end;
- end;
- //--------------- end local function ----------------------------------------
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled and TPanelCast(Control).ParentColor or
- (Assigned(Control.Parent) and (Control.Parent.Brush.Color = TPanelCast(Control).Color)) then
- begin
- case Message.Msg of
- WM_ERASEBKGND:
- with TPanelCast(Control) do
- begin
- DC := TWMEraseBkGnd(Message).DC;
- // Get the parent to draw its background into the control's background.
- if TWinControl(Control.Parent).DoubleBuffered then
- PerformEraseBackground(Control, DC)
- else
- begin
- Details := ThemeServices.GetElementDetails(tbGroupBoxNormal);
- ThemeServices.DrawParentBackground(Handle, DC, @Details, False);
- end;
- Message.Result := 1;
- end;
- WM_NCPAINT:
- with TPanelCast(Control) do
- begin
- FPanelList.DispatchMessage(Control, Message);
- if BorderStyle <> bsNone then
- begin
- DrawRect := BoundsRect;
- OffsetRect(DrawRect, -Left, -Top);
- DC := GetWindowDC(Handle);
- try
- Details := ThemeServices.GetElementDetails(trBandNormal);
- ThemeServices.DrawEdge(DC, Details, DrawRect, EDGE_SUNKEN, BF_RECT);
- finally
- ReleaseDC(Handle, DC);
- end;
- end;
- Message.Result := 0;
- end;
- WM_PAINT:
- with TPanelCast(Control) do
- begin
- DC := BeginPaint(Handle, PS);
- NewPaint;
- PaintControls(DC, nil);
- EndPaint(Handle, PS);
- Message.Result := 0;
- end;
- WM_PRINTCLIENT:
- with TPanelCast(Control) do
- begin
- DC := TWMPrintClient(Message).DC;
- NewPaint;
- PaintControls(DC, nil);
- Message.Result := 0;
- end;
- else
- FPanelList.DispatchMessage(Control, Message);
- end;
- end
- else
- FPanelList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.SetThemeOptions(const Value: TThemeOptions);
- var
- Flags: Cardinal;
- I: Integer;
- begin
- // If this instance is the main manager then apply the options directly. Otherwise let the current main manager do it.
- Lock.Enter;
- try
- if Assigned(MainManager) and (MainManager <> Self) then
- MainManager.Options := Value
- else
- begin
- if FOptions <> Value then
- begin
- FOptions := Value;
- if ThemeServices.ThemesAvailable and not FSubclassingDisabled and not (csDesigning in ComponentState) then
- begin
- Flags := 0;
- if toAllowNonClientArea in FOptions then
- Flags := Flags or STAP_ALLOW_NONCLIENT;
- if toAllowControls in FOptions then
- Flags := Flags or STAP_ALLOW_CONTROLS;
- if toAllowWebContent in FOptions then
- Flags := Flags or STAP_ALLOW_WEBCONTENT;
- SetThemeAppProperties(Flags);
- if ComponentState * [csLoading, csReading] = [] then
- begin
- UpdateThemes;
- // Tell the application that we changed the options.
- BroadcastThemeChange;
- // Notify all theme manager instances about the change.
- SendAppMessage(WM_THEMECHANGED, 0, 0);
- for I := 0 to Screen.FormCount - 1 do
- RedrawWindow(Screen.Forms[I].Handle, nil, 0, RDW_ERASE or RDW_FRAME or RDW_INVALIDATE or RDW_INTERNALPAINT or
- RDW_ERASENOW or RDW_UPDATENOW or RDW_ALLCHILDREN);
- end;
- end;
- end;
- end;
- finally
- Lock.Leave;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- type
- TSpeedButtonCast = class(TSpeedButton);
- procedure TThemeManager.SpeedButtonWindowProc(Control: TControl; var Message: TMessage);
- var
- Button: TThemedButton;
- P: TPoint;
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- WM_PAINT:
- with TWMPaint(Message) do
- begin
- // We cannot use the theme parent paint for the background of general speed buttons (because they are not
- // window controls).
- PerformEraseBackground(Control, DC);
- // Speed buttons are not window controls and are painted by a call of their parent with a given DC.
- if not Control.Enabled then
- Button := tbPushButtonDisabled
- else
- if TSpeedButtonCast(Control).FState in [bsDown, bsExclusive] then
- Button := tbPushButtonPressed
- else
- with TSpeedButtonCast(Control) do
- begin
- // Check the hot style here. If the button has a flat style then this check is easy. Otherwise
- // some more work is necessary.
- Button := tbPushButtonNormal;
- if Flat then
- begin
- if MouseInControl then
- Button := tbPushButtonHot;
- end
- else
- begin
- GetCursorPos(P);
- if FindDragTarget(P, True) = Control then
- Button := tbPushButtonHot;
- end;
- end;
- DrawButton(Control, Button, DC, Control.ClientRect, False);
- Message.Result := 0;
- end;
- CM_MOUSEENTER,
- CM_MOUSELEAVE:
- begin
- // Non-flat speed buttons don't have a hot-tracking style. We have to emulate this.
- if not TSpeedButtonCast(Control).Flat and Control.Enabled then
- Control.Invalidate;
- FSpeedButtonList.DispatchMessage(Control, Message);
- end;
- else
- FSpeedButtonList.DispatchMessage(Control, Message);
- end;
- end
- else
- FSpeedButtonList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.SplitterWindowProc(Control: TControl; var Message: TMessage);
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- WM_PAINT:
- with TWMPaint(Message) do
- begin
- PerformEraseBackground(Control, DC);
- Message.Result := 0;
- end;
- else
- FSplitterList.DispatchMessage(Control, Message);
- end;
- end
- else
- FSplitterList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- type
- TCustomStatusBarCast = class(TCustomStatusBar);
- procedure TThemeManager.StatusBarWindowProc(Control: TControl; var Message: TMessage);
- var
- Details: TThemedElementDetails;
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- WM_NCCALCSIZE:
- with TWMNCCalcSize(Message) do
- begin
- FStatusBarList.DispatchMessage(Control, Message);
- // We cannot simply override the window class' CS_HREDRAW and CS_VREDRAW styles but the following
- // does the job very well too.
- // Note: this may produce trouble with embedded controls (e.g. progress bars).
- if CalcValidRects then
- Result := Result or WVR_REDRAW;
- end;
- WM_ERASEBKGND:
- with TWMEraseBkGnd(Message) do
- begin
- Details := ThemeServices.GetElementDetails(tsStatusRoot);
- ThemeServices.DrawElement(DC, Details, Control.ClientRect);
- Message.Result := 1;
- end;
- else
- FStatusBarList.DispatchMessage(Control, Message);
- end;
- end
- else
- FStatusBarList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.TabSheetWindowProc(Control: TControl; var Message: TMessage);
- var
- DrawRect: TRect;
- Details: TThemedElementDetails;
- DC: HDC;
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- // Paint the border (and erase the background)
- WM_NCPAINT:
- with TTabSheet(Control) do
- begin
- DC := GetWindowDC(Handle);
- try
- // Exclude the client area from painting. We only want to erase the non-client area.
- DrawRect := ClientRect;
- OffsetRect(DrawRect, BorderWidth, BorderWidth);
- with DrawRect do
- ExcludeClipRect(DC, Left, Top, Right, Bottom);
- // The parent paints relative to the control's client area. We have to compensate for this by
- // shifting the dc's window origin.
- SetWindowOrgEx(DC, -BorderWidth, -BorderWidth, nil);
- Details := ThemeServices.GetElementDetails(ttBody);
- ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, @Details, False);
- finally
- ReleaseDC(Handle, DC);
- end;
- Message.Result := 0;
- end;
- WM_PRINTCLIENT,
- WM_ERASEBKGND:
- begin
- if Message.Msg = WM_PRINTCLIENT then
- DC := TWMPrintClient(Message).DC
- else
- DC := TWMEraseBkGnd(Message).DC;
- // Using the parent's background here does not always work. Particularly, it does not work in cases
- // where the parent (pane) background does not include the body background. One way to solve this problem
- // would be to paint the body background here. However this produces a lot of problems all caused by
- // the fact that these backgrounds might be tiled or might otherwise have special drawing style.
- // Due to the near-to-non-existing documentation on all the themes APIs I use the lesser evil by default and
- // paint the parent background, which works in most cases very well.
- // However you may want to enable the other way, if needed.
- if toAlternateTabSheetDraw in FOptions then
- begin
- Details := ThemeServices.GetElementDetails(ttBody);
- DrawRect := Control.ClientRect;
- ThemeServices.DrawElement(DC, Details, DrawRect);
- end
- else
- ThemeServices.DrawParentBackground(TWinControl(Control).Handle, DC, nil, False);
- Message.Result := 1;
- end;
- else
- FTabSheetList.DispatchMessage(Control, Message);
- end;
- end
- else
- FTabSheetList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.TrackBarWindowProc(Control: TControl; var Message: TMessage);
- var
- Info: PNMCustomDraw;
- R: TRect;
- Rgn: HRGN;
- Details: TThemedElementDetails;
- Offset: Integer;
- FocusBorderWidth,
- FocusBorderHeight: Integer;
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- CN_NOTIFY:
- with TWMNotify(Message) do
- if NMHdr.code = NM_CUSTOMDRAW then
- begin
- Info := Pointer(NMHdr);
- case Info.dwDrawStage of
- CDDS_PREPAINT:
- Result := CDRF_NOTIFYITEMDRAW;
- CDDS_ITEMPREPAINT:
- with Control as TTrackBar do
- begin
- // Take action based on which item is about to be painted.
- case Info.dwItemSpec of
- TBCD_TICS: // Before re-painting ticks redo whole background.
- begin
- R := ClientRect;
- // Leave room for the focus rectangle if there is one.
- if Focused and ((Perform(WM_QUERYUISTATE, 0, 0) and UISF_HIDEFOCUS) = 0) then
- begin
- SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH, 0, @FocusBorderWidth, 0);
- SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, @FocusBorderHeight, 0);
- InflateRect(R, -FocusBorderWidth, -FocusBorderHeight);
- end;
- ThemeServices.DrawParentBackground(Handle, Info.hDC, nil, False, @R);
- end;
- TBCD_CHANNEL: // Before re-painting channel just redo strip of background overlapped.
- begin
- // Retrieve the bounding box for the thumb.
- SendMessage(Handle, TBM_GETTHUMBRECT, 0, Integer(@R));
- // Extend this rectangle to the top/bottom or left/right border, respectively.
- Offset := 0;
- if Orientation = trHorizontal then
- begin
- // Leave room for the focus rectangle if there is one.
- if Focused then
- begin
- SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH, 0, @FocusBorderWidth, 0);
- Inc(Offset, FocusBorderWidth);
- end;
- R.Left := ClientRect.Left + Offset;
- R.Right := ClientRect.Right - Offset;
- end
- else
- begin
- // Leave room for the focus rectangle if there is one.
- if Focused then
- begin
- SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, @FocusBorderHeight, 0);
- Inc(Offset, FocusBorderWidth);
- end;
- R.Top := ClientRect.Top + Offset;
- R.Bottom := ClientRect.Bottom - Offset;
- end;
- with R do
- Rgn := CreateRectRgn(Left, Top, Right, Bottom);
- SelectClipRgn(Info.hDC, Rgn);
- Details := ThemeServices.GetElementDetails(ttbThumbTics);
- ThemeServices.DrawParentBackground(Handle, Info.hDC, @Details, False);
- DeleteObject(Rgn);
- SelectClipRgn(Info.hDC, 0);
- end;
- end;
- Result := CDRF_DODEFAULT;
- end;
- else
- Result := CDRF_DODEFAULT;
- end;
- end;
- else
- FTrackBarList.DispatchMessage(Control, Message);
- end;
- end
- else
- FTrackBarList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.WinControlWindowProc(Control: TControl; var Message: TMessage);
- var
- DC: HDC;
- SavedDC: Integer;
- begin
- if not DoControlMessage(Control, Message) then
- begin
- if ThemeServices.ThemesEnabled then
- begin
- case Message.Msg of
- CN_KEYDOWN,
- WM_SYSKEYDOWN,
- WM_KEYDOWN:
- begin
- UpdateUIState(Control, TWMKey(Message).CharCode);
- FWinControlList.DispatchMessage(Control, Message);
- end;
- WM_ERASEBKGND:
- begin
- if Control is TScrollingWinControl then
- with Control as TWinControl do
- begin
- DC := TWMEraseBkGnd(Message).DC;
- if DoubleBuffered then
- PerformEraseBackground(Control, DC)
- else
- ThemeServices.DrawParentBackground(Handle, DC, nil, False);
- Message.Result := 1;
- end
- else
- FWinControlList.DispatchMessage(Control, Message);
- end;
- WM_NCPAINT:
- begin
- FWinControlList.DispatchMessage(Control, Message);
- ThemeServices.PaintBorder(Control as TWinControl, Control is TCustomGrid);
- end;
- CN_CTLCOLORSTATIC:
- if Control is TCustomStaticText then
- with TWMCtlColorStatic(Message), { MP } TWinControlCast(Control as TWinControl) do
- begin
- SetBkMode(ChildDC, Windows.TRANSPARENT);
- // MP BEGIN
- SetTextColor(ChildDC, ColorToRGB(Font.Color));
- SetBkColor(ChildDC, ColorToRGB(Brush.Color));
- // MP END
- SavedDC := SaveDC(ChildDC);
- ThemeServices.DrawParentBackground(Handle, ChildDC, nil, False);
- FWinControlList.DispatchMessage(Control, Message);
- RestoreDC(ChildDC, SavedDC);
- // Return an empty brush to prevent Windows from overpainting what we just have created.
- Result := GetStockObject(NULL_BRUSH);
- end
- else
- FWinControlList.DispatchMessage(Control, Message);
- else
- FWinControlList.DispatchMessage(Control, Message);
- end;
- end
- else
- FWinControlList.DispatchMessage(Control, Message);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreAnimateWindowProc(var Message: TMessage);
- // This and the other proxy window procs do an important step to make the entire subclassing work here.
- // Because we have only one window procedure for each class of subclassed controls (many to 1 relation), it is necessary
- // to know to which control the message was sent originally (read: whose WindowProc property had been called). This is
- // important because we have to forward the message to the original window procedure once we are finished with our own
- // processing and sometimes properties of the control are needed too.
- // When this method is called the hidden self parameter is not the actual theme manager instance but the
- // control reference to which the message was sent originally. This is the result from the explicit Data member
- // assignment done in TWindowProcList.Add. This is very helpful but has the side effect that we don't have the theme
- // manager instance anymore (since the self param is the control). Thus we need another reference, which we have
- // in the form of the main manager. Since only the main manager will subclass controls it is guaranteed that
- // there is a valid reference when we arrive here (and in the other proxy methods).
- begin
- Assert(Assigned(MainManager));
- MainManager.AnimateWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreButtonControlWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.ButtonControlWindowProc(TControl(Self), Message, { MP }MainManager.FButtonControlList);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- // MP BEGIN
- procedure TThemeManager.PreCheckBoxWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.ButtonControlWindowProc(TControl(Self), Message, MainManager.FCheckBoxList);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreButtonWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.ButtonControlWindowProc(TControl(Self), Message, MainManager.FButtonList);
- end;
- // MP END
- //----------------------------------------------------------------------------------------------------------------------
- {$ifdef CheckListSupport}
- procedure TThemeManager.PreCheckListBoxWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.CheckListBoxWindowProc(TControl(Self), Message);
- end;
- {$endif CheckListSupport}
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreFormWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.FormWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- {$ifdef COMPILER_5_UP}
- procedure TThemeManager.PreFrameWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.FrameWindowProc(TControl(Self), Message);
- end;
- {$endif COMPILER_5_UP}
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreGroupBoxWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.GroupBoxWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreListviewWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.ListviewWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PrePanelWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.PanelWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreSpeedButtonWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.SpeedButtonWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreSplitterWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.SplitterWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreStatusBarWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.StatusBarWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreTabSheetWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.TabSheetWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreTrackBarWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.TrackBarWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PreWinControlWindowProc(var Message: TMessage);
- // Read more about this code in PreAnimateWindowProc.
- begin
- Assert(Assigned(MainManager));
- MainManager.WinControlWindowProc(TControl(Self), Message);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.AddRecreationCandidate(Control: TControl);
- begin
- if FPendingRecreationList.IndexOf(Control) = -1 then
- FPendingRecreationList.Add(Control);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.BroadcastThemeChange;
- //--------------- local function --------------------------------------------
- procedure BroadcastChildren(Control: TWinControl);
- var
- I: Integer;
- ChildControl: TWinControl;
- begin
- for I := 0 to Control.ControlCount - 1 do
- if Control.Controls[I] is TWinControl then
- begin
- ChildControl := TWinControl(Control.Controls[I]);
- if ChildControl.HandleAllocated then
- ChildControl.Perform(WM_THEMECHANGED, 0, 0);
- // We must force recreation of some window handles (to reapply all the control settings).
- if (ChildControl is TCustomListView) or (ChildControl is TCoolBar) then
- TWinControlCast(ChildControl).RecreateWnd
- else
- BroadcastChildren(ChildControl);
- end;
- end;
- //--------------- local function --------------------------------------------
- var
- I: Integer;
- Form: TCustomForm;
- begin
- for I := 0 to Screen.FormCount - 1 do
- begin
- Form := Screen.Forms[I];
- Form.Perform(WM_THEMECHANGED, 0, 0);
- BroadcastChildren(Form);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- class function TThemeManager.CurrentThemeManager: TThemeManager;
- begin
- Result := MainManager;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TThemeManager.DoAllowSubclassing(Control: TControl): Boolean;
- begin
- Result := True;
- if Assigned(FOnAllowSubclassing) then
- FOnAllowSubclassing(Self,Control,Result);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TThemeManager.DoControlMessage(Control: TControl; var Message: TMessage): Boolean;
- var
- I: Integer;
- Event: PControlMessageEvent;
- begin
- Result := False;
- if Assigned(FOnControlMessage) then
- FOnControlMessage(Self, Control, Message, Result);
- if not Result then
- begin
- I := 0;
- while I < FListeners.Count do
- begin
- Event := FListeners[I];
- try
- Event^(Self, Control, Message, Result);
- if Result then
- Break;
- Inc(I);
- except
- // Raised an exception, so delete the registration
- UnregisterListener(Event^);
- end;
- end;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.DoOnThemeChange;
- begin
- if Assigned(FOnThemeChange) then
- FOnThemeChange(Self);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.DrawBitBtn(Control: TBitBtn; var DrawItemStruct: TDrawItemStruct);
- var
- Button: TThemedButton;
- R: TRect;
- Wnd: HWND;
- P: TPoint;
- begin
- with DrawItemStruct do
- begin
- // For owner drawn buttons we will never get the ODS_HIGHLIGHT flag. This makes it necessary to
- // check ourselves if the button is "hot".
- GetCursorPos(P);
- Wnd := WindowFromPoint(P);
- if Wnd = TWinControl(Control).Handle then
- itemState := itemState or ODS_HOTLIGHT;
- R := rcItem;
- if not Control.Enabled then
- Button := tbPushButtonDisabled
- else
- if (itemState and ODS_SELECTED) <> 0 then
- Button := tbPushButtonPressed
- else
- if (itemState and ODS_HOTLIGHT) <> 0 then
- Button := tbPushButtonHot
- else
- // It seems ODS_DEFAULT is never set, so we have to check the control's properties.
- if Control.Default or ((itemState and ODS_FOCUS) <> 0) then
- Button := tbPushButtonDefaulted
- else
- Button := tbPushButtonNormal;
- DrawButton(Control, Button, hDC, R, itemState and ODS_FOCUS <> 0);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.DrawButton(Control: TControl; Button: TThemedButton; DC: HDC; R: TRect; Focused: Boolean);
- // Common paint routine for TBitBtn and TSpeedButton.
- var
- TextBounds: TRect;
- LastFont: HFONT;
- Glyph: TBitmap;
- GlyphPos: TPoint;
- GlyphWidth: Integer;
- GlyphSourceX: Integer;
- GlyphMask: TBitmap;
- Offset: TPoint;
- ToolButton: TThemedToolBar;
- Details: TThemedElementDetails;
- begin
- GlyphSourceX := 0;
- GlyphWidth := 0;
- ToolButton := ttbToolbarDontCare;
- if Control is TBitBtn then
- begin
- Glyph := TBitBtn(Control).Glyph;
- // Determine which image to use (if there is more than one in the glyph).
- with TBitBtn(Control), Glyph do
- begin
- if not Empty then
- begin
- GlyphWidth := Width div NumGlyphs;
- if not Enabled and (NumGlyphs > 1) then
- GlyphSourceX := GlyphWidth
- else
- if (Button = tbPushButtonPressed) and (NumGlyphs > 2) then
- GlyphSourceX := 2 * GlyphWidth;
- end;
- end;
- end
- else
- begin
- Glyph := TSpeedButton(Control).Glyph;
- with TSpeedButtonCast(Control) do
- begin
- // Determine which image to use (if there is more than one in the glyph).
- with Glyph do
- if not Empty then
- begin
- GlyphWidth := Width div NumGlyphs;
- if not Enabled and (NumGlyphs > 1) then
- GlyphSourceX := GlyphWidth
- else
- case FState of
- bsDown:
- if NumGlyphs > 2 then
- GlyphSourceX := 2 * GlyphWidth;
- bsExclusive:
- if NumGlyphs > 3 then
- GlyphSourceX := 3 * GlyphWidth;
- end;
- end;
- // If the speed button is flat then we use toolbutton images for drawing.
- if Flat then
- begin
- case Button of
- tbPushButtonDisabled:
- Toolbutton := ttbButtonDisabled;
- tbPushButtonPressed:
- Toolbutton := ttbButtonPressed;
- tbPushButtonHot:
- Toolbutton := ttbButtonHot;
- tbPushButtonNormal:
- Toolbutton := ttbButtonNormal;
- end;
- end;
- end;
- end;
- if ToolButton = ttbToolbarDontCare then
- begin
- Details := ThemeServices.GetElementDetails(Button);
- ThemeServices.DrawElement(DC, Details, R);
- R := ThemeServices.ContentRect(DC, Details, R);
- end
- else
- begin
- Details := ThemeServices.GetElementDetails(ToolButton);
- ThemeServices.DrawElement(DC, Details, R);
- R := ThemeServices.ContentRect(DC, Details, R);
- end;
- // The XP style does no longer indicate pressed buttons by moving the caption one pixel down and right.
- Offset := Point(0, 0);
- with TControlCast(Control) do
- begin
- LastFont := SelectObject(DC, Font.Handle);
- CalcButtonLayout(Control, DC, R, Offset, GlyphPos, TextBounds, DrawTextBidiModeFlags(0));
- // Note: Currently we cannot do text output via the themes services because the second flags parameter (which is
- // used for graying out strings) is ignored (bug in XP themes implementation?).
- // Hence we have to do it the "usual" way.
- if Button = tbPushButtonDisabled then
- SetTextColor(DC, ColorToRGB(clGrayText))
- else
- SetTextColor(DC, ColorToRGB(Font.Color));
- SetBkMode(DC, TRANSPARENT);
- DrawText(DC, PChar(Caption), Length(Caption), TextBounds, DT_CENTER or DT_VCENTER);
- with Glyph do
- if not Empty then
- begin
- GlyphMask := TBitmap.Create;
- GlyphMask.Assign(Glyph);
- GlyphMask.Mask(Glyph.TransparentColor);
- TransparentStretchBlt(DC, GlyphPos.X, GlyphPos.Y, GlyphWidth, Height, Canvas.Handle, GlyphSourceX, 0,
- GlyphWidth, Height, GlyphMask.Canvas.Handle, GlyphSourceX, 0);
- GlyphMask.Free;
- end;
- SelectObject(DC, LastFont);
- end;
- if Focused then
- begin
- SetTextColor(DC, 0);
- DrawFocusRect(DC, R);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TThemeManager.FindListener(AControlMessage: TControlMessageEvent; var Index: Integer): Boolean;
- var
- I: Integer;
- begin
- Result := False;
- for I := 0 to FListeners.Count - 1 do
- if @PControlMessageEvent(FListeners[I])^ = @AControlMessage then
- begin
- Result := True;
- Index := I;
- Break;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- type
- // Cast to access the Transparent property in TCustomLabel which is protected there.
- TLabelCast = class(TCustomLabel);
- procedure TThemeManager.FixControls(Form: TCustomForm);
- // Iterates through all existing controls in all forms which are registered with Screen and checks for TToolBar and
- // TCustomLabel. Both controls will get their Transparent property set to True.
- var
- MakeTransparent: Boolean;
- RemoveMouseCapture: Boolean;
- //--------------- local function --------------------------------------------
- procedure IterateControls(Parent: TWinControl);
- var
- I, J: Integer;
- ToolBar: TToolBar;
- Control: TControl;
- begin
- for I := 0 to Parent.ControlCount - 1 do
- begin
- Control := Parent.Controls[I];
- // MP
- if not DoAllowSubClassing(Control) then Continue;
- // Allow all window controls to use themed background if they are placed on a tab sheet. This works only for controls
- // whose background is drawn by Windows and which can be transparent. There aren't many which qualify, though.
- if (Control is TWinControl) and ThemeServices.ThemesEnabled then
- begin
- // MP BEGIN
- try
- TWinControl(Control).HandleNeeded;
- except
- // if allocating handle fails, just do not fix the control and continue
- // strangelly happens irregularly for buttons
- Continue;
- end;
- // MP END
- EnableThemeDialogTexture(TWinControl(Control).Handle, ETDT_ENABLETAB);
- end;
- if Control is TToolBar then
- begin
- ToolBar := TToolBar(Control);
- if MakeTransparent then
- ToolBar.Transparent := True;
- if RemoveMouseCapture then
- begin
- for J := 0 to ToolBar.ButtonCount - 1 do
- if ToolBar.Buttons[J].Style <> tbsDropDown then
- ToolBar.Buttons[J].ControlStyle := ToolBar.Buttons[J].ControlStyle - [csCaptureMouse];
- end;
- end
- else
- if Control is TCustomLabel then
- begin
- if MakeTransparent then
- TLabelCast(Control).Transparent := True;
- end
- else
- if (Control is TWinControl) and (TWinControl(Control).ControlCount > 0) then
- IterateControls(Control as TWinControl);
- end;
- end;
- //--------------- end local function ----------------------------------------
- var
- I: Integer;
- begin
- MakeTransparent := toSetTransparency in FOptions;
- RemoveMouseCapture := toResetMouseCapture in FOptions;
- if Form = nil then
- begin
- for I := 0 to Screen.FormCount - 1 do
- begin
- Form := Screen.Forms[I];
- IterateControls(Form);
- end;
- end
- else
- IterateControls(Form);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.ForceAsMainManager;
- // Forces this instance to become the main manager. This is useful for descentants to provide additional functionality.
- begin
- if MainManager <> Self then
- begin
- Lock.Enter;
- try
- if Assigned(MainManager) then
- begin
- MainManager.FSubclassingDisabled := True;
- MainManager.ClearLists;
- end;
- MainManager := Self;
- FSubclassingDisabled := False;
- CollectForms;
- finally
- Lock.Release;
- end;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.HandleControlChange(Control: TControl; Inserting: Boolean);
- var
- List: TWindowProcList;
- Index: Integer;
- WinControl: TWinControl;
- begin
- List := nil;
- // Do subclassing work only on Windows XP or higher.
- if IsWindowsXP then
- begin
- if not ThemeServices.ThemesEnabled then
- begin
- // TCustomListview always must be subclassed.
- if Control is TCustomListView then
- begin
- if (toSubclassListView in FOptions) or not Inserting then
- begin
- List := FListViewList;
- // We have to force the listview to recreate its window handle (to reapply all the control settings).
- // However if it is already in our list then don't touch the window anymore.
- WinControl := Control as TWinControl;
- if Inserting and not List.Find(Control, Index) and WinControl.HandleAllocated then
- PostMessage(WinControl.Handle, CM_RECREATEWND, 0, 0);
- end;
- end;
- end
- else
- begin
- // MP BEGIN
- // Including checkboxes and buttons to button-control list makes it strangely fail
- // for some dialogs (irregularly). Introducing separate list for
- // them solves the problem
- if Control is TCheckBox then
- begin
- if (toSubclassButtons in FOptions) or not Inserting then
- List := FCheckBoxList;
- end
- else
- if Control is TButton then
- begin
- if (toSubclassButtons in FOptions) or not Inserting then
- List := FButtonList;
- end
- else
- // MP END
- if Control is TButtonControl then
- begin
- if (toSubclassButtons in FOptions) or not Inserting then
- List := FButtonControlList;
- end
- else
- if Control is TSpeedButton then
- begin
- if (toSubclassSpeedButtons in FOptions) or not Inserting then
- List := FSpeedButtonList;
- end
- else
- if Control is TCustomGroupBox then
- begin
- if (toSubclassGroupBox in FOptions) or not Inserting then
- List := FGroupBoxList;
- end
- else
- if Control is TTabSheet then
- begin
- if (toSubclassTabSheet in FOptions) or not Inserting then
- List := FTabSheetList;
- end
- else
- if Control is TCustomPanel then
- begin
- if (toSubclassPanel in FOptions) or not Inserting then
- List := FPanelList;
- end
- else
- {$ifdef COMPILER_5_UP}
- if Control is TCustomFrame then
- begin
- if (toSubclassFrame in FOptions) or not Inserting then
- List := FFrameList;
- end
- else
- {$endif COMPILER_5_UP}
- if Control is TCustomListView then
- begin
- if (toSubclassListView in FOptions) or not Inserting then
- begin
- List := FListViewList;
- // We have to force the listview to recreate its window handle (to reapply all the control settings).
- // However if it is already in our list then don't touch the window anymore.
- WinControl := Control as TWinControl;
- if Inserting and not List.Find(Control, Index) and WinControl.HandleAllocated then
- PostMessage(WinControl.Handle, CM_RECREATEWND, 0, 0);
- end;
- end
- else
- if Control is TTrackBar then
- begin
- if (toSubclassTrackBar in FOptions) or not Inserting then
- List := FTrackBarList;
- end
- else
- {$ifdef CheckListSupport}
- if Control is TCheckListBox then
- begin
- if (toSubclassCheckListBox in FOptions) or not Inserting then
- List := FCheckListBoxList;
- end
- else
- {$endif CheckListSupport}
- if Control is TCustomStatusBar then
- begin
- if (toSubclassStatusBar in FOptions) or not Inserting then
- List := FStatusBarList;
- end
- else
- if Control is TSplitter then
- begin
- if (toSubclassSplitter in FOptions) or not Inserting then
- List := FSplitterList;
- end
- else
- if Control is TAnimate then
- begin
- if (toSubclassAnimate in FOptions) or not Inserting then
- List := FAnimateList;
- end
- else
- if Control is TCustomForm then
- begin
- List := FFormList;
- if Inserting then
- FPendingFormsList.Remove(Control);
- end
- else
- if Control is TWinControl then
- begin
- if (toSubclassWinControl in FOptions) or not Inserting then
- List := FWinControlList;
- end;
- end;
- if Assigned(List) then
- begin
- if Inserting and (DoAllowSubClassing(Control) and (Control.Perform(CM_DENYSUBCLASSING, 0, 0) = 0)) then
- begin
- List.Add(Control);
- // We need a notification for this control about its destruction.
- Control.FreeNotification(Self);
- // Automatically collect the child controls when a TWinControl is added.
- if (Control is TWinControl) and (TWinControl(Control).ControlCount > 0) then
- CollectControls(TWinControl(Control));
- end
- else
- List.Remove(Control);
- end;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TThemeManager.IsRecreationCandidate(Control: TControl): Boolean;
- // Tells the caller whether the given controls is being recreated.
- begin
- Result := FPendingRecreationList.IndexOf(Control) > -1;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.Loaded;
- begin
- // Collect all controls which already exist. Those controls, which are later added/removed are handled by the
- // subclassing of their old/new parent.
- if (MainManager = Self) and not (csDesigning in ComponentState) then
- CollectForms;
- inherited;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TThemeManager.NeedsBorderPaint(Control: TControl): Boolean;
- // Some controls need their frame (non-client area with 3D border) explicitely painted in a themed fashion.
- // This method determines, which controls need this.
- begin
- Result := (Control is TScrollingWinControl) or (Control is TCustomGrid) or (Control is TCustomRichEdit);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- if not (csDesigning in ComponentState) then
- begin
- case Operation of
- opInsert:
- // At this place we cannot subclass the control because it did not yet get its initial window procedure.
- // So we add it to an intermediate list and subclass it at a later moment.
- if (AComponent is TCustomForm) and (FPendingFormsList.IndexOf(AComponent) < 0) then
- begin
- if (MainManager = Self) then
- begin
- FPendingFormsList.Add(AComponent);
- // Under some circumstances (e.g. when a MDI child is created) there is no application message, which we
- // need to subclass the form. By posting a dummy message this problem is circumvented.
- PostMessage(Application.Handle, WM_NULL, 0, 0);
- end
- else
- MainManager.Notification(AComponent, Operation);
- end;
- opRemove:
- if (MainManager = Self) and (AComponent is TControl) then
- begin
- if AComponent is TCustomForm then
- // A form is being destroyed. Remove it from the pending forms list if it is still there.
- FPendingFormsList.Remove(AComponent);
- HandleControlChange(AComponent as TControl, False);
- end;
- end;
- end;
- inherited;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.RemoveChildSubclassing(Control: TWinControl);
- // Child controls may be released without further notice if their parent control is destroyed.
- // One can use the WM_DESTORY message to get notified but if the control haven't even created their window handle
- // then also this possibility does not exist anymore.
- // Hence when we get notice of a control which is being destroyed then we implicitely remove all subclassed child
- // controls from our lists too.
- var
- I: Integer;
- begin
- for I := 0 to Control.ControlCount - 1 do
- if Control.Controls[I] is TWinControl then
- begin
- RemoveChildSubclassing(TWinControl(Control.Controls[I]));
- HandleControlChange(Control.Controls[I], False);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.RemoveRecreationCandidate(Control: TControl);
- begin
- FPendingRecreationList.Remove(Control);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.UpdateThemes;
- var
- Flags: Cardinal;
- begin
- ThemeServices.UpdateThemes;
- if ThemeServices.ThemesAvailable and not (csDesigning in ComponentState) then
- begin
- Flags := GetThemeAppProperties;
- if (Flags and STAP_ALLOW_NONCLIENT) <> 0 then
- Include(FOptions, toAllowNonClientArea)
- else
- Exclude(FOptions, toAllowNonClientArea);
- if (Flags and STAP_ALLOW_CONTROLS) <> 0 then
- Include(FOptions, toAllowControls)
- else
- Exclude(FOptions, toAllowControls);
- if (Flags and STAP_ALLOW_WEBCONTENT) <> 0 then
- Include(FOptions, toAllowWebContent)
- else
- Exclude(FOptions, toAllowWebContent);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.UpdateUIState(Control: TControl; CharCode: Word);
- // Beginning with Windows 2000 the UI in an application may hide focus rectangles and accelerator key indication.
- // We have to take care to show them if the user starts navigating using the keyboard.
- var
- Form: TCustomForm;
- //--------------- Local functions --------------------------------------------
- procedure InvalidateStaticText(Control: TWinControl);
- var
- I: Integer;
- begin
- if Control is TCustomStaticText then
- Control.Invalidate;
- for I := 0 to Control.ControlCount - 1 do
- if Control.Controls[I] is TWinControl then
- InvalidateStaticText(Control.Controls[I] as TWinControl);
- end;
- //--------------- End local functions ----------------------------------------
- begin
- Form := GetParentForm(Control);
- if Assigned(Form) then
- case CharCode of
- VK_LEFT..VK_DOWN,
- VK_TAB:
- Form.Perform(WM_CHANGEUISTATE, MakeLong(UIS_CLEAR, UISF_HIDEFOCUS), 0);
- VK_MENU:
- begin
- Form.Perform(WM_CHANGEUISTATE, MakeLong(UIS_CLEAR, UISF_HIDEACCEL), 0);
- // For no appearent reason does TCustomStaticText not correctly redraw when the accelerator underline
- // is enabled. So we have manually invalide all instances of TCustomStaticText.
- InvalidateStaticText(Form);
- end;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.ClearLists;
- begin
- // Listview controls must always be subclassed, otherwise they produce trouble on XP with
- // classic themes.
- FListViewList.Clear;
- if ThemeServices.ThemesEnabled then
- begin
- {$ifdef CheckListSupport}
- FCheckListBoxList.Clear;
- {$endif CheckListSupport}
- FStatusBarList.Clear;
- FAnimateList.Clear;
- FTrackBarList.Clear;
- FSpeedButtonList.Clear;
- // MP BEGIN
- FCheckBoxList.Clear;
- FButtonList.Clear;
- // MP END
- FButtonControlList.Clear;
- FTabSheetList.Clear;
- FWinControlList.Clear;
- FGroupBoxList.Clear;
- FFormList.Clear;
- FPanelList.Clear;
- {$ifdef COMPILER_5_UP}
- FFrameList.Clear;
- {$endif COMPILER_5_UP}
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.CollectForms(Form: TCustomForm = nil);
- // (Re)initiates collecting all controls which need to be subclassed to fixed one or more problems.
- var
- I: Integer;
- begin
- if not FSubclassingDisabled and not (csDesigning in ComponentState) then
- begin
- if Form = nil then
- begin
- ClearLists;
- for I := 0 to Screen.FormCount - 1 do
- begin
- FFormList.Add(Screen.Forms[I]);
- CollectControls(Screen.Forms[I]);
- end;
- end
- else
- begin
- FFormList.Add(Form);
- CollectControls(Form);
- end;
- if ([toResetMouseCapture, toSetTransparency] * FOptions) <> [] then
- FixControls(Form);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.CollectControls(Parent: TWinControl);
- var
- I: Integer;
- begin
- Assert(Assigned(Parent), 'Parent of controls to be collected must be valid.');
- if not FSubclassingDisabled and not (csDesigning in ComponentState) then
- begin
- for I := 0 to Parent.ControlCount - 1 do
- begin
- HandleControlChange(Parent.Controls[I], True);
- if (Parent.Controls[I] is TWinControl) and (TWinControl(Parent.Controls[I]).ControlCount > 0) then
- CollectControls(Parent.Controls[I] as TWinControl);
- end;
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.PerformEraseBackground(Control: TControl; DC: HDC);
- // Repainting the background of a control using theme services relies on the ability of the parent to handle
- // WM_PRINT messages. Usually the default behavior of a window is enough to make this possible. However
- // double buffered and non-windowed controls are quite different and need so special handling.
- // This method uses the WM_ERASEBKGND message to achieve the same effect.
- var
- LastOrigin: TPoint;
- begin
- GetWindowOrgEx(DC, LastOrigin);
- SetWindowOrgEx(DC, LastOrigin.X + Control.Left, LastOrigin.Y + Control.Top, nil);
- Control.Parent.Perform(WM_ERASEBKGND, Integer(DC), Integer(DC));
- SetWindowOrgEx(DC, LastOrigin.X, LastOrigin.Y, nil);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.RegisterListener(AControlMessage: TControlMessageEvent);
- var
- I: Integer;
- Ptr: PControlMessageEvent;
- begin
- if not FindListener(AControlMessage, I) then
- begin
- New(Ptr);
- Ptr^ := AControlMessage;
- FListeners.Add(Ptr);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- procedure TThemeManager.UnregisterListener(AControlMessage: TControlMessageEvent);
- var
- I: Integer;
- begin
- if FindListener(AControlMessage, I) then
- begin
- Dispose(PControlMessageEvent(FListeners[I]));
- FListeners.Delete(I);
- end;
- end;
- //----------------------------------------------------------------------------------------------------------------------
- // MP BEGIN
- function TThemeManager.GetColor(Element: TThemedElement; PartId: Integer;
- StateId: Integer; PropId: Integer): TColor;
- begin
- Result := ThemeServices.GetColor(Element, PartId, StateId, PropId);
- end;
- //----------------------------------------------------------------------------------------------------------------------
- function TThemeManager.GetThemesEnabled: Boolean;
- begin
- Result := ThemeServices.ThemesEnabled;
- end;
- // MP END
- //----------------------------------------------------------------------------------------------------------------------
- initialization
- Lock := TCriticalSection.Create;
- GetCheckSize;
- IsWindowsXP := (Win32MajorVersion > 5) or ((Win32MajorVersion = 5) and (Win32MinorVersion >= 1));
- finalization
- Lock.Free;
- Lock := nil;
- end.
|