{ ------------------------------------------------------------- }
{ Purpose : NextInspector 6                                     }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2015. All Rights Reserved.         }
{ ------------------------------------------------------------- }
{$R 'Nodes.res'}
{$I '../../NxSuite6.inc'}
unit NxInspectorNodesEditor6;

interface

uses
  { Standard }
  Classes, Types, Windows, Graphics,
  { Standard - Design }
  DesignIntf, DesignEditors, VCLEditors,
  { Standard.Controls }
  Controls, StdCtrls, Menus, ComCtrls, ExtCtrls,
  { Next Standard 6 }
  NxConsts6, NxTypes6, NxIntf6, NxClasses6, NxStdCtrls6, NxControls6,
  { Next Standard 6 - Design }
  NxStandardReg6, NxDesignWindow6,
  { NextInspector 6 }
  NxInspector6, NxInspectorNode6, NxInspectorNodeClasses6,
  {$IFDEF IMAGELIST_UNIT}ImageList, ImgList{$ELSE}ImgList{$ENDIF};

type
  { TNxInspectorNodesEditor }

  TNxInspectorNodesEditor = class(TNxDesignWindow)
    Palette: TNxToolPalette6;
    NodesTree: TTreeView;
    PopupMenu1: TPopupMenu;
    DeleteOption: TMenuItem;
    N1: TMenuItem;
    ConvertTo: TMenuItem;
    Duplicate1: TMenuItem;
    N2: TMenuItem;
    MoveUpMenu: TMenuItem;
    MoveDownMenu: TMenuItem;
    Icons: TImageList;
    Status: TStatusBar;
    BestFit1: TMenuItem;
    N3: TMenuItem;
    ShowPreview1: TMenuItem;
    N4: TMenuItem;
    CopyStyle: TMenuItem;
    PasteStyle: TMenuItem;
    procedure PaletteButtonClick(Sender: TObject; Index: Integer);
    procedure NodesTreeMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure NodesTreeChange(Sender: TObject; Node: TTreeNode);
    procedure FormShow(Sender: TObject);
    procedure DeleteOptionClick(Sender: TObject);
    procedure NodesTreeEdited(Sender: TObject; Node: TTreeNode; var S: string);
    procedure MoveUpMenuClick(Sender: TObject);
    procedure MoveDownMenuClick(Sender: TObject);
    procedure NodesTreeDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure NodesTreeDragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure StatusDblClick(Sender: TObject);
    procedure BestFit1Click(Sender: TObject);
    procedure Duplicate1Click(Sender: TObject);
    procedure ShowPreview1Click(Sender: TObject);
    procedure NodesTreeEditing(Sender: TObject; Node: TTreeNode;
      var AllowEdit: Boolean);
    procedure PopupMenu1Popup(Sender: TObject);
    procedure CopyStyleClick(Sender: TObject);
    procedure PasteStyleClick(Sender: TObject);
  private
    FNodeStyle: TNxInspectorNodeStyle;
    function GetInspector: TNextInspector6;
    function GetSelectedNode: TNxInspectorNode6;
    { Private declarations }
  protected
    procedure AddButton(Palette: string; ComponentClass: TComponentClass;
      ImageIndex: Integer); virtual;
    procedure AddButtons; virtual;
    procedure AddIcon(Res: string);
    procedure AddIcons;
    procedure DoConvertMenuClick(Sender: TObject);
    function GetImageIndex(NodeClass: TNxInspectorNodeClass): Integer;
    procedure LoadNodes;
  public
    constructor Create(AOwner: TComponent); override;
    { Methods }
    function CreateNode(NodeClass: TNxInspectorNodeClass;
      TreeNode: TTreeNode; AttachMode: TNodeAttachMode): TNxInspectorNode6;
    { Properties }
    property Inspector: TNextInspector6 read GetInspector;
    property SelectedNode: TNxInspectorNode6 read GetSelectedNode;
  end;

implementation

uses
  Dialogs, SysUtils;

{$R *.dfm}

{ TNxInspectorNodesEditor }

procedure TNxInspectorNodesEditor.AddButton(Palette: string;
  ComponentClass: TComponentClass; ImageIndex: Integer);
var
  MenuItem: TMenuItem;
begin
  with Self.Palette.Add(Palette, ComponentClass, ImageIndex) do
  begin
    ShowHint := True;
    Hint := ComponentClass.ClassName;

    { Populate "Convert To" }
    MenuItem := TMenuItem.Create(ConvertTo);
    MenuItem.Caption := ComponentClass.ClassName;
    MenuItem.OnClick := DoConvertMenuClick;
    MenuItem.ImageIndex := ImageIndex;

    ConvertTo.Add(MenuItem);
  end;
end;

procedure TNxInspectorNodesEditor.AddButtons;
begin
  AddButton('Standard', TNxInspectorNode6, 0);
  AddButton('Standard', TNxSpinInspectorNode6, 1);
  AddButton('Standard', TNxCheckBoxInspectorNode6, 2);
  AddButton('Standard', TNxButtonInspectorNode6, 3);
  AddButton('Standard', TNxColorPaletteInspectorNode6, 4);
  AddButton('Standard', TNxDateInspectorNode6, 5);
  AddButton('Standard', TNxMemoInspectorNode6, 6);
  AddButton('Standard', TNxListInspectorNode6, 7);
  AddButton('Standard', TNxComboBoxInspectorNode6, 8);
  AddButton('Standard', TNxTrackBarInspectorNode6, 9);
  AddButton('Standard', TNxToolbarInspectorNode6, 10);

  AddButton('Additional', TNxPictureInspectorNode6, 11);
  AddButton('Additional', TNxRadioInspectorNode6, 12);
  AddButton('Additional', TNxRateInspectorNode6, 13);
  AddButton('Additional', TNxProgressInspectorNode6, 14);
end;

procedure TNxInspectorNodesEditor.AddIcon(Res: string);
var
  Bitmap: TBitmap;
begin
  Bitmap := TBitmap.Create;

  try
    Bitmap.LoadFromResourceName(HInstance, Res);

    { Add to list }
    Icons.AddMasked(Bitmap, Bitmap.Canvas.Pixels[0, Bitmap.Height - 1]);
  finally

  end;
end;

procedure TNxInspectorNodesEditor.AddIcons;
begin
  AddIcon('TEXTNODE');
  AddIcon('SPINNODE');
  AddIcon('CHECKBOXNODE');
  AddIcon('BUTTONNODE');
  AddIcon('COLORPALETTENODE');
  AddIcon('DATENODE');
  AddIcon('MEMONODE');
  AddIcon('LISTNODE');
  AddIcon('COMBOBOXNODE');
  AddIcon('TRACKBARNODE');
  AddIcon('TOOLBARNODE');

  AddIcon('PICTURENODE'); // 11
  AddIcon('RADIONODE');
  AddIcon('RATENODE');
  AddIcon('PROGRESSNODE');
end;

procedure TNxInspectorNodesEditor.BestFit1Click(Sender: TObject);
begin
  if Assigned(SelectedNode) then SelectedNode.BestFit;  
end;

procedure TNxInspectorNodesEditor.CopyStyleClick(Sender: TObject);
begin
  if not Assigned(FNodeStyle) then
    FNodeStyle := TNxInspectorNodeStyle.Create;
  FNodeStyle.Copy(SelectedNode);
end;

constructor TNxInspectorNodesEditor.Create(AOwner: TComponent);
begin
  inherited;
  FNodeStyle := nil;
  AddButtons;
  AddIcons;
end;

function TNxInspectorNodesEditor.CreateNode(NodeClass: TNxInspectorNodeClass;
  TreeNode: TTreeNode; AttachMode: TNodeAttachMode): TNxInspectorNode6;
var
  NewName: TComponentName;
begin
  Result := NodeClass.Create(Inspector.Owner);

  { Set Unique Name to Node }
  NewName := Designer.UniqueName(Result.ClassName);

  Result.Name := NewName;
  Result.Caption := Result.Name;

  { Add Node to Inspector }
  case AttachMode of
    naAddChild: Inspector.Nodes.AddNode(SelectedNode, Result);
    naInsert: Inspector.Nodes.InsertNode(SelectedNode, Result);
  end;

  { Link Inspector Node & TTreeNode }
  with TreeNode do
  begin
    { Attach Inspector Node to TreeNode }
    Data := Result;
    Text := Result.Caption;
    ImageIndex := GetImageIndex(TNxInspectorNodeClass(Result.ClassType));
    SelectedIndex := ImageIndex;
  end;

  if Result.IsTopLevel then
  begin
    Result.ReadOnly := True;
    Result.VisibleCells := [icCaption];
  end;

  if Assigned(NodesTree.Selected)
    then NodesTree.Selected.Expand(False);

  { Enable Save Btn. }
  Designer.Modified;
end;

procedure TNxInspectorNodesEditor.DeleteOptionClick(Sender: TObject);
var
  Node: TNxInspectorNode6;
begin
  if Assigned(NodesTree.Selected) and
    not NodesTree.IsEditing then
  begin
    Node := TNxInspectorNode6(NodesTree.Selected.Data);
    Node.Free;

    { TTreeView }
    NodesTree.Items.Delete(NodesTree.Selected);

    Designer.Modified;
  end;
end;

procedure TNxInspectorNodesEditor.DoConvertMenuClick(Sender: TObject);
var
  InspectorNode: TNxInspectorNode6;
  NodeClass: TNxInspectorNodeClass;
  NodeName: string;
  NewCaption: string;
begin
  NewCaption := TMenuItem(Sender).Caption;

  if MessageDlg('Convert to ' + NewCaption + '?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
  begin

    if Assigned(SelectedNode) then
    begin
      Designer.ClearSelection;

      NodeName := SelectedNode.Name;
      NodeClass := TNxInspectorNodeClass(GetClass(NewCaption));

      { Create & copy old node }
      InspectorNode := NodeClass.Create(Inspector.Owner);
      InspectorNode.Assign(SelectedNode);

      Inspector.Nodes.AddNode(SelectedNode.ParentNode, InspectorNode, apLast);
      InspectorNode.MoveTo(SelectedNode, naInsert);

      { Destroy old Node }
      TNxInspectorNode6(NodesTree.Selected.Data).Free;

      InspectorNode.Name := NodeName;

      { Update TTreeNode }
      NodesTree.Selected.ImageIndex := GetImageIndex(NodeClass);
      NodesTree.Selected.Data := InspectorNode;

      { Designer }
      Designer.SelectComponent(InspectorNode);
      Designer.Modified;
    end;

  end;
end;

procedure TNxInspectorNodesEditor.Duplicate1Click(Sender: TObject);
var
  InspectorNode: TNxInspectorNode6;
  TreeNode: TTreeNode;
  NodeClass: TNxInspectorNodeClass;
begin
  if Assigned(SelectedNode) then
  begin
    NodeClass := TNxInspectorNodeClass(SelectedNode.ClassType);

    TreeNode := NodesTree.Items.Insert(NodesTree.Selected, SelectedNode.Caption);

    InspectorNode := CreateNode(NodeClass, TreeNode, naInsert);
    InspectorNode.Assign(SelectedNode);

    TreeNode.Text := InspectorNode.Caption;
  end;
end;

procedure TNxInspectorNodesEditor.FormShow(Sender: TObject);
begin
  Caption := 'Nodes Editor - ' + Inspector.Name;
  LoadNodes;
end;

function TNxInspectorNodesEditor.GetImageIndex(
  NodeClass: TNxInspectorNodeClass): Integer;
var
  i, j: Integer;
begin
  { Default }
  Result := -1;

  for i := 0 to Pred(Palette.Count) do
    for j := 0 to Pred(Palette.Palette[Palette.ItemIndex].Count) do
      if Palette.Palette[i].Button[j].ReferenceType = NodeClass then
      begin
        Result := Palette.Palette[i].Button[j].ImageIndex;

        { Done }
        Exit;
      end;
end;

function TNxInspectorNodesEditor.GetInspector: TNextInspector6;
begin
  Result := TNextInspector6(OwnerControl);
end;

function TNxInspectorNodesEditor.GetSelectedNode: TNxInspectorNode6;
begin
  Result := nil;
  if Assigned(NodesTree.Selected)
    then Result := TNxInspectorNode6(NodesTree.Selected.Data);
end;

procedure TNxInspectorNodesEditor.LoadNodes;
var
  i, j: Integer;

  procedure CreateNodes(Nodes: TNxInspectorNodes; Node: TNxInspectorNode6;
    ParentNode: TTreeNode);
  var
    NewNode: TTreeNode;
  begin
    NewNode := nil;
    while i < Nodes.Count do
    begin
      if Nodes[i].Level > Node.Level then
        CreateNodes(Nodes, Nodes[i], NewNode);

      if (i = Nodes.Count) or (Nodes[i].Level < Node.Level)
        then Exit;

      NewNode := NodesTree.Items.AddChild(ParentNode, Nodes[i].Caption);

      with NewNode do
      begin
        ImageIndex := GetImageIndex(TNxInspectorNodeClass(Nodes[i].ClassType));
        SelectedIndex := ImageIndex;

        { Link InspectorNode and TTreeNode }
        Data := Nodes[i];
      end;

      Inc(i);
    end;
  end;

begin
	with Inspector do
    if Nodes.Count > 0 then
    begin
      NodesTree.Items.Clear;

      i := 0;

      CreateNodes(Nodes, Nodes[0], nil);

      for j := 0 to Pred(NodesTree.Items.Count)
        do NodesTree.Items[j].Expand(True);

    end;
end;

procedure TNxInspectorNodesEditor.MoveDownMenuClick(Sender: TObject);
var
  InspectorNode: TNxInspectorNode6;
  ANode, ANextNode: TTreeNode;
  Expanded: Boolean;
begin
  ANode := NodesTree.Selected;

  if TObject(ANode.Data) is TNxInspectorNode6 then
  begin
    InspectorNode := TNxInspectorNode6(ANode.Data);

    Designer.NoSelection;

    { Move Tree Node }
    ANextNode := ANode.Parent.Item[ANode.Index + 1];
    Expanded := ANode.Expanded;

    ANextNode.MoveTo(ANode, naInsert);
    ANode.Expanded := Expanded;

    NodesTree.Selected := ANode;

    { Move Inspector Node }
    InspectorNode.NextSibling.MoveTo(InspectorNode, naInsert);

    { Trigger event. Disable MoveUp/Down if needed. }
    NodesTreeChange(Self, ANode);
  end;
end;

procedure TNxInspectorNodesEditor.MoveUpMenuClick(Sender: TObject);
var
  InspectorNode: TNxInspectorNode6;
  ANode, APrevNode: TTreeNode;
  Expanded: Boolean;
begin
  ANode := NodesTree.Selected;

  if TObject(ANode.Data) is TNxInspectorNode6 then
  begin
    InspectorNode := TNxInspectorNode6(ANode.Data);

    Designer.NoSelection;

    { Move Tree Node }
    APrevNode := ANode.GetPrevSibling;
    Expanded := ANode.Expanded;

    ANode.MoveTo(APrevNode, naInsert);
    ANode.Expanded := Expanded;

    NodesTree.Selected := ANode;

    { Move Inspector Node }
    InspectorNode.MoveTo(InspectorNode.PrevSibling, naInsert);

    { Trigger event. Disable MoveUp/Down if needed. }
    NodesTreeChange(Self, ANode);
  end;
end;

procedure TNxInspectorNodesEditor.NodesTreeChange(Sender: TObject;
  Node: TTreeNode);
var
  InspectorNode: TNxInspectorNode6;
begin
  if Assigned(Node)
    and Assigned(Node.Data)
    and (TObject(Node.Data) is TNxInspectorNode6) then
  begin
    InspectorNode := TNxInspectorNode6(Node.Data);

    Designer.SelectComponent(InspectorNode);

    with InspectorNode do
    begin
      Status.Panels[1].Text := Value;
      if ReadOnly then Status.Panels[2].Text := 'Read only'
        else Status.Panels[2].Text := 'Editable';

      ShowPreview1.Enabled := True;
      ShowPreview1.Checked := ShowPreview;
    end;

    MoveUpMenu.Enabled := Node.Index > 0;
    MoveDownMenu.Enabled := (Node.Parent <> nil)
      and (Node.Index < Node.Parent.Count - 1);

  end else
  begin
    ShowPreview1.Enabled := False;
    MoveUpMenu.Enabled := False;
    MoveDownMenu.Enabled := False;
    Designer.NoSelection;
  end;

end;

procedure TNxInspectorNodesEditor.NodesTreeDragDrop(Sender, Source: TObject; X,
  Y: Integer);
var
  SourceNode, TargetNode: TNxInspectorNode6;
  Node: TTreeNode;
begin
  Node := NodesTree.GetNodeAt(X, Y);
  SourceNode := TNxInspectorNode6(NodesTree.Selected.Data);
  if Assigned(Node) then
  begin
    TargetNode := TNxInspectorNode6(Node.Data);
    SourceNode.MoveTo(TargetNode, naAddChild);
    NodesTree.Selected.MoveTo(Node, naAddChild);
  end else
  begin
    NodesTree.Selected.MoveTo(NodesTree.Items[0], naAdd);
    SourceNode.MoveToTopLevel;
  end;
  Designer.Modified;
end;

procedure TNxInspectorNodesEditor.NodesTreeDragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var
  TargetNode, DraggingNode: TTreeNode;
begin
  TargetNode := NodesTree.GetNodeAt(X, Y);

  DraggingNode := NodesTree.Selected;

  Accept := not Assigned(TargetNode) or ((TargetNode <> DraggingNode)
    and (not DraggingNode.IsFirstNode)
    and (not TargetNode.HasAsParent(DraggingNode)) and (DraggingNode.Parent <> TargetNode));

  if Assigned(TargetNode) then
  begin
    if Accept
      then Status.Panels[0].Text := 'Move into ' + TNxInspectorNode6(TargetNode.Data).Caption
    else
      Status.Panels[0].Text := '';

  end else Status.Panels[0].Text := 'Move to top level';
end;

procedure TNxInspectorNodesEditor.NodesTreeEdited(Sender: TObject;
  Node: TTreeNode; var S: string);
begin
  TNxInspectorNode6(Node.Data).Caption := S;
  DeleteOption.ShortCut := VK_DELETE;
end;

procedure TNxInspectorNodesEditor.NodesTreeEditing(Sender: TObject;
  Node: TTreeNode; var AllowEdit: Boolean);
begin
  DeleteOption.ShortCut := 0;
end;

procedure TNxInspectorNodesEditor.NodesTreeMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if NodesTree.GetNodeAt(X, Y) = nil then
    NodesTree.Selected := nil;
end;

procedure TNxInspectorNodesEditor.PaletteButtonClick(Sender: TObject;
  Index: Integer);
var
  TreeNode: TTreeNode;
begin
  TreeNode := NodesTree.Items.AddChild(NodesTree.Selected, '');
  CreateNode(TNxInspectorNodeClass(Palette.Palette[Palette.ItemIndex].Button[Index].ReferenceType),
    TreeNode, naAddChild);
end;

procedure TNxInspectorNodesEditor.PasteStyleClick(Sender: TObject);
begin
  FNodeStyle.Paste(SelectedNode);
end;

procedure TNxInspectorNodesEditor.PopupMenu1Popup(Sender: TObject);
begin
  CopyStyle.Enabled := Assigned(SelectedNode);
  PasteStyle.Enabled := Assigned(FNodeStyle) and Assigned(SelectedNode);
end;

procedure TNxInspectorNodesEditor.ShowPreview1Click(Sender: TObject);
begin
  if Assigned(SelectedNode) then SelectedNode.ShowPreview := TMenuItem(Sender).Checked;
end;

procedure TNxInspectorNodesEditor.StatusDblClick(Sender: TObject);
var
  Location: TPoint;
  X, i: Integer;
begin
  GetCursorPos(Location);
  Location := Status.ScreenToClient(Location);
  X := 0;
  for i := 0 to Pred(Status.Panels.Count) do
  begin
    X := X + Status.Panels[i].Width;
    if X > Location.X then
    begin
      if i = 2 then
      begin
        if Assigned(SelectedNode) then
        begin
          with SelectedNode do ReadOnly := not ReadOnly;

          { Event }
          NodesTreeChange(Self, NodesTree.Selected);
        end;
      end;
      Break;
    end;
  end;
end;

end.
