unit VA508AccessibilityPE; interface uses Windows, SysUtils, DesignIntf, DesignEditors, DesignConst, TypInfo, Controls, StdCtrls, Classes, Forms, VA508AccessibilityManager, Dialogs, ColnEdit, RTLConsts; type TVA508AccessibilityManager4PE = class(TVA508AccessibilityManager); TVA508AccessibilityPropertyMapper = class(TStringProperty) public function GetAttributes: TPropertyAttributes; override; procedure GetProperties(Proc: TGetPropProc); override; end; TVA508NestedPropertyType = (ptText, ptLabel, ptProperty, ptDefault); //, ptEvent); TVA508NestedPropertyEditor = class(TNestedProperty) strict private FName: String; FType: TVA508NestedPropertyType; FManager: TVA508AccessibilityManager4PE; protected property Manager: TVA508AccessibilityManager4PE read FManager; public constructor Create(AParent: TVA508AccessibilityPropertyMapper; AName: String; PType: TVA508NestedPropertyType); function AllEqual: Boolean; override; procedure Edit; override; function GetEditLimit: Integer; override; function GetAttributes: TPropertyAttributes; override; function GetName: string; override; function GetValue: string; override; procedure GetValues(Proc: TGetStrProc); override; procedure SetValue(const Value: string); override; end; { TVA508AccessibilityEventPropertyEditor = class(TVA508NestedPropertyEditor, IMethodProperty) protected function GetMethodValue(Index: Integer): TMethod; public function AllNamed: Boolean; virtual; procedure Edit; override; function GetValue: string; override; procedure GetValues(Proc: TGetStrProc); override; procedure SetValue(const AValue: string); override; function GetFormMethodName: string; virtual; function GetTrimmedEventName: string; end; } TVA508CollectionPropertyEditor = class(TCollectionProperty) public function GetColOptions: TColOptions; override; end; TVA508AccessibilityLabelPropertyEditor = class(TComponentProperty) private FManager: TVA508AccessibilityManager4PE; function GetManager: TVA508AccessibilityManager4PE; public function GetAttributes: TPropertyAttributes; override; procedure GetProperties(Proc: TGetPropProc); override; function GetValue: string; override; procedure GetValues(Proc: TGetStrProc); override; procedure SetValue(const Value: string); override; end; TVA508AccessibilityPropertyPropertyEditor = class(TStringProperty) private FManager: TVA508AccessibilityManager4PE; function GetManager: TVA508AccessibilityManager4PE; function GetRootComponent(index: integer): TWinControl; public function AllEqual: Boolean; override; function GetAttributes: TPropertyAttributes; override; function GetEditLimit: Integer; override; function GetValue: string; override; procedure GetValues(Proc: TGetStrProc); override; procedure SetValue(const Value: string); override; end; TVA508AccessibilityComponentPropertyEditor = class(TComponentProperty) public function GetAttributes: TPropertyAttributes; override; end; const WinControlPropertyToMap = 'Hint'; procedure Register; implementation function GetAccessibilityManager(Editor: TPropertyEditor; Index: integer): TVA508AccessibilityManager4PE; var Control, Root: TComponent; i: integer; begin Result := nil; if assigned(Editor.GetComponent(Index)) and (Editor.GetComponent(Index) is TComponent) then begin Control := TComponent(Editor.GetComponent(Index)); Root := Control; while (assigned(Root) and (not (Root is TCustomForm))) do Root := Root.Owner; if assigned(Root) and (Root is TCustomForm) then begin for i := 0 to Root.ComponentCount-1 do begin if Root.Components[i] is TVA508AccessibilityManager then begin Result := TVA508AccessibilityManager4PE(Root.Components[i]); exit; end; end; end; end; end; function AllComponentsHaveSameManager(Editor: TPropertyEditor): boolean; var i: integer; manager: TVA508AccessibilityManager4PE; begin manager := GetAccessibilityManager(Editor, 0); Result := assigned(manager); if (not result) or (Editor.PropCount < 2) then exit; for i := 1 to Editor.PropCount-1 do begin if (GetAccessibilityManager(Editor, i) <> manager) then begin Result := FALSE; exit; end; end; end; procedure GetStringPropertyNames(Manager: TVA508AccessibilityManager4PE; Component: TWinControl; List: TStringList; Add: boolean); var i: Integer; current: TStringList; begin current := TStringList.Create; try Manager.GetProperties(Component, current); if Add then list.Assign(current) else begin for I := List.Count - 1 downto 0 do begin if current.IndexOf(list[i]) < 0 then List.Delete(i); end; end; finally current.Free; end; end; function QVal(txt: string): string; begin Result := '="' + txt + '"'; end; function StripQVal(text: string): string; var i: integer; begin i := pos('=', text); if (i > 0) then Result := copy(text,1,i-1) else Result := text; end; { TVA508AccessibilityPropertyMapper } const DelphiPaletteName = 'VA 508'; function TVA508AccessibilityPropertyMapper.GetAttributes: TPropertyAttributes; begin if AllComponentsHaveSameManager(Self) then Result := [paMultiSelect, paRevertable, paSubProperties] else Result := inherited GetAttributes; end; procedure TVA508AccessibilityPropertyMapper.GetProperties( Proc: TGetPropProc); begin if not AllComponentsHaveSameManager(Self) then exit; Proc(TVA508NestedPropertyEditor.Create(Self, AccessibilityLabelPropertyName, ptLabel)); Proc(TVA508NestedPropertyEditor.Create(Self, AccessibilityPropertyPropertyName, ptProperty)); Proc(TVA508NestedPropertyEditor.Create(Self, AccessibilityTextPropertyName, ptText)); Proc(TVA508NestedPropertyEditor.Create(Self, AccessibilityUseDefaultPropertyName, ptDefault)); // Proc(TVA508AccessibilityEventPropertyEditor.Create(Self, AccessibilityEventPropertyName, ptEvent)); end; { TVA508NestedStringProperty } function TVA508NestedPropertyEditor.AllEqual: Boolean; var i: Integer; txt, prop: string; lbl: TLabel; // V, T: TMethod; default: boolean; begin if PropCount > 1 then begin Result := False; if not (GetComponent(0) is TWinControl) then exit; case FType of ptText: begin txt := FManager.AccessText[TWinControl(GetComponent(0))]; for i := 1 to PropCount - 1 do if txt <> FManager.AccessText[TWinControl(GetComponent(i))] then exit; end; ptLabel: begin lbl := FManager.AccessLabel[TWinControl(GetComponent(0))]; for i := 1 to PropCount - 1 do if lbl <> FManager.AccessLabel[TWinControl(GetComponent(i))] then exit; end; ptProperty: begin prop := FManager.AccessProperty[TWinControl(GetComponent(0))]; for i := 1 to PropCount - 1 do if prop <> FManager.AccessProperty[TWinControl(GetComponent(i))] then exit; end; ptDefault: begin default := FManager.UseDefault[TWinControl(GetComponent(0))]; for i := 1 to PropCount - 1 do if default <> FManager.UseDefault[TWinControl(GetComponent(i))] then exit; end; { ptEvent: begin V := TMethod(FManager.OnComponentAccessRequest[TWinControl(GetComponent(0))]); for i := 1 to PropCount - 1 do begin T := TMethod(FManager.OnComponentAccessRequest[TWinControl(GetComponent(i))]); if (T.Code <> V.Code) or (T.Data <> V.Data) then Exit; end; end;} end; end; Result := True; end; constructor TVA508NestedPropertyEditor.Create(AParent: TVA508AccessibilityPropertyMapper; AName: String; PType: TVA508NestedPropertyType); begin inherited Create(AParent); FManager := GetAccessibilityManager(AParent, 0); FName := AName; FType := PType; end; procedure TVA508NestedPropertyEditor.Edit; var lbl: TLabel; begin if (FType = ptLabel) and (Designer.GetShiftState * [ssCtrl, ssLeft] = [ssCtrl, ssLeft]) then begin lbl := FManager.AccessLabel[TWinControl(GetComponent(0))]; if assigned(lbl) then Designer.SelectComponent(lbl) else inherited Edit; end else inherited Edit; end; function TVA508NestedPropertyEditor.GetAttributes: TPropertyAttributes; begin case FType of ptText: Result := [paMultiSelect, paRevertable, paAutoUpdate]; ptLabel, ptProperty: Result := [paMultiSelect, paRevertable, paValueList, paSortList, paAutoUpdate]; ptDefault: Result := [paMultiSelect, paValueList, paSortList, paRevertable]; // ptEvent: // Result := [paMultiSelect, paValueList, paSortList, paRevertable]; else Result := []; end; end; function TVA508NestedPropertyEditor.GetEditLimit: Integer; begin case FType of ptText: Result := 32767; ptDefault : Result := 63; // ptEvent: Result := MaxIdentLength; else // ptLabel, ptProperty: Result := 127; end; end; function TVA508NestedPropertyEditor.GetName: string; begin Result := FName; end; function TVA508NestedPropertyEditor.GetValue: string; var lbl: TLabel; Default: boolean; begin Result := ''; if not (GetComponent(0) is TWinControl) then exit; case FType of ptLabel: begin lbl := FManager.AccessLabel[TWinControl(GetComponent(0))]; if assigned(lbl) then Result := FManager.GetComponentName(lbl) + QVal(lbl.Caption); end; ptText: Result := FManager.AccessText[TWinControl(GetComponent(0))]; ptProperty: begin Result := FManager.AccessProperty[TWinControl(GetComponent(0))]; if Result <> '' then Result := Result + QVal(GetPropValue(GetComponent(0), Result)); end; ptDefault: begin Default := FManager.UseDefault[TWinControl(GetComponent(0))]; Result := GetEnumName(TypeInfo(Boolean), Ord(Default)); end; end; end; procedure TVA508NestedPropertyEditor.GetValues(Proc: TGetStrProc); var list: TStringList; i: integer; name: string; begin list := TStringList.Create; try case FType of ptLabel: begin FManager.GetLabelStrings(list); for i := 0 to list.count-1 do Proc(list[i]); end; ptProperty: begin GetStringPropertyNames(FManager, TWinControl(GetComponent(0)), list, TRUE); if PropCount > 1 then begin for i := 1 to PropCount-1 do begin if GetComponent(i) is TWinControl then GetStringPropertyNames(FManager, TWinControl(GetComponent(i)), list, FALSE); end; end; list.Sort; for i := 0 to list.count-1 do begin name := list[i]; if PropCount = 1 then name := name + QVal(GetPropValue(GetComponent(0), name)); Proc(name); end; end; ptDefault: begin Proc(GetEnumName(TypeInfo(Boolean), Ord(False))); Proc(GetEnumName(TypeInfo(Boolean), Ord(True))); end; end; finally list.free; end; end; procedure TVA508NestedPropertyEditor.SetValue(const Value: string); var i, BVal: Integer; lbl: TLabel; cmp: TComponent; Name: String; begin BVal := Ord(FALSE); lbl := nil; case FType of ptLabel: begin Name := StripQVal(Value); cmp := Designer.GetComponent(Name); if (cmp is TLabel) then lbl := TLabel(cmp); end; ptProperty: Name := StripQVal(Value); ptDefault: begin BVal := GetEnumValue(TypeInfo(Boolean), Value); with GetTypeData(TypeInfo(Boolean))^ do if (BVal < MinValue) or (BVal > MaxValue) then raise EPropertyError.CreateRes(@SInvalidPropertyValue); end; end; for i := 0 to PropCount - 1 do begin if GetComponent(i) is TWinControl then begin case FType of ptText: FManager.AccessText[TWinControl(GetComponent(i))] := Value; ptLabel: FManager.AccessLabel[TWinControl(GetComponent(i))] := lbl; ptProperty: FManager.AccessProperty[TWinControl(GetComponent(i))] := Name; ptDefault: FManager.UseDefault[TWinControl(GetComponent(i))] := Boolean(BVal); end; end; end; Modified; end; (* { TVA508AccessibilityEventPropertyEditor } function TVA508AccessibilityEventPropertyEditor.AllNamed: Boolean; var I: Integer; begin Result := True; for I := 0 to PropCount - 1 do if GetComponent(I).GetNamePath = '' then begin Result := False; Break; end; end; procedure TVA508AccessibilityEventPropertyEditor.Edit; var FormMethodName: string; CurDesigner: IDesigner; begin CurDesigner := Designer; { Local property so if designer is nil'ed out, no AV will happen } if not AllNamed then raise EPropertyError.CreateRes(@SCannotCreateName); FormMethodName := GetValue; if (FormMethodName = '') or CurDesigner.MethodFromAncestor(GetMethodValue(0)) then begin if FormMethodName = '' then FormMethodName := GetFormMethodName; if FormMethodName = '' then raise EPropertyError.CreateRes(@SCannotCreateName); SetValue(FormMethodName); end; CurDesigner.ShowMethod(FormMethodName); end; function TVA508AccessibilityEventPropertyEditor.GetFormMethodName: string; var I: Integer; begin if GetComponent(0) = Designer.GetRoot then begin Result := Designer.GetRootClassName; if (Result <> '') and (Result[1] = 'T') then Delete(Result, 1, 1); end else begin Result := Designer.GetObjectName(GetComponent(0)); for I := Length(Result) downto 1 do if Result[I] in ['.', '[', ']', '-', '>'] then Delete(Result, I, 1); end; if Result = '' then raise EPropertyError.CreateRes(@SCannotCreateName); Result := Result + GetTrimmedEventName; end; function TVA508AccessibilityEventPropertyEditor.GetMethodValue(Index: Integer): TMethod; begin if not (GetComponent(Index) is TWinControl) then begin Result.Code := nil; Result.Data := nil; end else Result := TMethod(Manager.OnComponentAccessRequest[TWinControl(GetComponent(Index))]); end; { TVA508AccessibilityEventPropertyEditor } function TVA508AccessibilityEventPropertyEditor.GetTrimmedEventName: string; begin Result := GetName; if (Length(Result) >= 2) and (Result[1] in ['O', 'o']) and (Result[2] in ['N', 'n']) then Delete(Result,1,2); end; function TVA508AccessibilityEventPropertyEditor.GetValue: string; begin Result := Designer.GetMethodName(GetMethodValue(0)); end; procedure TVA508AccessibilityEventPropertyEditor.GetValues(Proc: TGetStrProc); begin Designer.GetMethods(GetTypeData(TypeInfo(TVA508ComponentScreenReaderEvent)), Proc); end; procedure TVA508AccessibilityEventPropertyEditor.SetValue(const AValue: string); var CurDesigner: IDesigner; procedure CheckChainCall(const MethodName: string; Method: TMethod); var Persistent: TPersistent; Component: TComponent; InstanceMethod: string; Instance: TComponent; begin Persistent := GetComponent(0); if Persistent is TComponent then begin Component := TComponent(Persistent); if (Component.Name <> '') and (Method.Data <> CurDesigner.GetRoot) and (TObject(Method.Data) is TComponent) then begin Instance := TComponent(Method.Data); InstanceMethod := Instance.MethodName(Method.Code); if InstanceMethod <> '' then CurDesigner.ChainCall(MethodName, Instance.Name, InstanceMethod, GetTypeData(TypeInfo(TVA508ComponentScreenReaderEvent))); end; end; end; var NewMethod: Boolean; CurValue: string; OldMethod: TMethod; i: integer; event: TVA508ComponentScreenReaderEvent; begin CurDesigner := Designer; if not AllNamed then raise EPropertyError.CreateRes(@SCannotCreateName); CurValue:= GetValue; if (CurValue <> '') and (AValue <> '') and (SameText(CurValue, AValue) or not CurDesigner.MethodExists(AValue)) and not CurDesigner.MethodFromAncestor(GetMethodValue(0)) then CurDesigner.RenameMethod(CurValue, AValue) else begin NewMethod := (AValue <> '') and not CurDesigner.MethodExists(AValue); OldMethod := GetMethodValue(0); event := TVA508ComponentScreenReaderEvent(CurDesigner.CreateMethod(AValue, GetTypeData(TypeInfo(TVA508ComponentScreenReaderEvent)))); for i := 0 to PropCount - 1 do begin if (GetComponent(i) is TWinControl) then Manager.OnComponentAccessRequest[TWinControl(GetComponent(i))] := event; end; if NewMethod then begin { Designer may have been nil'ed out this point when the code editor recieved focus. This fixes an AV by using a local variable which keeps a reference to the designer } if (PropCount = 1) and (OldMethod.Data <> nil) and (OldMethod.Code <> nil) then CheckChainCall(AValue, OldMethod); CurDesigner.ShowMethod(AValue); end; end; Modified; end; *) { TVA508CollectionProperty } function TVA508CollectionPropertyEditor.GetColOptions: TColOptions; begin Result := [coMove]; end; { TVA508AccessibilityLabelPropertyEditor } function TVA508AccessibilityLabelPropertyEditor.GetAttributes: TPropertyAttributes; begin Result := [paMultiSelect, paRevertable, paValueList, paSortList, paAutoUpdate]; end; function TVA508AccessibilityLabelPropertyEditor.GetManager: TVA508AccessibilityManager4PE; begin if not assigned(FManager) then FManager := TVA508AccessibilityManager4PE(TVA508AccessibilityItem(GetComponent(0)).Manager); Result := FManager; end; procedure TVA508AccessibilityLabelPropertyEditor.GetProperties( Proc: TGetPropProc); begin exit; end; function TVA508AccessibilityLabelPropertyEditor.GetValue: string; var lbl: TLabel; begin lbl := TVA508AccessibilityItem(GetComponent(0)).AccessLabel; if assigned(lbl) then Result := GetManager.GetComponentName(lbl) + QVal(lbl.Caption); end; procedure TVA508AccessibilityLabelPropertyEditor.GetValues(Proc: TGetStrProc); var i: integer; list: TStringList; begin list := TStringList.Create; try GetManager.GetLabelStrings(list); for i := 0 to list.count-1 do Proc(list[i]); finally list.Free; end; end; procedure TVA508AccessibilityLabelPropertyEditor.SetValue(const Value: string); begin inherited SetValue(StripQVal(Value)); end; { TVA508AccessibilityPropertyPropertyEditor } function TVA508AccessibilityPropertyPropertyEditor.AllEqual: Boolean; var i: integer; prop: string; begin if PropCount > 1 then begin Result := FALSE; prop := GetManager.AccessProperty[TWinControl(GetComponent(0))]; for i := 1 to PropCount - 1 do if prop <> FManager.AccessProperty[TWinControl(GetComponent(i))] then exit; end; Result := TRUE; end; function TVA508AccessibilityPropertyPropertyEditor.GetAttributes: TPropertyAttributes; begin Result := [paMultiSelect, paRevertable, paValueList, paSortList, paAutoUpdate]; end; function TVA508AccessibilityPropertyPropertyEditor.GetEditLimit: Integer; begin Result := 127; end; function TVA508AccessibilityPropertyPropertyEditor.GetManager: TVA508AccessibilityManager4PE; begin if not assigned(FManager) then FManager := TVA508AccessibilityManager4PE(TVA508AccessibilityItem(GetComponent(0)).Manager); Result := FManager; end; function TVA508AccessibilityPropertyPropertyEditor.GetRootComponent( index: integer): TWinControl; begin Result := TVA508AccessibilityItem(GetComponent(index)).Component; end; function TVA508AccessibilityPropertyPropertyEditor.GetValue: string; begin Result := inherited GetValue; if Result <> '' then Result := Result + QVal(GetPropValue(GetRootComponent(0), Result)); end; procedure TVA508AccessibilityPropertyPropertyEditor.GetValues( Proc: TGetStrProc); var list: TStringList; i: integer; name: string; begin list := TStringList.Create; try GetStringPropertyNames(GetManager, GetRootComponent(0), list, TRUE); if PropCount > 1 then begin for i := 1 to PropCount-1 do GetStringPropertyNames(FManager, GetRootComponent(i), list, FALSE); end; list.Sort; for i := 0 to list.count-1 do begin name := list[i]; if PropCount = 1 then name := name + QVal(GetPropValue(GetRootComponent(0), name)); Proc(name); end; finally list.free; end; end; procedure TVA508AccessibilityPropertyPropertyEditor.SetValue( const Value: string); begin inherited SetValue(StripQVal(Value)); end; { TVA508AccessibilityClassPropertyEditor } function TVA508AccessibilityComponentPropertyEditor.GetAttributes: TPropertyAttributes; begin Result := [paDisplayReadOnly]; end; procedure Register; begin RegisterComponents(DelphiPaletteName, [TVA508AccessibilityManager, TVA508ComponentAccessibility, TVA508StaticText]); RegisterPropertyEditor(TypeInfo(TVA508AccessibilityCollection), TVA508AccessibilityManager, VA508DataPropertyName, TVA508CollectionPropertyEditor); RegisterPropertyEditor(TypeInfo(String), TWinControl, WinControlPropertyToMap, TVA508AccessibilityPropertyMapper); RegisterPropertyEditor(TypeInfo(TLabel), TVA508AccessibilityItem, AccessibilityLabelPropertyName, TVA508AccessibilityLabelPropertyEditor); RegisterPropertyEditor(TypeInfo(String), TVA508AccessibilityItem, AccessibilityPropertyPropertyName, TVA508AccessibilityPropertyPropertyEditor); RegisterPropertyEditor(TypeInfo(TComponent), TVA508AccessibilityItem, AccessDataComponentText, TVA508AccessibilityComponentPropertyEditor); end; end.