unit abSecEdit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
{$IFDEF DELPHI5}
  dsgnintf,
{$ENDIF}
{$IFDEF CPPB5}
  dsgnintf,
{$ENDIF}
{$IFDEF DELPHI6}
  DesignIntf, DesignEditors,
{$ENDIF}
  ComCtrls, StdCtrls, ImgList, ExtCtrls, ToolWin, abSecurity, Buttons, Menus,
  ActnList, Mask;

type
  TevSecurityTemplateEditor = class(TForm)
    btnOK: TButton;
    btnCancel: TButton;
    ilActions: TImageList;
    img1: TImage;
    img2: TImage;
    ToolBar1: TToolBar;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    pnlParent: TPanel;
    ActionList2: TActionList;
    actNewHierarchicalNode: TAction;
    actDeleteNode: TAction;
    pmnuNode: TPopupMenu;
    mnuNewNode: TMenuItem;
    mnuDeleteNode: TMenuItem;
    actNewCheckableNode: TAction;
    actChangeId: TAction;
    mnuNewCheckableNode: TMenuItem;
    pnlId: TPanel;
    edtSecurityId: TMaskEdit;
    procedure ToolButton1Click(Sender: TObject);
    procedure ToolButton2Click(Sender: TObject);
    procedure tvDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
    procedure tvDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure tvStartDrag(Sender: TObject; var DragObject: TDragObject);
    procedure tvEndDrag(Sender, Target: TObject; X, Y: Integer);
    procedure actNewHierarchicalNodeUpdate(Sender: TObject);
    procedure actNewHierarchicalNodeExecute(Sender: TObject);
    procedure actDeleteNodeExecute(Sender: TObject);
    procedure ftvSecurityTemplateEditing(Sender: TObject; Node: TTreeNode; var AllowEdit: Boolean);
    procedure ftvSecurityTemplateEdited(Sender: TObject; Node: TTreeNode; var S: String);
    procedure ftvSecurityTemplateChange(Sender: TObject; Node: TTreeNode);
    procedure actNewCheckableNodeExecute(Sender: TObject);
    procedure edtSecurityIdKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

    procedure ftvSecurityTemplateKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure ftvSecurityTemplateChanging(Sender: TObject; Node: TTreeNode; var AllowChange: Boolean);
    procedure btnOKClick(Sender: TObject);
  private
    fDraggedNode: TTreeNode;
    ftvSecurityTemplate: TabSecurityTemplate;
    fSecurityObject: TabSecurity;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

  TevSecurityTemplateProperty = class(TPropertyEditor)
  public
    procedure Edit; override;
    function GetAttributes: TPropertyAttributes; override;
  end;

  TevSecurityTemplateEdit = class(TComponentEditor)
  public
    procedure Edit; override;
    procedure ExecuteVerb(Index: Integer); override;
    function GetVerb(Index: Integer): string; override;
    function GetVerbCount: Integer; override;
  end;

  procedure Register;

implementation

{$R *.DFM}

uses abActnLst;

procedure Register;
begin
  RegisterPropertyEditor(TypeInfo(TabSecurityTemplate), TabSecurity, 'SecurityTemplate', TevSecurityTemplateProperty);
  RegisterComponentEditor(TabSecurity, TevSecurityTemplateEdit);
end;

function SecurityDesign(SecObject: TabSecurity): boolean;
begin
  with TevSecurityTemplateEditor.Create(Application) do
  try
    fSecurityObject := SecObject;
    ftvSecurityTemplate.ClearTemplate;
    CopyEntireUserTemplate(fSecurityObject.SecurityTemplate, ftvSecurityTemplate);
    if soShowIcons in fSecurityObject.Options then
      ftvSecurityTemplate.Images.Assign(fSecurityObject.Images)
    else
      ftvSecurityTemplate.Images := nil;

    if ftvSecurityTemplate.Items.Count > 0 then
      ftvSecurityTemplate.Items[0].Expand(true);
    if mrOK = ShowModal then
    begin
      fSecurityObject.Tag := Succ(fSecurityObject.tag);
      fSecurityObject.SecurityTemplate.ClearTemplate;
      CopyEntireUserTemplate(ftvSecurityTemplate, fSecurityObject.SecurityTemplate);
      fSecurityObject.Images.Assign(ftvSecurityTemplate.Images);
      ftvSecurityTemplate.DeleteKey(fSecurityObject.UserTemplateRegistryKeyName);
      Result := true;
    end
    else
      Result := false;
  finally
    Destroy;
  end;
end;
(*
procedure SecurityDesign(SecObject: TabSecurity);
begin
  with TevSecurityTemplateEditor.Create(Application) do
  try
    fSecurityObject := SecObject;
    ftvSecurityTemplate.ClearTemplate;
    CopyEntireUserTemplate(fSecurityObject.SecurityTemplate, ftvSecurityTemplate);
    if soShowIcons in fSecurityObject.Options then
      ftvSecurityTemplate.Images.Assign(fSecurityObject.Images)
    else
      ftvSecurityTemplate.Images := nil;

    if ftvSecurityTemplate.Items.Count > 0 then
      ftvSecurityTemplate.Items[0].Expand(true);
    if mrOK = ShowModal then
    begin
      fSecurityObject.Tag := Succ(fSecurityObject.tag);
      fSecurityObject.SecurityTemplate.ClearTemplate;
      CopyEntireUserTemplate(ftvSecurityTemplate, fSecurityObject.SecurityTemplate);
      fSecurityObject.Images.Assign(ftvSecurityTemplate.Images);
      ftvSecurityTemplate.DeleteKey(fSecurityObject.UserTemplateRegistryKeyName);//v1.4
      //fSecurityObject.Tag := Pred(fSecurityObject.tag);
      //TControl(fSecurityObject).Update;
      //SendMessage(TWinControl(fSecurityObject.GetParentComponent).Handle, CM_CHANGED, 0, 0);//TControl(fSecurityObject.SecurityTemplate).Changed;
    end;
  finally
    Destroy;
  end;
end;
*)

procedure TevSecurityTemplateProperty.Edit;
begin
  if SecurityDesign(TabSecurity(GetComponent(0))) then
    Modified;
end;

function TevSecurityTemplateProperty.GetAttributes: TPropertyAttributes;
begin
  Result := [paDialog];
end;


//TevSecurityTemplateEdit class
procedure TevSecurityTemplateEdit.Edit;
begin
  ExecuteVerb(0);
end;


procedure TevSecurityTemplateEdit.ExecuteVerb(Index: Integer);
begin
  if Index = 0 then
    if SecurityDesign(TabSecurity(Component)) then
      GetDesigner.Modified;
end;

function TevSecurityTemplateEdit.GetVerb(Index: Integer): string;
begin
  Result := 'Security Template Editor';
end;

function TevSecurityTemplateEdit.GetVerbCount: Integer;
begin
  Result := 1;
end;

procedure TevSecurityTemplateEditor.ToolButton1Click(Sender: TObject);
begin
  if (ftvSecurityTemplate.Items.Count <= 2) or
      (mrOK = MessageDlg('By rebuilding Security Template from clients you will lose all your changes.', mtWarning, [mbOK, mbCancel], 0)) then
  begin
    fSecurityObject.DoBuildSecurityTemplateFromClients(ftvSecurityTemplate, ilActions);
    if ftvSecurityTemplate.Items.Count > 0 then
      ftvSecurityTemplate.Items[0].Expand(true);
  end;
end;

procedure TevSecurityTemplateEditor.ToolButton2Click(Sender: TObject);
begin
  if (ftvSecurityTemplate.Items.Count <= 2) or
      (mrOK = MessageDlg('Do you really want to clear the Security Template?', mtConfirmation, [mbOK, mbCancel], 0)) then
  begin
    ftvSecurityTemplate.ClearTemplate;//Leaks was Items.Clear;
    ilActions.Clear;
    edtSecurityId.Visible := false;
  end;
end;


constructor TevSecurityTemplateEditor.Create(AOwner: TComponent);
begin
  inherited ;
  ftvSecurityTemplate := TabSecurityTemplate.Create(self);
  ftvSecurityTemplate.Parent := pnlParent;
  ftvSecurityTemplate.align := alClient;
  ftvSecurityTemplate.Images := ilActions;

  fDraggedNode := nil;
  ftvSecurityTemplate.DragMode := dmAutomatic;
  ftvSecurityTemplate.OnDragOver := tvDragOver;
  ftvSecurityTemplate.OnDragDrop := tvDragDrop;
  ftvSecurityTemplate.OnStartDrag := tvStartDrag;
  ftvSecurityTemplate.OnEndDrag := tvEndDrag;
  ftvSecurityTemplate.ReadOnly := false;
  ftvSecurityTemplate.ParentShowHint := false;
  ftvSecurityTemplate.ShowHint := true;
  ftvSecurityTemplate.Hint := 'Right click to add/delete or drag/drop to re-structure';
  ftvSecurityTemplate.PopupMenu := pmnuNode;

  ftvSecurityTemplate.OnEditing := ftvSecurityTemplateEditing;
  ftvSecurityTemplate.OnEdited := ftvSecurityTemplateEdited;
  ftvSecurityTemplate.OnChange := ftvSecurityTemplateChange;
  ftvSecurityTemplate.OnKeyDown := ftvSecurityTemplateKeyDown;
  ftvSecurityTemplate.OnChanging := ftvSecurityTemplateChanging;
end;

destructor TevSecurityTemplateEditor.Destroy;
begin
  ftvSecurityTemplate.Destroy;
  inherited;
end;

procedure TevSecurityTemplateEditor.tvStartDrag(Sender: TObject; var DragObject: TDragObject);
var
  tnSource: TTreeNode;
begin
  if Sender = ftvSecurityTemplate then
  begin
    if Assigned(ftvSecurityTemplate.Selected) then
    begin
      tnSource:= TTreeNode(ftvSecurityTemplate.Selected);
      case ftvSecurityTemplate.GetNodeTypeSecurityTemplate(tnSource) of
      ntNone, ntPassword, ntUserRegular, ntSupervisor:;
      ntUserLeave, ntIntermediate:
      begin
        fDraggedNode := ftvSecurityTemplate.Selected;
        exit;
      end;
      end;
    end
    else
      ;
  end;
  ftvSecurityTemplate.EndDrag(false);
end;

procedure TevSecurityTemplateEditor.tvDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
var
  tnDest: TTreeNode;
begin
  if (Sender = ftvSecurityTemplate) and Assigned(fDraggedNode) then
  begin
    tnDest:= ftvSecurityTemplate.GetNodeAt(X, Y);
    if Assigned(tnDest) then
      case ftvSecurityTemplate.GetNodeTypeSecurityTemplate(tnDest) of
      ntNone:;
      ntPassword:;
      ntUserLeave:;
      ntIntermediate:
      begin
        Accept := true;
        exit;
      end;
      end;
  end;
  Accept := false;
end;

procedure TevSecurityTemplateEditor.tvDragDrop(Sender, Source: TObject; X, Y: Integer);
var
  tnDest: TTreeNode;
begin
  if (Sender = ftvSecurityTemplate) and Assigned(fDraggedNode) then
  begin
    tnDest:= ftvSecurityTemplate.GetNodeAt(X, Y);
    case ftvSecurityTemplate.GetNodeTypeSecurityTemplate(tnDest) of
    ntNone:;
    ntPassword:;
    ntUserLeave:;
    ntIntermediate:
    begin
      fDraggedNode.MoveTo(tnDest, naAddChild );
      fDraggedNode := nil;
    end;
    end;
  end;
end;

procedure TevSecurityTemplateEditor.tvEndDrag(Sender, Target: TObject; X, Y: Integer);
begin
  fDraggedNode := nil;
end;

procedure TevSecurityTemplateEditor.actNewHierarchicalNodeUpdate(Sender: TObject);
var
  nodeType: TNodeType;
begin
  nodeType := ftvSecurityTemplate.GetNodeTypeSecurityTemplate(ftvSecurityTemplate.Selected);
  actNewHierarchicalNode.Enabled := Assigned(ftvSecurityTemplate.Selected) and (nodeType in [ntUserRegular, ntIntermediate]);
  actNewCheckableNode.Enabled := Assigned(ftvSecurityTemplate.Selected) and (nodeType <> ntUserLeave);
  actChangeId.Enabled := Assigned(ftvSecurityTemplate.Selected) and (PNodeData(ftvSecurityTemplate.Selected.Data)^.iId > 0);
{$B-}
  actDeleteNode.Enabled := Assigned(ftvSecurityTemplate.Selected) and (ftvSecurityTemplate.Selected.Level > 0);
end;

procedure TevSecurityTemplateEditor.actNewCheckableNodeExecute(Sender: TObject);
var
  lpNodeData: PNodeData;
begin
  with ftvSecurityTemplate.Items.AddChild(ftvSecurityTemplate.Selected, 'New Checkable Node') do
  begin
    new(lpNodeData);
    Data := lpNodeData;
    lpNodeData^.iId := 0;//To prevent error in GenerateSecurityId
    lpNodeData^.iId := ftvSecurityTemplate.GenerateSecurityId;
    lpNodeData^.sCaption := Text;
    lpNodeData^.bCheckOK := false;
    lpNodeData^.AdditionalInfo.iItemIndex := AbsoluteIndex;//No need because changes???
    lpNodeData^.AdditionalInfo.iImageIndex := 0;
    ImageIndex := 4;{checkable leave}
    SelectedIndex := 4;
  end;
  ftvSecurityTemplate.Selected.Expand(false);
end;

procedure TevSecurityTemplateEditor.actNewHierarchicalNodeExecute(Sender: TObject);
var
  lpNodeData: PNodeData;
begin
  with ftvSecurityTemplate.Items.AddChild(ftvSecurityTemplate.Selected, 'New Hierarchical Node') do
  begin
    new(lpNodeData);
    lpNodeData^.iId := 0;
    lpNodeData^.sCaption := Text;
    lpNodeData^.bCheckOK := false;
    lpNodeData^.AdditionalInfo.iItemIndex := AbsoluteIndex;//No need because changes???
    lpNodeData^.AdditionalInfo.iImageIndex := 0;
    Data := lpNodeData;

    ImageIndex := 2;{structural}
    SelectedIndex := 2;
  end;
  ftvSecurityTemplate.Selected.Expand(false);
end;

procedure TevSecurityTemplateEditor.actDeleteNodeExecute(Sender: TObject);
begin
  ftvSecurityTemplate.DeleteNode(ftvSecurityTemplate.Selected);
end;


procedure TevSecurityTemplateEditor.ftvSecurityTemplateEditing(Sender: TObject; Node: TTreeNode; var AllowEdit: Boolean);
begin
  AllowEdit := Node.Level <> 0;
end;

procedure TevSecurityTemplateEditor.ftvSecurityTemplateEdited(Sender: TObject; Node: TTreeNode; var S: String);
begin
  S := TString34(S);
  if Assigned(Node.Data) then //and (Integer(Node.Data) > C_MAX_ID) then
   PNodeData(Node.Data)^.sCaption := TString34(S);
end;

procedure TevSecurityTemplateEditor.ftvSecurityTemplateChange(Sender: TObject; Node: TTreeNode);
begin
  if PNodeData(Node.Data)^.iId = 0 then
  begin
    edtSecurityId.Visible := false;
    pnlId.Visible := false;//Caption := ' Security ID: None';
  end
  else
  begin
    edtSecurityId.Visible := true;
    pnlId.Visible := true;
    edtSecurityId.Text := intToStr(PNodeData(Node.Data)^.iId);
    //pnlId.Caption := ' Security ID: ' + intToStr(PNodeData(Node.Data)^.iId);
  end;
end;

procedure TevSecurityTemplateEditor.ftvSecurityTemplateKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if Assigned(ftvSecurityTemplate.Selected) then
    if key = VK_F2 then
      ftvSecurityTemplate.Selected.EditText;
end;

procedure TevSecurityTemplateEditor.ftvSecurityTemplateChanging(Sender: TObject; Node: TTreeNode; var AllowChange: Boolean);
var
  Key: Word;
begin
  if edtSecurityId.Visible then
  begin
    try
      if PNodeData(ftvSecurityTemplate.Selected.Data)^.iId = strToInt(edtSecurityId.Text) then
        exit
      else
        case MessageDlg('You have changed the security Id for the item selected. Do you want to apply the change.', mtWarning, [mbYes, mbNo, mbCancel], 0) of
        mrYes:
        begin
          Key := VK_RETURN;
          try
            edtSecurityIdKeyDown(Sender, Key, []);
          except
            AllowChange := false;
          end;
        end;
        mrNo:;
        mrCancel: AllowChange := false;
        end;
    except
      edtSecurityId.Text := IntToStr(PNodeData(ftvSecurityTemplate.Selected.Data)^.iId);
      ShowMessage('Security ID must be a positive integer. Reverted to the initial value.');
      AllowChange := false;
      edtSecurityId.SetFocus;
    end;
  end;
end;

procedure TevSecurityTemplateEditor.edtSecurityIdKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  abAction: TabAction;
begin
  if Assigned(ftvSecurityTemplate.Selected) then
    if key = VK_RETURN then
    try
      if PNodeData(ftvSecurityTemplate.Selected.Data)^.iId = strToInt(edtSecurityId.Text) then
        exit;
      if strToInt(edtSecurityId.Text) < 0 then
        raise Exception.Create('Security ID must be a positive integer');
      if ftvSecurityTemplate.IsValidSecurityId(strToInt(edtSecurityId.Text)) then
      begin
        abAction := TabAction(fSecurityObject.GetActionClientById(PNodeData(ftvSecurityTemplate.Selected.Data)^.iId));
        if Assigned(abAction) then
        begin
          case MessageDlg('Security Template item has a corresponding TabSecurity item. Do you want to modify its security id as well?', mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
          mrYes: abAction.SecurityId := strToInt(edtSecurityId.Text);
          mrNo:;
          mrCancel:
          begin
            edtSecurityId.Text := IntToStr(PNodeData(ftvSecurityTemplate.Selected.Data)^.iId);
            exit;
          end;
          end;
        end;
        PNodeData(ftvSecurityTemplate.Selected.Data)^.iId := strToInt(edtSecurityId.Text);
      end
      else
        ShowMessage('Security ID already exist');
    except
      on E:Exception do
      begin
        edtSecurityId.Text := intToStr(PNodeData(ftvSecurityTemplate.Selected.Data)^.iId);
        raise;//ShowMessage('Security ID must be a positive integer');
      end;
    end;
end;

procedure TevSecurityTemplateEditor.btnOKClick(Sender: TObject);
var
  AllowChange: Boolean;
begin
  AllowChange := true;
  ftvSecurityTemplateChanging(Sender, nil, AllowChange);
  if not AllowChange then
    ModalResult := mrNone;
end;

end.

