unit uTreeView;

//Главный модуль компонента DBTreeView

interface

uses
//  System.SysUtils, System.Classes, Vcl.Controls, Vcl.ComCtrls,vcl.Dialogs,

   Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls,
  uDBConnectionBPL,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, Data.DB, FireDAC.Comp.DataSet,
  FireDAC.Comp.Client,uDMImageListsBPL,syncobjs,uSearch3,uSQLQueries,Vcl.AppEvnts;


type


  TDBTreeView = class(TTreeView)
  private



  FDBTreeViewConnParams:TDBConnectionParams;

  FDBTreeViewTable:TDBTable;



  FDMImageLists:TDMImageList;  // Поле класса картинок файлов, папок, чек

  FIDList:TStringList; // << Поле для хранения ID узлов для копирования, вырезки, удаления

  FIDofCopiedNode:integer; // Поле для хранения выбранного узла при копировании, вырезке

  FCut:boolean; // << Отметка, что мы вырезаем

  FDragNode:TTreeNode;



  //Для Checkboxes
  FMouseOnCheckBox:boolean;
  FNodeUnderMice:TTreeNode;

  FUnderNode:boolean;

  //Для поиска

  FDataSource:TDataSource;
  FDMSearch:TDMSearch;

  FSQLQueries:TSQLQueries;


  //Сеттеры
  procedure SetDBTreeVIewConnParams (Value:TDBConnectionParams);
  procedure SetDBTreeViewTable(Value:TDBTable);


                        {Обработчики событий}

procedure DBTreeViewCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
      State: TCustomDrawState; var DefaultDraw: Boolean); virtual;
procedure DBTreeViewMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); virtual;

procedure DBTreeViewClick(Sender: TObject); virtual;

procedure DBTreeViewExpanding(Sender: TObject; Node: TTreeNode;
  var AllowExpansion: Boolean); virtual;

           //Drag&Drop обработчики

procedure DBTreeViewStartDrag(Sender: TObject; var DragObject: TDragObject); virtual;
procedure DBTreeViewDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState;
  var Accept: Boolean); virtual;
procedure DBTreeViewDragDrop(Sender, Source: TObject; X, Y: Integer);




                        PUBLIC // <<<<<<< PUBLIC секция


  CONSTRUCTOR Create(AOwner:TComponent); override; // <<<<<<< CONSTRUCTOR
  Destructor Destroy; Override;


  property
  DMImageLists:TDMImageList read  FDMImageLists write  FDMImageLists;

  property DataSource:TDataSource read FDataSource write FDataSource;

  procedure TestDBConnection;
  Procedure Test;

//--------------------ЗДЕСЬ ОСНОВНЫЕ ВОЗМОЖНОСТИ ДЕРЕВА---------------------------------------------


                         //Стартовое динамическое обновление дерева

  procedure InitialUpdate ;

                         // Вставка папки, файла, узла

  procedure InsertFolder;
  procedure InsertFile;
                        // Редактирование текста узла

  procedure EditNodeText;


                      // Удаление выбранных узлов

  Procedure DeleteNodes;

                    // Копирование, вырезка, вставка выбранных узлов

  Procedure CopyNodes;
  Procedure CutNodes;
  Procedure PasteNodes;


                         //Поднять опустить узлы
  procedure NodeDown;
  procedure NodeUp;

                         //Поиск...

  procedure Search(name:string);

                        //Обновление индексов
  procedure UpdateIndexes;

                        //Очистка дерева от галочек

  procedure ClearCheckBoxes;




//--------------------------------------------------------------------------------------------------



  published

  property DBTreeViewConnParams:TDBConnectionParams // <<Свойство непосредственно дерева
  read FDBTreeViewConnParams write SetDBTreeVIewConnParams;

  //Здесь заводим таблицу и 3 важных поля
  property DBTreeViewTable:TDBTable read FDBTreeViewTable write FDBTreeViewTable;
//  property SQLQueries:TSQLQueries read FSQLQueries write FSQLQueries;

  end;

procedure Register;

var

  DBTreeViewGlobal:TDBTreeView;



//--------------------------------------------------------------------------
                                    IMPLEMENTATION
//--------------------------------------------------------------------------

uses uInsertFolderFileNodeBPL,uUpdateTree,uCheckBoxSelectionThreadBPL,uNodeUpDown,uDeleteThread,
uSelectedOrChecked,uPasteThread,uEtcetra,uUpdateIndexesThread,uEditText,uSearchThread;




procedure Register;
begin
  RegisterComponents('Samples', [TDBTreeView]);
end;

                                       { TDBTreeView }

//-----------------------КОНСТРУКТОР И ДЕСТРУКТОР---------------------------------------------------


constructor TDBTreeView.Create(AOwner: TComponent);

var DMEtcetra:TDMEtcetra;

begin
  inherited;


  DBTreeViewGlobal:=Self; // В глобальной переменной ссылка на созданный
  // экземпляр

  FCut:=false;

  Self.Font.Size:=11;

  FDBTreeViewConnParams:=TDBConnectionParams.Create(Self);
  FDBTreeViewConnParams.SetSubComponent(true);


  FDBTreeViewTable:=TDBTable.Create(Self);
  FDBTreeViewTable.SetSubComponent(true);



  //Создаем и передаем ссылки
//  FSQLQueries:=TSQLQueries.Create(Self);
//  FSQLQueries.SetSubComponent(true);

  //  FSQLQueries.DBTableName:=Self.DBTreeViewTable;
// FSQLQueries.DBConnectionParams:=Self.DBTreeViewConnParams;



  //Имэдж листы иконок папок, файлов, чекбоксов
  FDMImageLists:=TDMImageList.Create(Self); //Уничтожится при уничтожении компонента

  Self.StateImages:=FDMImageLists.ilCheckBoxes;
  Self.Images:=FDMImageLists.ilFoldersAndFiles;

  //Для поиска
  FDMSearch:=TDMSearch.Create(Self);
  FDataSource:=TDataSource.Create(Self);


                         //ПРИСВОЕНИЕ ОБРАБОТЧИКОВ

  Self.OnCustomDrawItem:=DBTreeViewCustomDrawItem;

              // Мышь
  Self.OnMouseMove:=DBTreeViewMouseMove;
  Self.OnClick:=DBTreeViewClick;
  Self.OnExpanding:=DBTreeViewExpanding;

            //Drag&Drop

  Self.DragMode:=dmManual;
  Self.DragKind:=dkDrag;
  Self.DragCursor:=crDrag;

  Self.OnStartDrag:=DBTreeViewStartDrag;
  Self.OnDragOver:=DBTreeViewDragOver;
  Self.OnDragDrop:=DBTreeViewDragDrop;


   //FIDLIST
   FIDList:=TStringList.Create;



 //Снятие галочек при загрузке
// DMEtcetra:=TDMEtcetra.Create(Self);

try

//DMEtcetra.ClearAllCheckBoxes2(Self);
except
// гасим ошибку
end;



//FreeAndNil(DMEtcetra);


end;

//------------------ДЕСТРУКТОР

destructor TDBTreeView.Destroy;
begin
if FIDList<>nil then FreeAndNil(FIDList);
  inherited;
end;



//--------------------------------------------------------------------------------------------------

procedure TDBTreeView.TestDBConnection;
// --Проверка связи с базой данных
var DBConnectionLocal:TDBConnectionBPL;
begin

DBConnectionLocal:=TDBConnectionBPL.Create(nil);


//Здесь важный момент, передаем параметры соединения из дерева
//(ObjectInspector) в локальную переменную

//DBConnectionLocal.DBConnectionParams.Assign(Self.DBTreeViewConnParams);   //  <<Ответ находится здесь
//DBConnectionLocal.DBTable.Assign(Self.DBTreeViewTable);
//DBConnectionLocal.Connect; {Параметры из ObjectInspector уходят внутрь}

showmessage(
DBConnectionLocal.DBConnectionParams.DataBase
);

if DBConnectionLocal.FDConnection.Connected then
showmessage('Connected to DB') else
showmessage('Could not connect to DB');
FreeAndNil(DBConnectionLocal);

end;







procedure TDBTreeView.SetDBTreeVIewConnParams(Value:TDBConnectionParams);
begin
FDBTreeViewConnParams.Assign(Value);
end;




procedure TDBTreeView.SetDBTreeViewTable(Value: TDBTable);
begin

FDBTreeViewTable:=Value;

end;


//-----------------------ВСТАВКА ПАПКИ ФАЙЛА УЗЛА---------------------------------------------------

                                   {Вставка папки}
procedure TDBTreeView.InsertFolder;
//Вставка папки
var InsertFolderFileNodeBPL:TInsertFolderFileNodeBPL;
    FolderName:string;

begin

//Создаем
InsertFolderFileNodeBPL:=TInsertFolderFileNodeBPL.Create(Self);
InsertFolderFileNodeBPL.DBTreeView:=Self;

if InputQuery('Введите имя папки','',FolderName) then

begin

  if Assigned(Self.Selected)
  and not (InsertFolderFileNodeBPL.IsFolder(Self.Selected)) then

  begin
    showmessage('Вставка возможна только в папку или корень');
    FreeAndNil(InsertFolderFileNodeBPL);
    exit;
  end;



  InsertFolderFileNodeBPL.InsertFolderOrFileInTree(Self,true,FolderName);

  end;

//Освобождаем
FreeAndNil(InsertFolderFileNodeBPL);
end;



{Вставка файла}


procedure TDBTreeView.InsertFile;

var InsertFolderFileNodeBPL:TInsertFolderFileNodeBPL;
    FileName:string;

begin

//Создаем
InsertFolderFileNodeBPL:=TInsertFolderFileNodeBPL.Create(Self);
InsertFolderFileNodeBPL.DBTreeView:=Self;

if InputQuery('Введите имя файла','',FileName) then

begin

  if Assigned(Self.Selected)
  then

  begin
    //Если папка, то вставляем в неёё
   if  (InsertFolderFileNodeBPL.IsFolder(Self.Selected)) then
       InsertFolderFileNodeBPL.InsertFolderOrFileInTree(Self,false,FileName);

   //Если файл, то выходим из процедуры
   if not (InsertFolderFileNodeBPL.IsFolder(Self.Selected)) then
      begin
        showmessage('Вставка возможна только в папку или корень');
        FreeAndNil(InsertFolderFileNodeBPL);
        exit;
      end;

  end else
  // Иначе вставляем в корень
  InsertFolderFileNodeBPL.InsertFolderOrFileInTree(Self,false,FileName);

end;

//end else InsertFolderFileNodeBPL.InsertFolderOrFileInTree(Self,false,FileName);

//Освобождаем
FreeAndNil(InsertFolderFileNodeBPL);


end;

//----------------------------Редактирование названия узла

procedure TDBTreeView.EditNodeText;
var EditNodeText:TEditNodeText;
    NewName:string;

begin

 if not Assigned(Self.Selected) then exit;

 EditNodeText:=TEditNodeText.Create(Self);
 EditNodeText.DBTreeView:=Self; // << Ссылка на дерево

 if InputQuery('Введите новое название','',NewName)=true then

 begin

 if NewName='' then
    begin
     FreeAndNil(EditNodeText);
     exit;
    end else

 EditNodeText.UpdateNodeText(NewName);

 end;




 FreeAndNil(EditNodeText);
end;


//--------------------------------InitialUpdate

procedure TDBTreeView.InitialUpdate;

var UpdateTree:TUpdateTree; DMEtcetra:TDMEtcetra;

begin


UpdateTree:=TUpdateTree.Create(Self);

UpdateTree.ExpandingUpdate(nil,Self);

if Self.Items.GetFirstNode<>nil then
Self.Items.GetFirstNode.Expand(false);

FreeAndNil(UpdateTree);


//Снимаем галочки


DMEtcetra:=TDMEtcetra.Create(Self);

DMEtcetra.ClearAllCheckBoxes2(Self);

FreeAndNil(DMEtcetra);

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

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



//-----------------------------ОБРАБОТЧИКИ СОБЫТИЙ-------------------------------------------------


procedure TDBTreeView.DBTreeViewMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);

var Node:TTreeNode;

begin

 // Эта часть кода для Чекбоксов
if Self.GetNodeAt(X,Y)<>nil then

begin

if Self.GetNodeAt(X,Y)<>nil then FNodeUnderMice:=Self.GetNodeAt(X,Y);

                    {СОБСТВЕННО ЗДЕСЬ И СТАВИМ ГРАНИЦУ}
  if (X<FNodeUnderMice.DisplayRect(true).Left-16) and

  (X>FNodeUnderMice.DisplayRect(true).Left-35)

 then
  FMouseOnCheckBox:=true
 else
  FMouseOnCheckBox:=False;
end;

              //Отдельная часть кода для проверки на узлом ли мышка?
 if Self.GetNodeAt(X,Y)<>nil then

 begin

 if (X<=FNodeUnderMice.DisplayRect(true).Left+FNodeUnderMice.DisplayRect(true).Width)

 then
 FUnderNode:=true else FUnderNode:=false

 end else FUnderNode:=false;


 //------------------Ещё одна часть кода, для начала DragDrop
//Старт механизма DragDrop
if Assigned(FNodeUnderMice) and (ssLeft in Shift) then
Self.BeginDrag(false,10);


end;


//-----------------------------------CLICK----------------------------------------------------------

procedure TDBTreeView.DBTreeViewClick(Sender: TObject);

var CheckBoxSelectionThreadBPL:TCheckBoxSelectionThreadBPL;
    Node:TTreeNode;

begin

//Ставим галочку, вызывая поток
if FMouseOnCheckBox then

 begin

     CheckBoxSelectionThreadBPL:=TCheckBoxSelectionThreadBPL.Create(true);
     CheckBoxSelectionThreadBPL.DBTreeViewProperty:=Self;
     CheckBoxSelectionThreadBPL.MyCreate;

     CheckBoxSelectionThreadBPL.FreeOnTerminate:=true;
     CheckBoxSelectionThreadBPL.Start;

 end;

//Для клика на пустом поле, чтобы узел добавлять на нулевой уровень
if not FUnderNode then Self.Selected:=nil;

//Если на пустом месте, то снимаем выделение со всех Cutted


if not FUnderNode then
  begin

  Node:=Self.Items.GetFirstNode;

  repeat

  Node.Cut:=false;

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



  end;


end;




procedure TDBTreeView.DBTreeViewCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
  State: TCustomDrawState; var DefaultDraw: Boolean);

var NodeID:integer; qIsFolder:TFdQuery;
    DBConnectionLocal:TDBConnectionBPL;
    SQLQueriesLocal:TSQLQueries;

    idField:string;
    IsFolderField:string;
    DBNamePlusTable:string;


begin


//Создание
 DBConnectionLocal:=TDBConnectionBPL.Create(Self);
 qIsFolder:=TFdQuery.Create(Self);
// SQLQueriesLocal:=TSQLQueries.Create(Self);


 qIsFolder.Connection:=DBConnectionLocal.FDConnection;

//-------------------СТРОИМ ЗАПРОС ЛОКАЛЬНО-------------------------------------

//так как через FSQLQueries в данном случае не срабатывало

//Присвоение полей и переменных

 idField:=Self.DBTreeViewTable.id;
 isFolderField:=Self.DBTreeViewTable.IsFolder;

 DBNamePlusTable:=
  '`'
  +DBTreeViewGlobal.DBTreeViewConnParams.DataBase
  +'`'+'.'+'`'
  +DBTreeViewGlobal.DBTreeViewTable.TableName+'`';

// Построение самого запроса
 qIsFolder.SQL.Text:= 'SELECT '+idField+','+isFolderField+' FROM '+DBNamePlusTable+';';

NodeID:=integer(Node.Data^);

with  qIsFolder do
 begin
 Open();

//   showmessage(IsFolderField+'  '+idField);

  if Locate(idField,NodeId,[])=true
  then begin
   //  showmessage('Здесь');

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

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

    Node.ImageIndex:=0;


  end;

 Close();
 end;


 //Освобождение
 FreeAndNil(qIsFolder);
 FreeAndNil(DBConnectionLocal);

end;


//--------------------------------ONEXPANDING

procedure TDBTreeView.DBTreeViewExpanding(Sender: TObject; Node: TTreeNode;
  var AllowExpansion: Boolean);

var UpdateTree:TUpdateTree;

begin
    UpdateTree:=TUpdateTree.Create(Self);

    UpdateTree.ExpandingUpdate(Node,Self);

    FreeAndNil(UpdateTree);

end;

procedure TDBTreeView.Test;
begin
//
end;


//--------------------------------Поднятие, опускание узлов-----------------------------------------

procedure TDBTreeView.NodeDown;

var NodeUpDown:TNodeUpDown;

begin

NodeUpDown:=TNodeUpDown.Create(Self);
NodeUpDown.DBTReeView:=Self; // << Передаем в поле экземпляра класса ссылку на себя
NodeUpDown.NodeDown;
FreeAndNil(NodeUpDown);

end;


procedure TDBTreeView.NodeUp;
var NodeUpDown:TNodeUpDown;

begin

NodeUpDown:=TNodeUpDown.Create(Self);

NodeUpDown.DBTReeView:=Self; // << Передаем в поле экземпляра класса ссылку на себя
NodeUpDown.NodeUp;
FreeAndNil(NodeUpDown);

end;

//-----------------------------------Удаление узлов-------------------------------------------------

procedure TDBTreeView.DeleteNodes;

var DeleteThread:TDeleteThread;
    SelectedOrChecked:TSelectedOrChecked;

begin

// Собираем в список выделенные или отмеченные галочками и их потомков
SelectedOrChecked:=TSelectedOrChecked.Create(nil);
SelectedOrChecked.DBTreeView:=Self; //Передаем ссылку на объект
SelectedOrChecked.List; // Собрали в список

Self.Items.BeginUpdate;
//Запускаем поток
DeleteThread:=TDeleteThread.Create(true);
DeleteThread.DBtreeView:=Self; // Ссылка на объект
DeleteThread.MyCreate; // Создание внутренних переменных


//Передаем список в экземпляр класса
DeleteThread.IDList.Assign(SelectedOrChecked.IDListToCopyOrCutOrDelete);

//Запускаем поток
DeleteThread.Start;
DeleteThread.FreeOnTerminate:=true;
FreeAndNil(SelectedOrChecked);

Self.Items.EndUpdate;

end;



//--------------------Копирование узлов-------------------------------------------------------------

procedure TDBTreeView.CopyNodes;

var SelectedOrChecked:TSelectedOrChecked;


begin

//Спрашиваем что копировать и...
// Собираем в список выделенные или отмеченные галочками и их потомков
SelectedOrChecked:=TSelectedOrChecked.Create(nil);
SelectedOrChecked.DBTreeView:=Self; //Передаем ссылку на объект
SelectedOrChecked.List; // Собрали в список

FIDofCopiedNode:=integer(Self.Selected.Data^);  // << Отдельно омечаем выбранный узел


//if FIDList<>nil then FreeAndNil(FIDList);

//FIDList:=nil;
//FIDList:=TStringList.Create;

FIDList.Clear;
FIDList.Assign(SelectedOrChecked.IDListToCopyOrCutOrDelete);

//Освобождение
FreeAndNil(SelectedOrChecked);

end;

//----------------------Вырезка выбранных узлов-----------------------------------------------------

procedure TDBTreeView.CutNodes;
var SelectedOrChecked:TSelectedOrChecked; i:integer;
    DMEtcetra:TDMEtcetra;


begin

DMEtcetra:=TDMEtcetra.Create(Self);

FCut:=true;

//Спрашиваем что вырезать и...
// Собираем в список выделенные или отмеченные галочками и их потомков
SelectedOrChecked:=TSelectedOrChecked.Create(nil);
SelectedOrChecked.DBTreeView:=Self; //Передаем ссылку на объект
SelectedOrChecked.List; // Собрали в список

FIDofCopiedNode:=integer(Self.Selected.Data^); // << Отдельно омечаем выбранный узел


//if FIDList<>nil then FreeAndNil(FIDList);
//FIDList:=nil;
//FIDList:=TStringList.Create;
FIDList.Clear;
FIDList.Assign(SelectedOrChecked.IDListToCopyOrCutOrDelete);

//Отмечаем, что узлы под вырезание

for i := 0 to FIDList.Count-1  do
begin
if DMEtcetra.FindNodeIntree(FIDList[i].ToInteger,Self)<>nil then
DMEtcetra.FindNodeIntree(FIDList[i].ToInteger,Self).Cut:=true;
end;




//Освобождение
FreeAndNil(SelectedOrChecked);
FreeAndNil(DMEtcetra);

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

//----------------------Вставка выбранных узлов-----------------------------------------------------

procedure TDBTreeView.PasteNodes;

{Здесь можно увидеть наглядный пример синхронизации потоков!!!}

var PasteThread:TPasteThread;
    UpdateIndexesThread:TUpdateIndexesThread;

    Event:TEvent;

    IDSelected:integer;

begin

if not Assigned(FIDList) then FIDList:=TStringList.Create;

//Защита, если список пустой, то выходим
if FIDList.Count=0 then exit;


//Если копируем родителя в ребенка, тоже выходим

IDSelected:=integer(Self.Selected.Data^);

if FIDList.IndexOf(IDSelected.ToString())<>-1 then
 begin

 raise Exception.Create('Начальный элемент содержит конечный.Вставка невозможна');

 exit;
 end;



Event:=TEvent.Create(
nil,  //Security Attributes
true, //Manual Reset
false,// Initial State
''
);



//Event.ReSetEvent; // <<Вошли в событие
//Запускаем поток копирования, передаем ссылки...

Self.Items.BeginUpdate;


PasteThread:=TPasteThread.Create(true);//true означает ручное управление потоком
PasteThread.DBTreeView:=Self; //Передаем ссылку на дерево

PasteThread.Event:=Event; //Передали ссылку на событие снаружи

PasteThread.MyCreate; // Создаем внутренние переменные потока
PasteThread.IDList.Assign(FIDList);
PasteThread.IDofCopiedNode:=FIDofCopiedNode;
PasteThread.Cut:=FCut;


PasteThread.Start;
PasteThread.FreeOnTerminate:=true;

//Event.SetEvent; // << Вышли из события

//if PasteThread.Finished then


begin

//Перенумерация индексов
    UpdateIndexesThread:=TUpdateIndexesThread.Create(true);
    UpdateIndexesThread.Event:=Event;
    UpdateIndexesThread.DBTreeView:=Self;
    UpdateIndexesThread.MyCreate;
    UpdateIndexesThread.Start;

    UpdateIndexesThread.FreeOnTerminate:=true;

end;



Self.Items.EndUpdate;

FCut:=false;

//Освобождение Event происходит внутри UpdateIndexesThread потока по ссылке

//if Event<>nil then FreeAndNil( Event );// <<Так делать не надо, так как поток может
// закончиться позже

end;


//--------------------------------DRAG & DROP-------------------------------------------------------

procedure TDBTreeView.DBTreeViewStartDrag(Sender: TObject; var DragObject: TDragObject);

begin
FDragNode:=Self.Selected;
end;

procedure TDBTreeView.DBTreeViewDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState;
  var Accept: Boolean);

var SenderNode:TTReeNode; HitTests:THitTests;

begin

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

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



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

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

 end;


procedure TDBTreeView.DBTreeViewDragDrop(Sender, Source: TObject; X, Y: Integer);

var SenderNode,Node:TTreeNode;
    DBConnectionLocal:TDBConnectionBPL;
    qEditParentKey:TFDQuery;
    UpdateIndexesThread:TUpdateIndexesThread;
    SQLQueries:TSQLQueries;

    Event:TEvent;

begin

Event:=TEvent.Create(
nil,  //Security Attributes
true, //Manual Reset
false,// Initial State
''
);



 //Создание
 DBConnectionLocal:=TDBConnectionBPL.create(Self);
 qEditParentKey:=TFDQuery.Create(Self);
 SQLQueries:=TSQLQueries.Create(Self);


 qEditParentKey.Connection:=DBConnectionLocal.FDConnection;

 qEditParentKey.SQL.Text:=SQLQueries.EditParentKey.Text;
// 'UPDATE `treeview_db`.`tree` SET `idParent`=:idParent WHERE `id`=:id;';

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

 Event.ResetEvent;  // << Вошли в событие

 if Assigned(SenderNode) and
   (MessageBox(Application.Handle,
   PChar('Подчинить '+FDragNode.Text+#13+' узлу '+SenderNode.Text+' ?'),
   PChar('Перемещение узла'), MB_YESNO+MB_ICONQUESTION)=ID_YES)
   then

   begin
      Screen.Cursor:=crHourgLass;
      FDragNode.MoveTo(SenderNode,naAddChild); // Собственно перенос в дереве

      // Уведдомляем базу о смене родителя
      with qEditParentKey.Params do
      begin
       ParamValues['idParent']:=Integer(SenderNode.Data^);
       ParamValues['id']:=Integer(FDragNode.Data^);
      end;
      qEditParentKey.ExecSQL;

   Event.SetEvent; // <<Вышли из события


//Пересчитываем абсолютные индексы в базе
    UpdateIndexesThread:=TUpdateIndexesThread.Create(true);

    UpdateIndexesThread.DBTreeView:=Self;
    UpdateIndexesThread.Event:=Event;  // Передали ссылку на событие
    // Уничтожение события будет также по ссылке внутри

    UpdateIndexesThread.MyCreate;
    UpdateIndexesThread.Start;
    UpdateIndexesThread.FreeOnTerminate:=true;

    Screen.Cursor:=crDefault;

    //Обновление StateIndexes (обход бага)
   TDMEtcetra.UpdateStateIndexes(SenderNode);

   end;



//Освобождение
 FreeAndNil(DBConnectionLocal);
 FreeAndNil(qEditParentKey);
 FreeAndNil(SQLQueries);

end;


procedure TDBTreeView.Search(name: string);
begin

FDMSearch.Search(Name);
FDataSource:=FDMSearch.DataSource;

end;


//Обновление индексов в отдельном потоке
procedure TDBTreeView.UpdateIndexes;
var UpdateIndexesThread:TUpdateIndexesThread;
begin

//Пересчитываем абсолютные индексы в базе
    UpdateIndexesThread:=TUpdateIndexesThread.Create(true);
    UpdateIndexesThread.DBTreeView:=Self;
    UpdateIndexesThread.MyCreate;
    UpdateIndexesThread.Start;
    UpdateIndexesThread.FreeOnTerminate:=true;

end;


//Очистка галочек


procedure TDBTreeView.ClearCheckBoxes;
var DMEtcetra:TDMEtcetra;
begin

DMEtcetra:=TDMEtcetra.Create(Self);

DMEtcetra.ClearAllCheckBoxes2(Self);

FreeAndNil(DMEtcetra);

end;





END.
