unit U_FilterConfigurator;

interface

uses
  Windows, U_LNG, Messages, SysUtils, Variants, Classes, Graphics, Controls, Contnrs, Forms,
  Dialogs, StdCtrls, RzPanel, RzCommon, ExtCtrls, RzCmboBx, RzButton,
  exgrid, RapTree, FlytreePro, Treecoll, ispinedit, IsPlugEdit, RzRadChk,
  U_Common_Classes, ActnList, Menus, ComCtrls, ToolWin, ImgList, inifiles, RzShellDialogs,
  siComp, siLngLnk,

  U_BaseCommon, U_BaseConstants, {Tolik 01/08/25019}U_BaseSettings;

const
  ChildPanelHeight = 35;
  PanelBiggerFromChildDelta = 4;
  ChildControlLeft = 10;
  ChildControlTop = 10;


  //*** Column Index
  ciIsOn = 0;
  ciConditionType = 1;
  ciFieldName = 2;
  ciCompareType = 3;
  ciValue = 4;

  {ciConditionType = 0;
  ciFieldName = 1;
  ciCompareType = 2;
  ciValue = 3;}


  // ConditionTypeStr
  ctsAnd = 'AND';
  ctsOr = 'OR';
  ctsAndNot = 'ANDNOT';
  ctsOrNot = 'ORNOT';

  // ConditionTypeCaptionStr
  cntcAnd = cFilterConfigurator3_1;
  cntcOr = cFilterConfigurator3_2;
  cntcAndNot = cFilterConfigurator3_3;
  cntcOrNot = cFilterConfigurator3_4;

  // CompareTypeCaption
  ctmEqual = cFilterConfigurator4_1;
  ctmNoEqual = cFilterConfigurator4_2;


  cTrue = -1;
  cFalse = 0;
  cshTrue = 'T';
  cshFalse = 'F';
  csTrue = 'True';
  csFalse = 'False';


  //FilterFileSection
  scFilter = 'FILTER';
  // Ident
  idtFilterType = 'FilterType';
  idtVer = 'Ver';
  idtFilterStr = 'FilterStr';

  FilterBlockFormatLastVersion = 1;
type
  TFilterBlock = class;
  TFilterBlockControl = class;

  TConditionType = ({cdNone, }ctOr, ctAnd, ctOrNot, ctAndNot);

  // равно, не равно, меньше, меньше равно, больше, больше равно, строка начинается, строка содержит, строка равна полностью
  TCompareType = (ctEqual, ctNoEqual,
                  ctLess, ctLessEqual, ctMore, ctMoreEqual,
                  ctStrBegin, ctStrContent, ctStrIdentical, ctStrNotContent);

  TCondition = record
    FieldIndex: Integer;
    FieldName: String[255];
    FieldValue: String[255];
    FilterValue: String[255];

    UserFieldName: String[255];
    UserFilterValue: String[255];

    CompareType: TCompareType;
    TakeInCharCase: Boolean;
  end;
  PCondition = ^TCondition;

  TBlockType = (btBlock, btCondition);

  TFilterBlocks = class(TMyObject)
    protected
      FItems: TObjectList;
      function GetCount: Integer;
      function GetItem(Index: Integer): TFilterBlock;
      procedure SetItem(Index: Integer; AFilterBlock: TFilterBlock);
    public
      constructor Create(AOwnObjects: Boolean); overload;
      destructor  Destroy; override;

      function Add(AFilterBlock: TFilterBlock): Integer;
      procedure Assign(ListA: TFilterBlocks; AOperator: TListAssignOp = laCopy; ListB: TFilterBlocks = nil);
      procedure Clear;
      procedure Delete(AIndex: Integer);
      procedure Move(CurrIndex, NewIndex: Integer);
      function Remove(AFilterBlock: TFilterBlock): Integer;
      function RemoveItems(AItems: TFilterBlocks): Integer;
      function IndexOf(AFilterBlock: TFilterBlock): Integer;
      procedure Insert(Index: Integer; AFilterBlock: TFilterBlock);
      property Count: Integer read GetCount;
      property Items[index: integer]: TFilterBlock read GetItem write SetItem; default;
  end;

  TFilterBlock = class(TMyObject)
    private
      FParent: TFilterBlock;
      FBlockType: TBlockType;

      FConditionType: TConditionType;
      FCondition: PCondition;
      FIsOn: Boolean;

      FIsTrue: Boolean;

      FControl: TFilterBlockControl;

      function GetIsOnFromShortStr(AStr: String): Boolean;
    protected
      FChildBlocks: TFilterBlocks;
      FAllChildBlocks: TFilterBlocks;
    public
      property AllChildBlocks: TFilterBlocks read FAllChildBlocks write FAllChildBlocks;
      property BlockType: TBlockType read FBlockType;
      property ChildBlocks: TFilterBlocks read FChildBlocks write FChildBlocks;
      property Condition: PCondition read FCondition write FCondition;
      property ConditionType: TConditionType read FConditionType write FConditionType;
      property IsOn: Boolean read FIsOn write FIsOn;
      property IsTrue: Boolean read FIsTrue;

      procedure Assign(AFilterBlock: TFilterBlock);
      procedure AssignOnlyBlock(AFilterBlock: TFilterBlock);
      procedure AssignChilds(AFilterBlock: TFilterBlock);
      function CheckIsOnUp: Boolean;
      procedure Clear;
      constructor Create(AParent: TFilterBlock; ABlockType: TBlockType); overload;
      destructor Destroy; override;
      procedure DefineParamsFromFilterFields(AFilterFields: TObjectList; ADefIsOn: Boolean);
      function GetActiveConditionCount: Integer;
      function GetChildIsOnCount: Integer;
      function GetFilterAsString(AForUser: Boolean): String;
      function LoadFromStr(AStr: String; AFilterFields: TObjectList; ADefIsOn: Boolean=true): Boolean;
      function LoadFromFile(AFileName: String; AFilterType: Integer;
                  AFilterFields: TObjectList; AShowMesage: Boolean): Boolean;
      procedure SaveToFile(AFileName: String; AFilterType: Integer);
      function Execute: Boolean;
  end;

  TFilterField = class(TMyObject)
    private
      FFieldIndex: Integer;
      FFieldCaption: String;
      FFieldName: String;
    protected
      FValues: TStringList;
      FValueCaptions: TStringList;
    public
      property FieldIndex: Integer read FFieldIndex write FFieldIndex;
      property FieldCaption: String read FFieldCaption write FFieldCaption;
      property FieldName: String read FFieldName write FFieldName;
      property Values: TStringList read FValues write FValues;
      property ValueCaptions: TStringList read FValueCaptions write FValueCaptions;

      constructor Create; overload;
      destructor Destroy; override;
  end;

  TFilterBlockControl = class(TRzPanel)
    private
      FBlockControlType: TBlockType;
      FParentBlock: TFilterBlockControl;
      FOwner: TComponent; // ссылк на объект формы
      FContainerOwner: TObjectList; // список объеков TFilterBlockControl

      FControls: TObjectList; // список контролов, созданных на этой панели

      FSelected: Boolean;

      //*** контролы
      pnFieldCaption: TRzPanel;
      pnCompare: TRzPanel;
      pnValues: TRzPanel;

      lbFieldCaption: TLabel;
      lbCompare: TLabel;
      cbValues: TRzComboBox;

      procedure CreateConditionControls;
      function CreatePanel: TRzPanel;
      procedure DefineStyle;
      procedure RegisterControl(AControl: TControl);
      procedure SetSelected(Value: Boolean);
      procedure SetChildControlPos(AControl: TControl);
    public
      property Selected: Boolean read FSelected write SetSelected;

      constructor Create(ABlockControlType: TBlockType; AOwner: TComponent; AParentBlock: TFilterBlockControl; AContainerOwner: TObjectList);
  end;



  TF_FilterConfigurator = class(TForm)
    sbBlockContainer: TScrollBox;
    RzPanel1: TRzPanel;
    btAddCondition_: TRzButton;
    btAddBlock_: TRzButton;
    btAddCondition: TRzButton;
    btAddBlock: TRzButton;
    RzPanel2: TRzPanel;
    RzPanel3: TRzPanel;
    btOk: TRzBitBtn;
    btCancel: TRzBitBtn;
    RzPanel4: TRzPanel;
    cbIsUseFilter: TRzCheckBox;
    ActionList1: TActionList;
    Act_AddCondition: TAction;
    Act_AddBlock: TAction;
    Act_DelBlock: TAction;
    btDel: TRzButton;
    PopupMenu1: TPopupMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ToolButton5: TToolButton;
    ToolButton6: TToolButton;
    ImageList1: TImageList;
    Act_OpenFilterFromFile: TAction;
    Act_SaveFilterToFile: TAction;
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;
    lng_Forms: TsiLangLinked;
    tvFilter: TFlyTreeViewPro;
    procedure btAddCondition_Click(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure BlockFilterControlClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btAddBlock_Click(Sender: TObject);
    procedure tvFilterSelectedChanged(OldNode, NewNode: TFlyNode);
    procedure tvFilterCloseUp(Sender: TISPlugInplaceEdit;
      Section: TISPlugSection; DropDown: TISDropDown; var Accept: Boolean);
    procedure Act_AddConditionExecute(Sender: TObject);
    procedure Act_AddBlockExecute(Sender: TObject);
    procedure Act_DelBlockExecute(Sender: TObject);
    procedure Act_OpenFilterFromFileExecute(Sender: TObject);
    procedure Act_SaveFilterToFileExecute(Sender: TObject);
    procedure tvFilterEdited(Sender: TObject; Node: TFlyNode;
      var S: String);
    procedure tvFilterStateChange(Node: TFlyNode; Oldindex: Integer);
  private
    GForm: TForm;
    FBlockControls: TObjectList;

    FSelectedBlockControl: TFilterBlockControl;

    FFilterFields: TObjectList;

    //procedure AddBlock(ABlockType: TBlockType; AParentBlock: TFilterBlockControl);
    procedure AddBlock(ABlockType: TBlockType; AParentNode: TFlyNode);
    procedure AddNodesFromFilterBlock(AFilterBlock: TFilterBlock);
    procedure AddNodesFromFilterFile(AFilterFile: string);
    procedure ApplyNodeParams(ANode: TFlyNode);
    procedure DelNodeBlock(ANode: TFlyNode);
    procedure ClearNodes;
    function GetCurrFilterStr: String;
    function GetFilterFieldByFieldIndex(AFIndex: Integer): TFilterField;
    function GetIsOnConditionCount: Integer;
    procedure LoadFieldNames;
    procedure LoadFieldValuesByFieldIndex(AFIndex: Integer);
    procedure ReselectNode;
    procedure SetColumnCaption(AColumnIndex: Integer; ACaption: String);
    procedure SetControls;
  public
    ColumnCaptionConditionType: String;
    ColumnCaptionFieldName: String;
    ColumnCaptionCompareType: String;
    ColumnCaptionValue: String;
    OpenSaveDialogFilter: String;
    FilterType: Integer;

    Constructor Create(AOwner: TComponent; AForm: TForm);
    Destructor Destroy; override;

    function Execute(AFilterFields: TObjectList; AFilterBlock: TFilterBlock; AFilterType: Integer): Boolean;
    function GetMainFilterBlock: TFilterBlock;
  end;

  function IsEqualFilterBlocks(ABlock1, ABlock2: TFilterBlock): Boolean;

//var
//  F_FilterConfigurator: TF_FilterConfigurator;

implementation

//uses Unit1;

{$R *.dfm}


{ TFilterBlocks }

function TFilterBlocks.Add(AFilterBlock: TFilterBlock): Integer;
begin
  Result := FItems.Add(AFilterBlock); //inherited Add(Pointer(AInt));
end;

procedure TFilterBlocks.Assign(ListA: TFilterBlocks; AOperator: TListAssignOp = laCopy; ListB: TFilterBlocks = nil);
var
  LAItems: TList;
  LBItems: TList;
begin
  LAItems := nil;
  LBItems := nil;

  if Assigned(ListA) then
    LAItems := ListA.FItems;
  if Assigned(ListB) then
    LBItems := ListB.FItems;

  FItems.Assign(LAItems, AOperator, LBItems);
end;

procedure TFilterBlocks.Clear;
begin
  FItems.Clear;
end;

procedure TFilterBlocks.Delete(AIndex: Integer);
begin
  FItems.Delete(AIndex);
end;

procedure TFilterBlocks.Move(CurrIndex, NewIndex: Integer);
begin
  FItems.Move(CurrIndex, NewIndex);
end;

constructor TFilterBlocks.Create(AOwnObjects: Boolean);
begin
  inherited Create;
  FItems := TObjectList.Create(AOwnObjects);
end;

destructor TFilterBlocks.Destroy;
begin
  FItems.Free;
  inherited;
end;

function TFilterBlocks.GetCount: Integer;
begin
  Result := FItems.Count;
end;

function TFilterBlocks.GetItem(Index: Integer): TFilterBlock;
begin
  Result := TFilterBlock(FItems[Index]); //Integer(inherited Items[Index]);
end;

function TFilterBlocks.IndexOf(AFilterBlock: TFilterBlock): Integer;
begin
  Result := FItems.IndexOf(AFilterBlock); //inherited IndexOf(Pointer(AInt));
end;

procedure TFilterBlocks.Insert(Index: Integer; AFilterBlock: TFilterBlock);
begin
  FItems.Insert(Index, AFilterBlock); //inherited Insert(Index, Pointer(AInt));
end;

function TFilterBlocks.Remove(AFilterBlock: TFilterBlock): Integer;
begin
  Result := FItems.Remove(AFilterBlock); //inherited Remove(Pointer(AInt));
end;


function TFilterBlocks.RemoveItems(AItems: TFilterBlocks): Integer;
var
  i: Integer;
begin
  for i := 0 to AItems.Count - 1 do
    Remove(AItems[i]);
end;

procedure TFilterBlocks.SetItem(Index: Integer; AFilterBlock: TFilterBlock);
begin
  FItems.Items[Index] := AFilterBlock; //inherited Items[Index] := Pointer(AInt);
end;

{ TFilterBlock }

procedure TFilterBlock.Assign(AFilterBlock: TFilterBlock);
begin
  Clear;
  AssignOnlyBlock(AFilterBlock);
  AssignChilds(AFilterBlock);
end;

procedure TFilterBlock.AssignOnlyBlock(AFilterBlock: TFilterBlock);
begin
  FBlockType := AFilterBlock.FBlockType;

  FConditionType := AFilterBlock.FConditionType;
  FIsOn := AFilterBlock.FIsOn;
  if (FCondition <> nil) and (AFilterBlock.FCondition <> nil) then
  begin
    FCondition^ := AFilterBlock.FCondition^;
  end;
end;

procedure TFilterBlock.AssignChilds(AFilterBlock: TFilterBlock);
var
  SrcChild: TFilterBlock;
  NewChild: TFilterBlock;
  i: Integer;
begin
  for i := 0 to AFilterBlock.FChildBlocks.Count - 1 do
  begin
    SrcChild := TFilterBlock(AFilterBlock.FChildBlocks[i]);

    NewChild := TFilterBlock.Create(Self, SrcChild.FBlockType);
    NewChild.Assign(SrcChild);
  end;
end;

function TFilterBlock.CheckIsOnUp: Boolean;
var
  CurrParent: TFilterBlock;
begin
  Result := IsOn;
  if IsOn then
  begin
    CurrParent := FParent;
    while CurrParent <> nil do
    begin
      if Not CurrParent.IsOn then
      begin
        Result := false;
        Break; //// BREAK ////
      end;
      CurrParent := CurrParent.FParent;
    end;
  end;
end;

procedure TFilterBlock.Clear;
begin
  FChildBlocks.Clear;
end;

constructor TFilterBlock.Create(AParent: TFilterBlock; ABlockType: TBlockType);
var
  CurrParent: TFilterBlock;
begin
  inherited create;
  FParent := AParent;
  FBlockType := ABlockType;

  FCondition := nil;
  if FBlockType = btCondition then
  begin
    GetMem(FCondition, SizeOf(TCondition));
    ZeroMemory(FCondition, SizeOf(TCondition));
  end;

  FIsOn := true;
  FIsTrue := true;

  FChildBlocks := TFilterBlocks.Create(true);
  FAllChildBlocks := TFilterBlocks.Create(false);

  if FParent <> nil then
  begin
    FParent.FChildBlocks.Add(Self);
    CurrParent := FParent;
    while CurrParent <> nil do
    begin
      CurrParent.FAllChildBlocks.Add(Self);
      CurrParent := CurrParent.FParent;
    end;
  end;
end;

destructor TFilterBlock.Destroy;
var
  CurrParent: TFilterBlock;
  SavedOwnObjects: Boolean;
begin
  if FParent <> nil then
    if FParent.FChildBlocks <> nil then
    begin
      SavedOwnObjects := FParent.FChildBlocks.FItems.OwnsObjects;
      FParent.FChildBlocks.FItems.OwnsObjects := false;
      try
        FParent.FChildBlocks.Remove(Self);
      finally
        FParent.FChildBlocks.FItems.OwnsObjects := SavedOwnObjects;
      end;
    end;
  CurrParent := FParent;
  while CurrParent <> nil do
  begin
    CurrParent.FAllChildBlocks.Remove(Self);
    CurrParent := CurrParent.FParent;
  end;

  if FCondition <> nil then
    FreeMem(FCondition);
  FreeAndNil(FChildBlocks);
  FreeAndNil(FAllChildBlocks);
  inherited;
end;

procedure TFilterBlock.DefineParamsFromFilterFields(AFilterFields: TObjectList; ADefIsOn: Boolean);
var
  FilterBlock: TFilterBlock;
  FilterField: TFilterField;
  i, j: Integer;
  ValueIndex: Integer;
begin
  if AFilterFields <> nil then
    for i := 0 to FAllChildBlocks.Count - 1 do
    begin
      FilterBlock := TFilterBlock(FAllChildBlocks[i]);
      if FilterBlock.FBlockType = btCondition then
      begin
        FilterField := nil;
        for j :=0 to AFilterFields.Count - 1 do
          if TFilterField(AFilterFields[j]).FFieldName = FilterBlock.FCondition.FieldName then
          begin
            FilterField := TFilterField(AFilterFields[j]);
            Break; //// BREAK ////
          end;

        if FilterField <> nil then
        begin
          FilterBlock.FCondition.FieldIndex := FilterField.FieldIndex;
          FilterBlock.FCondition.UserFieldName := FilterField.FFieldCaption;
          ValueIndex := FilterField.FValues.IndexOf(FilterBlock.FCondition.FilterValue);
          if ValueIndex <> -1 then
            FilterBlock.FCondition.UserFilterValue := FilterField.FValueCaptions[ValueIndex]
          else
          if ADefIsOn then
            FilterBlock.IsOn := false;
        end;
      end;
    end;
end;

function TFilterBlock.GetActiveConditionCount: Integer;
  procedure StepBelow(AParentFilterBlock: TFilterBlock);
  var
    i: integer;
    FilterBlock: TFilterBlock;
  begin
    for i := 0 to AParentFilterBlock.FChildBlocks.Count - 1 do
    begin
      FilterBlock := AParentFilterBlock.FChildBlocks[i];
      if FilterBlock.FIsOn then
      begin
        if FilterBlock.FBlockType = btCondition then
          Inc(Result);
        StepBelow(FilterBlock);
      end;
    end;
  end;
begin
  Result := 0;
  StepBelow(Self);
end;

function TFilterBlock.GetChildIsOnCount: Integer;
var
  i: integer;
  FilterBlock: TFilterBlock;
begin
  Result := 0;
  for i := 0 to FChildBlocks.Count - 1 do
  begin
    FilterBlock := FChildBlocks[i];
      if FilterBlock.FIsOn then
        Inc(Result);
  end;
end;

function TFilterBlock.GetFilterAsString(AForUser: Boolean): String;
        {
  procedure DefineBlockString(AFilterBlock: TFilterBlock; AStepIndex: Integer);
  var
    i: Integer;
    ChildFilterBlock: TFilterBlock;
    LoadedOneChild: Boolean;
  begin
    if (Not AForUser) or (AFilterBlock.FIsOn = true) then
    begin
      if Not AForUser then
      begin
        Result := Result + ' ';
        if AFilterBlock.FIsOn then
          Result := Result + cshTrue
        else
          Result := Result + cshFalse;
      end;

      if AFilterBlock.FBlockType = btBlock then
      begin
        if AStepIndex > 0 then
          Result := Result + '(';

        LoadedOneChild := false;
        for i := 0 to AFilterBlock.FChildBlocks.Count - 1 do
        begin
          ChildFilterBlock := TFilterBlock(AFilterBlock.FChildBlocks[i]);
          CurrConditionType := ChildFilterBlock.FConditionType;

          if LoadedOneChild then
          begin
            if AForUser then
              case ChildFilterBlock.FConditionType of
                ctAnd:
                  Result := Result + ' И ';
                ctOr:
                  Result := Result + ' ИЛИ ';
                ctAndNot:
                  Result := Result + ' И НЕТ ';
                ctOrNot:
                  Result := Result + ' ИЛИ НЕТ ';
              end
            else
              case ChildFilterBlock.FConditionType of
                ctAnd:
                  Result := Result + ctsAnd;
                ctOr:
                  Result := Result + ctsOr;
                ctAndNot:
                  Result := Result + ctsAndNot;
                ctOrNot:
                  Result := Result + ctsOrNot;
              end;
          end;

          if (Not AForUser) or (ChildFilterBlock.FIsOn = true) then
          begin
            DefineBlockString(ChildFilterBlock, AStepIndex+1);
            LoadedOneChild := true;
          end;
        end;

        if AStepIndex > 0 then
          Result := Result + ')';
      end
      else
      if AFilterBlock.FBlockType = btCondition then
        if AFilterBlock.FCondition <> nil then
        begin
          Result := Result + '(';

          if AForUser then
            Result := Result + AFilterBlock.FCondition.UserFieldName
          else
            Result := Result + AFilterBlock.FCondition.FieldName;
          case AFilterBlock.FCondition.CompareType of
            ctEqual:
              Result := Result + '=';
            ctNoEqual:
              Result := Result + '<>';
          end;
          if AForUser then
            Result := Result + AFilterBlock.FCondition.UserFilterValue
          else
            Result := Result + AFilterBlock.FCondition.FilterValue;

          Result := Result + ')';
        end;
    end;
  end; }

  procedure DefineBlockStringForNoUser(AFilterBlock: TFilterBlock; AStepIndex: Integer);
  var
    i: Integer;
    ChildFilterBlock: TFilterBlock;
    LoadedOneChild: Boolean;
  begin
    Result := Result + ' ';
    if AFilterBlock.FIsOn then
      Result := Result + cshTrue
    else
      Result := Result + cshFalse;

    if AFilterBlock.FBlockType = btBlock then
    begin
      if AStepIndex > 0 then
        Result := Result + '(';

      LoadedOneChild := false;
      for i := 0 to AFilterBlock.FChildBlocks.Count - 1 do
      begin
        ChildFilterBlock := TFilterBlock(AFilterBlock.FChildBlocks[i]);

        if LoadedOneChild then
          case ChildFilterBlock.FConditionType of
            ctAnd:
              Result := Result + ctsAnd;
            ctOr:
              Result := Result + ctsOr;
            ctAndNot:
              Result := Result + ctsAndNot;
            ctOrNot:
              Result := Result + ctsOrNot;
          end;
        DefineBlockStringForNoUser(ChildFilterBlock, AStepIndex+1);
        LoadedOneChild := true;
      end;

      if AStepIndex > 0 then
        Result := Result + ')';
    end
    else
    if AFilterBlock.FBlockType = btCondition then
      if AFilterBlock.FCondition <> nil then
      begin
        Result := Result + '(';

        Result := Result + AFilterBlock.FCondition.FieldName;
        case AFilterBlock.FCondition.CompareType of
          ctEqual:
            Result := Result + '=';
          ctNoEqual:
            Result := Result + '<>';
        end;

        Result := Result + AFilterBlock.FCondition.FilterValue;
        Result := Result + ')';
      end;
  end;

  procedure DefineBlockStringForUser(AFilterBlock: TFilterBlock; AStepIndex: Integer);
  var
    i: Integer;
    ChildFilterBlock: TFilterBlock;
    LoadedOneChild: Boolean;
    ChildIsOnCount: Integer;
  begin
    if AFilterBlock.FIsOn or (AStepIndex = 0) then
    begin
      if AFilterBlock.FBlockType = btBlock then
      begin
        ChildIsOnCount := AFilterBlock.GetActiveConditionCount; //AFilterBlock.GetChildIsOnCount;
        if ChildIsOnCount > 0 then
        begin
          if AStepIndex > 0 then
            Result := Result + '(';

          LoadedOneChild := false;
          for i := 0 to AFilterBlock.FChildBlocks.Count - 1 do
          begin
            ChildFilterBlock := TFilterBlock(AFilterBlock.FChildBlocks[i]);
            if ChildFilterBlock.FIsOn = true then
            begin
              if LoadedOneChild then
                //if (ChildFilterBlock.FBlockType = btCondition) or
                //   ((ChildFilterBlock.FBlockType = btBlock) and (ChildFilterBlock.GetChildIsOnCount > 0)) then
                if (ChildFilterBlock.FBlockType = btCondition) or
                   ((ChildFilterBlock.FBlockType = btBlock) and (ChildFilterBlock.GetActiveConditionCount > 0)) then
                  case ChildFilterBlock.FConditionType of
                    ctAnd:
                      Result := Result + ' '+cFilterConfigurator3_1+' ';
                    ctOr:
                      Result := Result + ' '+cFilterConfigurator3_2+' ';
                    ctAndNot:
                      Result := Result + ' '+cFilterConfigurator3_3+' ';
                    ctOrNot:
                      Result := Result + ' '+cFilterConfigurator3_4+' ';
                  end;

              DefineBlockStringForUser(ChildFilterBlock, AStepIndex+1);
              if (ChildFilterBlock.FBlockType = btCondition) or
                 ((ChildFilterBlock.FBlockType = btBlock) and (ChildFilterBlock.GetActiveConditionCount > 0)) then
                LoadedOneChild := true;
            end;
          end;

          if AStepIndex > 0 then
            Result := Result + ')';
        end;
      end
      else
      if AFilterBlock.FBlockType = btCondition then
        if AFilterBlock.FCondition <> nil then
        begin
          Result := Result + '(';

          if AForUser then
            Result := Result + AFilterBlock.FCondition.UserFieldName
          else
            Result := Result + AFilterBlock.FCondition.FieldName;
          case AFilterBlock.FCondition.CompareType of
            ctEqual:
              Result := Result + '=';
            ctNoEqual:
              Result := Result + '<>';
          end;
          if AForUser then
            Result := Result + AFilterBlock.FCondition.UserFilterValue
          else
            Result := Result + AFilterBlock.FCondition.FilterValue;

          Result := Result + ')';
        end;
    end;
  end;

begin
  Result := '';
  if Not AForUser then
    DefineBlockStringForNoUser(Self, 0)
  else
    DefineBlockStringForUser(Self, 0)
end;

function TFilterBlock.GetIsOnFromShortStr(AStr: String): Boolean;
begin
  Result := false;
  if AStr = cshTrue then
    Result := true;
end;

function TFilterBlock.LoadFromStr(AStr: String; AFilterFields: TObjectList; ADefIsOn: Boolean=true): Boolean;
var
  StrLen: integer;
  PosStr: Integer;

  Ch: Char;
  {
  FilterBlock: TFilterBlock;
  FilterField: TFilterField;
  i, j: Integer;
  ValueIndex: Integer; }

  procedure LoadChildBlocksFromStr(AParentBlock: TFilterBlock);
  var
    Ch: Char;
    ChNext: Char;
    ChildBlockType: TBlockType;
    NextChildBlockConditionType: TConditionType;
    NextChildBlockIsOn: Boolean;
    Buffer: string;

    NewChildFilterBlock: TFilterBlock;
    FindedCompareType: Boolean;
  begin
    NextChildBlockConditionType := ctAnd;
    NextChildBlockIsOn := true;

    Buffer := '';
    while PosStr <= StrLen do
    begin
      Ch := AStr[PosStr];
      ChNext := #0;
      if (PosStr+1) <= StrLen then
        ChNext := AStr[PosStr+1];

      if Ch = ' ' then
      begin
        Inc(PosStr);
        Continue; //// CONTINUE ////
      end
      else
      if Ch = '(' then
      begin
        ChildBlockType := btBlock;
        if (ChNext = '(') or (ChNext = ' ') then
          ChildBlockType := btBlock
        else
          ChildBlockType := btCondition;

        NewChildFilterBlock := TFilterBlock.Create(AParentBlock, ChildBlockType);
        NewChildFilterBlock.FIsOn := NextChildBlockIsOn;
        NewChildFilterBlock.FConditionType := NextChildBlockConditionType;
        Inc(PosStr);
        case ChildBlockType of
          btBlock:
            LoadChildBlocksFromStr(NewChildFilterBlock);
          btCondition:
            begin
              FindedCompareType := false;
              while PosStr <= StrLen do
              begin
                Ch := AStr[PosStr];
                ChNext := #0;
                if (PosStr+1) <= StrLen then
                  ChNext := AStr[PosStr+1];

                if ch = ' ' then
                begin
                  Inc(PosStr);
                  Continue; //// CONTINUE ////
                end
                else
                if Ch = '=' then
                begin
                  NewChildFilterBlock.FCondition.CompareType := ctEqual;
                  FindedCompareType := true;
                  Inc(PosStr);
                end
                else
                if (ch = '<') and (ChNext = '>') then
                begin
                  NewChildFilterBlock.FCondition.CompareType := ctNoEqual;
                  FindedCompareType := true;
                  Inc(PosStr);
                  Inc(PosStr); // ChNext уже исвестен
                end
                else
                if ch = ')' then
                begin
                  NewChildFilterBlock.FCondition.FilterValue := Buffer;
                  Buffer := '';

                  Inc(PosStr);
                  Break; //// BREAK ////
                end
                else
                begin
                  Buffer := Buffer + Ch;
                  Inc(PosStr);
                end;

                if FindedCompareType then
                begin
                  FindedCompareType := false;
                  NewChildFilterBlock.FCondition.FieldName := Buffer;
                  Buffer := '';
                end;
              end;
            end;
        end;
        //Inc(PosStr);
      end
      else
      if Ch = ')' then
      begin
        Inc(PosStr);
        Break; //// BREAK ////
      end
      else
      begin
        Buffer := Buffer + Ch;
        if Buffer = ctsAnd then
        begin
          NextChildBlockConditionType := ctAnd;
          Buffer := '';
        end
        else
        if Buffer = ctsOr then
        begin
          NextChildBlockConditionType := ctOr;
          Buffer := '';
        end
        else
        if Buffer = ctsAndNot then
        begin
          NextChildBlockConditionType := ctAndNot;
          Buffer := '';
        end
        else
        if Buffer = ctsOrNot then
        begin
          NextChildBlockConditionType := ctOrNot;
          Buffer := '';
        end
        else
        if (Ch = cshTrue) or (Ch = cshFalse) then
        begin
          NextChildBlockIsOn := GetIsOnFromShortStr(Ch);
          Buffer := '';
        end;

        Inc(PosStr);
      end;
    end;
  end;

begin
  Result := false;

  StrLen := Length(AStr);
  PosStr := 1;
  try
    //*** ОРпределить Включен/выключен главный блок
    while PosStr <= StrLen do
    begin
      Ch := AStr[PosStr];

      if ch = ' ' then
      begin
        Inc(PosStr);
        Continue; //// CONTINUE ////
      end
      else
      if ch = '(' then
      begin
        Break; //// BREAK ////
      end
      else
      if (Ch = cshTrue) or (Ch = cshFalse) then
      begin
        Self.FIsOn := GetIsOnFromShortStr(Ch);
        Inc(PosStr);
        Break; //// BREAK ////
      end;

      Inc(PosStr);
    end;

    LoadChildBlocksFromStr(Self);

    //*** Определить остальные значения полей
    DefineParamsFromFilterFields(AFilterFields, ADefIsOn);

    {for i := 0 to FAllChildBlocks.Count - 1 do
    begin
      FilterBlock := TFilterBlock(FAllChildBlocks[i]);
      if FilterBlock.FBlockType = btCondition then
      begin
        FilterField := nil;
        for j :=0 to AFilterFields.Count - 1 do
          if TFilterField(AFilterFields[j]).FFieldName = FilterBlock.FCondition.FieldName then
          begin
            FilterField := TFilterField(AFilterFields[j]);
            Break; //// BREAK ////
          end;

        if FilterField <> nil then
        begin
          FilterBlock.FCondition.FieldIndex := FilterField.FieldIndex;
          FilterBlock.FCondition.UserFieldName := FilterField.FFieldCaption;
          ValueIndex := FilterField.FValues.IndexOf(FilterBlock.FCondition.FilterValue);
          if ValueIndex <> -1 then
            FilterBlock.FCondition.UserFilterValue := FilterField.FValueCaptions[ValueIndex];
        end;
      end;
    end;}
    Result := true;
  except
    Result := false;
  end;
end;

function TFilterBlock.LoadFromFile(AFileName: String; AFilterType: Integer;
  AFilterFields: TObjectList; AShowMesage: Boolean): Boolean;
var
  FilterFile: TIniFile;
  FilterStrFomFile: string;
  FilterTypeFomFile: Integer;
  FormatVersionFomFile: Integer;
begin
  Result := false;
  FilterFile := TIniFile.Create(AFileName);
  try
    FilterTypeFomFile := FilterFile.ReadInteger(scFilter, idtFilterType, 0);
    FormatVersionFomFile := FilterFile.ReadInteger(scFilter, idtVer, 0);
    FilterStrFomFile := FilterFile.ReadString(scFilter, idtFilterStr, '');

    if FilterTypeFomFile <> AFilterType then
    begin
      if AShowMesage then
        ShowMessage(cFilterConfigurator5);
    end
    else
    begin
      if FilterStrFomFile <> '' then
        Result := LoadFromStr(FilterStrFomFile, AFilterFields);
    end;
  finally
    FreeAndNil(FilterFile);
  end;
end;

procedure TFilterBlock.SaveToFile(AFileName: String; AFilterType: Integer);
var
  FilterStr: String;
  FilterFile: TIniFile;
begin
  FilterStr := GetFilterAsString(false);
  if FilterStr <> '' then
  begin
    if FileExists(AFileName) then
      DeleteFile(AFileName);

    FilterFile := TIniFile.Create(AFileName);
    try
      FilterFile.WriteInteger(scFilter, idtFilterType, AFilterType);
      FilterFile.WriteInteger(scFilter, idtVer, FilterBlockFormatLastVersion);
      FilterFile.WriteString(scFilter, idtFilterStr, FilterStr);
    finally
      FreeAndNil(FilterFile);
    end;
  end;
end;

function TFilterBlock.Execute: Boolean;
var
  i: Integer;
  CanLookANDConditionType: Boolean;
  ChildFilterBlock: TFilterBlock;
  NextChildFilterBlock: TFilterBlock;
  PrevChildFilterBlock: TFilterBlock;
  TmpChildFilterBlock: TFilterBlock;
  LookedIsOn: Boolean;
  CurrConditionType: TConditionType;

  function GetNextBlock(AIndex: Integer): TFilterBlock;
  var
    i: integer;
    FilterBlock: TFilterBlock;
  begin
    Result := nil;
    for i := (AIndex+1) to FChildBlocks.Count - 1 do
    begin
      FilterBlock := FChildBlocks[i];
      if FilterBlock.IsOn then
      begin
        Result := FilterBlock;
        Break; //// BREAK ////
      end;
    end;
  end;


  {function CheckAndConditions(var AIndex: Integer; AItemIndexConditionType: TConditionType): Boolean;
  var
    FilterBlock: TFilterBlock;
    NextFilterBlock: TFilterBlock;
    i: integer;
  begin
    Result := true;
    i := AIndex;
    while i <= FChildBlocks.Count - 1 do
    begin
      FilterBlock := FChildBlocks[i];
      if FilterBlock.IsOn then
      begin
        NextFilterBlock := GetNextBlock(i);

        if (CurrConditionType = ctAnd) or (CurrConditionType = ctAndNot) then
        begin
          FilterBlock.Execute;
          if (CurrConditionType = ctAnd) and Not FilterBlock.FIsTrue then
            CanLookANDConditionType := false
          else
          if (CurrConditionType = ctAndNot) and FilterBlock.FIsTrue then
            CanLookANDConditionType := false;
          Result := CanLookANDConditionType;
        end;

      end;
      Inc(i);
    end;
    AIndex := i;
  end;   }

begin
  Result := false;

  if FBlockType = btCondition then
  begin
    case FCondition.CompareType of
      ctEqual:
        if FCondition.FieldValue = FCondition.FilterValue then
          Result := true;
      ctNoEqual:
        if FCondition.FieldValue <> FCondition.FilterValue then
          Result := true;
    end;
  end
  else
  if FBlockType = btBlock then
  begin
    CanLookANDConditionType := true;
    LookedIsOn := false;
    {for i := 0 to FChildBlocks.Count - 1 do
    begin
      ChildFilterBlock := TFilterBlock(FChildBlocks[i]);

      CurrConditionType := ChildFilterBlock.FConditionType;
      //*** первое условие всегда типа AND
      if ChildFilterBlock.FIsOn then
      begin
        if Not LookedIsOn then
        begin
          CurrConditionType := ctAnd;
          LookedIsOn := true;
        end;

        //*** Условие типа AND
        if (CurrConditionType = ctAnd) or (CurrConditionType = ctAndNot) then
        begin
          if CanLookANDConditionType then
          begin
            ChildFilterBlock.Execute;
            if (CurrConditionType = ctAnd) and Not ChildFilterBlock.FIsTrue then
              CanLookANDConditionType := false
            else
            if (CurrConditionType = ctAndNot) and ChildFilterBlock.FIsTrue then
              CanLookANDConditionType := false;
            Result := CanLookANDConditionType;
          end;
        end
        else
        //*** Условие типа OR - если одно из таких условий выполняется, то результат Истина и выход из цикла
        if (CurrConditionType = ctOr) or (CurrConditionType = ctOrNot) then
        begin
          ChildFilterBlock.Execute;
          if (CurrConditionType = ctOr) and ChildFilterBlock.FIsTrue then
          begin
            Result := true;
            Break; //// BREAK ////
          end
          else
          if (CurrConditionType = ctOrNot) and Not ChildFilterBlock.FIsTrue then
          begin
            Result := true;
            Break; //// BREAK ////
          end;
        end;
      end;
    end;        }


    ChildFilterBlock := nil;
    PrevChildFilterBlock := nil;
    for i := 0 to FChildBlocks.Count - 1 do
    begin
      ChildFilterBlock := TFilterBlock(FChildBlocks[i]);

      CurrConditionType := ChildFilterBlock.FConditionType;
      //*** первое условие всегда типа AND
      if ChildFilterBlock.FIsOn then
      begin
        if Not LookedIsOn then
        begin
          CurrConditionType := ctAnd;
          LookedIsOn := true;
        end;

        //*** Условие типа AND
        if (CurrConditionType = ctAnd) or (CurrConditionType = ctAndNot) then
        begin
          if CanLookANDConditionType then
          begin
            ChildFilterBlock.Execute;
            if (CurrConditionType = ctAnd) and Not ChildFilterBlock.FIsTrue then
              CanLookANDConditionType := false
            else
            if (CurrConditionType = ctAndNot) and ChildFilterBlock.FIsTrue then
              CanLookANDConditionType := false;
            Result := CanLookANDConditionType;
          end;
        end
        else
        //*** Условие типа OR - если одно из таких условий выполняется, то результат Истина и выход из цикла
        if (CurrConditionType = ctOr) or (CurrConditionType = ctOrNot) then
        begin
          //*** Если предыдущее AND блоки были истиной  
          if Result then
            if PrevChildFilterBlock <> nil then
              if (PrevChildFilterBlock.ConditionType = ctAnd) or
                 (PrevChildFilterBlock.ConditionType = ctAndNot) then
              begin
                Break; //// BREAK ////
              end;

          ChildFilterBlock.Execute;
          if ((CurrConditionType = ctOr) and ChildFilterBlock.FIsTrue) or
             ((CurrConditionType = ctOrNot) and Not ChildFilterBlock.FIsTrue) then
          begin
            NextChildFilterBlock := GetNextBlock(i);
            //*** Если следующие блоки не AND
            if (NextChildFilterBlock = nil) or
               ((NextChildFilterBlock.ConditionType <> ctAnd) and (NextChildFilterBlock.ConditionType <> ctAndNot)) then
            begin
              Result := true;
              Break; //// BREAK ////
            end
            else
              CanLookANDConditionType := true;
          end;          
        end;
        PrevChildFilterBlock := ChildFilterBlock;
      end;
    end;

    if (Result = false) and (GetChildIsOnCount = 0) then
      Result := true;
  end;
  FIsTrue := Result;                     
end;

{ TFilterField }

constructor TFilterField.Create;
begin
  inherited;
  FFieldIndex := -1;
  FFieldName := '';
  FFieldCaption := '';

  FValues := TStringList.Create;
  FValueCaptions := TStringList.Create;
end;

destructor TFilterField.Destroy;
begin
  FreeAndNil(FValues);
  FreeAndNil(FValueCaptions);
  inherited;
end;



{ TFilterBlockControl }

constructor TFilterBlockControl.Create(ABlockControlType: TBlockType; AOwner: TComponent; AParentBlock: TFilterBlockControl; AContainerOwner: TObjectList);
begin
  inherited Create(AOwner);

  FBlockControlType := ABlockControlType;
  FOwner := AOwner;
  FParentBlock := AParentBlock;
  FControls := TObjectList.Create(false);
  FContainerOwner := AContainerOwner;

  Self.BorderInner := fsNone;

  //Self.AutoSize := true;
  Self.Height := ChildPanelHeight + PanelBiggerFromChildDelta;

  if FParentBlock <> nil then
  begin
    Self.Parent := FParentBlock;
    FParentBlock.AutoSize := true;
    FParentBlock.AutoSize := false;

    Self.Align := alBottom;
    Self.Align := alTop;
  end;

  FSelected := false;
  DefineStyle;

  pnFieldCaption := nil;
  pnCompare := nil;
  pnValues := nil;
  lbFieldCaption := nil;
  lbCompare := nil;
  cbValues := nil;

  //*** Создать панели с AutoSize с выравниванием по правому краю, на которых будут размещаться другие контролы
  if FBlockControlType = btCondition then
    CreateConditionControls;

  if AContainerOwner <> nil then
    AContainerOwner.Add(Self);
end;

procedure TFilterBlockControl.CreateConditionControls;
begin
  pnFieldCaption := CreatePanel;
  pnCompare := CreatePanel;
  pnValues := CreatePanel;

  lbFieldCaption := TLabel.Create(FOwner);
  lbFieldCaption.Parent := pnFieldCaption;
  lbFieldCaption.Caption := lbFieldCaption.ClassName + '  '+IntToStr(FContainerOwner.Count + 1);
  lbFieldCaption.Font.Style := lbFieldCaption.Font.Style + [fsBold];
  SetChildControlPos(lbFieldCaption);

  lbCompare := TLabel.Create(FOwner);
  lbCompare.Parent := pnCompare;
  lbCompare.Caption := lbCompare.ClassName;
  lbCompare.Top := ChildControlTop;
  SetChildControlPos(lbCompare);

  cbValues := TRzComboBox.Create(FOwner);
  cbValues.Parent := pnValues;
  cbValues.Top := ChildControlTop;
  SetChildControlPos(cbValues);
end;

function TFilterBlockControl.CreatePanel: TRzPanel;
begin
  Result := TRzPanel.Create(FOwner);
  Result.Parent := Self;

  Result.Align := alLeft;
  Result.BorderOuter := fsNone;
  Result.Height := ChildPanelHeight;
  Result.AutoSize := true;
end;

procedure TFilterBlockControl.DefineStyle;
begin
  if FSelected then
  begin
    BorderHighlight := clRed;
    BorderShadow := clRed;
    BorderOuter := fsPopup;
  end
  else
  begin
    BorderHighlight := clBtnFace;
    BorderShadow := clBtnShadow;

    if FBlockControlType = btBlock then
      BorderOuter := fsFlat
    else
    if FBlockControlType = btCondition then
      BorderOuter := fsNone;
  end;
end;

procedure TFilterBlockControl.RegisterControl(AControl: TControl);
begin
  FControls.Add(AControl);
end;

procedure TFilterBlockControl.SetChildControlPos(AControl: TControl);
begin
  AControl.Left := ChildControlLeft;
  AControl.Top := ChildControlTop;
end;

procedure TFilterBlockControl.SetSelected(Value: Boolean);
begin
  if Value <> FSelected then
  begin
    FSelected := Value;
    DefineStyle;
  end;
end;

{ TForm1 }


constructor TF_FilterConfigurator.Create(AOwner: TComponent; AForm: TForm);
begin
  GForm := AForm;
  inherited Create(AOwner);
end;

destructor TF_FilterConfigurator.Destroy;
begin
  inherited;
end;

function TF_FilterConfigurator.Execute(AFilterFields: TObjectList; AFilterBlock: TFilterBlock; AFilterType: Integer): Boolean;
begin
  Result := false;

  FilterType := AFilterType;

  FBlockControls.Clear;
  FSelectedBlockControl := nil;

  FFilterFields := AFilterFields;

  LoadFieldNames;

  SetColumnCaption(ciConditionType, ColumnCaptionConditionType);
  SetColumnCaption(ciFieldName, ColumnCaptionFieldName);
  SetColumnCaption(ciCompareType, ColumnCaptionCompareType);
  SetColumnCaption(ciValue, ColumnCaptionValue);

  cbIsUseFilter.Checked := true;
  if AFilterBlock <> nil then
  begin
    AddNodesFromFilterBlock(AFilterBlock);
    cbIsUseFilter.Checked := (AFilterBlock.FIsOn) or (AFilterBlock.FChildBlocks.Count = 0);
  end;

  SetControls;

  if ShowModal = mrOk then
  begin
    Result := true;
  end;
end;

function TF_FilterConfigurator.GetMainFilterBlock: TFilterBlock;
var
  TopLevelNode: TFlyNode;
  FilterBlock: TFilterBlock;
begin
  Result := TFilterBlock.Create(nil, btBlock);

  TopLevelNode := nil;
  if tvFilter.Items.Count > 0 then
    TopLevelNode := tvFilter.Items[0];

  if TopLevelNode <> nil then
    while TopLevelNode <> nil do
    begin
      FilterBlock := TFilterBlock.Create(Result, TFilterBlock(TopLevelNode.Data).FBlockType);
      FilterBlock.Assign(TFilterBlock(TopLevelNode.Data));

      TopLevelNode := TopLevelNode.GetNextSibling;
    end;
  Result.FIsOn := cbIsUseFilter.Checked and cbIsUseFilter.Enabled;
end;

procedure TF_FilterConfigurator.btAddCondition_Click(Sender: TObject);
var
  FilterBlockControl: TFilterBlockControl;
begin
  {if FBlockControls.Count = 0 then
    AddBlock(btBlock, nil)
  else
    AddBlock(btCondition, nil);}
end;

{procedure TF_FilterConfigurator.AddBlock(ABlockType: TBlockType; AParentBlock: TFilterBlockControl);
var
  FilterBlockControl: TFilterBlockControl;
  ParentBlock: TFilterBlockControl;
  CurrBlock: TFilterBlockControl;
begin
  ParentBlock := AParentBlock;
  if ParentBlock = nil then
    if FSelectedBlockControl <> nil then
      if FBlockControls.IndexOf(FSelectedBlockControl) <> -1 then
      begin
        CurrBlock := FSelectedBlockControl;

        //*** Парент должен быть Типа btBlock
        while CurrBlock <> nil do
        begin
          if CurrBlock.FBlockControlType = btBlock then
          begin
            ParentBlock := CurrBlock;
            Break; //// BREAK ////
          end;
          CurrBlock := CurrBlock.FParentBlock;
        end;
      end;

  FilterBlockControl := TFilterBlockControl.Create(ABlockType, Self, ParentBlock, FBlockControls);
  FilterBlockControl.OnClick := BlockFilterControlClick;

  if ParentBlock = nil then
  begin
    FilterBlockControl.Parent := sbBlockContainer;

    sbBlockContainer.AutoScroll := false;
    sbBlockContainer.AutoScroll := true;
  end;

  if ABlockType = btBlock then
    AddBlock(btCondition, FilterBlockControl);
end;}

procedure TF_FilterConfigurator.btAddBlock_Click(Sender: TObject);
begin
  //AddBlock(btBlock, nil);
end;

procedure TF_FilterConfigurator.FormMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  beep;
end;

procedure TF_FilterConfigurator.BlockFilterControlClick(Sender: TObject);
var
  i: Integer;

  CurrBlockControl: TFilterBlockControl;
begin
  //*** Выделить текущий блок
  if Sender is TFilterBlockControl then
  begin
    FSelectedBlockControl := TFilterBlockControl(Sender);

    //*** Снять выдиления с других
    for i := 0 to FBlockControls.Count - 1 do
    begin
      CurrBlockControl := TFilterBlockControl(FBlockControls[i]);
      if FSelectedBlockControl <> CurrBlockControl then
        TFilterBlockControl(CurrBlockControl).Selected := false;
    end;

    FSelectedBlockControl.Selected := true;
  end;
end;


procedure TF_FilterConfigurator.FormCreate(Sender: TObject);
var
  TreeCollection: TTreeCollectionClass;
  TreeCollectionClassName: String;

  NewSectionNode: TFlyNode;
begin
  OpenSaveDialogFilter := '';
  ColumnCaptionConditionType := '';
  ColumnCaptionFieldName := '';
  ColumnCaptionCompareType := '';
  ColumnCaptionValue := '';
  FilterType := 0;

  FBlockControls := TObjectList.Create(true);

  tvFilter.Items.Clear;
  tvFilter.Columns.Clear;
  TreeCollectionClassName := 'TTreeColumn';

  //*** Вкл/Выкл
  TreeCollection := tvFilter.Columns.Add(cFilterConfigurator6_1, TreeCollectionClassName);
  //*** Поле Логическое условие
  TreeCollection := tvFilter.Columns.Add(cFilterConfigurator6_2, TreeCollectionClassName);
  //*** Наименование поля
  TreeCollection := tvFilter.Columns.Add(cFilterConfigurator6_3, TreeCollectionClassName);
  //*** тип сравнения
  TreeCollection := tvFilter.Columns.Add(cFilterConfigurator6_4, TreeCollectionClassName);
  //*** Значение
  TreeCollection := tvFilter.Columns.Add(cFilterConfigurator6_5, TreeCollectionClassName);

  tvFilter.StructureCol := ciConditionType;
  //tvFilter.StructureCol := ciFieldName;


  tvFilter.Columns[ciIsOn].EditorStyle.EditorType := tetCheckBox;
  tvFilter.Columns[ciIsOn].EditorStyle.ButtonWidth := 7;
  tvFilter.Columns[ciIsOn].EditorStyle.Ctl3d := true;
  tvFilter.Columns[ciIsOn].Width := 30;
   
  tvFilter.Columns[ciConditionType].EditorStyle.EditorType := tetDropDownList;
  tvFilter.Columns[ciConditionType].EditorStyle.ButtonType := tbtDropDown;
  tvFilter.Columns[ciConditionType].Width := 100;
  //tvFilter.Columns[ciConditionType].AutoFit := true;

  tvFilter.Columns[ciFieldName].EditorStyle.EditorType := tetDropDownList;
  tvFilter.Columns[ciFieldName].EditorStyle.ButtonType := tbtDropDown;
  tvFilter.Columns[ciFieldName].Width := 150;
  tvFilter.Columns[ciFieldName].EditorStyle.AutoComplete := true;

  tvFilter.Columns[ciCompareType].EditorStyle.EditorType := tetDropDownList;
  tvFilter.Columns[ciCompareType].EditorStyle.ButtonType := tbtDropDown;
  tvFilter.Columns[ciCompareType].Width := 70;
  tvFilter.Columns[ciCompareType].EditorStyle.AutoDropDown := true;

  tvFilter.Columns[ciValue].EditorStyle.EditorType := tetDropDownList;
  tvFilter.Columns[ciValue].EditorStyle.ButtonType := tbtDropDown;
  tvFilter.Columns[ciValue].Width := 150;
  //tvFilter.Columns[ciValue].EditorStyle.AutoComplete := false; //true;
  tvFilter.Columns[ciValue].EditorStyle.AutoDropDown := true;


  NewSectionNode := tvFilter.Columns[ciConditionType].EditorStyle.Sections[0].Items.Add(nil, cntcOr);
  NewSectionNode.Data := Pointer(ctOr);

  NewSectionNode := tvFilter.Columns[ciConditionType].EditorStyle.Sections[0].Items.Add(nil, cntcAnd);
  NewSectionNode.Data := Pointer(ctAnd);

  NewSectionNode := tvFilter.Columns[ciConditionType].EditorStyle.Sections[0].Items.Add(nil, cntcOrNot);
  NewSectionNode.Data := Pointer(ctOrNot);

  NewSectionNode := tvFilter.Columns[ciConditionType].EditorStyle.Sections[0].Items.Add(nil, cntcAndNot);
  NewSectionNode.Data := Pointer(ctAndNot);


  //***
  NewSectionNode := tvFilter.Columns[ciCompareType].EditorStyle.Sections[0].Items.Add(nil, ctmEqual);
  NewSectionNode.Data := Pointer(ctEqual);

  NewSectionNode := tvFilter.Columns[ciCompareType].EditorStyle.Sections[0].Items.Add(nil, ctmNoEqual);
  NewSectionNode.Data := Pointer(ctNoEqual);

  //TConditionType = (cdNone, ctAnd, ctOr, ctAndNot, ctOrNot);
  //TCompareType = (ctEqual, ctNoEqual
end;

procedure TF_FilterConfigurator.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FBlockControls);
  ClearNodes;
end;


procedure TF_FilterConfigurator.AddBlock(ABlockType: TBlockType; AParentNode: TFlyNode);
var
  NewBlock: TFilterBlock;
  NewNode: TFlyNode;

  ParentNode: TFlyNode;
  ParentBlock: TFilterBlock;

  CurrNode: TFlyNode;
  CurrBlock: TFilterBlock;
  NextNodeAfterSelected: TFlyNode;
begin
  ParentBlock := nil;
  ParentNode := nil;

  if AParentNode <> nil then
  begin
    ParentBlock := TFilterBlock(AParentNode.Data);
    ParentNode := AParentNode;
  end;

  if ParentBlock = nil then
    if tvFilter.Selected <> nil then
    begin
      CurrNode := tvFilter.Selected;
      if CurrNode <> nil then
        CurrNode := CurrNode.Parent;

      //*** Парент должен быть Типа btBlock
      while CurrNode <> nil do
      begin
        CurrBlock := TFilterBlock(CurrNode.Data);
        if CurrBlock.FBlockType = btBlock then
        begin
          ParentNode := CurrNode;
          ParentBlock := CurrBlock;
          Break; //// BREAK ////
        end;
        CurrNode := CurrNode.Parent;
      end;
    end;

  NewBlock := TFilterBlock.Create(ParentBlock, ABlockType);

  NewBlock.FConditionType := ctOr;
  if NewBlock.FCondition <> nil then
  begin
    NewBlock.FCondition.CompareType := ctEqual;

    if FFilterFields <> nil then
      if FFilterFields.Count > 0 then
      begin
        NewBlock.FCondition.FieldIndex := TFilterField(FFilterFields[0]).FFieldIndex;
        NewBlock.FCondition.FieldName := TFilterField(FFilterFields[0]).FFieldName;
        NewBlock.FCondition.UserFieldName := TFilterField(FFilterFields[0]).FFieldCaption;
        if TFilterField(FFilterFields[0]).Values.Count > 0 then
        begin
          NewBlock.FCondition.FilterValue := TFilterField(FFilterFields[0]).Values[0];
          NewBlock.FCondition.UserFilterValue := TFilterField(FFilterFields[0]).ValueCaptions[0];
        end;
      end;
  end;

  NextNodeAfterSelected := nil;
  if tvFilter.Selected <> nil then
    NextNodeAfterSelected := tvFilter.Selected.GetNextSibling;

  NewNode := nil;
  if ParentNode = nil then
  begin
    if NextNodeAfterSelected <> nil then
      NewNode := tvFilter.Items.Insert(NextNodeAfterSelected, '')
    else
      NewNode := tvFilter.Items.Add(NextNodeAfterSelected, '');
  end
  else
  begin
    //NewNode := tvFilter.Items.AddChild(ParentNode, '');
    if (AParentNode <> nil) or (NextNodeAfterSelected = nil) then
      NewNode := tvFilter.Items.AddChild(ParentNode, '')
    else
      NewNode := tvFilter.Items.Insert(NextNodeAfterSelected, '');
    ParentNode.Expanded := true;
  end;
  NewNode.Data := NewBlock;

  ReselectNode;

  ApplyNodeParams(NewNode);

  if ABlockType = btBlock then
    AddBlock(btCondition, NewNode);
end;

procedure TF_FilterConfigurator.AddNodesFromFilterBlock(AFilterBlock: TFilterBlock);
var
  i: Integer;

  procedure AddNode(AParentNode: TFlyNode; ASrcFilterBlock: TFilterBlock);
  var
    NewFilterBlock: TFilterBlock;
    ParentFilterBlock: TFilterBlock;
    NewNode: TFlyNode;
    i: Integer;
  begin
    NewNode := nil;
    ParentFilterBlock := nil;

    if AParentNode = nil then
      NewNode := tvFilter.Items.Add(nil, '')
    else
    begin
      NewNode := tvFilter.Items.AddChild(AParentNode, '');
      ParentFilterBlock := TFilterBlock(AParentNode.Data);
    end;
    if NewNode <> nil then
    begin
      NewFilterBlock := TFilterBlock.Create(ParentFilterBlock, ASrcFilterBlock.FBlockType);
      NewFilterBlock.AssignOnlyBlock(ASrcFilterBlock);

      NewNode.Data := NewFilterBlock;

      for i := 0 to ASrcFilterBlock.FChildBlocks.Count - 1 do
        AddNode(NewNode, TFilterBlock(ASrcFilterBlock.FChildBlocks[i]));

      NewNode.Expanded := true;
      ApplyNodeParams(NewNode);
    end;
  end;

begin
  tvFilter.Items.BeginUpdate;
  try
    ClearNodes;
    for i := 0 to AFilterBlock.FChildBlocks.Count - 1 do
      AddNode(nil, TFilterBlock(AFilterBlock.FChildBlocks[i]));
  finally
    tvFilter.Items.EndUpdate;
  end;
end;

procedure TF_FilterConfigurator.AddNodesFromFilterFile(AFilterFile: string);
var
  MainFilteBlock: TFilterBlock;
begin
  MainFilteBlock := TFilterBlock.Create(nil, btBlock);

  if MainFilteBlock.LoadFromFile(AFilterFile, FilterType, FFilterFields, true) then
    AddNodesFromFilterBlock(MainFilteBlock);

  FreeAndNil(MainFilteBlock);
end;

procedure TF_FilterConfigurator.ApplyNodeParams(ANode: TFlyNode);
var
  i: integer;
  SectionNode: TFlyNode;
  FilterField: TFilterField;
  IndexOfValue: Integer;
begin
  //*** Включено ли поле
  ANode.Cells[ciIsOn] := csFalse;
  if TFilterBlock(ANode.Data).FIsOn then
    ANode.Cells[ciIsOn] := csTrue;

  {tvFilter.OnStateChange := nil;
  ANode.StateIndex := STATE_UNCHECKED;
  if TFilterBlock(ANode.Data).FIsOn then
    ANode.StateIndex := STATE_CHECKED;
  tvFilter.OnStateChange := tvFilterStateChange;}

  //*** Логическое условие
  if ANode.getPrevSibling = nil then
  begin
    ANode.Cells[ciConditionType] := '';
    if TFilterBlock(ANode.Data).BlockType = btBlock then
      ANode.Cells[ciConditionType] := cntcAnd; 
  end
  else
    for i := 0 to tvFilter.Columns[ciConditionType].EditorStyle.Sections[0].Items.Count - 1 do
    begin
      SectionNode := tvFilter.Columns[ciConditionType].EditorStyle.Sections[0].Items[i];
      if TConditionType(SectionNode.Data) = TFilterBlock(ANode.Data).FConditionType then
      begin
        ANode.Cells[ciConditionType] := SectionNode.Text;
        Break; //// BREAK ////
      end;
    end;
  if TFilterBlock(ANode.Data).FCondition = nil then
  begin
    ANode.Cells[ciCompareType] := '';
    ANode.Cells[ciFieldName] := '';
    ANode.Cells[ciValue] := '';
  end
  else
  begin
    //*** Наименование поля
    FilterField := GetFilterFieldByFieldIndex(TFilterBlock(ANode.Data).FCondition.FieldIndex);
    if FilterField <> nil then
      ANode.Cells[ciFieldName] := FilterField.FieldCaption
    else
      ANode.Cells[ciFieldName] := '';

    //*** Тип сравнения
    for i := 0 to tvFilter.Columns[ciCompareType].EditorStyle.Sections[0].Items.Count - 1 do
    begin
      SectionNode := tvFilter.Columns[ciCompareType].EditorStyle.Sections[0].Items[i];
      if TCompareType(SectionNode.Data) = TFilterBlock(ANode.Data).FCondition.CompareType then
      begin
        ANode.Cells[ciCompareType] := SectionNode.Text;
        Break; //// BREAK ////
      end;
    end;

    //*** Значение поля
    ANode.Cells[ciValue] := '';
    if FilterField <> nil then
    begin
      IndexOfValue := FilterField.FValues.IndexOf(TFilterBlock(ANode.Data).FCondition.FilterValue);
      if IndexOfValue <> -1 then
        ANode.Cells[ciValue] := FilterField.FValueCaptions[IndexOfValue];
    end;
  end;
end;

procedure TF_FilterConfigurator.DelNodeBlock(ANode: TFlyNode);
var
  FilterBlock: TFilterBlock;
begin
  FilterBlock := TFilterBlock(ANode.Data);
  if FilterBlock <> nil then
    FreeAndNil(FilterBlock);
  ANode.Delete;
end;

procedure TF_FilterConfigurator.ClearNodes;
var
  Node: TFlyNode;
begin
  Node := nil;
  if tvFilter.Items.Count > 0 then
    Node := tvFilter.Items[0];

  while Node <> nil do
  begin
    if Node.Data <> nil then
    begin
      TFilterBlock(Node.Data).Free;
      Node.Data := nil;
    end;
    Node := Node.GetNextSibling;
  end;
  tvFilter.Items.Clear;
end;

function TF_FilterConfigurator.GetCurrFilterStr: String;
var
  MainFilteBlock: TFilterBlock;
begin
  Result := '';
  MainFilteBlock := GetMainFilterBlock;
  Result := MainFilteBlock.GetFilterAsString(false);
  FreeAndNil(MainFilteBlock);
end;

function TF_FilterConfigurator.GetFilterFieldByFieldIndex(AFIndex: Integer): TFilterField;
var
  FilterField: TFilterField;
  i: Integer;
begin
  Result := nil;
  for i := 0 to FFilterFields.Count - 1 do
  begin
    FilterField := TFilterField(FFilterFields[i]);
    if FilterField.FFieldIndex = AFIndex then
    begin
      Result := FilterField;
      Break; //// BREAK ////
    end;
  end;
end;

function TF_FilterConfigurator.GetIsOnConditionCount: Integer;
var
  MainFilteBlock: TFilterBlock;
begin
  Result := 0;
  MainFilteBlock := GetMainFilterBlock;
  Result := MainFilteBlock.GetActiveConditionCount;
  FreeAndNil(MainFilteBlock);
end;

procedure TF_FilterConfigurator.LoadFieldNames;
var
  Node: TFlyNode;
  i: Integer;

  FilterField: TFilterField;
begin
  //*** Очистить Ноуды
  tvFilter.Columns[ciFieldName].EditorStyle.Sections[0].Items.Clear;

  for i := 0 to FFilterFields.Count - 1 do
  begin
    FilterField := TFilterField(FFilterFields[i]);

    Node := tvFilter.Columns[ciFieldName].EditorStyle.Sections[0].Items.Add(nil, FilterField.FFieldCaption);
    Node.Data := FilterField;
  end;
end;

procedure TF_FilterConfigurator.LoadFieldValuesByFieldIndex(AFIndex: Integer);
var
  FilterField: TFilterField;
  Node: TFlyNode;
  i: Integer;
begin
  tvFilter.Columns[ciValue].EditorStyle.Sections[0].Items.Clear;

  FilterField := GetFilterFieldByFieldIndex(AFIndex);
  if FilterField <> nil then
    for i := 0 to FilterField.Values.Count - 1 do
    begin
      Node := tvFilter.Columns[ciValue].EditorStyle.Sections[0].Items.Add(nil, FilterField.ValueCaptions[i]);
      Node.Data := Pointer(i); //*** Data будет хранить порядковый индес значения
    end;
end;

procedure TF_FilterConfigurator.SetControls;
var
  IsOnConditionCount: Integer;
begin
  try
    Act_AddCondition.Enabled := true;
    Act_AddBlock.Enabled := true;
    Act_DelBlock.Enabled := (tvFilter.Items.Count > 0) and (tvFilter.Selected <> nil);
    Act_SaveFilterToFile.Enabled := (tvFilter.Items.Count > 0);

    IsOnConditionCount := GetIsOnConditionCount;
    cbIsUseFilter.Enabled := IsOnConditionCount > 0;
    //if Not cbIsUseFilter.Enabled and cbIsUseFilter.Checked then
    //  cbIsUseFilter.Checked := false;
  except
    on E: Exception do AddExceptionToLogEx('TF_FilterConfigurator.SetControls', E.Message);
  end;
end;

procedure TF_FilterConfigurator.ReselectNode;
var
  SavedSelectedNode: TFlyNode;
begin
  SavedSelectedNode := tvFilter.Selected;
  tvFilter.Selected := nil;
  tvFilter.Selected := SavedSelectedNode;
end;

procedure TF_FilterConfigurator.SetColumnCaption(AColumnIndex: Integer; ACaption: String);
begin
  if ACaption <> '' then
    tvFilter.Columns[AColumnIndex].Caption := ACaption;
end;

procedure TF_FilterConfigurator.tvFilterSelectedChanged(OldNode,
  NewNode: TFlyNode);
var
  PrevSibling: TFlyNode;
  FilterBlock: TFilterBlock;
begin
  if NewNode <> nil then
  begin
    PrevSibling := NewNode.getPrevSibling;

    if NewNode.Data <> nil then
    begin
      FilterBlock := TFilterBlock(NewNode.Data);

      tvFilter.Columns[ciConditionType].ReadOnly := (PrevSibling = nil);
      tvFilter.Columns[ciCompareType].ReadOnly := (FilterBlock.FCondition = nil);
      tvFilter.Columns[ciFieldName].ReadOnly := (FilterBlock.FCondition = nil);
      tvFilter.Columns[ciValue].ReadOnly := (FilterBlock.FCondition = nil);

      //*** Если блок типа - условие, то подгрузить возможные значения условий
      if FilterBlock.FCondition <> nil then
        LoadFieldValuesByFieldIndex(FilterBlock.FCondition.FieldIndex);
    end;
  end;
end;

procedure TF_FilterConfigurator.tvFilterCloseUp(Sender: TISPlugInplaceEdit;
  Section: TISPlugSection; DropDown: TISDropDown; var Accept: Boolean);
var
  ActualColumn: Integer;
  NodeIndex: Integer;
  NodeFromSection: TFlyNode;

  CurrFilterBlock: TFilterBlock;
  CurrFilterField: TFilterField;
  CurrValueIndex: Integer;
begin
  ActualColumn := tvFilter.Columns.VisibleColumn[tvFilter.Col].Index;

  NodeFromSection := nil;
  NodeIndex := -1;

  if TPopupTree(DropDown.ContainedControl).Selected <> nil then
  begin
    NodeIndex := TPopupTree(DropDown.ContainedControl).Selected.Index;
    if (NodeIndex <> -1) and
       (NodeIndex <= tvFilter.Columns[ActualColumn].EditorStyle.Sections[0].Items.Count - 1) then
      NodeFromSection := tvFilter.Columns[ActualColumn].EditorStyle.Sections[0].Items[NodeIndex];
    if NodeFromSection <> nil then
    begin
      CurrFilterBlock := TFilterBlock(tvFilter.Selected.Data);

      case ActualColumn of
        ciConditionType:
          begin
            CurrFilterBlock.FConditionType := TConditionType(NodeFromSection.Data);
          end;
        ciFieldName:
          begin
            if CurrFilterBlock.FCondition <> nil then
            begin
              CurrFilterBlock.FCondition.FieldIndex := TFilterField(NodeFromSection.Data).FFieldIndex;
              CurrFilterBlock.FCondition.FieldName := TFilterField(NodeFromSection.Data).FFieldName;
              CurrFilterBlock.FCondition.UserFieldName := TFilterField(NodeFromSection.Data).FFieldCaption;

              //*** На смене типа, очистить текущее значение
              CurrFilterBlock.FCondition.FilterValue := '';
              tvFilter.Selected.Cells[ActualColumn] := '';
              //*** подгрузить текущие
              LoadFieldValuesByFieldIndex(CurrFilterBlock.FCondition.FieldIndex);

              //*** Если юзер переключил тип подгрузить первое значение
              if TFilterField(NodeFromSection.Data).FValues.Count > 0 then
              begin
                CurrFilterBlock.FCondition.FilterValue := TFilterField(NodeFromSection.Data).FValues[0];
                CurrFilterBlock.FCondition.UserFilterValue := TFilterField(NodeFromSection.Data).FValueCaptions[0];
                tvFilter.Selected.Cells[ciValue] := TFilterField(NodeFromSection.Data).FValueCaptions[0];
              end;
            end;
          end;
        ciCompareType:
          begin
            if CurrFilterBlock.FCondition <> nil then
              CurrFilterBlock.FCondition.CompareType := TCompareType(NodeFromSection.Data);
          end;
        ciValue:
          begin
            if CurrFilterBlock.FCondition <> nil then
            begin
              CurrFilterField := GetFilterFieldByFieldIndex(CurrFilterBlock.FCondition.FieldIndex);

              CurrValueIndex := Integer(NodeFromSection.Data);
              if (CurrFilterField <> nil) and (CurrValueIndex <= CurrFilterField.Values.Count-1) then
              begin
                CurrFilterBlock.FCondition.FilterValue := CurrFilterField.Values[CurrValueIndex];
                CurrFilterBlock.FCondition.UserFilterValue := CurrFilterField.ValueCaptions[CurrValueIndex];
              end;
            end;
          end;
      end;
    end;
  end;
end;

procedure TF_FilterConfigurator.Act_AddConditionExecute(Sender: TObject);
begin
  AddBlock(btCondition, nil);
  SetControls;
end;

procedure TF_FilterConfigurator.Act_AddBlockExecute(Sender: TObject);
begin
  AddBlock(btBlock, nil);
  SetControls;
end;

procedure TF_FilterConfigurator.Act_DelBlockExecute(Sender: TObject);
var
  NodeToDel: TFlyNode;
  NodeToSelect: TFlyNode;
  ParentNode: TFlyNode;
  FirstChild: TFlyNode;
begin
  try
    if tvFilter.Selected <> nil then
    begin
      NodeToDel := tvFilter.Selected;
      ParentNode := NodeToDel.Parent;
      //*** следующий Ноуд для выделения
      NodeToSelect := NodeToDel.GetNextSibling;

      DelNodeBlock(NodeToDel);

      //*** Первая ветвь, уровня на котором был удален NodeToDel, не должна отображать тип условия - подразумевается что это "И"
      FirstChild := nil;
      if ParentNode <> nil then
        FirstChild := ParentNode.getFirstChild;
      if FirstChild <> nil then
        ApplyNodeParams(FirstChild);

      if NodeToSelect <> nil then
        tvFilter.Selected := NodeToSelect;

      ReselectNode;
      SetControls;
    end;
  except
    on E: Exception do AddExceptionToLogEx('TF_FilterConfigurator.Act_DelBlockExecute', E.Message);
  end;
end;

procedure TF_FilterConfigurator.Act_OpenFilterFromFileExecute(
  Sender: TObject);
{var
  FilterFile: TIniFile;
  FilterStrFomFile: string;
  FilterTypeFomFile: Integer;}
begin
  OpenDialog.Filter := OpenSaveDialogFilter;
  if OpenDialog.Execute then
    if FileExists(OpenDialog.FileName) then
    begin
      // Tolik 01/08/2019 -- сохранить последний путь
      if GStoreLastPaths then
        WriteEnvironmentDir(dctFilters, ExtractFileDir(OpenDialog.FileName));
      //
      AddNodesFromFilterFile(OpenDialog.FileName);
      SetControls;

      {FilterFile := TIniFile.Create(OpenDialog.FileName);
      try
        FilterTypeFomFile := FilterFile.ReadInteger(scFilter, idtFilterType, 0);
        FilterStrFomFile := FilterFile.ReadString(scFilter, idtFilterStr, '');

        if FilterTypeFomFile <> FilterType then
          ShowMessage('Открываемый фильтр имеет другой тип')
        else
        begin
          if FilterStrFomFile <> '' then
            AddNodesFromFilterStr(FilterStrFomFile);
        end;
      finally
        FreeAndNil(FilterFile);
      end;}
    end;
end;

procedure TF_FilterConfigurator.Act_SaveFilterToFileExecute(
  Sender: TObject);
var
  //FilterStr: String;
  //FilterFile: TIniFile;}

  MainFilteBlock: TFilterBlock;
begin
  SaveDialog.Filter := OpenSaveDialogFilter;
  if SaveDialog.Execute then
  begin
    // Tolik 01/08/2019 -- сохранить последний путь
    if GStoreLastPaths then
      WriteEnvironmentDir(dctFilters, ExtractFileDir(SaveDialog.FileName));
    //
    MainFilteBlock := GetMainFilterBlock;
    MainFilteBlock.SaveToFile(SaveDialog.FileName, FilterType);
    FreeAndNil(MainFilteBlock);

    {FilterStr := GetCurrFilterStr;
    if FilterStr <> '' then
    begin
      if FileExists(SaveDialog.FileName) then
        DeleteFile(SaveDialog.FileName);

      FilterFile := TIniFile.Create(SaveDialog.FileName);
      try
        FilterFile.WriteInteger(scFilter, idtFilterType, FilterType);
        FilterFile.WriteString(scFilter, idtFilterStr, FilterStr);
      finally
        FreeAndNil(FilterFile);
      end;
    end;}
  end;
end;

procedure TF_FilterConfigurator.tvFilterEdited(Sender: TObject;
  Node: TFlyNode; var S: String);
begin
  if tvFilter.Col = ciIsOn then
  begin
    if S = csTrue then
      TFilterBlock(Node.Data).FIsOn := true
    else
    if S = csFalse then
      TFilterBlock(Node.Data).FIsOn := false;
    SetControls;
  end;
end;

procedure TF_FilterConfigurator.tvFilterStateChange(Node: TFlyNode;
  Oldindex: Integer);
begin
  //TFilterBlock(Node.Data).FIsOn := Node.StateIndex <> STATE_UNCHECKED;
end;


function IsEqualFilterBlocks(ABlock1, ABlock2: TFilterBlock): Boolean;
var
  FilterVarious: Boolean;
  FilterBlock1: TFilterBlock;
  FilterBlock2: TFilterBlock;
  i: Integer;
begin
  Result := false;
  FilterVarious := false;
  if (ABlock1 = nil) and (ABlock2 <> nil) then
    FilterVarious := true
  else
  if (ABlock1 <> nil) and (ABlock2 = nil) then
    FilterVarious := true
  else
  if (ABlock1 <> nil) and (ABlock2 <> nil) then
  begin
    if ABlock1.IsOn <> ABlock2.IsOn then
      FilterVarious := true
    else
    if ABlock1.AllChildBlocks.Count <> ABlock2.AllChildBlocks.Count then
      FilterVarious := true
    else
    begin
      for i := 0 to ABlock1.AllChildBlocks.Count - 1 do
      begin
        FilterBlock1 := TFilterBlock(ABlock1.AllChildBlocks[i]);
        FilterBlock2 := TFilterBlock(ABlock2.AllChildBlocks[i]);

        if FilterBlock1.BlockType <> FilterBlock2.BlockType then
        begin
          FilterVarious := true;
          Break; //// BREAK ////
        end
        else
        if FilterBlock1.ConditionType <> FilterBlock2.ConditionType then
        begin
          FilterVarious := true;
          Break; //// BREAK ////
        end
        else
        if FilterBlock1.IsOn <> FilterBlock2.IsOn then
        begin
          FilterVarious := true;
          Break; //// BREAK ////
        end
        else
        if ((FilterBlock1.Condition = nil) and (FilterBlock2.Condition <> nil)) or
           ((FilterBlock1.Condition <> nil) and (FilterBlock2.Condition = nil)) then
        begin
          FilterVarious := true;
          Break; //// BREAK ////
        end
        else
        if (FilterBlock1.Condition <> nil) and (FilterBlock2.Condition <> nil) then
        begin
          if FilterBlock1.Condition.CompareType <> FilterBlock2.Condition.CompareType then
          begin
            FilterVarious := true;
            Break; //// BREAK ////
          end
          else
          if FilterBlock1.Condition.FieldIndex <> FilterBlock2.Condition.FieldIndex then
          begin
            FilterVarious := true;
            Break; //// BREAK ////
          end
          else
          if FilterBlock1.Condition.FilterValue <> FilterBlock2.Condition.FilterValue then
          begin
            FilterVarious := true;
            Break; //// BREAK ////
          end;
        end;
      end;
    end;
  end;
  Result := Not FilterVarious;
end;

end.


