unit MainUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, uDMDB, Vcl.AppEvnts,
  System.ImageList, Vcl.ImgList, Data.DB, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf,
  FireDAC.Stan.Async, FireDAC.DApt, FireDAC.UI.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool,
  FireDAC.Phys, FireDAC.VCLUI.Wait, FireDAC.Comp.Client, FireDAC.Comp.DataSet, Vcl.Grids,
  Vcl.DBGrids, ABOUT, uCheckBoxSelectionThread, uDeleteCheckedThread, uDBConnection,
  uInsertNodeThread, uInsertFolderFileNode, uCutAndPasteThread, uCopyAndPasteThread,
  uCopyAndPasteThread2;

type
  TMainForm = class(TForm)
    TreeView1: TTreeView;
    bInsert: TButton;
    bDelete: TButton;
    bUpdate: TButton;
    bEdit: TButton;
    bTest: TButton;
    bClearSelection: TButton;
    NodeUp: TButton;
    NodeDown: TButton;
    ApplicationEvents: TApplicationEvents;
    InsertManyNodes: TButton;
    bDeleteManyNodes: TButton;
    ImageListCheckBoxes: TImageList;
    bDeleteChecked: TButton;
    ImageListFolders: TImageList;
    bInitialUpdate: TButton;
    Memo1: TMemo;
    bDeleteSubTree: TButton;
    bDeleteChecked2: TButton;
    bInsertFromThread: TButton;
    bInsertFolder: TButton;
    InsertFile: TButton;
    procedure FormShow(Sender: TObject);
    procedure bInsertClick(Sender: TObject);
    procedure bEditClick(Sender: TObject);
    procedure bUpdateClick(Sender: TObject);
    procedure bDeleteClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure NodeUpClick(Sender: TObject);
    procedure NodeDownClick(Sender: TObject);
    procedure TreeView1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure TreeView1StartDrag(Sender: TObject; var DragObject: TDragObject);
    procedure TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState;
      var Accept: Boolean);
    procedure TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure ApplicationEventsMessage(var Msg: tagMSG; var Handled: Boolean);
    procedure InsertManyNodesClick(Sender: TObject);
    procedure bDeleteManyNodesClick(Sender: TObject);
    procedure TreeView1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure TreeView1Expanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean);
    procedure TreeView1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    procedure TreeView1Collapsing(Sender: TObject; Node: TTreeNode; var AllowCollapse: Boolean);
    procedure TreeView1Collapsed(Sender: TObject; Node: TTreeNode);
    procedure TreeView1Expanded(Sender: TObject; Node: TTreeNode);
    procedure TreeView1MouseLeave(Sender: TObject);
    procedure bDeleteCheckedClick(Sender: TObject);
    procedure bTestClick(Sender: TObject);
    procedure TreeView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    procedure TreeView1Click(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure TreeView1CustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
      State: TCustomDrawState; var DefaultDraw: Boolean);
    procedure bInitialUpdateClick(Sender: TObject);
    procedure bDeleteSubTreeClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure bDeleteChecked2Click(Sender: TObject);
    procedure bInsertFromThreadClick(Sender: TObject);
    procedure bInsertFolderClick(Sender: TObject);
    procedure InsertFileClick(Sender: TObject);



  private
//   DropNode:TTreeNode;
    DragNode:TTreeNode;
    procedure DeleteSubTree;
    procedure WalkParentAndAllChildren(Node: TTreeNode);
    procedure CopySubTree(Src, DestParent: TTreeNode; DestNodes: TTreeNodes);

    { Private declarations }
  public
    { Public declarations }
  end;

var
DeleteCheckedThread:TDeleteCheckedThread;

InsertNodeThread:TInsertNodeThread;

InsertFolderFileNode:TInsertFolderFileNode;


CopyAndPasteThread:TCopyAndPasteThread;

CopyAndPasteThread2:TCopyAndPasteThread2;

  MainForm: TMainForm;
  DMDB:TDMDB;
  Checked:boolean;
  NotAllowed:Boolean;
  MouseOnCheckBox:Boolean;
  NodeUnderMice:TTreeNode;
  Enabled:boolean;


  CutAndPasteThread:TCutAndPasteThread;



  CheckBoxSelectionThread:TCheckBoxSelectionThread;
  qIsFolder:TFdQuery;



implementation

{$R *.dfm}



procedure TMainForm.bDeleteChecked2Click(Sender: TObject);

begin

DeleteCheckedThread:=TDeleteCheckedThread.Create(false); //<<Удаление из базы
DeleteCheckedThread.FreeOnTerminate:=true;

end;

procedure TMainForm.bDeleteCheckedClick(Sender: TObject);
begin

if
 (MessageBox(Application.Handle,
   PChar('Удалить выбранные узлы?'),
   PChar('Удаление узлов'), MB_YESNO+MB_ICONQUESTION)=ID_YES)

   then
DMDB.DeleteCheckedNodes(Self,TreeView1);

end;

procedure TMainForm.bDeleteClick(Sender: TObject);
begin

if
 (MessageBox(Application.Handle,
   PChar('Удалить выбранные узлы?'),
   PChar('Удаление узлов'), MB_YESNO+MB_ICONQUESTION)=ID_YES)

   then

DMDB.DeleteNode(Self,TreeView1);

end;

procedure TMainForm.bDeleteManyNodesClick(Sender: TObject);

begin
if
 (MessageBox(Application.Handle,
   PChar('Удалить выбранные узлы?'),
   PChar('Удаление узлов'), MB_YESNO+MB_ICONQUESTION)=ID_YES)

   then
DMDB.DeleteManyNodes3(Self,TreeView1);
end;

procedure TMainForm.bDeleteSubTreeClick(Sender: TObject);
begin

if not Assigned(TreeView1.Selected) then exit;

 DMDB.IDList.Clear;
 DeleteSubTree;

end;

procedure TMainForm.bEditClick(Sender: TObject);
begin
DMDB.EditNode(Self,TreeView1);
end;

procedure TMainForm.bInitialUpdateClick(Sender: TObject);
var i:integer;
begin

 DMDB.ClearTree(TreeView1);
// TreeView1.Items.Clear;
 DMDB.ExpandingUpdate(nil,TreeView1); //Загружаем 0-й уровень

if TreeView1.Items.GetFirstNode<>nil then
 TreeView1.Items.GetFirstNode.Expand(false); //Показываем 1-й уровень

end;

procedure TMainForm.DeleteSubTree;
var
  Node: TTreeNode; i, IDSelected:Integer;
begin


  IDSelected:=Integer(TreeView1.Selected.Data^);


//----------------Удаление из TreeView

   //Небольшой проход по дереву в пределах одного узла
   //Идея в том, что у всех детей уровни отличные от уровня
   //родителя и след. соседа родителя


   TreeView1.Selected.DeleteChildren;
   TreeView1.Items.Delete(TreeView1.Selected);

{     //Другой вариант через проход по узлу

      TreeView1.Items.BeginUpdate;
      Node := TreeView1.Selected;
      repeat
        TreeView1.Items.Delete(Node);
        Node := Node.GetNext;
      until not (Assigned(Node)) or (Node.Level = TreeView1.Selected.Level);
      TreeView1.Items.EndUpdate;
}

//-----------------Удаление из базы

   DMDB.FindChildren(IDSelected);
   DMDB.DeleteNodesByID(DMDB.IDList);

end;

procedure TMainForm.bInsertClick(Sender: TObject);
begin
  DMDB.InsertNodeInTree(Self,TreeView1);
end;

procedure TMainForm.bInsertFolderClick(Sender: TObject);

begin



// Если дерево пустое или выбранный узел тоже папка, то можно вставить папку
if (TreeView1.Items.Count=0) or (InsertFolderFileNode.IsFolder(TreeView1.Selected)=true)  then

InsertFolderFileNode.InsertFolderOrFileInTree(TreeView1,true,'SomeFolder');



end;

procedure TMainForm.bInsertFromThreadClick(Sender: TObject);
begin
InsertNodeThread:=TInsertNodeThread.Create(false);
InsertNodeThread.FreeOnTerminate:=true;
end;



Procedure TMainForm.WalkParentAndAllChildren (Node:TTreeNode);
var i:integer;
begin
memo1.Lines.Add(Node.Text);

for i := 0 to Node.Count-1 do
  WalkParentAndAllChildren(Node[i]); // Recurisively goes all children
end;



Procedure TMainForm.CopySubTree(Src, DestParent: TTreeNode; DestNodes: TTreeNodes);
var
  i: Integer;
  Dest: TTreeNode;
begin
  Dest := DestNodes.AddChild(DestParent, Src.Text);
  //copy other parameters from Src to Dest as you please
  for i := 0 to Src.Count-1 do begin
    CopySubTree(Src[i], Dest, DestNodes);
  end;
end;



procedure TMainForm.bTestClick(Sender: TObject);

var i:integer;

begin

CopyAndPasteThread:=TCopyAndPasteThread.Create(false);
CopyAndPasteThread.FreeOnTerminate:=true;

end;

procedure TMainForm.bUpdateClick(Sender: TObject);
var i:integer;
begin

//Загружает узлы дерева



DMDB.UpdateAllTree(Self,TreeView1);
//DMDB.ReadExpandingsCollapsings(Self,TreeView1);

//if TreeView1.Items.Count<>0 then
//TreeView1.Items.GetFirstNode.Expand(false);

end;



procedure TMainForm.InsertFileClick(Sender: TObject);
begin


// Если выбранный узел папка
If (InsertFolderFileNode.IsFolder(TreeView1.Selected)=true)  then
InsertFolderFileNode.InsertFolderOrFileInTree(TreeView1,false,'SomeFile');



end;

procedure TMainForm.InsertManyNodesClick(Sender: TObject);
begin
DMDB.InsertManyNodesInTree(Self,TreeView1);
end;





procedure TMainForm.ApplicationEventsMessage(var Msg: tagMSG; var Handled: Boolean);
var iPos:integer; i:Integer;
begin

if Msg.message=WM_Mousewheel then
 begin

 i:=HiWord(Msg.wParam);

 //Если колесо движется от пользователя
 if i=120 then
         begin
             iPos := GetScrollPos(MainForm.TreeView1.Handle, SB_VERT);
             SetScrollPos(MainForm.TreeView1.Handle, SB_VERT, iPos -1, True);
             TreeView1.TopItem := TreeView1.TopItem.GetPrevVisible;

         end else

 //Если колесо движется к пользователю
         begin
             iPos := GetScrollPos(MainForm.TreeView1.Handle, SB_VERT);
             SetScrollPos(MainForm.TreeView1.Handle, SB_VERT, iPos +1, True);
             TreeView1.TopItem := TreeView1.TopItem.GetNextVisible;
         end;

 end;



end;

procedure TMainForm.FormShow(Sender: TObject);
var i:integer; Node:TTreeNode;
begin

DMDB:=TDMDB.Create(Self);






DMDB.Connect(Self);
if DMDB.FDConnection.Connected then Self.Caption:=Self.Caption+' Connected to DB';



end;


procedure TMainForm.NodeDownClick(Sender: TObject);
begin
DMDB.NodeDOWN(Self,TreeView1);


end;

procedure TMainForm.NodeUpClick(Sender: TObject);

begin
DMDB.NodeUp(Self,TreeView1);


end;

procedure TMainForm.TreeView1Click(Sender: TObject);
begin
if NotAllowed then exit else

if Assigned(NodeUnderMice) and (MouseOnCheckBox)  then

begin

//DMDB.CheckBoxSelection2(NodeUnderMice,TreeView1);

  CheckBoxSelectionThread:=TCheckBoxSelectionThread.Create(false);

  CheckBoxSelectionThread.FreeOnTerminate:=true;

end;

//


end;

procedure TMainForm.TreeView1Collapsed(Sender: TObject; Node: TTreeNode);
begin

DMDB.WriteCollapsings(Node);

end;

procedure TMainForm.TreeView1Collapsing(Sender: TObject; Node: TTreeNode;
  var AllowCollapse: Boolean);
begin
NotAllowed:=true;
end;

procedure TMainForm.TreeView1CustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
  State: TCustomDrawState; var DefaultDraw: Boolean);

var NodeID:integer; qIsFolder:TFdQuery;

begin


 DBConnection:=TDBConnection.Create(Self);
 qIsFolder:=TFdQuery.Create(Self);
 qIsFolder.Connection:=DBConnection.FDConnection;
 qIsFolder.SQL.Text:='SELECT id,IsFolder FROM treeview_db.tree;';




NodeID:=integer(Node.Data^);

with  qIsFolder do
 begin
 Open();

  if Locate('id',NodeId,[])=true
  then begin

   if (FieldByName('IsFolder').AsBoolean=True) and (Node.Expanded) then
    begin
     Node.ImageIndex:=2;
     Node.SelectedIndex:=2
    end else

    if FieldByName('IsFolder').AsBoolean=True  then
    begin
     Node.ImageIndex:=1;
     Node.SelectedIndex:=1;
    end else


    Node.ImageIndex:=0;


  end;




 Close();
 end;

 qIsFolder.Free;
 DBConnection.Free;


end;



procedure TMainForm.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var SenderNode,Node:TTreeNode;
begin


 if TreeView1.GetNodeAt(x,y)=nil then exit;


 SenderNode:=TreeView1.GetNodeAt(x,y);

 if Assigned(SenderNode) and
   (MessageBox(Application.Handle,
   PChar('Подчинить '+DragNode.Text+#13+' узлу '+
   SenderNode.Text+' ?'),
   PChar('Перемещение узла'), MB_YESNO+MB_ICONQUESTION)=ID_YES)
   then
   begin
      Screen.Cursor:=crHourgLass;
      DragNode.MoveTo(SenderNode,naAddChild);


      // Меняем родителя
      with DMDB.qEditParentKey.Params do
      begin
       ParamValues['idParent']:=Integer(SenderNode.Data^);
       ParamValues['id']:=Integer(DragNode.Data^);
      end;

      DMDB.qEditParentKey.ExecSQL;

     {
      // Обход дерева и замена индексов
      Node:=TreeView1.Items.GetFirstNode;

      repeat
       with DMDB.qEditIndex.Params do
        begin
          ParamValues['Index']:=Node.AbsoluteIndex;
          ParamValues['id']:=Integer(Node.Data^);
          DMDB.qEditIndex.ExecSQL;
        end;

        Node:=Node.GetNext;
      until not Assigned(Node) ;

    }


      //Запоминаем уровни

      Screen.Cursor:=crDefault;

   end;

   //Ставим CheckBox рядом с перемещенным узлом
   DMDB.CheckBoxSelection(DragNode,TreeView1);
//   if DragNode.StateIndex=2 then DragNode.StateIndex:=1 else DragNode.StateIndex:=2;

  NotAllowed:=false;

//  TreeView1.Items.Clear;

//  DMDB.ClearTree(TreeView1);
//  DMDB.ExpandingUpdate(nil,TreeView1);

  DMDB.ExpandingUpdate(SenderNode,TreeView1);

  SenderNode.Expand(false);

end;

procedure TMainForm.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState;
  var Accept: Boolean);
var SenderNode:TTReeNode; HitTests:THitTests;
begin

SenderNode:=TreeView1.GetNodeAt(x,y);

Accept:=(Assigned(SenderNode))
 and
 not (SenderNode=DragNode) // Не подчинять самому себе
 and
 not (SenderNode.HasAsParent(DragNode)) //Родитель не
 //может быть подчинен ребенку
 and
 not (SenderNode=DragNode.Parent)

 and  (InsertFolderFileNode.IsFolder(SenderNode));

//TreeView1.MouseWheelHandler();

 HitTests := TreeView1.GetHitTestInfoAt(X,Y);
   if (htAbove in HitTests) then
      TreeView1.TopItem := TreeView1.TopItem.GetPrevVisible

   else if (htBelow in HitTests) then
      TreeView1.TopItem := TreeView1.TopItem.GetNextVisible;


end;

procedure TMainForm.TreeView1Expanded(Sender: TObject; Node: TTreeNode);
begin
DMDB.WriteExpandings(Node);





// NotAllowed:=true;
end;

procedure TMainForm.TreeView1Expanding(Sender: TObject; Node: TTreeNode;
  var AllowExpansion: Boolean);
begin

DMDB.ExpandingUpdate(Node, TreeView1);

NotAllowed:=true;



end;

procedure TMainForm.TreeView1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key=VK_DELETE then
 begin
    if
 (MessageBox(Application.Handle,
   PChar('Удалить выбранные узлы?'),
   PChar('Удаление узлов'), MB_YESNO+MB_ICONQUESTION)=ID_YES)

   then
    DMDB.DeleteManyNodes3(Self,TreeView1);
 end;

end;

procedure TMainForm.TreeView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
var  Node:TtreeNode;
  begin

end;

procedure TMainForm.TreeView1MouseLeave(Sender: TObject);
begin

NotAllowed:=false;
end;

procedure TMainForm.TreeView1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var Node:TTreeNode;
begin



if TreeView1.GetNodeAt(X,Y)<>nil then

begin
    NodeUnderMice:=TreeView1.GetNodeAt(X,Y);

    if X<NodeUnderMice.DisplayRect(true).Left then


     MouseOnCheckBox:=true

    else

    MouseOnCheckBox:=False;
end;

if Assigned(NodeUnderMice) and (ssLeft in Shift) then
TreeView1.BeginDrag(false,10);






end;



procedure TMainForm.TreeView1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
if NotAllowed=true then NotAllowed:=false;

end;

procedure TMainForm.TreeView1StartDrag(Sender: TObject; var DragObject: TDragObject);
begin
DragNode:=TreeView1.Selected;
end;

procedure TMainForm.FormActivate(Sender: TObject);
var Node:TTreeNode;
begin

{
if TreeView1.Items.GetFirstNode=nil then exit;


  Node:=TreeView1.Items.GetFirstNode;

  Repeat

//  if Node.HasChildren then Node.ImageIndex:=4 else Node.ImageIndex:=3;


  Node:=Node.GetNext;
  Until not Assigned(Node) ;

}

end;

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin

 DMDB.ClearTree(TreeView1);

 InsertFolderFileNode.Free;

// DBConnection.Free;

end;





procedure TMainForm.FormCreate(Sender: TObject);
begin
InsertFolderFileNode:=TInsertFolderFileNode.Create;


//qIsFolder:=TFdQuery.Create(Self);
// qIsFolder.Connection:=DBConnection.FDConnection;
// qIsFolder.SQL.Text:='SELECT id,IsFolder FROM treeview_db.tree;';


//ReportMemoryLeaksOnShutdown:=true;
end;

end.
