unit uCopyAndPasteThread;

interface

uses
    System.SysUtils, System.Classes, 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.Phys.MySQLDef, FireDAC.Phys.MySQL, Data.DB, FireDAC.Comp.Client,
  FireDAC.Comp.DataSet,
  Vcl.ComCtrls, // Для TTreeView
  Contnrs,vcl.dialogs,
  syncobjs,vcl.Forms,vcl.Controls, uDBConnection;

type
  TCopyAndPasteThread = class(TThread)
  private

  // Сбор списка отмеченных узлов
  Procedure ListParentAndAllChildren(NodeID:Integer);
  Procedure ListAllChecked;


  // Поиск родителя в базе
  Function FindParentInDB(ID:integer):integer;

  // Проверка на папку или файл по базе
  function IsFolderByID(NodeID:integer):boolean;

  //Вставка в базу
  Function  InsertFolderOrFileInDB(idParent, index: Integer;
    name: string; TreeView: TTreeView; IsFolder: boolean): integer;
  procedure InsertThatFolderOrFileInDB(TreeView: TTreeView; IsFolderLocalVar:boolean; name:string);

  Function CopyRecordInDB (SourceID:integer):integer;


  Procedure PasteIDListCheckedInDB;
  Procedure CopyAndPasteIDListCheckedInDB2;

  //Структурирование нового списка, восстановление иерархии в БД
  Procedure Structuring;


  //Очистка узла в TreeView
  Procedure ClearNodeChildrenData(Node:TTreeNode);
  Procedure ClearAllChildren;

  // Поиск родителя в дереве по ID
  Function FindNodeInTree(ID:Integer):TTreeNode;

  Procedure UpdateNodeInTree;

  protected
    procedure Execute; override;
  end;

 var IDListChecked:Tstringlist;
     IDListInserted:Tstringlist;

     DBConnection:TDBConnection;

implementation

uses MainUnit;

//---------------------------Execute потока --------------------------------


procedure TCopyAndPasteThread.Execute;
var i:integer;
begin
// Создание
IDListChecked:=Tstringlist.Create;
IDListInserted:=Tstringlist.Create;

DBConnection:=TDBConnection.Create(nil);

ListAllChecked;


CopyAndPasteIDListCheckedInDB2;
Structuring;

Synchronize(ClearAllChildren);

Synchronize(UpdateNodeInTree);


// Уничтожение
IDListChecked.Free;
IDListInserted.Free;
DBConnection.Free;

end;


//--------------------------ФОРМИРУЕМ IDList--------------------------------

procedure TCopyAndPasteThread.ListParentAndAllChildren(NodeID: Integer);

var qSelectAllChildren:TFDQuery;

begin

   qSelectAllChildren:=TFDQuery.Create(nil);
   qSelectAllChildren.Connection:=DBConnection.FDConnection;

   //Если ID не был добавлен ранее, тогда добавляем
   if IDListChecked.IndexOf(NodeID.Tostring)=-1 then
   IDListChecked.Add(NodeID.Tostring); // Записываем родителя и всех детей


   //Ищем всех детей
   with qSelectAllChildren do
   begin

    SQL.Text:='SELECT * FROM treeview_db.tree where IdParent='+NodeID.ToString;

    Open();

     while not eof do
     begin
     // Для каждого ребенка вызываем рекурсивно
     ListParentAndAllChildren(FieldByName('id').AsInteger);

     Next;
     end;

    Close();
   end;

   qSelectAllChildren.Free;

end;

//------------------------------Формируем IDList---------------------------------------

procedure TCopyAndPasteThread.ListAllChecked;
var i:integer; Node:TTreeNode; NodeID:integer;
    TreeView:TTreeView;
begin

 TreeView:=MainForm.TreeView1;

 if TreeView.Items.GetFirstNode=nil then exit else

    Node:=TreeView.Items.GetFirstNode;

    repeat

    if (Node.StateIndex=2) then
     begin
      NodeID:=Integer(Node.Data^);
      ListParentAndAllChildren(NodeID); // Добавляем в ListID
     end;


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


end;
//--------------------------------Формируем ID LIST---------------------------------


//-------------------------------Поиск родителя в БД по ID--------------------------------


function TCopyAndPasteThread.FindParentInDB(ID: integer): integer;
var qSelectParent:TFDQuery;
begin

 qSelectParent:=TFDQuery.Create(nil);
 qSelectParent.Connection:=DBConnection.FDConnection;

 with qSelectParent do
 begin

   SQL.Text:='Select * from `treeview_db`.`tree` where id='+ID.ToString;
   Open;
   Result:=FieldByName('idParent').AsInteger;
   Close;

 end;


 qSelectParent.Free;
end;
//----------------------------------------------------------------------------------------


//------------------------------Поиск узла в дереве по ID---------------------------------

function TCopyAndPasteThread.FindNodeInTree(ID:Integer): TTreeNode;

var i:integer; TreeView:TTreeView;

begin

TreeView:=MainForm.TreeView1;

 Result:=nil; //Если родитель не будет найден, то это нулевой уровень
//Иначе, это будет какой-то конкретный узел
 for i := 0 to TreeView.Items.Count-1 do
  if Integer(TreeView.Items.Item[i].Data^)=ID then

   begin
     Result:=TreeView.Items.Item[i];
     Break;
   end;


end;







//-------------------------------Проверка на файл или папку по ID-------------------------

function TCopyAndPasteThread.IsFolderByID(NodeID: integer): boolean;

var qCheckIsFolder:TFDQuery;
begin
qCheckIsFolder:=TFDQuery.Create(nil);
qCheckIsFolder.Connection:=DBConnection.FDConnection;

qCheckIsFolder.SQL.Text:='Select * from `treeview_db`.`tree` where id='+NodeID.ToString;

qCheckIsFolder.Open();

if qCheckIsFolder.FieldByName('IsFolder').AsBoolean=true then result:=true else
Result:=false;



qCheckIsFolder.Close;
qCheckIsFolder.Free;
//
end;

//-------------------Вспомогательная функция вставки папки или файла в базу-------------------------

function TCopyAndPasteThread.InsertFolderOrFileInDB(
idParent, index: Integer; name: string;  TreeView: TTreeView; IsFolder: boolean): integer;

var qInsert:TFDQuery;
    qGetKey:TFDQuery;

begin


 qInsert:=TFDQuery.Create(nil);
 qInsert.Connection:=DBConnection.FDConnection;

 qInsert.SQL.Text:=
'INSERT INTO `treeview_db`.`tree` (`IdParent`, `Name`, `Index`,`isChecked`,`Level`,`IsFolder`)'+
 'VALUES (:idParent, :name, :index,:isChecked,:Level,:IsFolder);';


 //Вставка записи
  with qInsert.Params do
   begin

           ParamValues['idParent']:=idParent;
           ParamValues['Index']:=Index;
           ParamValues['Name']:=Trim(Name);
           ParamValues['isChecked']:=false; // По умолчанию галочка не отмечена
           ParamValues['Level']:=0;


           if IsFolder then ParamValues['IsFolder']:=true else
           ParamValues['IsFolder']:=false

   end;

 qInsert.ExecSQL;

//----------Получение ID последней вставленной записи

           qGetKey:=TFDQuery.Create(nil);
           qGetKey.Connection:=DBConnection.FDConnection;
           qGetKey.SQL.Text:='SELECT Max(id) as newkey FROM treeview_db.tree;';

            //Получение id последней записи
           with qGetKey do
           begin
             Open;
             Result:=qGetKey.FieldByName('NewKey').AsInteger;
             Close;
           end;


// Освобождение

qGetKey.Free;
qInsert.Free;


end;
//------------------------------------------------------------------------------------------


//--------------------Непосредственная вставка папки или файла в базу данных---------------------------

procedure TCopyAndPasteThread.InsertThatFolderOrFileInDB(TreeView: TTreeView;
  IsFolderLocalVar: boolean; name: string);

var Node:TTreeNode;
    idParent,ItemIndex:integer; // Ключ и индекс узла

    LastInsertedID:^Integer;

    ParentNameInIDList:string;
    ParentIDInDB:integer;

begin
                 {Назначаем родителя новому узлу - TreeView.Selected}

                 if Assigned(TreeView.Selected) then
                     begin

                       idParent:=Integer(TreeView.Selected.Data^); //<<<<<
                       ItemIndex:=TreeView.Items.Count+1;

                     end else
                     //Если никакой узел не выбран, владельца нет
                     begin
                      idParent:=-1;
                      ItemIndex:=0;
                     end;


                            {Собственно вставка в БД}

New(LastInsertedID);

 if IsFolderLocalVar then
LastInsertedID^:=InsertFolderOrFileInDB(idParent, Itemindex, name, TreeView, true)
 else
LastInsertedID^:=InsertFolderOrFileInDB(idParent, Itemindex, name, TreeView, false);

                             {Ищем родителя в базе}

IDListInserted.Add(LastInsertedID^.ToString());




Dispose(LastInsertedID);
end;


//-------------------------------Вставка папки или файла в дерево---------------------------


//------------------------------Вставка всех узлов из IDList------------------

procedure TCopyAndPasteThread.PasteIDListCheckedInDB;

var i:integer; TreeView:TTreeView;

begin

 TreeView:=MainForm.TreeView1;

for i := 0 to IDListChecked.Count-1 do
 begin


    if IsFolderByID(IDListChecked[i].ToInteger()) then

  InsertThatFolderOrFileInDB(TreeView,true,'CuttedFolder') else
  InsertThatFolderOrFileInDB(TreeView,false,'CuttedFile');

 end;


end;


//---------------------------------Копирование записей в БД-----------------------------

function TCopyAndPasteThread.CopyRecordInDB(SourceID: integer): integer;

var qSelectInsert:TFDQuery; qGetKey:TFDQuery;

begin
 qSelectInsert:=TFDQuery.Create(nil);
 qSelectInsert.Connection:=DBConnection.FDConnection;

 qSelectInsert.SQL.Text:=
 'INSERT INTO `treeview_db`.`tree`'+

 '(`IdParent`,'+
  '`Name`,'+
  '`Index`,'+
  '`isChecked`,'+
  '`IsExpanded`,'+
  '`Level`,'+
  '`IsFolder`)'+

 'SELECT'+

 '`IdParent`,'+
 '`Name`,'+
 '`Index`,'+
 '`isChecked`,'+
 '`IsExpanded`,'+
 '`Level`,'+
 '`IsFolder`'+

 ' from'+
 '`treeview_db`.`tree` where id=:id' ;


 qSelectInsert.Params.ParamValues['id']:=SourceId;

 qSelectInsert.ExecSQL;

 qSelectInsert.Free;

 //----------Получение ID последней вставленной записи

           qGetKey:=TFDQuery.Create(nil);
           qGetKey.Connection:=DBConnection.FDConnection;
           qGetKey.SQL.Text:='SELECT Max(id) as newkey FROM treeview_db.tree;';

            //Получение id последней записи
           with qGetKey do
           begin
             Open;
             Result:=qGetKey.FieldByName('NewKey').AsInteger;
             Close;
           end;


// Освобождение

qGetKey.Free;


end;

//------------------------------Вставка всех узлов из IDList 2 вариант------------------


procedure TCopyAndPasteThread.CopyAndPasteIDListCheckedInDB2;
var i:integer; TreeView:TTreeView; LastInsertedID:integer;

begin

//Ограничение - можно копировать только в папку
if not IsFolderByID(Integer(MainForm.TreeView1.Selected.Data^)) then exit;

//Нельзя скопировать папку в саму себя
// Если в IDListChecked содержится выделенная папка, то выходим
if IDListChecked.IndexOf(Integer(MainForm.TreeView1.Selected.Data^).ToString())<>-1 then exit;


 TreeView:=MainForm.TreeView1;

for i := 0 to IDListChecked.Count-1 do
 begin

 LastInsertedID:=CopyRecordInDB(IDListChecked[i].ToInteger());

 IDListInserted.Add(LastInsertedID.ToString());

 end;


end;

//------------------------Востановление иерархии в записях из списка IDListInserted---------------

procedure TCopyAndPasteThread.Structuring;
var
  i: Integer; IDParent:Integer; ParentIndexInIDListChecked:integer;
  qUpdateIdParent:TFDQuery;

begin

//Готовим qUpdateIDParent

qUpdateIdParent:=TFDQuery.Create(nil);
qUpdateIdParent.Connection:=DBConnection.FDConnection;
qUpdateIdParent.SQL.Text:='UPDATE `treeview_db`.`tree` SET `IdParent`=:idParent WHERE `id`=:id;';
//----------------------------------------

// Цикл
for i := 0 to IDListChecked.Count-1 do
 begin
  IDParent:=FindParentInDB(IDListChecked[i].ToInteger()); // <<< Ищем ID родителя по базе


  // Если родитель не в списке IDListChecked
  if IDListChecked.IndexOf(IDParent.ToString())=-1 then
//   showmessage('Родитель в списке IDListChecked ');

        begin

        with qUpdateIdParent.Params

            do begin


            ParamValues['idParent']:=(Integer(MainForm.TreeView1.Selected.Data^));
            ParamValues['id']:=IDListInserted[i];   //<<< Так работает


            end;

             qUpdateIdParent.ExecSQL;

        end

        else

        begin
        ParentIndexInIDListChecked:=IDListChecked.IndexOf(IDParent.ToString());

        // IDListInserted[i] - индекс конкретного ребенка для которого найден родитель;

        with qUpdateIdParent.Params

            do begin

          //  showmessage(IDListInserted[ParentIndexInIDListChecked]);

            ParamValues['idParent']:=(IDListInserted[ParentIndexInIDListChecked]);
            ParamValues['id']:=IDListInserted[i];   //<<< Так работает


            end;

             qUpdateIdParent.ExecSQL;

        end;


 // for...
 end;


qUpdateIdParent.Free;

end;



//----------------------------Очищение узла от всех потомков в дереве-------------------------


procedure TCopyAndPasteThread.ClearNodeChildrenData(Node:TTreeNode);
//Вспомогательная процедура - рекурсивно очищает память под Data под всеми узлами

var i:integer;

begin

  if not (MainForm.TreeView1.Selected=Node) then
   begin
    Dispose(Node.Data);
   end;


   for i := 0 to Node.Count-1 do
     Self.ClearNodeChildrenData(Node[i]);

end;

procedure TCopyAndPasteThread.ClearAllChildren;
//Непосредственное удаление из дерева

var Node:TTreeNode;
begin

Node:=MainForm.TreeView1.Selected;

ClearNodeChildrenData(Node);

Node.DeleteChildren;

end;


//-------------------------Обновление одного узла---------------------------

procedure TCopyAndPasteThread.UpdateNodeInTree;


var IDParent:integer;

    ParentNode:TTreeNode;
    Node:TTreeNode;

    i:integer; TreeView:TTreeView;
    qSelectAll:TFdQuery;
    name:string;
    id:^integer;

begin

  TreeView:=MainForm.TreeView1;


  qSelectAll:=TFdQuery.Create(nil);
  qSelectAll.Connection:=DBConnection.FDConnection;
  qSelectAll.SQL.Text:=
  'SELECT * FROM treeview_db.tree where id=:id;';


  for i := 0 to IDListInserted.Count-1 do

    begin
    IDParent:=FindParentInDB(IDListInserted[i].ToInteger());


      // Если родитель во вставленном списке
      if   IDListInserted.IndexOf(IDParent.ToString())<>-1 then

      begin

        ParentNode:=FindNodeInTree(IDParent);

        qSelectAll.Params.ParamValues['id']:=IDListInserted[i];
        qSelectAll.Open();
        Name:=qSelectAll.FieldByName('name').AsString;
        qSelectAll.Close;

        new(id); id^:=IDListInserted[i].ToInteger;


      Node:=TreeView.Items.AddChildObject(ParentNode,Name,id);


        //Обновление StateIndex
         with qSelectAll do
          begin

          Open();

          if FieldByName('IsChecked').AsBoolean=true then
          Node.StateIndex:=2 else
          Node.StateIndex:=1;

          Close();

          end;

      end else

      begin

      //Если родителя нет в списке
         ParentNode:=TreeView.Selected;

        qSelectAll.Params.ParamValues['id']:=IDListInserted[i];
        qSelectAll.Open();
        Name:=qSelectAll.FieldByName('name').AsString;
        qSelectAll.Close;

        new(id); id^:=IDListInserted[i].ToInteger;


       Node:=TreeView.Items.AddChildObject(ParentNode,Name,id);



         //Обновление StateIndex
         with qSelectAll do
          begin

          Open();

          if FieldByName('IsExpanded').AsBoolean=true then
          Node.StateIndex:=2 else
          Node.StateIndex:=1;

          Close();

          end;



      end;



    //for
    end;


 TreeView.Selected.Expand(false);
 qSelectAll.Free;


end;




end.
