1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182 |
- unit NewCheckListBox;
- { TNewCheckListBox by Martijn Laan for Inno Setup
- Based on TPBCheckListBox by Patrick Brisacier and TCheckListBox by Borland
- Group item support, child item support, exclusive item support,
- ShowLines support and 'WantTabs mode' by Alex Yackimoff.
- Note: TNewCheckListBox uses Items.Objects to store the item state. Don't use
- Item.Objects yourself, use ItemObject instead.
- }
- interface
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
- StdCtrls, NewUxTheme;
- const
- WM_UPDATEUISTATE = $0128;
- type
- TItemType = (itGroup, itCheck, itRadio);
- TCheckBoxState2 = (cb2Normal, cb2Hot, cb2Pressed, cb2Disabled);
- TItemState = class (TObject)
- public
- Enabled: Boolean;
- HasInternalChildren: Boolean;
- CheckWhenParentChecked: Boolean;
- IsLastChild: Boolean;
- ItemType: TItemType;
- Level: Byte;
- Obj: TObject;
- State: TCheckBoxState;
- SubItem: string;
- ThreadCache: set of Byte;
- MeasuredHeight: Integer;
- ItemFontStyle: TFontStyles;
- SubItemFontStyle: TFontStyles;
- end;
- TCheckItemOperation = (coUncheck, coCheck, coCheckWithChildren);
- TEnumChildrenProc = procedure(Index: Integer; HasChildren: Boolean; Ext: Longint) of object;
- TNewCheckListBox = class (TCustomListBox)
- private
- FAccObjectInstance: TObject;
- FCaptureIndex: Integer;
- FSpaceDown: Boolean;
- FCheckHeight: Integer;
- FCheckWidth: Integer;
- FFormFocusChanged: Boolean;
- FFlat: Boolean;
- FLastMouseMoveIndex: Integer;
- FMinItemHeight: Integer;
- FOffset: Integer;
- FOnClickCheck: TNotifyEvent;
- FRequireRadioSelection: Boolean;
- FShowLines: Boolean;
- FStateList: TList;
- FWantTabs: Boolean;
- FThemeData: HTHEME;
- FThreadsUpToDate: Boolean;
- FHotIndex: Integer;
- FDisableItemStateDeletion: Integer;
- FWheelAccum: Integer;
- FUseRightToLeft: Boolean;
- procedure UpdateThemeData(const Close, Open: Boolean);
- function CanFocusItem(Item: Integer): Boolean;
- function CheckPotentialRadioParents(Index, ALevel: Integer): Boolean;
- procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
- procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
- procedure CMExit(var Message: TCMExit); message CM_EXIT;
- procedure CMFocusChanged(var Message: TCMFocusChanged); message CM_FOCUSCHANGED;
- procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
- procedure CMWantSpecialKey(var Message: TMessage); message CM_WANTSPECIALKEY;
- procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
- procedure EndCapture(Cancel: Boolean);
- function AddItem2(AType: TItemType; const ACaption, ASubItem: string;
- ALevel: Byte; AChecked, AEnabled, AHasInternalChildren,
- ACheckWhenParentChecked: Boolean; AObject: TObject): Integer;
- function FindAccel(VK: Word): Integer;
- function FindCheckedSibling(const AIndex: Integer): Integer;
- function FindNextItem(StartFrom: Integer; GoForward,
- SkipUncheckedRadios: Boolean): Integer;
- function GetItemState(Index: Integer): TItemState;
- procedure InvalidateCheck(Index: Integer);
- function RemeasureItem(Index: Integer): Integer;
- procedure Toggle(Index: Integer);
- procedure UpdateScrollRange;
- procedure LBDeleteString(var Message: TMessage); message LB_DELETESTRING;
- procedure LBResetContent(var Message: TMessage); message LB_RESETCONTENT;
- procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
- procedure WMGetObject(var Message: TMessage); message WM_GETOBJECT;
- procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
- procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
- procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
- procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
- procedure WMSize(var Message: TWMSize); message WM_SIZE;
- procedure WMThemeChanged(var Message: TMessage); message WM_THEMECHANGED;
- procedure WMUpdateUIState(var Message: TMessage); message WM_UPDATEUISTATE;
- protected
- procedure CreateParams(var Params: TCreateParams); override;
- procedure CreateWnd; override;
- procedure MeasureItem(Index: Integer; var Height: Integer); override;
- procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
- override;
- function GetCaption(Index: Integer): String;
- function GetChecked(Index: Integer): Boolean;
- function GetItemEnabled(Index: Integer): Boolean;
- function GetItemFontStyle(Index: Integer): TFontStyles;
- function GetLevel(Index: Integer): Byte;
- function GetObject(Index: Integer): TObject;
- function GetState(Index: Integer): TCheckBoxState;
- function GetSubItem(Index: Integer): string;
- function GetSubItemFontStyle(Index: Integer): TFontStyles;
- procedure KeyDown(var Key: Word; Shift: TShiftState); override;
- procedure KeyUp(var Key: Word; Shift: TShiftState); override;
- procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- override;
- procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
- procedure UpdateHotIndex(NewHotIndex: Integer);
- procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
- procedure SetCaption(Index: Integer; const Value: String);
- procedure SetChecked(Index: Integer; const AChecked: Boolean);
- procedure SetFlat(Value: Boolean);
- procedure SetItemEnabled(Index: Integer; const AEnabled: Boolean);
- procedure SetItemFontStyle(Index: Integer; const AItemFontStyle: TFontStyles);
- procedure SetObject(Index: Integer; const AObject: TObject);
- procedure SetOffset(AnOffset: Integer);
- procedure SetShowLines(Value: Boolean);
- procedure SetSubItem(Index: Integer; const ASubItem: String);
- procedure SetSubItemFontStyle(Index: Integer; const ASubItemFontStyle: TFontStyles);
- property ItemStates[Index: Integer]: TItemState read GetItemState;
- public
- constructor Create(AOwner: TComponent); override;
- procedure CreateWindowHandle(const Params: TCreateParams); override;
- destructor Destroy; override;
- function AddCheckBox(const ACaption, ASubItem: string; ALevel: Byte;
- AChecked, AEnabled, AHasInternalChildren, ACheckWhenParentChecked: Boolean;
- AObject: TObject): Integer;
- function AddGroup(const ACaption, ASubItem: string; ALevel: Byte;
- AObject: TObject): Integer;
- function AddRadioButton(const ACaption, ASubItem: string;
- ALevel: Byte; AChecked, AEnabled: Boolean; AObject: TObject): Integer;
- function CheckItem(const Index: Integer; const AOperation: TCheckItemOperation): Boolean;
- procedure EnumChildrenOf(Item: Integer; Proc: TEnumChildrenProc; Ext: Longint);
- function GetParentOf(Item: Integer): Integer;
- procedure UpdateThreads;
- property Checked[Index: Integer]: Boolean read GetChecked write SetChecked;
- property ItemCaption[Index: Integer]: String read GetCaption write SetCaption;
- property ItemEnabled[Index: Integer]: Boolean read GetItemEnabled write SetItemEnabled;
- property ItemFontStyle[Index: Integer]: TFontStyles read GetItemFontStyle write SetItemFontStyle;
- property ItemLevel[Index: Integer]: Byte read GetLevel;
- property ItemObject[Index: Integer]: TObject read GetObject write SetObject;
- property ItemSubItem[Index: Integer]: string read GetSubItem write SetSubItem;
- property State[Index: Integer]: TCheckBoxState read GetState;
- property SubItemFontStyle[Index: Integer]: TFontStyles read GetSubItemFontStyle write SetSubItemFontStyle;
- published
- property Align;
- property Anchors;
- property BorderStyle;
- property Color;
- property Ctl3D;
- property DragCursor;
- property DragMode;
- property Enabled;
- property Flat: Boolean read FFlat write SetFlat default False;
- property Font;
- property Items;
- property MinItemHeight: Integer read FMinItemHeight write FMinItemHeight default 16;
- property Offset: Integer read FOffset write SetOffset default 4;
- property OnClick;
- property OnClickCheck: TNotifyEvent read FOnClickCheck write FOnClickCheck;
- property OnDblClick;
- property OnDragDrop;
- property OnDragOver;
- property OnEndDrag;
- property OnEnter;
- property OnExit;
- property OnKeyDown;
- property OnKeyPress;
- property OnKeyUp;
- property OnMouseDown;
- property OnMouseMove;
- property OnMouseUp;
- property OnStartDrag;
- property ParentColor;
- property ParentCtl3D;
- property ParentFont;
- property ParentShowHint;
- property PopupMenu;
- property RequireRadioSelection: Boolean read FRequireRadioSelection write FRequireRadioSelection default False;
- property ShowHint;
- property ShowLines: Boolean read FShowLines write SetShowLines default True;
- property TabOrder;
- property Visible;
- property WantTabs: Boolean read FWantTabs write FWantTabs default False;
- end;
- procedure Register;
- implementation
- uses
- Themes, NewUxTheme.TmSchema, PathFunc, ActiveX, BidiUtils, UITypes, Types;
- const
- sRadioCantHaveDisabledChildren = 'Radio item cannot have disabled child items';
- OBM_CHECKBOXES = 32759;
- WM_CHANGEUISTATE = $0127;
- WM_QUERYUISTATE = $0129;
- UIS_SET = 1;
- UIS_CLEAR = 2;
- UIS_INITIALIZE = 3;
- UISF_HIDEFOCUS = $1;
- UISF_HIDEACCEL = $2;
- DT_HIDEPREFIX = $00100000;
- OBJID_CLIENT = $FFFFFFFC;
- CHILDID_SELF = 0;
- ROLE_SYSTEM_OUTLINE = $23;
- ROLE_SYSTEM_STATICTEXT = $29;
- ROLE_SYSTEM_CHECKBUTTON = $2c;
- ROLE_SYSTEM_RADIOBUTTON = $2d;
- STATE_SYSTEM_UNAVAILABLE = $1;
- STATE_SYSTEM_CHECKED = $10;
- STATE_SYSTEM_MIXED = $20;
- EVENT_OBJECT_STATECHANGE = $800A;
- IID_IUnknown: TGUID = (
- D1:$00000000; D2:$0000; D3:$0000; D4:($C0,$00,$00,$00,$00,$00,$00,$46));
- IID_IDispatch: TGUID = (
- D1:$00020400; D2:$0000; D3:$0000; D4:($C0,$00,$00,$00,$00,$00,$00,$46));
- IID_IAccessible: TGUID = (
- D1:$618736e0; D2:$3c3d; D3:$11cf; D4:($81,$0c,$00,$aa,$00,$38,$9b,$71));
- type
- TWinControlAccess = class (TWinControl);
- { Note: We have to use TVariantArg for Delphi 2 compat., because D2 passes
- Variant parameters by reference (wrong), unlike D3+ which pass
- Variant/OleVariant parameters by value }
- NewOleVariant = TVariantArg;
- NewWideString = Pointer;
- TIUnknown = class
- public
- function QueryInterface(const iid: TIID; var obj): HRESULT; virtual; stdcall; abstract;
- function AddRef: Longint; virtual; stdcall; abstract;
- function Release: Longint; virtual; stdcall; abstract;
- end;
- TIDispatch = class(TIUnknown)
- public
- function GetTypeInfoCount(var ctinfo: Integer): HRESULT; virtual; stdcall; abstract;
- function GetTypeInfo(itinfo: Integer; lcid: TLCID; var tinfo: ITypeInfo): HRESULT; virtual; stdcall; abstract;
- function GetIDsOfNames(const iid: TIID; rgszNames: POleStrList;
- cNames: Integer; lcid: TLCID; rgdispid: PDispIDList): HRESULT; virtual; stdcall; abstract;
- function Invoke(dispIDMember: TDispID; const iid: TIID; lcid: TLCID;
- flags: Word; var dispParams: TDispParams; varResult: PVariant;
- excepInfo: PExcepInfo; argErr: PInteger): HRESULT; virtual; stdcall; abstract;
- end;
- TIAccessible = class(TIDispatch)
- public
- function get_accParent(var ppdispParent: IDispatch): HRESULT; virtual; stdcall; abstract;
- function get_accChildCount(var pcountChildren: Integer): HRESULT; virtual; stdcall; abstract;
- function get_accChild(varChild: NewOleVariant; var ppdispChild: IDispatch): HRESULT; virtual; stdcall; abstract;
- function get_accName(varChild: NewOleVariant; var pszName: NewWideString): HRESULT; virtual; stdcall; abstract;
- function get_accValue(varChild: NewOleVariant; var pszValue: NewWideString): HRESULT; virtual; stdcall; abstract;
- function get_accDescription(varChild: NewOleVariant; var pszDescription: NewWideString): HRESULT; virtual; stdcall; abstract;
- function get_accRole(varChild: NewOleVariant; var pvarRole: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function get_accState(varChild: NewOleVariant; var pvarState: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function get_accHelp(varChild: NewOleVariant; var pszHelp: NewWideString): HRESULT; virtual; stdcall; abstract;
- function get_accHelpTopic(var pszHelpFile: NewWideString; varChild: NewOleVariant; var pidTopic: Integer): HRESULT; virtual; stdcall; abstract;
- function get_accKeyboardShortcut(varChild: NewOleVariant; var pszKeyboardShortcut: NewWideString): HRESULT; virtual; stdcall; abstract;
- function get_accFocus(var pvarID: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function get_accSelection(var pvarChildren: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function get_accDefaultAction(varChild: NewOleVariant; var pszDefaultAction: NewWideString): HRESULT; virtual; stdcall; abstract;
- function accSelect(flagsSelect: Integer; varChild: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function accLocation(var pxLeft: Integer; var pyTop: Integer; var pcxWidth: Integer;
- var pcyHeight: Integer; varChild: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function accNavigate(navDir: Integer; varStart: NewOleVariant; var pvarEnd: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function accHitTest(xLeft: Integer; yTop: Integer; var pvarID: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function accDoDefaultAction(varChild: NewOleVariant): HRESULT; virtual; stdcall; abstract;
- function put_accName(varChild: NewOleVariant; const pszName: NewWideString): HRESULT; virtual; stdcall; abstract;
- function put_accValue(varChild: NewOleVariant; const pszValue: NewWideString): HRESULT; virtual; stdcall; abstract;
- end;
- TAccObject = class(TIAccessible)
- private
- FControl: TNewCheckListBox;
- FRefCount: Integer;
- FStdAcc: TIAccessible;
- { TIUnknown }
- function QueryInterface(const iid: TIID; var obj): HRESULT; override;
- function AddRef: Longint; override;
- function Release: Longint; override;
- { TIDispatch }
- function GetTypeInfoCount(var ctinfo: Integer): HRESULT; override;
- function GetTypeInfo(itinfo: Integer; lcid: TLCID; var tinfo: ITypeInfo): HRESULT; override;
- function GetIDsOfNames(const iid: TIID; rgszNames: POleStrList;
- cNames: Integer; lcid: TLCID; rgdispid: PDispIDList): HRESULT; override;
- function Invoke(dispIDMember: TDispID; const iid: TIID; lcid: TLCID;
- flags: Word; var dispParams: TDispParams; varResult: PVariant;
- excepInfo: PExcepInfo; argErr: PInteger): HRESULT; override;
- { TIAccessible }
- function get_accParent(var ppdispParent: IDispatch): HRESULT; override;
- function get_accChildCount(var pcountChildren: Integer): HRESULT; override;
- function get_accChild(varChild: NewOleVariant; var ppdispChild: IDispatch): HRESULT; override;
- function get_accName(varChild: NewOleVariant; var pszName: NewWideString): HRESULT; override;
- function get_accValue(varChild: NewOleVariant; var pszValue: NewWideString): HRESULT; override;
- function get_accDescription(varChild: NewOleVariant; var pszDescription: NewWideString): HRESULT; override;
- function get_accRole(varChild: NewOleVariant; var pvarRole: NewOleVariant): HRESULT; override;
- function get_accState(varChild: NewOleVariant; var pvarState: NewOleVariant): HRESULT; override;
- function get_accHelp(varChild: NewOleVariant; var pszHelp: NewWideString): HRESULT; override;
- function get_accHelpTopic(var pszHelpFile: NewWideString; varChild: NewOleVariant; var pidTopic: Integer): HRESULT; override;
- function get_accKeyboardShortcut(varChild: NewOleVariant; var pszKeyboardShortcut: NewWideString): HRESULT; override;
- function get_accFocus(var pvarID: NewOleVariant): HRESULT; override;
- function get_accSelection(var pvarChildren: NewOleVariant): HRESULT; override;
- function get_accDefaultAction(varChild: NewOleVariant; var pszDefaultAction: NewWideString): HRESULT; override;
- function accSelect(flagsSelect: Integer; varChild: NewOleVariant): HRESULT; override;
- function accLocation(var pxLeft: Integer; var pyTop: Integer; var pcxWidth: Integer;
- var pcyHeight: Integer; varChild: NewOleVariant): HRESULT; override;
- function accNavigate(navDir: Integer; varStart: NewOleVariant; var pvarEnd: NewOleVariant): HRESULT; override;
- function accHitTest(xLeft: Integer; yTop: Integer; var pvarID: NewOleVariant): HRESULT; override;
- function accDoDefaultAction(varChild: NewOleVariant): HRESULT; override;
- function put_accName(varChild: NewOleVariant; const pszName: NewWideString): HRESULT; override;
- function put_accValue(varChild: NewOleVariant; const pszValue: NewWideString): HRESULT; override;
- public
- constructor Create(AControl: TNewCheckListBox);
- destructor Destroy; override;
- procedure ControlDestroying;
- end;
- function CoDisconnectObject(unk: TIUnknown; dwReserved: DWORD): HRESULT;
- stdcall; external 'ole32.dll';
- var
- NotifyWinEventFunc: procedure(event: DWORD; hwnd: HWND; idObject: DWORD;
- idChild: Longint); stdcall;
- OleAccInited: BOOL;
- OleAccAvailable: BOOL;
- LresultFromObjectFunc: function(const riid: TGUID; wParam: WPARAM;
- pUnk: TIUnknown): LRESULT; stdcall;
- CreateStdAccessibleObjectFunc: function(hwnd: HWND; idObject: Longint;
- const riidInterface: TGUID; var ppvObject: Pointer): HRESULT; stdcall;
- function InitializeOleAcc: Boolean;
- function GetSystemDir: String;
- var
- Buf: array[0..MAX_PATH-1] of Char;
- begin
- GetSystemDirectory(Buf, SizeOf(Buf) div SizeOf(Buf[0]));
- Result := StrPas(Buf);
- end;
- var
- M: HMODULE;
- begin
- if not OleAccInited then begin
- M := LoadLibrary(PChar(AddBackslash(GetSystemDir) + 'oleacc.dll'));
- if M <> 0 then begin
- LresultFromObjectFunc := GetProcAddress(M, 'LresultFromObject');
- CreateStdAccessibleObjectFunc := GetProcAddress(M, 'CreateStdAccessibleObject');
- if Assigned(LresultFromObjectFunc) and
- Assigned(CreateStdAccessibleObjectFunc) then
- OleAccAvailable := True;
- end;
- OleAccInited := True;
- end;
- Result := OleAccAvailable;
- end;
- { TNewCheckListBox }
- constructor TNewCheckListBox.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- with TBitmap.Create do
- begin
- try
- Handle := LoadBitmap(0, PChar(OBM_CHECKBOXES));
- FCheckWidth := Width div 4;
- FCheckHeight := Height div 3;
- finally
- Free;
- end;
- end;
- FStateList := TList.Create;
- FMinItemHeight := 16;
- FOffset := 4;
- FShowLines := True;
- Style := lbOwnerDrawVariable;
- FHotIndex := -1;
- FCaptureIndex := -1;
- end;
- procedure TNewCheckListBox.CreateParams(var Params: TCreateParams);
- begin
- inherited;
- FUseRightToLeft := SetBiDiStyles(Self, Params);
- end;
- procedure TNewCheckListBox.CreateWnd;
- begin
- { TCustomListBox.CreateWnd causes a LB_RESETCONTENT message to be sent when
- it's restoring FSaveItems. Increment FDisableItemStateDeletion so that
- our LB_RESETCONTENT handler doesn't delete any item states. }
- Inc(FDisableItemStateDeletion);
- try
- inherited;
- finally
- Dec(FDisableItemStateDeletion);
- end;
- end;
- procedure TNewCheckListBox.UpdateThemeData(const Close, Open: Boolean);
- begin
- if Close then begin
- if FThemeData <> 0 then begin
- CloseThemeData(FThemeData);
- FThemeData := 0;
- end;
- end;
- if Open then begin
- if UseThemes then
- FThemeData := OpenThemeData(Handle, 'Button')
- else
- FThemeData := 0;
- end;
- end;
- procedure TNewCheckListBox.CreateWindowHandle(const Params: TCreateParams);
- begin
- inherited CreateWindowHandle(Params);
- UpdateThemeData(True, True);
- end;
- destructor TNewCheckListBox.Destroy;
- var
- I: Integer;
- begin
- if Assigned(FAccObjectInstance) then begin
- { Detach from FAccObjectInstance if someone still has a reference to it }
- TAccObject(FAccObjectInstance).ControlDestroying;
- FAccObjectInstance := nil;
- end;
- if Assigned(FStateList) then begin
- for I := FStateList.Count-1 downto 0 do
- TItemState(FStateList[I]).Free;
- FStateList.Free;
- end;
- UpdateThemeData(True, False);
- inherited Destroy;
- end;
- function TNewCheckListBox.AddCheckBox(const ACaption, ASubItem: string;
- ALevel: Byte; AChecked, AEnabled, AHasInternalChildren,
- ACheckWhenParentChecked: Boolean; AObject: TObject): Integer;
- begin
- if not AEnabled and CheckPotentialRadioParents(Items.Count, ALevel) then
- raise Exception.Create(sRadioCantHaveDisabledChildren);
- Result := AddItem2(itCheck, ACaption, ASubItem, ALevel, AChecked, AEnabled,
- AHasInternalChildren, ACheckWhenParentChecked, AObject);
- end;
- function TNewCheckListBox.AddGroup(const ACaption, ASubItem: string;
- ALevel: Byte; AObject: TObject): Integer;
- begin
- Result := AddItem2(itGroup, ACaption, ASubItem, ALevel, False, True, False,
- True, AObject);
- end;
- function TNewCheckListBox.AddRadioButton(const ACaption, ASubItem: string;
- ALevel: Byte; AChecked, AEnabled: Boolean; AObject: TObject): Integer;
- begin
- if not AEnabled then
- AChecked := False;
- Result := AddItem2(itRadio, ACaption, ASubItem, ALevel, AChecked, AEnabled,
- False, True, AObject);
- end;
- function TNewCheckListBox.CanFocusItem(Item: Integer): Boolean;
- begin
- with ItemStates[Item] do
- Result := Self.Enabled and Enabled and (ItemType <> itGroup);
- end;
- function TNewCheckListBox.CheckPotentialRadioParents(Index, ALevel: Integer): Boolean;
- begin
- Result := True;
- Dec(Index);
- Dec(ALevel);
- while Index >= 0 do
- begin
- with ItemStates[Index] do
- if Level = ALevel then
- if ItemType = itRadio then
- Exit
- else
- Break;
- Dec(Index);
- end;
- if Index >= 0 then
- begin
- Index := GetParentOf(Index);
- while Index >= 0 do
- begin
- if ItemStates[Index].ItemType = itRadio then
- Exit;
- Index := GetParentOf(Index);
- end;
- end;
- Result := False;
- end;
- procedure TNewCheckListBox.CMDialogChar(var Message: TCMDialogChar);
- var
- I: Integer;
- begin
- if FWantTabs and CanFocus then
- with Message do
- begin
- I := FindAccel(CharCode);
- if I >= 0 then
- begin
- SetFocus;
- if (FCaptureIndex <> I) or FSpaceDown then EndCapture(not FSpaceDown);
- ItemIndex := I;
- Toggle(I);
- Result := 1
- end;
- end;
- end;
- procedure TNewCheckListBox.CMEnter(var Message: TCMEnter);
- var
- GoForward, Arrows: Boolean;
- begin
- if FWantTabs and FFormFocusChanged and (GetKeyState(VK_LBUTTON) >= 0) then
- begin
- if GetKeyState(VK_TAB) < 0 then begin
- Arrows := False;
- GoForward := (GetKeyState(VK_SHIFT) >= 0);
- end
- else if (GetKeyState(VK_UP) < 0) or (GetKeyState(VK_LEFT) < 0) then begin
- Arrows := True;
- GoForward := False;
- end
- else if (GetKeyState(VK_DOWN) < 0) or (GetKeyState(VK_RIGHT) < 0) then begin
- Arrows := True;
- GoForward := True;
- end
- else begin
- { Otherwise, just select the first item }
- Arrows := False;
- GoForward := True;
- end;
- if GoForward then
- ItemIndex := FindNextItem(-1, True, not Arrows)
- else
- ItemIndex := FindNextItem(Items.Count, False, not Arrows)
- end;
- inherited;
- end;
- procedure TNewCheckListBox.CMExit(var Message: TCMExit);
- begin
- EndCapture(not FSpaceDown or (GetKeyState(VK_MENU) >= 0));
- inherited;
- end;
- procedure TNewCheckListBox.CMFocusChanged(var Message: TCMFocusChanged);
- begin
- FFormFocusChanged := True;
- inherited;
- end;
- procedure TNewCheckListBox.CMFontChanged(var Message: TMessage);
- begin
- inherited;
- Canvas.Font := Font;
- end;
- procedure LineDDAProc(X, Y: Integer; Canvas: TCanvas); stdcall;
- begin
- if ((X xor Y) and 1) = 0 then
- begin
- Canvas.MoveTo(X, Y);
- Canvas.LineTo(X + 1, Y)
- end;
- end;
- procedure TNewCheckListBox.CMWantSpecialKey(var Message: TMessage);
- begin
- Message.Result := Ord(FWantTabs and (Message.WParam = VK_TAB));
- end;
- procedure TNewCheckListBox.CNDrawItem(var Message: TWMDrawItem);
- var
- L: Integer;
- begin
- with Message.DrawItemStruct^ do
- begin
- { Note: itemID is -1 when there are no items }
- if Integer(itemID) >= 0 then begin
- L := ItemStates[itemID].Level;
- if ItemStates[itemID].ItemType <> itGroup then Inc(L);
- rcItem.Left := rcItem.Left + (FCheckWidth + 2 * FOffset) * L;
- FlipRect(rcItem, ClientRect, FUseRightToLeft);
- end;
- { Don't let TCustomListBox.CNDrawItem draw the focus }
- if FWantTabs or
- (SendMessage(Handle, WM_QUERYUISTATE, 0, 0) and UISF_HIDEFOCUS <> 0) then
- itemState := itemState and not ODS_FOCUS;
- inherited;
- end;
- end;
- function TNewCheckListBox.RemeasureItem(Index: Integer): Integer;
- { Recalculates an item's height. Does not repaint and does not update the
- vertical scroll range (as the LB_SETITEMHEIGHT message does neither). }
- begin
- Result := ItemHeight;
- MeasureItem(Index, Result);
- SendMessage(Handle, LB_SETITEMHEIGHT, Index, Result);
- end;
- procedure TNewCheckListBox.UpdateScrollRange;
- { Updates the vertical scroll range, hiding/showing the scroll bar if needed.
- This should be called after any RemeasureItem call. }
- begin
- { Update the scroll bounds by sending a seemingly-ineffectual LB_SETTOPINDEX
- message. This works on Windows 95 and 2000.
- NOTE: This causes the selected item to be repainted for no apparent reason!
- I wish I knew of a better way to do this... }
- SendMessage(Handle, LB_SETTOPINDEX, SendMessage(Handle, LB_GETTOPINDEX, 0, 0), 0);
- end;
- procedure TNewCheckListBox.MeasureItem(Index: Integer; var Height: Integer);
- var
- DrawTextFormat: Integer;
- Rect, SubItemRect: TRect;
- ItemState: TItemState;
- L, SubItemWidth: Integer;
- S: String;
- begin
- with Canvas do begin
- ItemState := ItemStates[Index];
- Rect := Classes.Rect(0, 0, ClientWidth, 0);
- L := ItemState.Level;
- if ItemState.ItemType <> itGroup then
- Inc(L);
- Rect.Left := Rect.Left + (FCheckWidth + 2 * FOffset) * L;
- Inc(Rect.Left);
- if ItemState.SubItem <> '' then begin
- DrawTextFormat := DT_CALCRECT or DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE;
- if FUseRightToLeft then
- DrawTextFormat := DrawTextFormat or (DT_RIGHT or DT_RTLREADING);
- SetRectEmpty(SubItemRect);
- DrawText(Canvas.Handle, PChar(ItemState.SubItem), Length(ItemState.SubItem),
- SubItemRect, DrawTextFormat);
- SubItemWidth := SubItemRect.Right + 2 * FOffset;
- Dec(Rect.Right, SubItemWidth)
- end else
- Dec(Rect.Right, FOffset);
- if not FWantTabs then
- Inc(Rect.Left);
- DrawTextFormat := DT_NOCLIP or DT_CALCRECT or DT_WORDBREAK or DT_WORD_ELLIPSIS;
- if not FWantTabs or (ItemState.ItemType = itGroup) then
- DrawTextFormat := DrawTextFormat or DT_NOPREFIX;
- if FUseRightToLeft then
- DrawTextFormat := DrawTextFormat or (DT_RIGHT or DT_RTLREADING);
- S := Items[Index]; { Passing Items[Index] directly into DrawText doesn't work on Unicode build. }
- ItemState.MeasuredHeight := DrawText(Canvas.Handle, PChar(S), Length(S), Rect, DrawTextFormat);
- if ItemState.MeasuredHeight < FMinItemHeight then
- Height := FMinItemHeight
- else
- Height := ItemState.MeasuredHeight + 4;
- { The height must be an even number for tree lines to be painted correctly }
- if Odd(Height) then
- Inc(Height);
- end;
- end;
- procedure TNewCheckListBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
- const
- ButtonStates: array [TItemType] of Integer =
- (
- 0,
- DFCS_BUTTONCHECK,
- DFCS_BUTTONRADIO
- );
- ButtonPartIds: array [TItemType] of Integer =
- (
- 0,
- BP_CHECKBOX,
- BP_RADIOBUTTON
- );
- ButtonStateIds: array [TCheckBoxState, TCheckBoxState2] of Integer =
- (
- //Can be used for both checkboxes and radiobuttons because RBS_... constants
- //equal CBS_... constants
- (CBS_UNCHECKEDNORMAL, CBS_UNCHECKEDHOT, CBS_UNCHECKEDPRESSED, CBS_UNCHECKEDDISABLED),
- (CBS_CHECKEDNORMAL, CBS_CHECKEDHOT, CBS_CHECKEDPRESSED, CBS_CHECKEDDISABLED),
- (CBS_MIXEDNORMAL, CBS_MIXEDHOT, CBS_MIXEDPRESSED, CBS_MIXEDDISABLED)
- );
- FontColorStates: array[Boolean] of TStyleFont = (sfListItemTextDisabled, sfListItemTextNormal);
- CheckListItemStates: array[Boolean] of TThemedCheckListBox = (tclListItemDisabled, tclListItemNormal);
- CheckBoxCheckedStates: array[Boolean] of TThemedButton = (tbCheckBoxCheckedDisabled, tbCheckBoxCheckedNormal);
- CheckBoxUncheckedStates: array[Boolean] of TThemedButton = (tbCheckBoxUncheckedDisabled, tbCheckBoxUncheckedNormal);
- CheckBoxMixedStates: array[Boolean] of TThemedButton = (tbCheckBoxMixedDisabled, tbCheckBoxMixedNormal);
- RadioButtonCheckedStates: array[Boolean] of TThemedButton = (tbRadioButtonCheckedDisabled, tbRadioButtonCheckedNormal);
- RadioButtonUncheckedStates: array[Boolean] of TThemedButton = (tbRadioButtonUncheckedDisabled, tbRadioButtonUncheckedNormal);
- var
- SavedClientRect: TRect;
- function FlipX(const X: Integer): Integer;
- begin
- if FUseRightToLeft then
- Result := (SavedClientRect.Right - 1) - X
- else
- Result := X;
- end;
- procedure InternalDrawText(const S: string; var R: TRect; Format: Integer;
- Embossed: Boolean);
- begin
- if Embossed then
- begin
- Canvas.Brush.Style := bsClear;
- OffsetRect(R, 1, 1);
- SetTextColor(Canvas.Handle, GetSysColor(COLOR_BTNHIGHLIGHT));
- DrawText(Canvas.Handle, PChar(S), Length(S), R, Format);
- OffsetRect(R, -1, -1);
- SetTextColor(Canvas.Handle, GetSysColor(COLOR_BTNSHADOW));
- DrawText(Canvas.Handle, PChar(S), Length(S), R, Format);
- end
- else
- DrawText(Canvas.Handle, PChar(S), Length(S), R, Format);
- end;
- var
- Disabled: Boolean;
- uState, I, ThreadPosX, ThreadBottom, ThreadLevel, ItemMiddle,
- DrawTextFormat: Integer;
- CheckRect, SubItemRect, FocusRect: TRect;
- NewTextColor: TColor;
- OldColor: TColorRef;
- ItemState: TItemState;
- UIState: DWORD;
- SubItemWidth: Integer;
- PartId, StateId: Integer;
- Size: TSize;
- begin
- if FShowLines and not FThreadsUpToDate then begin
- UpdateThreads;
- FThreadsUpToDate := True;
- end;
- SavedClientRect := ClientRect;
- { Undo flipping performed by TNewCheckListBox.CNDrawItem }
- FlipRect(Rect, SavedClientRect, FUseRightToLeft);
- ItemState := ItemStates[Index];
- UIState := SendMessage(Handle, WM_QUERYUISTATE, 0, 0);
- Disabled := not Enabled or not ItemState.Enabled;
- { Style code below is based on Vcl.StdCtrls' TCustomListBox.CNDrawItem and Vcl.CheckLst's
- TCustomCheckListBox.DrawItem and .DrawCheck }
- var LStyle := StyleServices(Self);
- if not LStyle.Enabled or LStyle.IsSystemStyle then
- LStyle := nil;
- with Canvas do begin
- { Initialize colors }
- if not FWantTabs and (odSelected in State) and Focused then begin
- NewTextColor := clHighlightText;
- if (LStyle <> nil) and (seClient in StyleElements) then begin
- Brush.Color := LStyle.GetSystemColor(clHighlight);
- if seFont in StyleElements then
- NewTextColor := LStyle.GetStyleFontColor(sfListItemTextSelected);
- end else
- Brush.Color := clHighlight;
- end else begin
- if Disabled then
- NewTextColor := clGrayText
- else
- NewTextColor := Self.Font.Color;
- if (LStyle <> nil) and (seClient in StyleElements) then begin
- Brush.Color := LStyle.GetStyleColor(scListBox);
- if seFont in StyleElements then begin
- NewTextColor := LStyle.GetStyleFontColor(FontColorStates[not Disabled]);
- const Details = LStyle.GetElementDetails(CheckListItemStates[Enabled]);
- var LColor: TColor;
- if LStyle.GetElementColor(Details, ecTextColor, LColor) and (LColor <> clNone) then
- NewTextColor := LColor;
- end;
- end else
- Brush.Color := Self.Color;
- end;
- { Draw threads }
- if FShowLines then begin
- Pen.Color := clGrayText;
- ThreadLevel := ItemLevel[Index];
- for I := 0 to ThreadLevel - 1 do
- if I in ItemStates[Index].ThreadCache then begin
- ThreadPosX := (FCheckWidth + 2 * FOffset) * I + FCheckWidth div 2 + FOffset;
- ItemMiddle := (Rect.Bottom - Rect.Top) div 2 + Rect.Top;
- ThreadBottom := Rect.Bottom;
- if I = ThreadLevel - 1 then begin
- if ItemStates[Index].IsLastChild then
- ThreadBottom := ItemMiddle;
- LineDDA(FlipX(ThreadPosX), ItemMiddle, FlipX(ThreadPosX + FCheckWidth div 2 + FOffset),
- ItemMiddle, @LineDDAProc, Integer(Canvas));
- end;
- LineDDA(FlipX(ThreadPosX), Rect.Top, FlipX(ThreadPosX), ThreadBottom,
- @LineDDAProc, Integer(Canvas));
- end;
- end;
- { Draw checkmark}
- if ItemState.ItemType <> itGroup then begin
- CheckRect := Bounds(Rect.Left - (FCheckWidth + FOffset),
- Rect.Top + ((Rect.Bottom - Rect.Top - FCheckHeight) div 2),
- FCheckWidth, FCheckHeight);
- FlipRect(CheckRect, SavedClientRect, FUseRightToLeft);
- if LStyle <> nil then begin
- var Detail: TThemedButton;
- if ItemState.State <> cbGrayed then begin
- if ItemState.ItemType = itCheck then begin
- if ItemState.State = cbChecked then
- Detail := CheckBoxCheckedStates[not Disabled]
- else
- Detail := CheckBoxUncheckedStates[not Disabled];
- end else begin
- if ItemState.State = cbChecked then
- Detail := RadioButtonCheckedStates[not Disabled]
- else
- Detail := RadioButtonUncheckedStates[not Disabled];
- end;
- end else
- Detail := CheckBoxMixedStates[not Disabled];
- const ElementDetails = LStyle.GetElementDetails(Detail);
- const SaveColor = Brush.Color;
- const SaveIndex = SaveDC(Handle);
- try
- LStyle.DrawElement(Handle, ElementDetails, CheckRect, nil, CurrentPPI);
- finally
- RestoreDC(Handle, SaveIndex);
- end;
- Brush.Color := SaveColor;
- end else if FThemeData = 0 then begin
- case ItemState.State of
- cbChecked: uState := ButtonStates[ItemState.ItemType] or DFCS_CHECKED;
- cbUnchecked: uState := ButtonStates[ItemState.ItemType];
- else
- uState := DFCS_BUTTON3STATE or DFCS_CHECKED;
- end;
- if FFlat then
- uState := uState or DFCS_FLAT;
- if Disabled then
- uState := uState or DFCS_INACTIVE;
- if (FCaptureIndex = Index) and (FSpaceDown or (FLastMouseMoveIndex = Index)) then
- uState := uState or DFCS_PUSHED;
- DrawFrameControl(Handle, CheckRect, DFC_BUTTON, uState)
- end else begin
- PartId := ButtonPartIds[ItemState.ItemType];
- if Disabled then
- StateId := ButtonStateIds[ItemState.State][cb2Disabled]
- else if Index = FCaptureIndex then
- if FSpaceDown or (FLastMouseMoveIndex = Index) then
- StateId := ButtonStateIds[ItemState.State][cb2Pressed]
- else
- StateId := ButtonStateIds[ItemState.State][cb2Hot]
- else if (FCaptureIndex < 0) and (Index = FHotIndex) then
- StateId := ButtonStateIds[ItemState.State][cb2Hot]
- else
- StateId := ButtonStateIds[ItemState.State][cb2Normal];
- GetThemePartSize(FThemeData, Handle, PartId, StateId, @CheckRect, TS_TRUE, Size);
- if (Size.cx <> FCheckWidth) or (Size.cy <> FCheckHeight) then begin
- CheckRect := Bounds(Rect.Left - (Size.cx + FOffset),
- Rect.Top + ((Rect.Bottom - Rect.Top - Size.cy) div 2),
- Size.cx, Size.cy);
- FlipRect(CheckRect, SavedClientRect, FUseRightToLeft);
- end;
- //if IsThemeBackgroundPartiallyTransparent(FThemeData, PartId, StateId) then
- // DrawThemeParentBackground(Self.Handle, Handle, @CheckRect);
- DrawThemeBackGround(FThemeData, Handle, PartId, StateId, CheckRect, @CheckRect);
- end;
- end;
- { Draw SubItem }
- FlipRect(Rect, SavedClientRect, FUseRightToLeft);
- FillRect(Rect);
- FlipRect(Rect, SavedClientRect, FUseRightToLeft);
- Inc(Rect.Left);
- OldColor := SetTextColor(Handle, ColorToRGB(NewTextColor));
- if ItemState.SubItem <> '' then
- begin
- DrawTextFormat := DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER;
- if FUseRightToLeft then
- DrawTextFormat := DrawTextFormat or (DT_RIGHT or DT_RTLREADING);
- Font.Style := ItemState.SubItemFontStyle;
- SetRectEmpty(SubItemRect);
- InternalDrawText(ItemState.SubItem, SubItemRect, DrawTextFormat or
- DT_CALCRECT, False);
- SubItemWidth := SubItemRect.Right + 2 * FOffset;
- SubItemRect := Rect;
- SubItemRect.Left := SubItemRect.Right - SubItemWidth + FOffset;
- FlipRect(SubItemRect, SavedClientRect, FUseRightToLeft);
- InternalDrawText(ItemState.SubItem, SubItemRect, DrawTextFormat,
- FWantTabs and Disabled);
- Dec(Rect.Right, SubItemWidth);
- end
- else
- Dec(Rect.Right, FOffset);
- { Draw item text }
- if not FWantTabs then
- Inc(Rect.Left);
- OffsetRect(Rect, 0, (Rect.Bottom - Rect.Top - ItemState.MeasuredHeight) div 2);
- DrawTextFormat := DT_NOCLIP or DT_WORDBREAK or DT_WORD_ELLIPSIS;
- if not FWantTabs or (ItemState.ItemType = itGroup) then
- DrawTextFormat := DrawTextFormat or DT_NOPREFIX;
- if (UIState and UISF_HIDEACCEL) <> 0 then
- DrawTextFormat := DrawTextFormat or DT_HIDEPREFIX;
- if FUseRightToLeft then
- DrawTextFormat := DrawTextFormat or (DT_RIGHT or DT_RTLREADING);
- Font.Style := ItemState.ItemFontStyle;
- { When you call DrawText with the DT_CALCRECT flag and there's a word wider
- than the rectangle width, it increases the rectangle width and wraps
- at the new Right point. On the other hand, when you call DrawText
- _without_ the DT_CALCRECT flag, it always wraps at the Right point you
- specify -- it doesn't check for long words first.
- Therefore, to ensure we wrap at the same place when drawing as when
- measuring, pass our rectangle to DrawText with DT_CALCRECT first.
- Wrapping at the same place is important because it can affect how many
- lines are drawn -- and we mustn't draw too many. }
- InternalDrawText(Items[Index], Rect, DrawTextFormat or DT_CALCRECT, False);
- FlipRect(Rect, SavedClientRect, FUseRightToLeft);
- InternalDrawText(Items[Index], Rect, DrawTextFormat, FWantTabs and Disabled and (LStyle = nil));
- { Draw focus rectangle }
- if FWantTabs and not Disabled and (odSelected in State) and Focused and
- (UIState and UISF_HIDEFOCUS = 0) then
- begin
- FocusRect := Rect;
- InflateRect(FocusRect, 1, 1);
- DrawFocusRect(FocusRect);
- end;
- SetTextColor(Handle, OldColor);
- end;
- end;
- procedure TNewCheckListBox.EndCapture(Cancel: Boolean);
- var
- InvalidateItem: Boolean;
- Item: Integer;
- begin
- Item := FCaptureIndex;
- if Item >= 0 then
- begin
- InvalidateItem := FSpaceDown or (FCaptureIndex = FLastMouseMoveIndex) or (FThemeData <> 0);
- FSpaceDown := False;
- FCaptureIndex := -1;
- FLastMouseMoveIndex := -1;
- if not Cancel then
- Toggle(Item);
- if InvalidateItem then
- InvalidateCheck(Item);
- end;
- if MouseCapture then
- MouseCapture := False;
- end;
- procedure TNewCheckListBox.EnumChildrenOf(Item: Integer; Proc: TEnumChildrenProc;
- Ext: Longint);
- var
- L: Integer;
- begin
- if (Item < -1) or (Item >= Items.Count) then
- Exit;
- if Item = -1 then
- begin
- L := 0;
- Item := 0;
- end
- else
- begin
- L := ItemLevel[Item] + 1;
- Inc(Item);
- end;
- while (Item < Items.Count) and (ItemLevel[Item] >= L) do
- begin
- if ItemLevel[Item] = L then
- Proc(Item, (Item < Items.Count - 1) and (ItemLevel[Item + 1] > L), Ext);
- Inc(Item);
- end;
- end;
- function TNewCheckListBox.AddItem2(AType: TItemType;
- const ACaption, ASubItem: string; ALevel: Byte;
- AChecked, AEnabled, AHasInternalChildren, ACheckWhenParentChecked: Boolean;
- AObject: TObject): Integer;
- var
- ItemState: TItemState;
- I: Integer;
- begin
- if Items.Count <> FStateList.Count then { sanity check }
- raise Exception.Create('List item and state item count mismatch');
- if Items.Count > 0 then
- begin
- if ItemLevel[Items.Count - 1] + 1 < ALevel then
- ALevel := ItemLevel[Items.Count - 1] + 1;
- end
- else
- ALevel := 0;
- FThreadsUpToDate := False;
- { Use our own grow code to minimize heap fragmentation }
- if FStateList.Count = FStateList.Capacity then begin
- if FStateList.Capacity < 64 then
- FStateList.Capacity := 64
- else
- FStateList.Capacity := FStateList.Capacity * 2;
- end;
- ItemState := TItemState.Create;
- try
- ItemState.ItemType := AType;
- ItemState.Enabled := AEnabled;
- ItemState.Obj := AObject;
- ItemState.Level := ALevel;
- ItemState.SubItem := ASubItem;
- ItemState.HasInternalChildren := AHasInternalChildren;
- ItemState.CheckWhenParentChecked := ACheckWhenParentChecked;
- except
- ItemState.Free;
- raise;
- end;
- FStateList.Add(ItemState);
- try
- Result := Items.Add(ACaption);
- except
- FStateList.Delete(FStateList.Count-1);
- ItemState.Free;
- raise;
- end;
- { If the first item in a radio group is being added, and it is top-level or
- has a checked parent, force it to be checked. (We don't want to allow radio
- groups with no selection.) }
- if (AType = itRadio) and not AChecked and AEnabled then begin
- I := GetParentOf(Result);
- { FRequireRadioSelection only affects top-level items; we never allow
- child radio groups with no selection (because nobody should need that) }
- if FRequireRadioSelection or (I <> -1) then
- if (I = -1) or (GetState(I) <> cbUnchecked) then
- if FindCheckedSibling(Result) = -1 then
- AChecked := True;
- end;
- SetChecked(Result, AChecked);
- end;
- function TNewCheckListBox.FindAccel(VK: Word): Integer;
- begin
- for Result := 0 to Items.Count - 1 do
- if CanFocusItem(Result) and IsAccel(VK, Items[Result]) then
- Exit;
- Result := -1;
- end;
- function TNewCheckListBox.FindNextItem(StartFrom: Integer; GoForward,
- SkipUncheckedRadios: Boolean): Integer;
- function ShouldSkip(Index: Integer): Boolean;
- begin
- with ItemStates[Index] do
- Result := (ItemType = itRadio) and (State <> cbChecked)
- end;
- var
- Delta: Integer;
- begin
- if StartFrom < -1 then
- StartFrom := ItemIndex;
- if Items.Count > 0 then
- begin
- Delta := Ord(GoForward) * 2 - 1;
- Result := StartFrom + Delta;
- while (Result >= 0) and (Result < Items.Count) and
- (not CanFocusItem(Result) or SkipUncheckedRadios and ShouldSkip(Result)) do
- Result := Result + Delta;
- if (Result < 0) or (Result >= Items.Count) then
- Result := -1;
- end
- else
- Result := -1;
- end;
- function TNewCheckListBox.GetCaption(Index: Integer): String;
- begin
- Result := Items[Index];
- end;
- function TNewCheckListBox.GetChecked(Index: Integer): Boolean;
- begin
- Result := GetState(Index) <> cbUnchecked;
- end;
- function TNewCheckListBox.GetItemEnabled(Index: Integer): Boolean;
- begin
- Result := ItemStates[Index].Enabled;
- end;
- function TNewCheckListBox.GetItemFontStyle(Index: Integer): TFontStyles;
- begin
- Result := ItemStates[Index].ItemFontStyle;
- end;
- function TNewCheckListBox.GetItemState(Index: Integer): TItemState;
- begin
- Result := FStateList[Index];
- end;
- function TNewCheckListBox.GetLevel(Index: Integer): Byte;
- begin
- Result := ItemStates[Index].Level;
- end;
- function TNewCheckListBox.GetObject(Index: Integer): TObject;
- begin
- Result := ItemStates[Index].Obj;
- end;
- function TNewCheckListBox.GetParentOf(Item: Integer): Integer;
- { Gets index of Item's parent, or -1 if there is none. }
- var
- Level, I: Integer;
- begin
- Level := ItemStates[Item].Level;
- if Level > 0 then
- for I := Item-1 downto 0 do begin
- if ItemStates[I].Level < Level then begin
- Result := I;
- Exit;
- end;
- end;
- Result := -1;
- end;
- function TNewCheckListBox.GetState(Index: Integer): TCheckBoxState;
- begin
- Result := ItemStates[Index].State;
- end;
- function TNewCheckListBox.GetSubItem(Index: Integer): String;
- begin
- Result := ItemStates[Index].SubItem;
- end;
- function TNewCheckListBox.GetSubItemFontStyle(Index: Integer): TFontStyles;
- begin
- Result := ItemStates[Index].SubItemFontStyle;
- end;
- procedure TNewCheckListBox.InvalidateCheck(Index: Integer);
- var
- IRect: TRect;
- begin
- IRect := ItemRect(Index);
- Inc(IRect.Left, (FCheckWidth + 2 * Offset) * (ItemLevel[Index]));
- IRect.Right := IRect.Left + (FCheckWidth + 2 * Offset);
- FlipRect(IRect, ClientRect, FUseRightToLeft);
- InvalidateRect(Handle, @IRect, FThemeData <> 0);
- end;
- procedure TNewCheckListBox.KeyDown(var Key: Word; Shift: TShiftState);
- begin
- if (Key = VK_SPACE) and not (ssAlt in Shift) and (ItemIndex >= 0) and
- (FCaptureIndex < 0) and CanFocusItem(ItemIndex) then
- if FWantTabs then begin
- if not FSpaceDown then begin
- FCaptureIndex := ItemIndex;
- FSpaceDown := True;
- InvalidateCheck(ItemIndex);
- if (FHotIndex <> ItemIndex) and (FHotIndex <> -1) and (FThemeData <> 0) then
- InvalidateCheck(FHotIndex);
- end;
- end
- else
- Toggle(ItemIndex);
- inherited;
- end;
- procedure TNewCheckListBox.KeyUp(var Key: Word; Shift: TShiftState);
- begin
- if (Key = VK_SPACE) and FWantTabs and FSpaceDown and (FCaptureIndex >= 0) then begin
- EndCapture(False);
- if (FHotIndex <> -1) and (FThemeData <> 0) then
- InvalidateCheck(FHotIndex);
- end;
- inherited;
- end;
- procedure TNewCheckListBox.MouseDown(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- var
- Index: Integer;
- begin
- if Button = mbLeft then begin
- Index := ItemAtPos(Point(X, Y), True);
- if (Index <> -1) and CanFocusItem(Index) then
- begin
- if FWantTabs then begin
- if not FSpaceDown then begin
- if not MouseCapture then
- MouseCapture := True;
- FCaptureIndex := Index;
- FLastMouseMoveIndex := Index;
- InvalidateCheck(Index);
- end;
- end
- else
- Toggle(Index);
- end;
- end;
- inherited;
- end;
- procedure TNewCheckListBox.MouseUp(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- var
- Index: Integer;
- begin
- if (Button = mbLeft) and FWantTabs and not FSpaceDown and (FCaptureIndex >= 0) then
- begin
- Index := ItemAtPos(Point(X, Y), True);
- EndCapture(Index <> FCaptureIndex);
- if (FHotIndex <> -1) and (FThemeData <> 0) then
- InvalidateCheck(FHotIndex);
- end;
- end;
- procedure TNewCheckListBox.UpdateHotIndex(NewHotIndex: Integer);
- var
- OldHotIndex: Integer;
- begin
- OldHotIndex := FHotIndex;
- if NewHotIndex <> OldHotIndex then
- begin
- FHotIndex := NewHotIndex;
- if FCaptureIndex = -1 then begin
- if (OldHotIndex <> -1) and (FThemeData <> 0) then
- InvalidateCheck(OldHotIndex);
- if (NewHotIndex <> -1) and (FThemeData <> 0) then
- InvalidateCheck(NewHotIndex);
- end;
- end;
- end;
- procedure TNewCheckListBox.CMMouseLeave(var Message: TMessage);
- begin
- UpdateHotIndex(-1);
- inherited;
- end;
- procedure TNewCheckListBox.SetCaption(Index: Integer; const Value: String);
- begin
- { Changing an item's text actually involves deleting and re-inserting the
- item. Increment FDisableItemStateDeletion so the item state isn't lost. }
- Inc(FDisableItemStateDeletion);
- try
- Items[Index] := Value;
- finally
- Dec(FDisableItemStateDeletion);
- end;
- end;
- procedure TNewCheckListBox.SetChecked(Index: Integer; const AChecked: Boolean);
- begin
- if AChecked then
- CheckItem(Index, coCheck)
- else
- CheckItem(Index, coUncheck);
- end;
- function TNewCheckListBox.FindCheckedSibling(const AIndex: Integer): Integer;
- { Finds a checked sibling of AIndex (which is assumed to be a radio button).
- Returns -1 if no checked sibling was found. }
- var
- ThisLevel, I: Integer;
- begin
- ThisLevel := ItemStates[AIndex].Level;
- for I := AIndex-1 downto 0 do begin
- if ItemStates[I].Level < ThisLevel then
- Break;
- if ItemStates[I].Level = ThisLevel then begin
- if ItemStates[I].ItemType <> itRadio then
- Break;
- if GetState(I) <> cbUnchecked then begin
- Result := I;
- Exit;
- end;
- end;
- end;
- for I := AIndex+1 to Items.Count-1 do begin
- if ItemStates[I].Level < ThisLevel then
- Break;
- if ItemStates[I].Level = ThisLevel then begin
- if ItemStates[I].ItemType <> itRadio then
- Break;
- if GetState(I) <> cbUnchecked then begin
- Result := I;
- Exit;
- end;
- end;
- end;
- Result := -1;
- end;
- function TNewCheckListBox.CheckItem(const Index: Integer;
- const AOperation: TCheckItemOperation): Boolean;
- { Tries to update the checked state of Index. Returns True if any changes were
- made to the state of Index or any of its children. }
- procedure SetItemState(const AIndex: Integer; const AState: TCheckBoxState);
- begin
- if ItemStates[AIndex].State <> AState then begin
- ItemStates[AIndex].State := AState;
- InvalidateCheck(AIndex);
- { Notify MSAA of the state change }
- if Assigned(NotifyWinEventFunc) then
- NotifyWinEventFunc(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT,
- 1 + AIndex);
- end;
- end;
- function CalcState(const AIndex: Integer; ACheck: Boolean): TCheckBoxState;
- { Determines new state for AIndex based on desired checked state (ACheck) and
- current state of the item's immediate children. }
- var
- RootLevel, I: Integer;
- HasChecked, HasUnchecked: Boolean;
- begin
- HasChecked := False;
- HasUnchecked := False;
- RootLevel := ItemStates[AIndex].Level;
- for I := AIndex+1 to Items.Count-1 do begin
- if ItemStates[I].Level <= RootLevel then
- Break;
- if (ItemStates[I].Level = RootLevel+1) and
- (ItemStates[I].ItemType in [itCheck, itRadio]) then begin
- case GetState(I) of
- cbUnchecked: begin
- if (ItemStates[I].ItemType <> itRadio) or
- (FindCheckedSibling(I) = -1) then
- HasUnchecked := True;
- end;
- cbChecked: begin
- HasChecked := True;
- end;
- cbGrayed: begin
- HasChecked := True;
- HasUnchecked := True;
- end;
- end;
- end;
- end;
- { If the parent is a check box with children, don't allow it to be checked
- if none of its children are checked, unless it "has internal children" }
- if HasUnchecked and not HasChecked and
- (ItemStates[AIndex].ItemType = itCheck) and
- not ItemStates[AIndex].HasInternalChildren then
- ACheck := False;
- if ACheck or HasChecked then begin
- if HasUnchecked and (ItemStates[AIndex].ItemType = itCheck) then
- Result := cbGrayed
- else
- Result := cbChecked;
- end
- else
- Result := cbUnchecked;
- end;
- function RecursiveCheck(const AIndex: Integer;
- const AOperation: TCheckItemOperation): Boolean;
- { Checks or unchecks AIndex and all enabled child items of AIndex at any
- level. In radio button groups, only one item per group is checked.
- Returns True if any of the items' states were changed. }
- var
- RootLevel, I: Integer;
- NewState: TCheckBoxState;
- begin
- Result := False;
- RootLevel := ItemStates[AIndex].Level;
- for I := AIndex+1 to Items.Count-1 do begin
- if ItemStates[I].Level <= RootLevel then
- Break;
- if (ItemStates[I].Level = RootLevel+1) and ItemStates[I].Enabled and
- ((AOperation = coUncheck) or
- ((AOperation = coCheckWithChildren) and ItemStates[I].CheckWhenParentChecked) or
- (ItemStates[I].ItemType = itRadio)) then
- { If checking and I is a radio button, don't recurse if a sibling
- already got checked in a previous iteration of this loop. This is
- needed in the following case to prevent all three radio buttons from
- being checked when "Parent check" is checked. In addition, it
- prevents "Child check" from being checked.
- [ ] Parent check
- ( ) Radio 1
- ( ) Radio 2
- ( ) Radio 3
- [ ] Child check
- }
- if (AOperation = coUncheck) or (ItemStates[I].ItemType <> itRadio) or
- (FindCheckedSibling(I) = -1) then
- if RecursiveCheck(I, AOperation) then
- Result := True;
- end;
- NewState := CalcState(AIndex, AOperation <> coUncheck);
- if GetState(AIndex) <> NewState then begin
- SetItemState(AIndex, NewState);
- Result := True;
- end;
- end;
- procedure UncheckSiblings(const AIndex: Integer);
- { Unchecks all siblings (and their children) of AIndex, which is assumed to
- be a radio button. }
- var
- I: Integer;
- begin
- while True do begin
- I := FindCheckedSibling(AIndex);
- if I = -1 then
- Break;
- RecursiveCheck(I, coUncheck);
- end;
- end;
- procedure EnsureChildRadioItemsHaveSelection(const AIndex: Integer);
- { Ensures all radio button groups that are immediate children of AIndex have
- a selected item. }
- var
- RootLevel, I: Integer;
- begin
- RootLevel := ItemStates[AIndex].Level;
- for I := AIndex+1 to Items.Count-1 do begin
- if ItemStates[I].Level <= RootLevel then
- Break;
- if (ItemStates[I].Level = RootLevel+1) and
- (ItemStates[I].ItemType = itRadio) and
- ItemStates[I].Enabled and
- (GetState(I) <> cbChecked) and
- (FindCheckedSibling(I) = -1) then
- { Note: This uses coCheck instead of coCheckWithChildren (or the value
- of AOperation) in order to keep side effects to a minimum. Seems
- like the most logical behavior. For example, in this case:
- [ ] A
- ( ) B
- [ ] C
- [ ] D
- clicking D will cause the radio button B to be checked (out of
- necessity), but won't automatically check its child check box, C.
- (If C were instead a radio button, it *would* be checked.) }
- RecursiveCheck(I, coCheck);
- end;
- end;
- procedure UpdateParentStates(const AIndex: Integer);
- var
- I: Integer;
- ChildChecked: Boolean;
- NewState: TCheckBoxState;
- begin
- I := AIndex;
- while True do begin
- ChildChecked := (GetState(I) <> cbUnchecked);
- I := GetParentOf(I);
- if I = -1 then
- Break;
- { When a child item is checked, must ensure that all sibling radio button
- groups have selections }
- if ChildChecked then
- EnsureChildRadioItemsHaveSelection(I);
- NewState := CalcState(I, GetState(I) <> cbUnchecked);
- { If a parent radio button is becoming checked, uncheck any previously
- selected sibling of that radio button }
- if (NewState <> cbUnchecked) and (ItemStates[I].ItemType = itRadio) then
- UncheckSiblings(I);
- SetItemState(I, NewState);
- end;
- end;
- begin
- if ItemStates[Index].ItemType = itRadio then begin
- { Setting Checked to False on a radio button is a no-op. (A radio button
- may only be unchecked by checking another radio button in the group, or
- by unchecking a parent check box.) }
- if AOperation = coUncheck then begin
- Result := False;
- Exit;
- end;
- { Before checking a new item in a radio group, uncheck any siblings and
- their children }
- UncheckSiblings(Index);
- end;
- { Check or uncheck this item and all its children }
- Result := RecursiveCheck(Index, AOperation);
- { Update state of parents. For example, if a child check box is being
- checked, its parent must also become checked if it isn't already. }
- UpdateParentStates(Index);
- end;
- procedure TNewCheckListBox.SetFlat(Value: Boolean);
- begin
- if Value <> FFlat then
- begin
- FFlat := Value;
- Invalidate;
- end;
- end;
- procedure TNewCheckListBox.SetItemEnabled(Index: Integer; const AEnabled: Boolean);
- begin
- if ItemStates[Index].Enabled <> AEnabled then
- begin
- ItemStates[Index].Enabled := AEnabled;
- InvalidateCheck(Index);
- end;
- end;
- procedure TNewCheckListBox.SetItemFontStyle(Index: Integer; const AItemFontStyle: TFontStyles);
- var
- R: TRect;
- begin
- if ItemStates[Index].ItemFontStyle <> AItemFontStyle then begin
- ItemStates[Index].ItemFontStyle := AItemFontStyle;
- R := ItemRect(Index);
- InvalidateRect(Handle, @R, True);
- end;
- end;
- procedure TNewCheckListBox.SetObject(Index: Integer; const AObject: TObject);
- begin
- ItemStates[Index].Obj := AObject;
- end;
- procedure TNewCheckListBox.SetOffset(AnOffset: Integer);
- begin
- if FOffset <> AnOffset then
- begin
- FOffset := AnOffset;
- Invalidate;
- end;
- end;
- procedure TNewCheckListBox.SetShowLines(Value: Boolean);
- begin
- if FShowLines <> Value then
- begin
- FShowLines := Value;
- Invalidate;
- end;
- end;
- procedure TNewCheckListBox.SetSubItem(Index: Integer; const ASubItem: String);
- var
- OldHeight, NewHeight: Integer;
- R, R2: TRect;
- begin
- if ItemStates[Index].SubItem <> ASubItem then
- begin
- ItemStates[Index].SubItem := ASubItem;
- OldHeight := SendMessage(Handle, LB_GETITEMHEIGHT, Index, 0);
- NewHeight := RemeasureItem(Index);
- R := ItemRect(Index);
- { Scroll subsequent items down or up, if necessary }
- if NewHeight <> OldHeight then begin
- if Index >= TopIndex then begin
- R2 := ClientRect;
- R2.Top := R.Top + OldHeight;
- if not IsRectEmpty(R2) then
- ScrollWindowEx(Handle, 0, NewHeight - OldHeight, @R2, nil, 0, nil,
- SW_INVALIDATE or SW_ERASE);
- end;
- UpdateScrollRange;
- end;
- InvalidateRect(Handle, @R, True);
- end;
- end;
- procedure TNewCheckListBox.SetSubItemFontStyle(Index: Integer; const ASubItemFontStyle: TFontStyles);
- var
- R: TRect;
- begin
- if ItemStates[Index].SubItemFontStyle <> ASubItemFontStyle then begin
- ItemStates[Index].SubItemFontStyle := ASubItemFontStyle;
- R := ItemRect(Index);
- InvalidateRect(Handle, @R, True);
- end;
- end;
- procedure TNewCheckListBox.Toggle(Index: Integer);
- begin
- case ItemStates[Index].ItemType of
- itCheck:
- case ItemStates[Index].State of
- cbUnchecked: CheckItem(Index, coCheckWithChildren);
- cbChecked: CheckItem(Index, coUncheck);
- cbGrayed:
- { First try checking, but if that doesn't work because of children
- that are disabled and unchecked, try unchecking }
- if not CheckItem(Index, coCheckWithChildren) then
- CheckItem(Index, coUncheck);
- end;
- itRadio: CheckItem(Index, coCheckWithChildren);
- end;
- if Assigned(FOnClickCheck) then
- FOnClickCheck(Self);
- end;
- procedure TNewCheckListBox.UpdateThreads;
- function LastImmediateChildOf(Item: Integer): Integer;
- var
- L: Integer;
- begin
- Result := -1;
- L := ItemLevel[Item] + 1;
- Inc(Item);
- while (Item < Items.Count) and (ItemLevel[Item] >= L) do
- begin
- if ItemLevel[Item] = L then
- Result := Item;
- Inc(Item);
- end;
- if Result >= 0 then
- ItemStates[Result].IsLastChild := True;
- end;
- var
- I, J, LastChild, L: Integer;
- begin
- for I := 0 to Items.Count - 1 do
- begin
- ItemStates[I].ThreadCache := [0]; //Doing ':= []' causes a "F2084 Internal Error: C21846" compiler error on Delphi 10.3 Rio }
- Exclude(ItemStates[I].ThreadCache, 0); //
- ItemStates[I].IsLastChild := False;
- end;
- for I := 0 to Items.Count - 1 do
- begin
- LastChild := LastImmediateChildOf(I);
- L := ItemLevel[I];
- for J := I + 1 to LastChild do
- Include(ItemStates[J].ThreadCache, L);
- end;
- end;
- procedure TNewCheckListBox.LBDeleteString(var Message: TMessage);
- var
- I: Integer;
- ItemState: TItemState;
- begin
- inherited;
- if FDisableItemStateDeletion = 0 then begin
- I := Message.WParam;
- if (I >= 0) and (I < FStateList.Count) then begin
- ItemState := FStateList[I];
- FStateList.Delete(I);
- ItemState.Free;
- end;
- end;
- end;
- procedure TNewCheckListBox.LBResetContent(var Message: TMessage);
- var
- I: Integer;
- ItemState: TItemState;
- begin
- inherited;
- if FDisableItemStateDeletion = 0 then
- for I := FStateList.Count-1 downto 0 do begin
- ItemState := FStateList[I];
- FStateList.Delete(I);
- ItemState.Free;
- end;
- end;
- procedure TNewCheckListBox.WMGetDlgCode(var Message: TWMGetDlgCode);
- begin
- inherited;
- if FWantTabs then
- Message.Result := Message.Result and not DLGC_WANTCHARS;
- end;
- procedure TNewCheckListBox.WMKeyDown(var Message: TWMKeyDown);
- var
- GoForward, Arrows: Boolean;
- I: Integer;
- Prnt, Ctrl: TWinControl;
- begin
- { If space is pressed, avoid flickering -- exit now. }
- if not FWantTabs or (Message.CharCode = VK_SPACE) then
- begin
- inherited;
- Exit;
- end;
- Arrows := True;
- case Message.CharCode of
- VK_TAB:
- begin
- GoForward := GetKeyState(VK_SHIFT) >= 0;
- Arrows := False
- end;
- VK_DOWN, VK_RIGHT: GoForward := True;
- VK_UP, VK_LEFT: GoForward := False
- else
- if FSpaceDown then EndCapture(True);
- inherited;
- Exit;
- end;
- EndCapture(not FSpaceDown);
- SendMessage(Handle, WM_CHANGEUISTATE, UIS_CLEAR or (UISF_HIDEFOCUS shl 16), 0);
- if Arrows or TabStop then
- I := FindNextItem(-2, GoForward, not Arrows)
- else
- I := -1;
- if I < 0 then
- begin
- Prnt := nil;
- if not Arrows then
- Prnt := GetParentForm(Self);
- if Prnt = nil then Prnt := Parent;
- if Prnt <> nil then
- begin
- Ctrl := TWinControlAccess(Prnt).FindNextControl(Self, GoForward, True, Arrows);
- if (Ctrl <> nil) and (Ctrl <> Self) then
- begin
- Ctrl.SetFocus;
- Exit;
- end;
- end;
- if GoForward then
- I := FindNextItem(-1, True, not Arrows)
- else
- I := FindNextItem(Items.Count, False, not Arrows);
- end;
- ItemIndex := I;
- if (I <> -1) and (ItemStates[I].ItemType = itRadio) and Arrows then
- Toggle(I);
- end;
- procedure TNewCheckListBox.WMMouseMove(var Message: TWMMouseMove);
- var
- Pos: TPoint;
- Index, NewHotIndex: Integer;
- Rect: TRect;
- Indent: Integer;
- begin
- Pos := SmallPointToPoint(Message.Pos);
- Index := ItemAtPos(Pos, True);
- if FCaptureIndex >= 0 then begin
- if not FSpaceDown and (Index <> FLastMouseMoveIndex) then begin
- if (FLastMouseMoveIndex = FCaptureIndex) or (Index = FCaptureIndex) then
- InvalidateCheck(FCaptureIndex);
- FLastMouseMoveIndex := Index;
- end
- end;
- NewHotIndex := -1;
- if (Index <> -1) and CanFocusItem(Index) then
- begin
- Rect := ItemRect(Index);
- Indent := (FOffset * 2 + FCheckWidth);
- if FWantTabs or ((Pos.X >= Rect.Left + Indent * ItemLevel[Index]) and
- (Pos.X < Rect.Left + Indent * (ItemLevel[Index] + 1))) then
- NewHotIndex := Index;
- end;
- UpdateHotIndex(NewHotIndex);
- end;
- procedure TNewCheckListBox.WMNCHitTest(var Message: TWMNCHitTest);
- var
- I: Integer;
- begin
- inherited;
- if FWantTabs and not (csDesigning in ComponentState) then
- begin
- if Message.Result = HTCLIENT then
- begin
- I := ItemAtPos(ScreenToClient(SmallPointToPoint(Message.Pos)), True);
- if (I < 0) or not CanFocusItem(I) then
- begin
- UpdateHotIndex(-1);
- Message.Result := 12345;
- Exit;
- end;
- end;
- end;
- end;
- procedure TNewCheckListBox.WMSetFocus(var Message: TWMSetFocus);
- begin
- FWheelAccum := 0;
- inherited;
- end;
- procedure TNewCheckListBox.WMSize(var Message: TWMSize);
- var
- I: Integer;
- begin
- inherited;
- { When the scroll bar appears/disappears, the client width changes and we
- must recalculate the height of the items }
- for I := Items.Count-1 downto 0 do
- RemeasureItem(I);
- UpdateScrollRange;
- end;
- procedure TNewCheckListBox.WMThemeChanged(var Message: TMessage);
- begin
- { Don't Run to Cursor into this function, it will interrupt up the theme change }
- UpdateThemeData(True, True);
- inherited;
- end;
- procedure TNewCheckListBox.WMUpdateUIState(var Message: TMessage);
- begin
- Invalidate;
- inherited;
- end;
- procedure TNewCheckListBox.WMGetObject(var Message: TMessage);
- begin
- if (Message.LParam = Integer(OBJID_CLIENT)) and InitializeOleAcc then begin
- if FAccObjectInstance = nil then begin
- try
- FAccObjectInstance := TAccObject.Create(Self);
- except
- inherited;
- Exit;
- end;
- end;
- Message.Result := LresultFromObjectFunc(IID_IAccessible, Message.WParam,
- TAccObject(FAccObjectInstance));
- end
- else
- inherited;
- end;
- { TAccObject }
- constructor TAccObject.Create(AControl: TNewCheckListBox);
- begin
- inherited Create;
- if CreateStdAccessibleObjectFunc(AControl.Handle, Integer(OBJID_CLIENT),
- IID_IAccessible, Pointer(FStdAcc)) <> S_OK then begin
- { Note: The user will never actually see this message since the call to
- TAccObject.Create in TNewCheckListBox.WMGetObject is protected by a
- try..except. }
- raise Exception.Create('CreateStdAccessibleObject failed');
- end;
- FControl := AControl;
- end;
- destructor TAccObject.Destroy;
- begin
- { If FControl is assigned, then we are being destroyed before the control --
- the usual case. Clear FControl's reference to us. }
- if Assigned(FControl) then begin
- FControl.FAccObjectInstance := nil;
- FControl := nil;
- end;
- if Assigned(FStdAcc) then
- FStdAcc.Release;
- inherited;
- end;
- procedure TAccObject.ControlDestroying;
- begin
- { Set FControl to nil, since it's no longer valid }
- FControl := nil;
- { Take this opportunity to disconnect remote clients, i.e. don't allow them
- to call us anymore. This prevents invalid memory accesses if this unit's
- code is in a DLL, and the application subsequently unloads the DLL while
- remote clients still hold (and are using) references to this TAccObject. }
- CoDisconnectObject(Self, 0);
- { NOTE: Don't access Self in any way at this point. The CoDisconnectObject
- call likely caused all references to be relinquished and Self to be
- destroyed. }
- end;
- function TAccObject.QueryInterface(const iid: TIID; var obj): HRESULT;
- begin
- if IsEqualIID(iid, IID_IUnknown) or
- IsEqualIID(iid, IID_IDispatch) or
- IsEqualIID(iid, IID_IAccessible) then begin
- Pointer(obj) := Self;
- AddRef;
- Result := S_OK;
- end
- else begin
- Pointer(obj) := nil;
- Result := E_NOINTERFACE;
- end;
- end;
- function TAccObject.AddRef: Longint;
- begin
- Inc(FRefCount);
- Result := FRefCount;
- end;
- function TAccObject.Release: Longint;
- begin
- Dec(FRefCount);
- Result := FRefCount;
- if Result = 0 then
- Destroy;
- end;
- function TAccObject.GetTypeInfoCount(var ctinfo: Integer): HRESULT;
- begin
- Result := E_NOTIMPL;
- end;
- function TAccObject.GetTypeInfo(itinfo: Integer; lcid: TLCID; var tinfo: ITypeInfo): HRESULT;
- begin
- Result := E_NOTIMPL;
- end;
- function TAccObject.GetIDsOfNames(const iid: TIID; rgszNames: POleStrList;
- cNames: Integer; lcid: TLCID; rgdispid: PDispIDList): HRESULT;
- begin
- Result := E_NOTIMPL;
- end;
- function TAccObject.Invoke(dispIDMember: TDispID; const iid: TIID; lcid: TLCID;
- flags: Word; var dispParams: TDispParams; varResult: PVariant;
- excepInfo: PExcepInfo; argErr: PInteger): HRESULT;
- begin
- Result := E_NOTIMPL;
- end;
- function TAccObject.accDoDefaultAction(varChild: NewOleVariant): HRESULT;
- begin
- { A list box's default action is Double Click, which is useless for a
- list of check boxes. }
- Result := DISP_E_MEMBERNOTFOUND;
- end;
- function TAccObject.accHitTest(xLeft, yTop: Integer;
- var pvarID: NewOleVariant): HRESULT;
- begin
- Result := FStdAcc.accHitTest(xLeft, yTop, pvarID);
- end;
- function TAccObject.accLocation(var pxLeft, pyTop, pcxWidth,
- pcyHeight: Integer; varChild: NewOleVariant): HRESULT;
- begin
- Result := FStdAcc.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);
- end;
- function TAccObject.accNavigate(navDir: Integer; varStart: NewOleVariant;
- var pvarEnd: NewOleVariant): HRESULT;
- begin
- Result := FStdAcc.accNavigate(navDir, varStart, pvarEnd);
- end;
- function TAccObject.accSelect(flagsSelect: Integer;
- varChild: NewOleVariant): HRESULT;
- begin
- Result := FStdAcc.accSelect(flagsSelect, varChild);
- end;
- function TAccObject.get_accChild(varChild: NewOleVariant;
- var ppdispChild: IDispatch): HRESULT;
- begin
- Result := FStdAcc.get_accChild(varChild, ppdispChild);
- end;
- function TAccObject.get_accChildCount(var pcountChildren: Integer): HRESULT;
- begin
- Result := FStdAcc.get_accChildCount(pcountChildren);
- end;
- function TAccObject.get_accDefaultAction(varChild: NewOleVariant;
- var pszDefaultAction: NewWideString): HRESULT;
- begin
- { A list box's default action is Double Click, which is useless for a
- list of check boxes. }
- pszDefaultAction := nil;
- Result := S_FALSE;
- end;
- function TAccObject.get_accDescription(varChild: NewOleVariant;
- var pszDescription: NewWideString): HRESULT;
- begin
- Result := FStdAcc.get_accDescription(varChild, pszDescription);
- end;
- function TAccObject.get_accFocus(var pvarID: NewOleVariant): HRESULT;
- begin
- Result := FStdAcc.get_accFocus(pvarID);
- end;
- function TAccObject.get_accHelp(varChild: NewOleVariant;
- var pszHelp: NewWideString): HRESULT;
- begin
- Result := FStdAcc.get_accHelp(varChild, pszHelp);
- end;
- function TAccObject.get_accHelpTopic(var pszHelpFile: NewWideString;
- varChild: NewOleVariant; var pidTopic: Integer): HRESULT;
- begin
- Result := FStdAcc.get_accHelpTopic(pszHelpFile, varChild, pidTopic);
- end;
- function TAccObject.get_accKeyboardShortcut(varChild: NewOleVariant;
- var pszKeyboardShortcut: NewWideString): HRESULT;
- begin
- Result := FStdAcc.get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
- end;
- function TAccObject.get_accName(varChild: NewOleVariant;
- var pszName: NewWideString): HRESULT;
- begin
- Result := FStdAcc.get_accName(varChild, pszName);
- end;
- function TAccObject.get_accParent(var ppdispParent: IDispatch): HRESULT;
- begin
- Result := FStdAcc.get_accParent(ppdispParent);
- end;
- function TAccObject.get_accRole(varChild: NewOleVariant;
- var pvarRole: NewOleVariant): HRESULT;
- begin
- pvarRole.vt := VT_EMPTY;
- if FControl = nil then begin
- Result := E_FAIL;
- Exit;
- end;
- if varChild.vt <> VT_I4 then begin
- Result := E_INVALIDARG;
- Exit;
- end;
- if varChild.lVal = CHILDID_SELF then begin
- pvarRole.lVal := ROLE_SYSTEM_OUTLINE;
- pvarRole.vt := VT_I4;
- Result := S_OK;
- end
- else begin
- try
- case FControl.ItemStates[varChild.lVal-1].ItemType of
- itCheck: pvarRole.lVal := ROLE_SYSTEM_CHECKBUTTON;
- itRadio: pvarRole.lVal := ROLE_SYSTEM_RADIOBUTTON;
- else
- pvarRole.lVal := ROLE_SYSTEM_STATICTEXT;
- end;
- pvarRole.vt := VT_I4;
- Result := S_OK;
- except
- Result := E_INVALIDARG;
- end;
- end;
- end;
- function TAccObject.get_accSelection(var pvarChildren: NewOleVariant): HRESULT;
- begin
- Result := FStdAcc.get_accSelection(pvarChildren);
- end;
- function TAccObject.get_accState(varChild: NewOleVariant;
- var pvarState: NewOleVariant): HRESULT;
- var
- ItemState: TItemState;
- begin
- Result := FStdAcc.get_accState(varChild, pvarState);
- try
- if (Result = S_OK) and (varChild.vt = VT_I4) and
- (varChild.lVal <> CHILDID_SELF) and (pvarState.vt = VT_I4) and
- Assigned(FControl) then begin
- ItemState := FControl.ItemStates[varChild.lVal-1];
- case ItemState.State of
- cbChecked: pvarState.lVal := pvarState.lVal or STATE_SYSTEM_CHECKED;
- cbGrayed: pvarState.lVal := pvarState.lVal or STATE_SYSTEM_MIXED;
- end;
- if not ItemState.Enabled then
- pvarState.lVal := pvarState.lVal or STATE_SYSTEM_UNAVAILABLE;
- end;
- except
- Result := E_INVALIDARG;
- end;
- end;
- function TAccObject.get_accValue(varChild: NewOleVariant;
- var pszValue: NewWideString): HRESULT;
- begin
- pszValue := nil;
- if FControl = nil then begin
- Result := E_FAIL;
- Exit;
- end;
- if varChild.vt <> VT_I4 then begin
- Result := E_INVALIDARG;
- Exit;
- end;
- if varChild.lVal = CHILDID_SELF then
- Result := S_FALSE
- else begin
- { Return the level as the value, like standard tree view controls do.
- Not sure if any screen readers will actually use this, seeing as we
- aren't a real tree view control. }
- try
- pszValue := StringToOleStr(IntToStr(FControl.ItemStates[varChild.lVal-1].Level));
- Result := S_OK;
- except
- Result := E_INVALIDARG;
- end;
- end;
- end;
- function TAccObject.put_accName(varChild: NewOleVariant;
- const pszName: NewWideString): HRESULT;
- begin
- Result := S_FALSE;
- end;
- function TAccObject.put_accValue(varChild: NewOleVariant;
- const pszValue: NewWideString): HRESULT;
- begin
- Result := S_FALSE;
- end;
- procedure Register;
- begin
- RegisterComponents('JR', [TNewCheckListBox]);
- end;
- { Note: This COM initialization code based on code from DBTables }
- var
- SaveInitProc: Pointer;
- NeedToUninitialize: Boolean;
- procedure InitCOM;
- begin
- if SaveInitProc <> nil then TProcedure(SaveInitProc);
- NeedToUninitialize := SUCCEEDED(CoInitialize(nil));
- end;
- initialization
- if not IsLibrary then begin
- SaveInitProc := InitProc;
- InitProc := @InitCOM;
- end;
- InitThemeLibrary;
- NotifyWinEventFunc := GetProcAddress(GetModuleHandle(user32), 'NotifyWinEvent');
- finalization
- if NeedToUninitialize then
- CoUninitialize;
- end.
|