unit uDeleteCheckedThread;

{
Собрать в список IDList все, отмеченные галочками
Собрать всех их потомков
Удалить все собранные в список IDList из базы и из дерева

}

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;

type
  TDeleteCheckedThread = class(TThread)
  private
    { Private declarations }

  FDConnection:TFDConnection;
  procedure Connect;

  Procedure ListParentAndAllChildren(NodeID:Integer);
  Procedure ListAllChecked;

  Procedure DeleteNodesFromDB(IDList:TStringlist);

  Procedure DeleteNodesFromTree(TreeView:TTreeView); // << Не понадобилась
  procedure DeleteNodesFromTree2;


  protected
    procedure Execute; override;
  published

  end;


 var IDList:Tstringlist;
     CheckedNodes:array of integer;
     CS:TCriticalSection;

implementation

uses MainUnit;


procedure TDeleteCheckedThread.Execute;
var i:integer;
begin



// Создание
IDList:=Tstringlist.Create;
FDConnection:=TFDConnection.Create(nil);
CS:=TCriticalSection.Create;


// Основная работа
Self.Connect;
Synchronize(ListAllChecked); // << Сбор всех видимых чекнутых в один список



DeleteNodesFromDB(IDList); //Удаляем собранные из базы


Screen.Cursor:=crHourGlass;

Synchronize(DeleteNodesFromTree2);

Screen.Cursor:=crDefault;

IDList.Clear;

// Уничтожение
IDList.Free;
FDConnection.Free;
CS.Free;
  { Place thread code here }
end;


procedure TDeleteCheckedThread.DeleteNodesFromDB(IDList: TStringlist);

var qDelete:TFDQuery;  i:integer;

begin

qDelete:=TFDQuery.Create(nil);

qDelete.SQL.Text:='DELETE FROM `treeview_db`.`tree` WHERE `id`=:id;';

qDelete.Connection:=FDConnection;

 for i := 0 to IDList.Count-1 do
   begin
    with qDelete.Params do
     ParamValues['ID']:=IDList[i];
     qDelete.ExecSQL;
   end;



qDelete.Free;

end;

procedure TDeleteCheckedThread.DeleteNodesFromTree(TreeView: TTreeView);
var Node,Node2:TTreeNOde;  i:integer;
begin
//

if TreeView.Items.GetFirstNode<>nil then
    Node:=TreeView.Items.GetFirstNode;



    Repeat
    Node2:=Node; // Переприсваиваем для дальнейшей проверки на этом шаге
    Node:=Node.GetNext; //Перешагиваем заранее, так как узел будет удален

    if Node2.SelectedIndex =2 then
     begin
//      Node2.DeleteChildren;
      TreeView.Items.Delete(Node2);  // <<Удаляя узел, удаляем и его детей
     end;


    Until not Assigned(Node) ;


end;



{ 
  Important: Methods and properties of objects in visual components can only be
  used in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);  

  and UpdateCaption could look like,

    procedure TDeleteCheckedThread.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end; 
    
    or 
    
    Synchronize( 
      procedure 
      begin
        Form1.Caption := 'Updated in thread via an anonymous method' 
      end
      )
    );
    
  where an anonymous method is passed.

  Similarly, the developer can call the Queue method with similar parameters as 
  above, instead passing another TThread class as the first parameter, putting
  the calling thread in a queue with the other thread.
    
}

{ TDeleteCheckedThread }

procedure TDeleteCheckedThread.Connect;
var oParams:Tstrings;
begin
  oParams := TStringList.Create;
//  oParams.Add('DriverID='+DriverID);
  oParams.Add('DataBase=treeview_db');
  oParams.Add('Password=masterkey');
  oParams.Add('User_Name=root');
  oParams.Add('Port=3306');
  oParams.Add('Server=localhost');
  oParams.Add('CharacterSet=utf8');

   FDConnection.Params.Assign(oParams);
   FDConnection.DriverName:='MySQL';

   FDConnection.Connected:=true;

   oParams.Free;
end;




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

 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;

procedure TDeleteCheckedThread.ListParentAndAllChildren(NodeID:Integer);

var qSelectAllChildren:TFDQuery;

begin

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

   //Если ID не был добавлен ранее, тогда добавляем
   if IDList.IndexOf(NodeID.Tostring)=-1 then
   IDList.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;



procedure TDeleteCheckedThread.DeleteNodesFromTree2;
// Удаляет только узлы с галочками из дерева (не из базы)
var Node:TTreeNode; i:integer; TreeView: TTreeView;
begin

CS.Enter;

  TreeView:=MainForm.TreeView1;

 //Присваиваем первый узел или выходим
 if TreeView.Items.GetFirstNode<>nil then
 Node:=TreeView.Items.GetFirstNode else exit;

 SetLength(CheckedNodes,0);

 repeat
   if Node.StateIndex=2 then
      begin
        SetLength(CheckedNodes,length(CheckedNodes)+1);
        CheckedNodes[High(CheckedNodes)]:=Node.AbsoluteIndex;
      end;
 Node:=Node.GetNext;
 until not Assigned(Node) ;

//Выходим, если ничего не выделено
if length(CheckedNodes)=0 then exit;

 //От последнего узла идем к первому
   i:=High(CheckedNodes);
   repeat

                  //Удаляем всех детей
            while TreeView.Items.Item[CheckedNodes[i]].HasChildren do
              begin
                Node:=TreeView.Items.Item[CheckedNodes[i]].GetLastChild;


                while Node.HasChildren do Node:=Node.GetLastChild;

                Dispose(Node.Data); // <<Удаляем место под указателями
                TreeView.Items.Delete(Node); // Удаляем из дерева

              end;

                  //Удаляем родителя
         TreeView.Items.Item[CheckedNodes[i]].Delete;
dec(i);

 until  i=-1; //  до нулевого включительно

SetLength(CheckedNodes,0); //Освобождаем память

CS.Leave;

end;



end.
