unit uCheckBoxSelectionThread;

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,
  syncobjs;

type
  TCheckBoxSelectionThread = class(TThread)
  private



  FDConnection:TFDConnection;


    procedure Connect;

    Procedure ListParentAndAllChildren(NodeID:Integer); // <<Список всех потомков по базе
    procedure CheckListedInDB; // << Запись в базу всех "чекнутых" узлов
    procedure UncheckListedInDB(Node: TTreeNode); // << Запись в базу всех "Анчекнутых" узлов
    procedure CheckBoxSelection; // << Интеграция 3 предыдущих процедур


    function FindParent(Node:TTreeNode;ParentNode:TTreeNode):boolean;



    { Private declarations }
  protected
    procedure Execute; override;
  end;

  var
  IDList:Tstringlist;
  CS:TCritiCalSection;

implementation

{
  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 TCheckBoxSelectionThread.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.

}

uses MainUnit;

{ TCheckBoxSelectionThread }

procedure TCheckBoxSelectionThread.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 TCheckBoxSelectionThread.Execute;

begin

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


//Основная работа
Self.Connect;
CheckBoxSelection;


//Уничтожение
IDList.Free;
FDConnection.Free;

CS.Free;
  { Place thread code here }
end;



function TCheckBoxSelectionThread.FindParent(Node, ParentNode: TTreeNode): boolean;
begin

Result:=false;

Repeat

if Node=ParentNode then
    begin
     Result:=true;
     break;
    end;

Node:=Node.Parent;

Until not Assigned(Node);


end;

procedure TCheckBoxSelectionThread.ListParentAndAllChildren(NodeID:Integer);

var qSelectAllChildren:TFDQuery;

begin

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


   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 TCheckBoxSelectionThread.CheckListedInDB;
// Расстановка галочек в базе
var i:integer; qCheckedUpdate:TFDQuery;

begin
//

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

   qCheckedUpdate.SQL.Text:=
'UPDATE `treeview_db`.`tree` SET `IsChecked`=:IsChecked WHERE `id`=:id;';


for i := 0 to IDList.Count-1 do
  begin
    qCheckedUpdate.Params.ParamValues['IsChecked']:=1;
    qCheckedUpdate.Params.ParamValues['id']:=IDList[i];
    qCheckedUpdate.ExecSQL;
  end;


qCheckedUpdate.Free;


end;


procedure TCheckBoxSelectionThread.UncheckListedInDB(Node:TTreeNode);
//Снятие галочек в базе
var
  i: Integer;
  ParentNode:TTreeNode;
  qCheckedUpdate:TFDQuery;

begin

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

   qCheckedUpdate.SQL.Text:=
'UPDATE `treeview_db`.`tree` SET `IsChecked`=:IsChecked WHERE `id`=:id;';


     // Снимаем галочку у всех родителей, если они есть
    if Node.Parent<>nil then
     begin
             ParentNode:=Node.Parent;
         while true do
          begin
             if ParentNode.Parent=nil then

             begin
              qCheckedUpdate.Params.ParamValues['IsChecked']:=0;
              qCheckedUpdate.Params.ParamValues['id']:=Integer(Node.Parent.Data^);
              qCheckedUpdate.ExecSQL;
              break;
             end;




              qCheckedUpdate.Params.ParamValues['IsChecked']:=0;
              qCheckedUpdate.Params.ParamValues['id']:=Integer(Node.Parent.Data^);
              qCheckedUpdate.ExecSQL;

              ParentNode:=ParentNode.Parent;
          end;

     end;




     //Снимаем галочку у всех, кто в списке
      for i := 0 to IDList.Count-1 do

      begin

          qCheckedUpdate.Params.ParamValues['IsChecked']:=0;
          qCheckedUpdate.Params.ParamValues['id']:=IDList[i];
          qCheckedUpdate.ExecSQL;
      end;


 qCheckedUpdate.Free;

end;




procedure TCheckBoxSelectionThread.CheckBoxSelection;

var NodeID:integer;
    Node:TTreeNode;
    ParentNode:TTreeNode;
    TreeView:TTreeView;

begin

 CS.Enter;

 TreeView:=MainForm.TreeView1;


 if not Assigned(TreeView.Selected) then exit;

 Node := TreeView.Selected;
 NodeID:=integer(TreeView.Selected.Data^);


 ListParentAndAllChildren(NodeID); // Собрали родителя и всех потомков в список



 //Если выделенный узел не отмечен галочкой

    if TreeView.Selected.StateIndex<>2 then
    begin


     //Отмечаем галочками все видимые, загруженные подузлы
     Node := TreeView.Selected;

      repeat
        Node.StateIndex:=2;
        Node:=Node.GetNext;
      until not (Assigned(Node)) or not FindParent(Node,TreeView.Selected);//      (Node.Level > TreeView.Selected.Level);


    CheckListedInDB; //Отметили галочкой все подузлы в базе

      exit; // Выход, иначе далее галочка опять снимется
    end;


 //Если выделенный узел отмечен галочкой

     if TreeView.Selected.StateIndex=2 then
    begin


     //Снимаем галочки со всех родителей в TreeView

     if Node.Parent<>nil then
     begin

     ParentNode:=Node.Parent;
         while true do
          begin
                 if ParentNode.Parent=nil then

                 begin
                  ParentNode.StateIndex:=1;
                  break;
                 end;

              ParentNode.StateIndex:=1;
              ParentNode:=ParentNode.Parent;

          end;

     end;


     //Снимаем галочки со всех видимых, загруженных подузлов
     //Node стоит на Selected, начинаем перебор с него
      repeat
        Node.StateIndex:=1;
        Node:=Node.GetNext;
      until not (Assigned(Node)) or not FindParent(Node,TreeView.Selected);

     Node := TreeView.Selected; //Снова поставили ссылку на TreeView.Selected
     UncheckListedInDB(Node); // Сняли галочки с родителя и детей в базе

    exit;
    end;


IDList.Clear; // Чистим список

CS.Leave;

end;




end.
