{*******************************************************}
{                                                       }
{               FastCube 2 engine unit                  }
{                                                       }
{               Copyright (c) 2001-2016                 }
{           by Oleg Pryalkov, Paul Ishenin              }
{                  Fast Reports Inc.                    }
{*******************************************************}
//VCL uses section
{$IFNDEF FMX}
unit fcxSlice;
{$INCLUDE fcx.inc}
interface
uses
{$IFNDEF FPC}
  Windows,
{$ELSE}
  LCLType, LCLIntf,
{$ENDIF}
{$IFDEF SQL_TYPES_EXTRA1}
  SqlTimSt,
  FMTBcd,
{$ENDIF}
  Classes, Types,
  fcxTypes, fcxXML, fcxHeap,
  fcxDefaultSettings, fcxStringUtils, fcxList, fcxCube, fcxFilters,
  fcxComponent, fcxAlerts, fcxUniqueValue, fcxQSort, fcxUniqueArray,
  fcxFormats, fcxRange;
//FMX uses
{$ELSE FMX}
{$INCLUDE fcx.inc}
interface
uses
  System.Classes, System.Types,
  FMX.fcxTypes, FMX.fcxXML, FMX.fcxHeap, FMX.fcxDefaultSettings,
  FMX.fcxStringUtils, FMX.fcxList, FMX.fcxCube, FMX.fcxFilters,
  FMX.fcxComponent, FMX.fcxAlerts, FMX.fcxUniqueValue, FMX.fcxQSort,
{$IFDEF SQL_TYPES_EXTRA1}
  Data.SqlTimSt,
  Data.FMTBcd,
{$ENDIF}
  FMX.fcxUniqueArray, FMX.fcxFormats, FMX.fcxRange;
{$ENDIF FMX}
//{$DEFINE SHOWAXIS}
//{$DEFINE fc_debug}

type
  TfcxSlice = class;
  TfcxAxisContainer = class;
  TfcxMeasuresContainer = class;
  TfcxMeasureField = class;
  TfcxCommonFieldsOfRegion = class;
  TfcxAxisFields = class;
  TfcxMeasureFields = class;
  TfcxOrderListHack = class(TfcxOrderList);
  TfcxHackAttributesManager = class(TfcxAttributesManager);
  TfcxHackCube = class(TfcxCube);
  TfcxSliceTimeStat = class
//    FullTime: cardinal;
    XBuildTime: cardinal;
    YBuildTime: cardinal;
    DataBuildTime: cardinal;
  public
    procedure Clear;
  end;

  TfcxTraverseStep = (fcts_Down, fcts_Up, fcts_Next);

// Fact diplay mode
  TfcxDisplayAs = (
    da_Value,             // Cell Value
    da_RowPercentTotal,   // Percent by row total
    da_ColPercentTotal,   // Percent by col total
    da_RowPercentLevel,   // Percent by row level total
    da_ColPercentLevel,   // Percent by col level total
    da_GrandTotalPercent, // Percent by grand total
    da_RowRank,           // Rank by row
    da_ColRank            // Rank by col
  );

{ TODO -cNeed : Set TypeSort for each field separately}
  TfcxTypeSortAxis = (
    md_tsa_ByAxisValue,
    md_tsa_ByTotalValue,
    md_tsa_BySelected
  );

  TfcxAxisType = (
    at_Standard,          // grid like axis
    at_Tree               // tree like axis
  );

  TfcxCellRect = packed record 
    Level: TfcxSmallCount;
    SizeLevel: TfcxSmallCount;
    Cell: Integer;
    SizeCell: Integer;
  end;

// cell for draw or export
  TfcxSliceDrawHeader = record
    CellProperties: TfcxPropertiesOfCellAxis;
    Cell: Integer;
    Level: TfcxSmallCount;
    MeasureIndex: TfcxSmallCount;
    TotalIndex: TfcxSmallCount;
    NodeLevel: TfcxSmallCount;
    NodeIndex: Integer;
    TreeRect: TfcxCellRect;
    Text: TfcxString;
    Alignment: TAlignment;
    ValueIndex: Integer;
  end;

  TfcxPos = record
    Pos: Integer;
    RealPos: Integer;
  end;

  _fcxDinamicPosArray = array[0..0] of TfcxPos;
  PfcxDinamicPosArray = ^_fcxDinamicPosArray;

//  TDinamicPosArray = array of TfcxPos;
  TfcxSliceDrawHeaderResult = record
    ARight: integer;
    ABottom: integer;
    AArrayLeftCoords: PfcxDinamicPosArray;
    AArrayLeftCoordsForY: PfcxDinamicPosArray;
  end;

  TfcxFieldsListSortCompare = function(const AFieldName1, AFieldName2: TfcxString): Integer of Object; // compare fields by name
  TfcxSliceAxisDrawCellProc = function(Sender: TfcxAxisContainer; const ARec: TfcxSliceDrawHeader): Boolean of Object; // travers function for draw or export

  TfcxTypeChartData = (
    tcd_ByAxisAxis,     // Categories and Series from Dimensions. Use for one measure.
    tcd_ByAxisMeasures, // Categories from Dimensions and Series from measures
    tcd_ByMeasuresAxis  // Categories from measures and Series from Dimensions
  );

  TfcxAxisRegion = (
    ar_ColAxis,
    ar_RowAxis
  );

  TfcxChartParam = record
    TypeChartData: TfcxTypeChartData;
    SeriesAxis: TfcxAxisRegion;
    SeriesFieldCount: integer;
    CategoriesAxis: TfcxAxisRegion;
    CategoriesFieldCount: integer;
    MeasureFieldIndex: integer;
  end;

  TfcxChartDataProc = procedure(Sender: TfcxSlice; ATypeChartData: TfcxTypeChartData;
    AData1: array of String; AData2: array of String; AValues: array of variant; ASerIndex: integer = -1) of object;

  TfcxChartsManagerOnNeedChart = procedure(Sender: TObject; ACount: integer) of object;
  TfcxChartsManager = class
  private
    FSlice: TfcxSlice;
    FOnNeedChart: TfcxChartsManagerOnNeedChart;
    function GetChart(AIndex: Integer): IfcxChart;
    function GetCount: Integer;
    procedure SaveToXMLItem(AItem: TfcxXMLItem);
    function LoadFromXMLItem(AItem: TfcxXMLItem): Boolean;
  public
    constructor Create(ASlice: TfcxSlice);
    procedure NeedChart(ACount: Integer);
    property Count: Integer read GetCount;
    property Chart[AIndex: Integer]: IfcxChart read GetChart; default;
    property OnNeedChart: TfcxChartsManagerOnNeedChart read FOnNeedChart write FOnNeedChart;
  end;

  TfcxAxisField = class;

  TfcxSliceFieldRegionChange = procedure(ASliceFieldName: String; ARegionOfField: TfcxRegionOfField; var AAllow: Boolean) of Object;
  TfcxSliceFieldRegionChanged = procedure(ARegionFieldIndex: Integer; ARegionOfField: TfcxRegionOfField) of Object;

  TfcxTopNProcessor = class
  private
    FAxisField: TfcxAxisField;
    FMeasure: TfcxMeasureField;
    FCount: integer;
    FList: PfcxByteArray; // 1 if in others
    FTopType: TfcxTopType;
    FTopCount: integer;
    FCreateOthers: Boolean;
    FActive: Boolean;
    FRankList: PfcxIntegerArray;
    FRankListCount: integer;
    function GetIsOther(ARecIndex: Integer): Boolean;
    procedure SetIsOther(ARecIndex: Integer; const Value: Boolean);
    procedure SetCount(ACount: Integer);
    procedure SetTopType(const Value: TfcxTopType);
    procedure SetTopCount(const Value: integer);
    procedure SetMeasure(const Value: TfcxMeasureField);
    procedure SetCreateOthers(const Value: Boolean);
    procedure SetActive(const Value: Boolean);
    procedure Prepare;
    function GetAsString: String;
  protected
  public
    constructor Create(AAxisField: TfcxAxisField); virtual;
    destructor Destroy; override;
    procedure SaveToXML(AItem: TfcxXMLItem);
    procedure LoadFromXML(AItem: TfcxXMLItem);
// record in "Others"
    property IsOther[ARecIndex: Integer]: Boolean read GetIsOther write SetIsOther;
    property TopType: TfcxTopType read FTopType write SetTopType;
    property TopCount: integer read FTopCount write SetTopCount;
    property AxisField: TfcxAxisField read FAxisField;
    property Measure: TfcxMeasureField read FMeasure write SetMeasure;
    property CreateOthers: Boolean read FCreateOthers write SetCreateOthers;
    property Active: Boolean read FActive write SetActive;
    property AsString: String read GetAsString;
  end;

  TfcxTopNs = class
  private
    FSlice: TfcxSlice;
    FAsFilter: Boolean;
    FFilterTopN: TfcxTopNProcessor;
    FActiveCount: integer;
    FStep: integer; // 0 - First step (ignore TopN), 1 - Second step (apply TopN)
    FGoodRecCount: integer; // when FAsFilter
    FGoodRecIndex: PfcxIntegerArray; // when FAsFilter
    FGoodRecIndexReverse: PfcxIntegerArray; // when FAsFilter
    procedure MeasureDeleting(AMeasure: TfcxMeasureField);
    procedure Prepare;
    procedure Calculate;
    procedure CalcTopInBaseAxis(ABaseLevel: TfcxSmallCount;
      ATopNProcessor: TfcxTopNProcessor);
    procedure CalcTopInSecondAxis(ASecondLevel: TfcxSmallCount;
      ATopNProcessor: TfcxTopNProcessor);
    function GetGoodTopNRec(AGoodIndex: integer): integer;
    function GetGoodIndex(AGoodTopNRec: integer): integer;
    function GetNeedSortXAxis: Boolean;
    function GetNeedSortYAxis: Boolean;
    function GetNeedReSortXAxis: Boolean;
    function GetNeedReSortYAxis: Boolean;
  protected
  public
    constructor Create(ASlice: TfcxSlice); virtual;
    destructor Destroy; override;
    procedure ClearTopN;
    procedure ClearTopNInAxis(AAxis: TfcxAxisContainer);
    procedure SetTopN(ADimension: TfcxAxisField; AMeasure: TfcxMeasureField;
      ATopType: TfcxTopType; ATopCount: Integer; ACreateOthers: Boolean);
    procedure SaveToXML(AItem: TfcxXMLItem);
    procedure LoadFromXML(AItem: TfcxXMLItem);

    function BuildTree: TfcxTree;

    property ActiveCount: integer read FActiveCount;
    property GoodTopNRec[AGoodIndex: integer]: integer read GetGoodTopNRec;
    property GoodIndex[AGoodTopNRec: integer]: integer read GetGoodIndex;
    property AsFilter: Boolean read FAsFilter;
    property FilterTopN: TfcxTopNProcessor read FFilterTopN;
    property NeedSortXAxis: Boolean read GetNeedSortXAxis;
    property NeedSortYAxis: Boolean read GetNeedSortYAxis;
  end;

  TfcxSliceContainer = class
  private
    FSlice: TfcxSlice;
    FRegion: TfcxRegionOfField;
    FAlignment: TAlignment;
    procedure SetAlignment(const Value: TAlignment);
//    FFieldsOfRegion: TfcxFieldsOfRegion;
  public
    constructor Create(ASlice: TfcxSlice; ARegion: TfcxRegionOfField); virtual;
    destructor Destroy; override;
    procedure SaveToXML(AItem: TfcxXMLItem); virtual;
    procedure LoadFromXML(AItem: TfcxXMLItem); virtual;
    property Alignment: TAlignment read FAlignment write SetAlignment;
    property Slice: TfcxSlice read FSlice;
    property Region: TfcxRegionOfField read FRegion;
  end;

  TfcxSliceField = class(TPersistent)
  private
    FSlice: TfcxSlice;
    FCaption: TfcxString;
    FUVFilterSupport: boolean;
    FWidth, FHeight: SmallInt;
    FPopUpWidth: SmallInt;
    FHeightSubGroup: SmallInt;
    FWidthSubGroup: SmallInt;
    FCollapsed: Boolean;
    FCollapsedSubGroup: Boolean;
    FShowAllValues: Boolean;
    FPopupShowAvailable: Boolean;
    function GetUVFilterActive: boolean; virtual;
    function GetUVFilteredValuesCount: integer; virtual;
    function GetUVFilterOf(AUVIndex: integer): boolean; virtual;
    procedure SetUVFilterOf(AUVIndex: integer; const Value: boolean); virtual;
    function GetUVFilterOfValue(AVarValue: Variant): boolean; virtual;
    procedure SetUVFilterOfValue(AVarValue: Variant; const Value: boolean); virtual;
    function GetUVFilterStateOfGroup(AGroupIndex: integer): TfcxCheckState; virtual;
    procedure SetUVFilterStateOfGroup(AGroupIndex: integer; const Value: TfcxCheckState); virtual;
    function GetUVCaption(AUVIndex: integer): TfcxString; virtual;
    function GetUVValue(AUVIndex: integer): Variant; virtual;
    function GetUVCount: integer; virtual;
    function GetUVIndexByOrder(AOrder: integer): integer; virtual;
    function GetUVOrderByIndex(AIndex: integer): integer; virtual;
    procedure SetUVCaption(AUVIndex: integer; const Value: TfcxString);

    function GetCanGroup: Boolean; virtual;
    procedure SetCanGroup(const Value: Boolean); virtual;
    function GetHasGroups: Boolean; virtual;
    function GetGroupCount: Integer; virtual;
    function GetGroupCaption(AGroupIndex: integer): TfcxString; virtual;
    procedure SetGroupCaption(AGroupIndex: integer; const Value: TfcxString); virtual;
    function GetGroupIndexByOrder(AOrder: integer): integer; virtual;
    function GetGroupUVCount(AGroupIndex: integer): integer; virtual;
    function GetUVCaptionInGroup(AGroupIndex, AOrder: integer): TfcxString; virtual;
    function GetNonGroupUVCount: integer; virtual;
    function GetUVCaptionInNonGroups(AOrder: integer): TfcxString; virtual;
    function GetNonGroupUVIndexByOrder(AOrder: integer): integer; virtual;
    function GetGroupUVIndexByOrder(AGroupIndex, AOrder: integer): integer; virtual;
    function GetGroupManager: TfcxCommonGroupManager; virtual;

    procedure SetUVFilterType(const Value: TfcxUVFilterType); virtual;
    function GetUVFilterType: TfcxUVFilterType; virtual;
    function GetUVSingleIndex: Integer; virtual;
    procedure SetUVSingleIndex(const Value: Integer); virtual;
    procedure SetUVGroupSingleIndex(const Value: Integer); virtual;
    function GetUniqueValues: TfcxBaseUniqueValues; virtual;
    procedure SetHeight(const Value: SmallInt);
    procedure SetWidth(const Value: SmallInt);
    function GetSplitManager: TfcxSplitManager; virtual;
    function GetCurrentCaption: String;
    function GetCurrentValue: Variant;
    procedure SetHeightSubGroup(const Value: SmallInt);
    procedure SetWidthSubGroup(const Value: SmallInt);
    procedure SetCollapsed(const Value: Boolean);
    procedure SetCollapsedSubGroup(const Value: Boolean);
    procedure SetShowAllValues(const Value: Boolean);
    function GetDisplayFormat: TfcxFormat; virtual;
    procedure SetDisplayFormat(const Value: TfcxFormat); virtual;
    function GetDataType: TfcxDataType; virtual;
    function GetUVAvailable(AUVIndex: integer): boolean; virtual;
    function GetUVAvailableCount: integer; virtual;
    procedure SetPopUpWidth(const Value: SmallInt);
    procedure SetPopupShowAvailable(const Value: Boolean);
    function HasDisplayFormat: Boolean; virtual;
  protected
    function GetCaption: TfcxString; virtual;
    function GetFieldName: TfcxString; virtual; abstract;
    procedure SetCaption(const Value: TfcxString); virtual;
  public
    constructor Create(ASlice: TfcxSlice); overload; virtual;
    procedure SaveToXML(AItem: TfcxXMLItem); virtual;
    procedure LoadFromXML(AItem: TfcxXMLItem); virtual;
    function GetUVIndexAndVariantFromRec(ARecIndex: integer;var AVariantValue: Variant): integer; virtual; abstract;
    function GetUVIndexFromRec(ARecIndex: integer): integer; virtual; abstract;
    function GetUVValueFromRec(ARecIndex: integer): Variant; virtual; abstract;
    function GetUVCaptionFromRec(ARecIndex: integer): TfcxString; virtual; abstract;
    procedure SetAllFilter; virtual; // Check all UV
    procedure InverseFilter; virtual;// Inverse
    procedure SetNoneFilter; virtual;// Uncheck all UV
    procedure SetRangeFilter(ARange: TfcxRanges); virtual;
    procedure BeginUpdateFieldFilter; virtual;
    function EndUpdateFieldFilter: boolean; virtual;
    procedure RollBackFieldFilter; virtual;
    function BuildUVTree(ShowAvailable: Boolean): TfcxTree;
    property UVFilterSupport: boolean read FUVFilterSupport;
    property UVFilterType: TfcxUVFilterType read GetUVFilterType write SetUVFilterType;
    property UVSingleIndex: Integer read GetUVSingleIndex write SetUVSingleIndex;
    property UVGroupSingleIndex: Integer {read GetUVGroupSingleIndex }write SetUVGroupSingleIndex;
    property UVFilterActive: boolean read GetUVFilterActive;
    property UVFilteredValuesCount: integer read GetUVFilteredValuesCount;
    property UVFilterOf[AUVIndex: integer]: boolean read GetUVFilterOf write SetUVFilterOf;
    property UVFilterOfValue[AVarValue: Variant]: boolean read GetUVFilterOfValue write SetUVFilterOfValue;
    property UVFilterStateOfGroup[AGroupIndex: integer]: TfcxCheckState read GetUVFilterStateOfGroup write SetUVFilterStateOfGroup;
    property UVAvailable[AUVIndex: integer]: boolean read GetUVAvailable;
    property UVAvailableCount: integer read GetUVAvailableCount;
    property UVIndexByOrder[AOrder: integer]: integer read GetUVIndexByOrder;
    property UVOrderByIndex[AIndex: integer]: integer read GetUVOrderByIndex;
    property UVCount: Integer read GetUVCount;
    property UVCaption[AUVIndex: integer]: TfcxString read GetUVCaption write SetUVCaption;
    property UVValue[AUVIndex: integer]: Variant read GetUVValue;
    property UniqueValues: TfcxBaseUniqueValues read GetUniqueValues;
// Can work with groups
    property CanGroup: Boolean read GetCanGroup write SetCanGroup;
    property HasGroups: Boolean read GetHasGroups;
    property GroupManager: TfcxCommonGroupManager read GetGroupManager;
// Group Count
    property GroupCount: Integer read GetGroupCount;
// Group Caption
    property GroupCaption[AGroupIndex: integer]: TfcxString read GetGroupCaption write SetGroupCaption;
    property GroupIndexByOrder[AOrder: integer]: integer read GetGroupIndexByOrder;
    property GroupUVCount[AGroupIndex: integer]: integer read GetGroupUVCount;
    property UVCaptionInGroup[AGroupIndex, AOrder: integer]: TfcxString read GetUVCaptionInGroup;
    property GroupUVIndexByOrder[AGroupIndex, AOrder: integer]: integer read GetGroupUVIndexByOrder;
    property NonGroupUVCount: integer read GetNonGroupUVCount;
    property UVCaptionInNonGroups[AOrder: integer]: TfcxString read GetUVCaptionInNonGroups;
    property NonGroupUVIndexByOrder[AOrder: integer]: integer read GetNonGroupUVIndexByOrder;
//  Split Manager
    property SplitManager: TfcxSplitManager read GetSplitManager;

// Level Height in axis X, Width in axis  Y. 0 - default
    property Width: SmallInt read FWidth write SetWidth;
    property Height: SmallInt read FHeight write SetHeight;
    property WidthSubGroup: SmallInt read FWidthSubGroup write SetWidthSubGroup;
    property HeightSubGroup: SmallInt read FHeightSubGroup write SetHeightSubGroup;
// PopUp Filter options
    property PopupWidth: SmallInt read FPopupWidth write SetPopupWidth;
    property PopupShowAvailable: Boolean read FPopupShowAvailable write SetPopupShowAvailable;
// Level Collapsed (collapsed all nodes in level)
    property Collapsed: Boolean read FCollapsed write SetCollapsed;
    property CollapsedSubGroup: Boolean read FCollapsedSubGroup write SetCollapsedSubGroup;
// reference to the owner slice
    property Slice: TfcxSlice read FSlice;
// show all values in axis (for sequence)
    property ShowAllValues: Boolean read FShowAllValues write SetShowAllValues;
    property DisplayFormat: TfcxFormat read GetDisplayFormat write SetDisplayFormat;
    property DataType: TfcxDataType read GetDataType;
  published
// Field name
    property FieldName: TfcxString read GetFieldName;
// Field caption
    property Caption: TfcxString read GetCaption write SetCaption;
    property CurrentValue: Variant read GetCurrentValue;
    property CurrentCaption: String read GetCurrentCaption;

  end;

  TfcxCommonSliceCubeField = class(TfcxSliceField)
  private
    FCubeField: TfcxCommonField;
    FCubeFieldFilter: TfcxCubeFieldFilter;
    function GetUVCaption(AUVIndex: integer): TfcxString; override;
    function GetUVValue(AUVIndex: integer): Variant; override;

    function GetCanGroup: Boolean; override;
    procedure SetCanGroup(const Value: Boolean); override;
    function GetHasGroups: Boolean; override;
    function GetGroupCount: Integer; override;
    function GetGroupCaption(AGroupIndex: integer): TfcxString; override;
    procedure SetGroupCaption(AGroupIndex: integer; const Value: TfcxString); override;
    function GetGroupIndexByOrder(AOrder: integer): integer; override;
    function GetGroupUVCount(AGroupIndex: integer): integer; override;
    function GetUVCaptionInGroup(AGroupIndex, AOrder: integer): TfcxString; override;
    function GetNonGroupUVCount: integer; override;
    function GetUVCaptionInNonGroups(AOrder: integer): TfcxString; override;
    function GetNonGroupUVIndexByOrder(AOrder: integer): integer; override;
    function GetGroupUVIndexByOrder(AGroupIndex, AOrder: integer): integer; override;
    function GetGroupManager: TfcxCommonGroupManager; override;

    function GetUVFilterActive: boolean; override;
    function GetUVFilteredValuesCount: integer; override;
    function GetUVFilterOf(AUVIndex: integer): boolean; override;
    procedure SetUVFilterOf(AUVIndex: integer; const Value: boolean); override;
    function GetUVFilterOfValue(AVarValue: Variant): boolean; override;
    procedure SetUVFilterOfValue(AVarValue: Variant; const Value: boolean); override;
    procedure SetUVFilterType(const Value: TfcxUVFilterType); override;
    function GetUVFilterType: TfcxUVFilterType; override;
    function GetUVSingleIndex: Integer; override;
    procedure SetUVSingleIndex(const Value: Integer); override;
    function GetUVFilterStateOfGroup(AGroupIndex: integer): TfcxCheckState; override;
    procedure SetUVFilterStateOfGroup(AGroupIndex: integer; const Value: TfcxCheckState); override;
    procedure SetUVGroupSingleIndex(const Value: Integer); override;
    function GetDisplayFormat: TfcxFormat; override;
    procedure SetDisplayFormat(const Value: TfcxFormat); override;
    function GetUVAvailable(AUVIndex: integer): boolean; override;
    function GetUVAvailableCount: integer; override;
    function HasDisplayFormat: Boolean; override;
  protected
    function GetFieldName: TfcxString; override;
  public
    constructor Create(ASlice: TfcxSlice; ACubeField: TfcxCommonField); overload; virtual;

    function GetUVCaptionFromRec(ARecIndex: integer): TfcxString; override;
    function GetUVIndexFromRec(ARecIndex: integer): integer; override;
    function GetUVValueFromRec(ARecIndex: integer): Variant; override;
    function GetUVIndexAndVariantFromRec(ARecIndex: integer;var AVariantValue: Variant): integer; override;

    procedure SetAllFilter; override; // Check all UV
    procedure InverseFilter; override;// Inverse
    procedure SetNoneFilter; override;// Uncheck all UV
    procedure SetRangeFilter(ARange: TfcxRanges); override;
    procedure BeginUpdateFieldFilter; override;
    function EndUpdateFieldFilter: boolean; override;
    procedure RollBackFieldFilter; override;

    property CubeField: TfcxCommonField read FCubeField;
    property CubeFieldFilter: TfcxCubeFieldFilter read FCubeFieldFilter;
  end;

  TfcxCommonUVSliceCubeField = class(TfcxCommonSliceCubeField)
  private
    function GetSplitManager: TfcxSplitManager; override;
    function GetUniqueValues: TfcxBaseUniqueValues; override;
    function GetUVCount: integer; override;
    function GetDataType: TfcxDataType; override;
  end;

  TfcxSliceAttributeCubeField = class(TfcxCommonUVSliceCubeField)
  private
{ TODO -cNeed : Make changing .FAttributeIndex after chenging in list of splits}
// FAttributeIndex must be getted from cube field
    FAttributeIndex: integer;
    FAttributeName: TfcxString;
  protected
  public
    constructor Create(ASlice: TfcxSlice; ACubeField: TfcxCommonField); override;
    property AttributeIndex: integer read FAttributeIndex;
    property AttributeName: TfcxString read FAttributeName;
  end;

// based class for slice field of date or time split
  TfcxSliceStdSplitCubeField = class(TfcxCommonSliceCubeField)
  private
    function GetUVCount: integer; override;
    function GetUVIndexByOrder(AOrder: integer): integer; override;
    function GetUVOrderByIndex(AIndex: integer): integer; override;
    function GetDataType: TfcxDataType; override;
  public
  end;

  TfcxSliceDateSplitCubeField = class(TfcxSliceStdSplitCubeField)
  private
    FDateType: TfcxDateType;
  protected
  public
    constructor Create(ASlice: TfcxSlice; ACubeField: TfcxCommonField); override;
    property DateType: TfcxDateType read FDateType;
  end;

  TfcxSliceTimeSplitCubeField = class(TfcxSliceStdSplitCubeField)
  private
    FTimeType: TfcxTimeType;
  protected
  public
    constructor Create(ASlice: TfcxSlice; ACubeField: TfcxCommonField); override;
    property TimeType: TfcxTimeType read FTimeType;
  end;

// Measures field
  TfcxSliceMeasuresField = class(TfcxSliceField)
  private
    function GetUVFilterType: TfcxUVFilterType; override;
  public
    constructor Create(ASlice: TfcxSlice); overload; override;
  end;

// Measure field
  TfcxSliceMeasureField = class(TfcxSliceField)
  private
    function GetUVFilterType: TfcxUVFilterType; override;
  public
    constructor Create(ASlice: TfcxSlice); overload; override;
  end;

  TfcxSliceCalcField = class(TfcxSliceField)
  private
    function GetUVFilterType: TfcxUVFilterType; override;
  public
    constructor Create(ASlice: TfcxSlice); overload; override;
  end;

  TfcxSliceFields = class(TfcxOrderList)
  private
    FSlice: TfcxSlice;
    FRow : Integer;
    function  GetItem(Index: Integer): TfcxSliceField;
    function GetItemsByOrder(Order: Integer): TfcxSliceField;
    procedure SetFieldsOrder(const Value: TfcxFieldsOrder);
//    function  GetFilterCount(Index: Integer): Integer;
//    procedure SetFilterCount(Index: Integer; const Value: Integer);
    function GetItemByCaption(AIndex: String): TfcxSliceField;
    function GetItemByName(AIndex: String): TfcxSliceField;
    function GetItemByCubeField(ACubeField: TfcxCommonField): TfcxSliceField;
//    function CompareNonDefault(const AIndex1, AIndex2: Integer; ACompare: TfcxIntegerCompare): Integer;
    function CompareByCaption(const AIndex1, AIndex2: Integer): Integer;
    function CompareByIndexInDataSet(const AIndex1, AIndex2: Integer): Integer;
    function CompareByName(const AIndex1, AIndex2: Integer): Integer;
    function CompareCustom(const AIndex1, AIndex2: Integer): Integer;
    procedure ProcessField(ACubeField: TfcxCommonField);
    function InternalAddCubeField(ACubeField: TfcxCommonField): Integer;
  protected
  public
    constructor Create(ASlice: TfcxSlice); overload;
    procedure Clear; override;
    procedure SaveToXML(AItem: TfcxXMLItem);
    procedure LoadFromXML(AItem: TfcxXMLItem);
// return index in List
    function AddCubeField(ACubeField: TfcxCommonField): Integer;
    function BuildTree: TfcxTree;
    procedure DeleteCubeField(ACubeField: TfcxCommonField);
    function IndexOfField(s: String): Integer;
    function IndexOfCubeField(ACubeField: TfcxCommonField): Integer;
    function Find(const S: String; out AIndex: Integer): Boolean;
    property Items[Index: Integer]: TfcxSliceField read GetItem; default;
    property ItemsByOrder[Order: Integer]: TfcxSliceField read GetItemsByOrder;
//    property FilterCount[Index: Integer]: Integer read GetFilterCount write SetFilterCount;
    property ItemByCaption[AIndex: String]: TfcxSliceField read GetItemByCaption;
    property ItemByName[AIndex: String]: TfcxSliceField read GetItemByName;
    property ItemByCubeField[ACubeField: TfcxCommonField]: TfcxSliceField read GetItemByCubeField;
  published
    property Count;
  end;

  PfcxSTNode = ^_fcxSTNode;
  TfcxSTNodeCompare = function(ANode1, ANode2: PfcxSTNode): Integer of Object;

// region field

  { TfcxCommonFieldOfRegion }

  TfcxCommonFieldOfRegion = class
  private
    FFormat: TfcxFormat;
    FOwner: TfcxCommonFieldsOfRegion;
    FAlignment: TAlignment;
    FCaptionWidth: SmallInt;
    FCaption: TfcxString;
    FName: TfcxString;
    procedure SetAlignment(const Value: TAlignment);
    function GetCaption: TfcxString; virtual;
    function GetCaptionWidth: SmallInt; virtual;
    procedure SetCaption(const Value: TfcxString); virtual;
    procedure SetCaptionWidth(const Value: SmallInt); virtual;
    procedure SetName(const Value: TfcxString); virtual;
    function GetIndex: Integer;
    function GetDisplayFormat: TfcxFormat; virtual; abstract;
    procedure SetDisplayFormat(const Value: TfcxFormat); virtual; abstract;
    function HasDisplayFormat: Boolean; virtual; abstract;
  protected
// Changed need redraw
    procedure Changed; virtual;
// Changed need rebild
    procedure GeneralChanged; virtual;
    procedure ExpressionChanged;
    procedure DoFormatChange(Sender: TObject); virtual;
    function GetExpressions: TfcxExpressions; virtual;

    function GetWidth: SmallInt; virtual; abstract;
    procedure SetWidth(const AValue: SmallInt); virtual; abstract;
    function GetWidthSubGroup: SmallInt; virtual;
    procedure SetWidthSubGroup(const AValue: SmallInt); virtual;
    function GetHeight: SmallInt; virtual; abstract;
    procedure SetHeight(const AValue: SmallInt); virtual; abstract;
    function GetHeightSubGroup: SmallInt; virtual;
    procedure SetHeightSubGroup(const AValue: SmallInt); virtual;
  public
    constructor Create(AOwner: TfcxCommonFieldsOfRegion); overload; virtual;
    constructor Create(AOwner: TfcxCommonFieldsOfRegion; AName, ACaption: TfcxString); overload; virtual;
    destructor Destroy; override;
    procedure BeginUpdate; virtual;
    procedure EndUpdate; virtual;

    procedure SaveToXML(AItem: TfcxXMLItem); virtual;
    function LoadFromXML(AItem: TfcxXMLItem): boolean; virtual;

    property Alignment: TAlignment read FAlignment write SetAlignment;
    property Caption: TfcxString read GetCaption write SetCaption;
    property Name: TfcxString read FName write SetName;
    property CaptionWidth: SmallInt read GetCaptionWidth write SetCaptionWidth;
    property Width: SmallInt read GetWidth write SetWidth;
    property WidthSubGroup: SmallInt read GetWidthSubGroup write SetWidthSubGroup;
    property Height: SmallInt read GetHeight write SetHeight;
    property HeightSubGroup: SmallInt read GetHeightSubGroup write SetHeightSubGroup;
    property DisplayFormat: TfcxFormat read GetDisplayFormat write SetDisplayFormat;
    property Owner: TfcxCommonFieldsOfRegion read FOwner;
    property Index: Integer read GetIndex;
  end;

// axis field

  { TfcxAxisField }

  TfcxAxisField = class(TfcxCommonFieldOfRegion)
  private
    FSliceField: TfcxSliceField;
{ TODO -cNeed : Compare mus be depend from SortType.}
// current comparer
    FSTNodeCompare: TfcxSTNodeCompare;
    FSortDirection: TfcxSortDirection;
    FTotalPosition: TfcxTotalPosition;
    FUseTotalPositionFromMeasure: Boolean;
    FGroupMemberField: boolean;
    FAdditionalTotalFunctions: TfcxSetAgrFunc;
    FCountAdditionalTotalFunctions: TfcxSmallCount;
    FTopNProcessor: TfcxTopNProcessor;
    FAdditionalTotalScriptFunction: String;
    FAdditionalTotalExpression: TfcxExpression;
    FAdditionalTotalScriptError: Boolean;
    CachedAdditionalTotalScriptFunction: Pointer;
    function GetAdditionalTotalExpression: String;
    function GetDisplayFormat: TfcxFormat; override;
    procedure SetAdditionalTotalExpression(const AValue: String);
    procedure SetDisplayFormat(const Value: TfcxFormat); override;
// compare by index UV
    class function InternalCompare(ANode1, ANode2: PfcxSTNode): Integer;
// compare by Measure Value
    function MeasureValueCompare(ANode1, ANode2: PfcxSTNode): Integer;
// compare in event
    function ByEventCompare(ANode1, ANode2: PfcxSTNode): Integer;
// compre in script
    function ByScriptCompare(ANode1, ANode2: PfcxSTNode): Integer;
    procedure SetSortDirection(const Value: TfcxSortDirection); virtual;
    procedure SetTotalPosition(const Value: TfcxTotalPosition); virtual;
    procedure SetUseTotalPositionFromMeasure(const Value: Boolean); virtual;
    function GetHasGroups: boolean; virtual;
    function GetGroupCount: Integer; virtual;
    function GetGroupCaption(AGroupIndex: integer): TfcxString; virtual;
    function GetCanGroup: boolean;
    procedure SetAdditionalTotalFunctions(const Value: TfcxSetAgrFunc);
    function GetAdditionalTotalFunction(
      AAdditionalTotalIndex: TfcxSmallCount): TfcxAgrFunc;
    procedure SetAdditionalTotalScriptFunction(const Value: String);
    function HasDisplayFormat: Boolean; override;
  protected
    function GetExpressions: TfcxExpressions; override;
    function GetWidth: SmallInt; override;
    procedure SetWidth(const AValue: SmallInt); override;
    function GetWidthSubGroup: SmallInt; override;
    procedure SetWidthSubGroup(const AValue: SmallInt); override;
    function GetHeight: SmallInt; override;
    procedure SetHeight(const AValue: SmallInt); override;
    function GetHeightSubGroup: SmallInt; override;
    procedure SetHeightSubGroup(const AValue: SmallInt); override;
  public
    constructor Create(AOwner: TfcxCommonFieldsOfRegion); overload; override;
    constructor Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion; ASliceField: TfcxSliceField; AName, ACaption: TfcxString); overload;
    destructor Destroy; override;
    procedure SaveToXML(AItem: TfcxXMLItem); override;
    function LoadFromXML(AItem: TfcxXMLItem): boolean; override;
    property SortDirection: TfcxSortDirection read FSortDirection write SetSortDirection;
    property TotalPosition: TfcxTotalPosition read FTotalPosition write SetTotalPosition;
    property UseTotalPositionFromMeasure: Boolean read FUseTotalPositionFromMeasure write SetUseTotalPositionFromMeasure;
    property SliceField: TfcxSliceField read FSliceField;
    property CanGroup: boolean read GetCanGroup;
    property HasGroups: boolean read GetHasGroups;
    property GroupCount: Integer read GetGroupCount;
    property GroupCaption[AGroupIndex: integer]: TfcxString read GetGroupCaption;
    property GroupMemberField: boolean read FGroupMemberField;
    property AdditionalTotalFunctions: TfcxSetAgrFunc read FAdditionalTotalFunctions write SetAdditionalTotalFunctions;
    property CountAdditionalTotalFunctions: TfcxSmallCount read FCountAdditionalTotalFunctions;
    property AdditionalTotalFunction[AAdditionalTotalIndex: TfcxSmallCount]: TfcxAgrFunc read GetAdditionalTotalFunction;
    property AdditionalTotalExpression: String read GetAdditionalTotalExpression write SetAdditionalTotalExpression;
    property AdditionalTotalScriptFunction: String read FAdditionalTotalScriptFunction write SetAdditionalTotalScriptFunction;
    property TopNProcessor: TfcxTopNProcessor read FTopNProcessor;
  end;

  TfcxAxisGroupMemberField = class(TfcxAxisField)
  private
    FParentAxisField: TfcxAxisField;
    function GetHasGroups: boolean; override;
    function GetGroupCount: Integer; override;
    function GetGroupCaption(AGroupIndex: integer): TfcxString; override;
  public
    constructor CreateGroupMemberField(AAxisField: TfcxAxisField; AName, ACaption: TfcxString); overload;
    property ParentAxisField: TfcxAxisField read FParentAxisField;
  end;

// List of Fields in Region

  { TfcxCommonFieldsOfRegion }

  TfcxCommonFieldsOfRegion = class
  private
    FFields: TfcxList;
    FContainer: TfcxSliceContainer;
    function GetCount: TfcxSmallCount;
    function GetIndexByName(AFieldName: TfcxString): TfcxSmallCount;
    function GetItem(Index: TfcxSmallCount): TfcxCommonFieldOfRegion;
    function GetIndexOf(AField: TfcxCommonFieldOfRegion): TfcxSmallCount;
    procedure Clear;
    procedure Delete(AIndex: TfcxSmallCount);
    function Add(AField: TfcxCommonFieldOfRegion): TfcxSmallCount;
    procedure Insert(AField: TfcxCommonFieldOfRegion; AIndex: TfcxSmallCount);
  public
    constructor Create(AContainer: TfcxSliceContainer);
    destructor Destroy; override;
    property Container: TfcxSliceContainer read FContainer;
    property Count: TfcxSmallCount read GetCount;
    property Items[Index: TfcxSmallCount]: TfcxCommonFieldOfRegion read GetItem; {$IFNDEF Delphi_9UP}default;{$ENDIF} // default keyword breaks Delphi XE4 compilation
    property IndexOf[AField: TfcxCommonFieldOfRegion]: TfcxSmallCount read GetIndexOf;
    property IndexByName[AFieldName: TfcxString]: TfcxSmallCount read GetIndexByName;
  end;

  TfcxAxisFields = class(TfcxCommonFieldsOfRegion)
  private
    function GetItem(Index: Integer): TfcxAxisField;
    function FindSliceField(ASliceField: TfcxSliceField): integer;
  public
    property Items[Index: Integer]: TfcxAxisField read GetItem; default;
  end;

  TfcxFieldsContainer = class(TfcxSliceContainer)
  private
//    FSlice: TfcxSlice;
    FFields: TfcxAxisFields;
    function GetMeasuresLevel: TfcxSmallCount; virtual; abstract;
    function GetMeasuresLevelVisibleIndex: TfcxSmallCount; virtual; abstract;
    function GetMeasuresPosition: TfcxSmallCount; virtual;
    function GetLevelCount: TfcxSmallCount; virtual;
    function RemoveField(ASliceField: TfcxSliceField): boolean;
    function RemoveFieldByIndex(AIndexInList: TfcxSmallCount): boolean;
    function VisiblePositionInRegionToIndex(AVisPosition: TfcxSmallCount; var ABeforeMeasures: boolean): integer;
  protected
    function InsertField(ASliceField: TfcxSliceField; AIndexInList: TfcxSmallCount; AName: TfcxString = ''; ACaption: TfcxString = ''; ABeforeMeasures: Boolean = True): boolean;
    function InsertFieldInPosition(ASliceField: TfcxSliceField; AVisPosition: TfcxSmallCount; AName: TfcxString = ''; ACaption: TfcxString = ''): boolean;
    function InsertMeasuresFieldInPosition(AVisPosition: TfcxSmallCount): boolean;
  public
    constructor Create(ASlice: TfcxSlice; ARegion: TfcxRegionOfField); override;
    destructor Destroy; override;

    function ContainField(ASliceField: TfcxSliceField): boolean;

    procedure SaveToXML(AItem: TfcxXMLItem); override;
    procedure LoadFromXML(AItem: TfcxXMLItem); override;
// add,insert,remove "measures" field
    function AddMeasuresField: integer;
    function InsertMeasuresField(AIndex: TfcxSmallCount): integer;
    function DeleteMeasuresField: boolean;

    property Fields: TfcxAxisFields read FFields;
// Measures leve for axis with measures or -1
    property MeasuresLevel: TfcxSmallCount read GetMeasuresLevel;
    property MeasuresLevelVisibleIndex: TfcxSmallCount read GetMeasuresLevelVisibleIndex;
    property MeasuresPosition: TfcxSmallCount read GetMeasuresPosition;
    property LevelCount: TfcxSmallCount read GetLevelCount;
  end;

  TfcxPageContainer = class(TfcxFieldsContainer)
  private
    function GetMeasuresLevel: TfcxSmallCount; override;
    function GetMeasuresLevelVisibleIndex: TfcxSmallCount; override;
  protected
  public
// add,insert,remove FilterField
    function AddFilterField(ASliceField: TfcxSliceField; AName: TfcxString = ''; ACaption: TfcxString = ''): integer;
    function InsertFilterField(ASliceField: TfcxSliceField; AIndex: integer; AName: TfcxString = ''; ACaption: TfcxString = ''): integer;
    function DeleteFilterField(AIndex: integer): boolean;
  end;

// Properties of Node in Axis Tree
  _fcxAxisTreeNodeProperties = record
    State: Byte;          // set of Total, Expanded, Zero, Visible ...
  end;
// List Properties of Node in Axis Level Tree for one Measure
  _fcxAxisTreeNodePropertiesList = array[0..0] of _fcxAxisTreeNodeProperties;
  PfcxAxisTreeNodePropertiesList = ^_fcxAxisTreeNodePropertiesList;
// Array of List Properties of Node in Axis Tree Level
  _fcxAxisTreeNodePropertiesListArray = array[0..0] of PfcxAxisTreeNodePropertiesList;
  PfcxAxisTreeNodePropertiesListArray = ^_fcxAxisTreeNodePropertiesListArray;

// List Sizes of Node in Axis Level Tree for one Measure
  _fcxAxisTreeNodeSizesList = array[0..0] of SmallInt;
  PfcxAxisTreeNodeSizesList = ^_fcxAxisTreeNodeSizesList;
// Array of List Sizes of Node in Axis Tree Level
  _fcxAxisTreeNodeSizesListArray = array[0..0] of PfcxAxisTreeNodeSizesList;
  PfcxAxisTreeNodeSizesListArray = ^_fcxAxisTreeNodeSizesListArray;
  PfcxSourceTreeNode = ^_fcxSourceTreeNode;

// Node of Axis Tree
  _fcxAxisTreeNode = record
    SSelfNode: PfcxSourceTreeNode;  // Node in Source Tree
    IndParent: Integer;    // Parent Index in previous level
    IndFirst: Integer;     // Index of First Child in next level (-1 = no children)
    IndLast: Integer;      // Index of Last Child in next level
  end;
  PfcxAxisTreeNode = ^_fcxAxisTreeNode;

// List of Nodes in Axis Tree Level
  _fcxAxisTreeNodeList = array[0..0] of _fcxAxisTreeNode;
  PfcxAxisTreeNodeList = ^_fcxAxisTreeNodeList;

  TfcxAxisTreeLevelType =
  (
    fcATLT_Simple, // simple level
    fcATLT_HasGroup, // level with group
    fcATLT_SubGroup   // level with expanded group
  );
// Level type (Measures level position)
  TfcxAxisTreeLevelPosType =
  (
    fcATLPT_WOMeasures_NotLastLevel,
    fcATLPT_WOMeasures_LastLevel,
    fcATLPT_WMeasures_NotBaseLevel_High_NotLastLevel,
    fcATLPT_WMeasures_NotBaseLevel_Low_NotLastLevel,
    fcATLPT_WMeasures_NotBaseLevel_LastLevel,
    fcATLPT_WMeasures_BaseLevel_NotLastLevel,
    fcATLPT_WMeasures_BaseLevel_LastLevel
  );

// node type
  TfcxAxisTreeNodeType =
  (
    fcATNT_SimpleValue, // simple
    fcATNT_GroupValue,  // group value
    fcATNT_EmptyValue,  // empty value in fcATLPT_SubGroup. need to see parent.
    fcATNT_OtherValue   // other value for TopN
  );


// Level of Axis Tree
  _fcxAxisTreeLevel = record
    Nodes: PfcxAxisTreeNodeList; // List of Nodes
// type of position level (need recalc)
    LevelPosType: TfcxAxisTreeLevelPosType;
    LevelType: TfcxAxisTreeLevelType;
    Count: Integer;             // Nodes Count
    Capacity: Integer;          // Capacity
// cell index in full list
    IndexInLastLevel: PfcxIntegerArray;  // Without Totals!!!
    CountWithTotals: Integer;        // Count include totals count
    RegionFieldIndex: TfcxSmallCount;  // Index of Field in Region
    HasGroups: boolean; // contain groups
    IsGroupMembers: boolean; // contain group members (from expanded group)
    Visible: Boolean; // Invisibe if all parent level collapsed
    GSubLevelVisible: Boolean; // has expanded groups
  end;

  _fcxAxisTree = array[-1..-1] of _fcxAxisTreeLevel;
  PfcxAxisTree = ^_fcxAxisTree;

  PfcxSourceTreeNodeArray = ^_fcxSourceTreeNodeArray;
  _fcxSourceTreeNode = record
    Index: Integer;  // UV index
    NodeType: TfcxAxisTreeNodeType;
    IndexInProp: integer; // cell index in properties array.
    Count: Integer;  // Count of children
    Nodes: Pointer; // PfcxSourceTreeNodeArray; // children array
  end;
  _fcxSourceTreeNodeArray = array[0..0] of _fcxSourceTreeNode;

  PfcxSourceTreeLevel = ^_fcxSourceTreeLevel;

  _fcxSourceTreeLevel = record
    FLevelPosType: TfcxAxisTreeLevelPosType;
    FLevelType: TfcxAxisTreeLevelType;
    FGSubLevelVisible: Boolean;
    FGroupExpandedCount: integer;
    FHiddenCount: integer;
    FPropSelfCount: TfcxSmallCount;
    FPropTotalSelfCount: TfcxSmallCount;
    FPropAdditionalTotalSelfCount: TfcxSmallCount;
    FPropMeasuresCount: TfcxSmallCount;
    FSizeCount: TfcxSmallCount;
    FGoodNodeCount: integer;
    FFullNodeCount: integer;
    FCollapsedNodeCount: integer;
    FUseCollapsedFromSliceField: boolean;
    FCanCollapseNodeMultiplier: integer;
    FPropSelf: PfcxAxisTreeNodePropertiesListArray;
    FPropTotalSelf: PfcxAxisTreeNodePropertiesListArray;
    FPropAdditionalTotalSelf: PfcxAxisTreeNodePropertiesListArray;
    FPropMeasures: PfcxAxisTreeNodePropertiesListArray;
    FWidths: PfcxAxisTreeNodeSizesListArray;
    FHeights: PfcxAxisTreeNodeSizesListArray;
    FAdditionalTotalWidths: PfcxAxisTreeNodeSizesListArray;
    FAdditionalTotalHeights: PfcxAxisTreeNodeSizesListArray;
// TopN
    FTopNCheck: Boolean;
  end;

  _fcxSourceTreeLevelArray = array[-1..-1] of _fcxSourceTreeLevel;
  PfcxSourceTreeLevelArray = ^_fcxSourceTreeLevelArray;

  TfcxAxisSourceTree = class
  private
    FAxisContainer: TfcxAxisContainer;
    FSourceTreeLevels: PfcxSourceTreeLevelArray;
    FSourceOrderIndexes: PfcxIntegerArray;
    FRootNodeNew: PfcxSourceTreeNode;
    FLevelCount: integer;
    FFieldIndexOfLevel: PfcxSmallCountArray;
    FGoodRecordCount: integer;
    FFullRecordCount: integer;
    FMeasuresCount: TfcxSmallCount;
    FMeasuresLevel: TfcxSmallCount;
    AFilled: Boolean;
    procedure SetLevelPropCounts(ALevelIndex: TfcxSmallCount);
    procedure GetMemForLevelProp(ASourceTreeLevel: PfcxSourceTreeLevel);
    procedure GetMemForLevelPropDetail(ATarget, ASource: PfcxSourceTreeLevel);
    procedure ReallocMemForLevelPropDetail(ATarget, ASource: PfcxSourceTreeLevel);
    procedure ReallocMemForLevelPropDetailNewCount(ASourceTreeLevel: PfcxSourceTreeLevel; ANewCount, AOldCount, AStartIndex: Integer);
    procedure FreeMemForLevelProp(ASourceTreeLevel: PfcxSourceTreeLevel);
    procedure ClearNode(AfcSourceTreeNode: PfcxSourceTreeNode; ALevelIndex: TfcxSmallCount);
    procedure ClearTree;
    function FindPlace(AfcSourceTreeNode: PfcxSourceTreeNode; AUVIndex: integer; AWithGroup: boolean; AUVType: TfcxAxisTreeNodeType; ATopNCheck: Boolean; var AIndex: integer): boolean;
    procedure FillSourceTree;
    function SetSubGroupVisible(ALevelIndex: TfcxSmallCount; AValue: Boolean): Boolean;
    procedure InsertSubLevel(AMainLevelIndex: TfcxSmallCount);
    procedure DeleteSubLevel(AMainLevelIndex: TfcxSmallCount);
    procedure SetGroupExpandedInternal(ALevel, ARealLevel: TfcxSmallCount; ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean; ANonChange: boolean);

    procedure FillSourceOrderIndexes(ALevelsSaved: Boolean = False);
    procedure Recapacity;
    function CalcLevelPosType(ALevelIndex: TfcxSmallCount): TfcxAxisTreeLevelPosType;
    procedure ResetLevelsType;
    procedure SetGroupExpanded(ARealLevel: TfcxSmallCount; ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean);
    procedure ClearPropertiesByMask(AMask: byte);
    procedure SetPropertiesByMask(AMask: byte);
  public
    constructor Create(AAxisContainer: TfcxAxisContainer);
    destructor Destroy; override;
  end;

  _fcxSTNodeArray = array[0..0] of PfcxSTNode;
  PfcxSTNodeArray = ^_fcxSTNodeArray;

  _fcxSTNode = record
    Nodes: PfcxSTNodeArray;   // Array of child nodes
    FLevel: TfcxSmallCount;            // Level of AxisTree
    FIndex: Integer;         // Index in Level of AxisTree
  end;

  TfcxAxisSortTree = class
  private
    FAxisContainer: TfcxAxisContainer;
    FMainTreeHeap: TfcxHeap;
    FRootNode: PfcxSTNode;
    function InternalCompare(ANode1, ANode2: PfcxSTNode): Integer;
    function CompareByTotal(ANode1, ANode2: PfcxSTNode): Integer;
    function CompareBySelected(ANode1, ANode2: PfcxSTNode): Integer;
    procedure Clear;
    function  CreateSTNode(ALevel: TfcxSmallCount; AIndex: Integer): PfcxSTNode;
    procedure FillSortTree;
    procedure SortTree;
  public
    constructor Create(AAxisContainer: TfcxAxisContainer);
    destructor Destroy; override;
  end;

  TfcxVisibleAxisNodes = class
  private
    FAxisContainer: TfcxAxisContainer;
    FIndexes: PfcxIntegerArray;
    FMeasureIndexes: PfcxSmallCountArray;
    FAdditionalTotalsIndexes: PfcxSmallCountArray;
    FVisIndexes: PfcxArrIntegerArray;
    FCount: Integer;
    FCapacity: Integer;
    procedure SetCapacity(ACapacity: Integer);
    function GetMeasureIndex(AIndex: Integer): TfcxSmallCount;
    function GetAdditionalTotalIndex(AIndex: Integer): TfcxSmallCount;
    function GetIndex(AIndex: Integer): Integer;
    function AddWM(AIndex: Integer; AMeasureIndex: TfcxSmallCount; AAdditionalTotalIndex: Integer = -1): Integer;
    procedure DeleteWM(AVisIndex: Integer);
    function AddWOM(AIndex: Integer; AAdditionalTotalIndex: Integer = -1): Integer;
    procedure DeleteWOM(AVisIndex: Integer);
    procedure Clear;
    procedure FillVisIndexes;
    function GetVisibleIndex(AAbsIndex: Integer; AMeasureIndex,
      AAdditionalTotalIndex: TfcxSmallCount): Integer;
    function GetLevelOf(AIndex: Integer): TfcxSmallCount;
  public
    constructor Create(AAxisContainer: TfcxAxisContainer);
    destructor  Destroy; override;
    property Index[AIndex: Integer]: Integer read GetIndex{ write SetIndex};
    property VisibleIndex[AAbsIndex: Integer; AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount]: Integer read GetVisibleIndex;
    property MeasureIndex[AIndex: Integer]: TfcxSmallCount read GetMeasureIndex{ write SetMeasureIndex};
    property LevelOf[AIndex: Integer]: TfcxSmallCount read GetLevelOf;
    property AdditionalTotalIndex[AIndex: Integer]: TfcxSmallCount read GetAdditionalTotalIndex{ write SetAdditionalTotalIndex};
    property Count: Integer read FCount;
  end;

  TfcxAxisLevelInfo = record
    IsVisible: Boolean;
    IsMeasure: Boolean;
    RegionField: TfcxAxisField;
    LevelType: TfcxAxisTreeLevelType;
    Level: TfcxSmallCount;
    SubLevel: TfcxSmallCount;
  end;

  TfcxAxisContainer = class(TfcxFieldsContainer)
  private
    FAxisSourceTree: TfcxAxisSourceTree;
    FAxisSortTree: TfcxAxisSortTree;
    FVisibleAxisNodes: TfcxVisibleAxisNodes;
    FAxisTree: PfcxAxisTree;
// Level Count in FAxisTree (without "MEASURES") (with subgroups)
    FLevelCount: TfcxSmallCount;
// LevelCount + 1 if "MEASURES" in this Axis
    FRealLevelCount: TfcxSmallCount;
// Visible Level Count (+ 1 for axis with Measures)
    FVisibleLevelCount: TfcxSmallCount;
// Count all cells (all cells from all levels)
    FCellsCount: Integer;
// index of node from last levels for each record from source (after appling filters)
    FCoordinate: PfcxIntegerArray;
// Level of node
// Node with index 0 from level -1 (grand total). Count = FCellsCount
    FLevelOf: PfcxSmallCountArray;
// Node Index in Level FLevelOf without totals
// Level see in FLevelOf. Count = FCellsCount
    FIndexInLevel: PfcxIntegerArray;
// Index of Parent node (Total) for node
// For node with index 0 Index Of Parent is -1. Count = FCellsCount
    FIndexOfParent: PfcxIntegerArray;
// no fields in axis.
    FNoFields: Boolean;
// Level of Measures for axis with Measures field or -1
    FMeasuresLevel: TfcxSmallCount;
    FMeasuresLevelVisibleIndex: TfcxSmallCount;
    FUseGrandTotalPositionFromMeasure: Boolean;
    FGrandTotalPosition: TfcxTotalPosition;
    FDefaultTypeSort: TfcxTypeSortAxis;
    FAxisType: TfcxAxisType;
    FShowTotalAs: TfcxShowTotalAs;
    FCountAdditionalGrandTotalFunctions: TfcxSmallCount;
    FAdditionalGrandTotalFunctions: TfcxSetAgrFunc;

// Selected (for sort by total and selected)
    FSelectedLevelIndex,
    FSelectedMeasureIndex,
    FSelectedAdditionalTotalIndex: TfcxSmallCount;
//    FSortLevelIndex,FSortMeasureIndex,FSortAdditionalTotalIndex: TfcxSmallCount;
    FSelectedIndexInLevel: Integer;
    FAdditionalGrandTotalExpression: TfcxExpression;
    FAdditionalGrandTotalScriptFunction: String;
    FAdditionalGrandTotalScriptError: Boolean;
    CachedAdditionalGrandTotalScriptFunction: Pointer;
//    FSortIndexInLevel, FSortVisIndex: Integer;
    function CheckSelected(ATypeSort: TfcxTypeSortAxis): boolean;
    procedure SetSelected(ATypeSort: TfcxTypeSortAxis);
    procedure SelectedMeasureMoved(AFromIndex, AToIndex: TfcxSmallCount); // Measure moved
    procedure SelectedMeasureDeleted(AMeasureIndex: TfcxSmallCount);

    function GetLevelCount: TfcxSmallCount; override;
    function GetMeasuresLevel: TfcxSmallCount; override;
    function GetMeasuresLevelVisibleIndexInternal: TfcxSmallCount;
    function GetMeasuresLevelVisibleIndex: TfcxSmallCount; override;
    procedure SetCountInLevel(ALevel: TfcxSmallCount; const Value: Integer);
    procedure SetGrandTotalPosition(const Value: TfcxTotalPosition);
    procedure SetUseGrandTotalPositionFromMeasure(const Value: Boolean);
    function GetCellSize(AVisCellIndex: integer): Smallint;
    procedure SetCellSize(AVisCellIndex: integer; const Value: Smallint);
    procedure SetCellsSize(const Value: Smallint);
    function GetLevelSize(AAbsLevelIndex: TfcxSmallCount): Smallint;
    procedure SetLevelSize(AAbsLevelIndex: TfcxSmallCount; const Value: Smallint);
    function GetVisibleLevelSize(AVisLevelIndex: TfcxSmallCount): Smallint;
    procedure SetVisibleLevelSize(AVisLevelIndex: TfcxSmallCount; const Value: Smallint);
    function GetCellSizeByNode(ALevel: TfcxSmallCount; AIndexInProp: integer; AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): Smallint;
    procedure SetCellSizeByNode(ALevel: TfcxSmallCount; AIndexInProp: integer; AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount; const Value: Smallint);
    function GetVisibleNodeCount: Integer;
    procedure ClearAxisTree(ASaveLevels: boolean = False);
    procedure ResetLevelsType;
// run when
    procedure MeasuresInserted; // "Measures" inserted to Axis
    procedure MeasuresDeleted; // "Measures" deleted from Axis
    procedure MeasureDeleted(AMeasureIndex: TfcxSmallCount); // Measure deleted
    procedure MeasureInserted(AMeasureIndex: TfcxSmallCount); // Measure inserted
    procedure MeasuresMoved(AFromIndex: TfcxSmallCount); // "Measures" moved (changed level)
    procedure MeasureMoved(AFromIndex, AToIndex: TfcxSmallCount); // Measure moved
    function CellPropertiesByNodeSelf(ANodeIndex: integer; ANodeLevelIndex, AMeasureIndex: TfcxSmallCount): _fcxAxisTreeNodeProperties;
    function CellPropertiesByNodeTotalSelf(ANodeIndex: integer; ANodeLevelIndex, AMeasureIndex: TfcxSmallCount): _fcxAxisTreeNodeProperties;
    function CellPropertiesByNodeAdditionalTotalSelf(ANodeIndex: integer; ANodeLevelIndex, AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): _fcxAxisTreeNodeProperties;
    function CellPropertiesByNodeMeasures(ANodeIndex: integer; ANodeLevelIndex, AMeasureIndex: TfcxSmallCount): _fcxAxisTreeNodeProperties;
    function GetVisibleLevelCount: TfcxSmallCount;
    function GetLevelInfo(AAbsLevelIndex: TfcxSmallCount): TfcxAxisLevelInfo;
    function GetLevelInfoWOMeasures(ALevelIndex: TfcxSmallCount): TfcxAxisLevelInfo;
    function GetIndexOfTotalInLevel(AIndex: integer; ATargetLevel: TfcxSmallCount): integer;
    function GetLevelOfRegionField(ARegionFieldIndex: integer): TfcxSmallCount;
    function GetLevelOfRegionFieldAbs(ARegionFieldIndex: integer): TfcxSmallCount;
    procedure SetDefaultTypeSort(const Value: TfcxTypeSortAxis);
    procedure MakeHideZeros;
    procedure ClearHideZeros;
    procedure SetAxisType(const Value: TfcxAxisType);
    procedure SetShowTotalAs(const Value: TfcxShowTotalAs);
    function GetHiddenCount: Integer;
    procedure SetAdditionalGrandTotalFunctions(const Value: TfcxSetAgrFunc);
    function GetAdditionalGrandTotalFunction(AAdditionalTotalIndex: TfcxSmallCount): TfcxAgrFunc;
    function GetCountAdditionalTotalFunctionsInLevel(ALevel: TfcxSmallCount): TfcxSmallCount;
    function GetAdditionalTotalFunctionInLevel(ALevel, AAdditionalTotalIndex: TfcxSmallCount): TfcxAgrFunc;
    function GetVisibleLevelCollapsed(AVisLevelIndex: TfcxSmallCount): Boolean;
    function GetVisibleLevelCollapsedReal(AVisLevelIndex: TfcxSmallCount): Boolean;
    procedure CheckVisibleLevelCollapsedReal(AVisLevelIndex: TfcxSmallCount);
    procedure SetVisibleLevelCollapsed(AVisLevelIndex: TfcxSmallCount;
      const Value: Boolean);
    procedure SetExpandedLevelInternal(ALevelIndex: TfcxSmallCount; AMeasureLevel: Boolean; const Value: Boolean);
    procedure SetAdditionalGrandTotalScriptFunction(const Value: String);
    function GetIndexOfVisibleLevel(
      AVisLevelIndex: TfcxSmallCount): TfcxSmallCount;
    function GetVisibleIndexOfLevel(
      AAbsLevelIndex: TfcxSmallCount): TfcxSmallCount;
    function GetVisibleLevelInfo(
      AVisLevelIndex: TfcxSmallCount): TfcxAxisLevelInfo;
    function GetDimValue(ALevel: TfcxSmallCount; AVisIndex: Integer): variant;
    function GetDimCaption(ALevel: TfcxSmallCount; AVisIndex: Integer): TfcxString;
    function GetDimValueID(ALevel: TfcxSmallCount; AVisIndex: Integer): integer;
    function GetAdditionalGrandTotalExpression: String;
    procedure SetAdditionalGrandTotalExpression(const Value: String);
  protected
    procedure SetExpandedInternal(ALevel, ARealLevel: TfcxSmallCount; ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean; ANonChange: boolean);
    procedure SetHiddenInternal(ALevel, ARealLevel: TfcxSmallCount; ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; ATotalCell: Boolean; ANonChange: boolean);
    procedure ShowAllHiddenInternal(ANonChange: boolean);
    procedure CreateAxisTree(ALevelsSaved: Boolean = False);
    procedure FillVisibleArray;
    procedure FillVisibleArrayWOM;
    procedure FillVisibleArrayWM;
    function GetDisplayValue(ALevel: TfcxSmallCount; AIndex: Integer) : TfcxString;
    function GetDisplayValueLI(ALevel: TfcxSmallCount; AIndexInLevel: Integer) : TfcxString;
    function GetValueLI(ALevel: TfcxSmallCount; AIndexInLevel: Integer) : Variant;
    function GetValueIDLI(ALevel: TfcxSmallCount; AIndexInLevel: Integer) : integer;
    function GetExpressions: TfcxExpressions;
  public
    constructor Create(ASlice: TfcxSlice; ARegion: TfcxRegionOfField); override;
    destructor Destroy; override;
    procedure SaveToXML(AItem: TfcxXMLItem); override;
    procedure LoadFromXML(AItem: TfcxXMLItem); override;
    procedure SetTotalPosition(const Value: TfcxTotalPosition);
    procedure SetExpanded(ARealLevel: TfcxSmallCount; ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean);
    procedure SetExpandedLevel(ARealLevel: TfcxSmallCount; const Value: Boolean);
    procedure SetExpandedGroup(ARealLevel: TfcxSmallCount; ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean);
// Hide value
    procedure SetHidden(ANodeLevel: TfcxSmallCount; ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; ATotalCell: Boolean; AAdditionalTotalIndex: integer);
// Show all hidden values
    procedure ShowAllHidden;
// axis tree walkers
    function TraverseAxis(AStartLevel, AStopLevel: TfcxSmallCount; AStartCell: Integer;
      ADrawProc: TfcxSliceAxisDrawCellProc): TfcxSliceDrawHeaderResult;
    function TraverseAxis_Grid(AStartLevel, AStopLevel: TfcxSmallCount; AStartCell: Integer;
      ADrawProc: TfcxSliceAxisDrawCellProc): TfcxSliceDrawHeaderResult;
    function TraverseAxis_Tree(AStartCell: Integer;
      ADrawProc: TfcxSliceAxisDrawCellProc): TfcxSliceDrawHeaderResult;
    function VisIndexToTreeCellPos(const AVisIndex: Integer; var ALevel: TfcxSmallCount;
      var AIndexInLevel: Integer; var AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): Boolean;
// Index of Cell with Offset
    function CellIndexWithOffset(const AStartLevel, AOffsetLevel: TfcxSmallCount; const AIndexInLevel, AOffset: Integer): Integer;
// Index of Cell with Offset Through
    function CellIndexWithOffsetThrough(const ALevel: TfcxSmallCount; const AIndexInLevel, AOffset: Integer): Integer;
// Index of Cell with Offset on Dim Value (AOffsetLevel <= AStartLevel)
    function CellIndexWithOffsetOnValue(const AStartLevel, AOffsetLevel: TfcxSmallCount; const AIndexInLevel: Integer; AOffsetValue: Variant): Integer;
// Index of Cell on TargetLevel with Dims Values
    function CellIndexWithValues(AWayValues: Array of Variant): Integer;
    function GetAsPlainText(AStartCell, AStopCell: Integer; ACloneHeaders: Boolean): TfcxString;
    function GetDimValuesID(ALevel: TfcxSmallCount; AStartVisIndex, AEndVisIndex: Integer): TIntegerDynArray;

// add,insert,remove Dimension
    function AddDimension(ASliceField: TfcxSliceField; AName: TfcxString = ''; ACaption: TfcxString = ''): integer;
    function InsertDimension(ASliceField: TfcxSliceField; AIndex: integer; AName: TfcxString = ''; ACaption: TfcxString = ''): integer;
    function DeleteDimension(AIndex: integer): boolean;

    property AxisTree: PfcxAxisTree read FAxisTree;
    property VisibleNodeCount: Integer read GetVisibleNodeCount;
    property HiddenCount: Integer read GetHiddenCount;
// full level count (FLevelCount + 1 for axis with measures)
    property RealLevelCount: TfcxSmallCount read FRealLevelCount;
// visible level count ( + 1 for axis with measures)
    property VisibleLevelCount: TfcxSmallCount read FVisibleLevelCount;
    property VisibleIndexOfLevel[AAbsLevelIndex: TfcxSmallCount]: TfcxSmallCount read GetVisibleIndexOfLevel;
    property IndexOfVisibleLevel[AVisLevelIndex: TfcxSmallCount]: TfcxSmallCount read GetIndexOfVisibleLevel;
// Cell size. For X axis - width, for Y  axis - height.
    property CellSize[AVisCellIndex: integer]: Smallint read GetCellSize write SetCellSize;
// Node size. For X axis - width, for Y  axis - height.
    property CellSizeByNode[ALevel: TfcxSmallCount; AIndexInProp: integer; AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount]: Smallint read GetCellSizeByNode write SetCellSizeByNode;
// Level size. For X axis - height, for Y  axis - width.
    property LevelSize[AAbsLevelIndex: TfcxSmallCount]: Smallint read GetLevelSize write SetLevelSize;
    property VisibleLevelSize[AVisLevelIndex: TfcxSmallCount]: Smallint read GetVisibleLevelSize write SetVisibleLevelSize;
    property LevelInfo[AAbsLevelIndex: TfcxSmallCount]: TfcxAxisLevelInfo read GetLevelInfo;
    property VisibleLevelInfo[AVisLevelIndex: TfcxSmallCount]: TfcxAxisLevelInfo read GetVisibleLevelInfo;
    property LevelInfoWOMeasures[ALevelIndex: TfcxSmallCount]: TfcxAxisLevelInfo read GetLevelInfoWOMeasures;
    property VisibleLevelCollapsed[AVisLevelIndex: TfcxSmallCount]: Boolean read GetVisibleLevelCollapsed write SetVisibleLevelCollapsed;
    property LevelOfRegionField[ARegionFieldIndex: integer]: TfcxSmallCount read GetLevelOfRegionField;
    property LevelOfRegionFieldAbs[ARegionFieldIndex: integer]: TfcxSmallCount read GetLevelOfRegionFieldAbs;
    property IndexOfTotalInLevel[AIndex: integer; ATargetLevel: TfcxSmallCount]: integer read GetIndexOfTotalInLevel;
    property VisibleAxisNodes: TfcxVisibleAxisNodes read FVisibleAxisNodes;
    property GrandTotalPosition: TfcxTotalPosition read FGrandTotalPosition write SetGrandTotalPosition;
    property UseGrandTotalPositionFromMeasure: Boolean read FUseGrandTotalPositionFromMeasure write SetUseGrandTotalPositionFromMeasure;
    property DefaultTypeSort: TfcxTypeSortAxis read FDefaultTypeSort write SetDefaultTypeSort;
    property AxisType: TfcxAxisType read FAxisType write SetAxisType;
    property AdditionalGrandTotalFunctions: TfcxSetAgrFunc read FAdditionalGrandTotalFunctions write SetAdditionalGrandTotalFunctions;
    property CountGrandAdditionalTotalFunctions: TfcxSmallCount read FCountAdditionalGrandTotalFunctions;
    property AdditionalGrandTotalFunction[AAdditionalTotalIndex: TfcxSmallCount]: TfcxAgrFunc read GetAdditionalGrandTotalFunction;
// ALevel - abs without measures
    property CountAdditionalTotalFunctionsInLevel[ALevel: TfcxSmallCount]: TfcxSmallCount read GetCountAdditionalTotalFunctionsInLevel;
    property AdditionalTotalFunctionInLevel[ALevel, AAdditionalTotalIndex: TfcxSmallCount]: TfcxAgrFunc read GetAdditionalTotalFunctionInLevel;
    property AdditionalGrandTotalScriptFunction: String read FAdditionalGrandTotalScriptFunction write SetAdditionalGrandTotalScriptFunction;
    property AdditionalGrandTotalExpression: String read GetAdditionalGrandTotalExpression write SetAdditionalGrandTotalExpression;
// Total Dispaly As
    property ShowTotalAs: TfcxShowTotalAs read FShowTotalAs write SetShowTotalAs;
    property DimValue[ALevel: TfcxSmallCount; AVisIndex: Integer]: variant read GetDimValue;
    property DimCaption[ALevel: TfcxSmallCount; AVisIndex: Integer]: TfcxString read GetDimCaption;
    property DimValueID[ALevel: TfcxSmallCount; AVisIndex: Integer]: integer read GetDimValueID;
  end;


// measure value
  PfcxMeasureValue = ^_fcxMeasureValue;
  _fcxMeasureValue = record
    Value: Variant; // Main Value
  end;

  PfcxMeasureValueWithCount = ^_fcxMeasureValueWithCount;
  _fcxMeasureValueWithCount = record
    MeasureValue: _fcxMeasureValue;
    Count: Integer;
  end;

  PfcxMeasureValueWithCountAndVar = ^_fcxMeasureValueWithCountAndVar;
  _fcxMeasureValueWithCountAndVar = record
    MeasureValueWithCount: _fcxMeasureValueWithCount;
    VarTemp: Variant;
  end;

  PfcxMeasureValueWithBitMask = ^_fcxMeasureValueWithBitMask;
  _fcxMeasureValueWithBitMask = record
    MeasureValue: _fcxMeasureValue;
    UCArr: PfcxArrRecPointerArray;
  end;

  PfcxMeasureValueWithPointer = ^_fcxMeasureValueWithPointer;
  _fcxMeasureValueWithPointer = record
    MeasureValue: _fcxMeasureValue;
    PValue: Pointer;
  end;

  PfcxMeasureValueWithValuesList = ^_fcxMeasureValueWithValuesList;
  _fcxMeasureValueWithValuesList = record
    MeasureValue: _fcxMeasureValue;
    VLArr: PfcxIntegerArray; // [0] - count of values
  end;

  PfcxMeasureValueWithVariant = ^_fcxMeasureValueWithVariant;
  _fcxMeasureValueWithVariant = record
    MeasureValue: _fcxMeasureValue;
    Value2: Variant;
  end;

// measure processor
  TfcxMeasureProcessor = class(TObject)
  private
    class function GetSize: Integer; virtual;
    class function GetItem(AMeasureValue: PfcxMeasureValue): Variant; virtual;
    class procedure SetItem(AMeasureValue: PfcxMeasureValue; const Value: Variant); virtual;
// create new value
    class function NewMeasureValue(AMeasureField: TfcxMeasureField): PfcxMeasureValue; virtual;
// destroy value
    class procedure DestroyMeasure(AMeasureValue: PfcxMeasureValue); virtual;
// calc value
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); virtual;
    class procedure FuncValues(AMeasureValue: PfcxMeasureValue; AValue: Variant; ZeroNull: Boolean; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField);
// calc value based on 2 fields
    class procedure Func2(const AMeasureValue: PfcxMeasureValue; AValue, AValue2: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); virtual;
    class procedure FuncValues2(AMeasureValue: PfcxMeasureValue; AValue, AValue2: Variant; ZeroNull: Boolean; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField);
  protected
  public
  end;

  TfcxFormulaDetailMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class function GetSize: Integer; override;
    class function NewMeasureValue(AMeasureField: TfcxMeasureField): PfcxMeasureValue; override;
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxCountMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxFormulaMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxSumMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxMinMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxMaxMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxFirstValueMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxLastValueMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxAvgMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class function GetSize: Integer; override;
    class function GetItem(AMeasureValue: PfcxMeasureValue): Variant; override;
    class function NewMeasureValue(AMeasureField: TfcxMeasureField): PfcxMeasureValue; override;
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxMulMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxVarianceMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class function GetSize: Integer; override;
    class function GetItem(AMeasureValue: PfcxMeasureValue): Variant; override;
    class procedure DestroyMeasure(AMeasureValue: PfcxMeasureValue); override;
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxVarianceSMeasureProcessor = class(TfcxVarianceMeasureProcessor)
  private
    class function GetItem(AMeasureValue: PfcxMeasureValue): Variant; override;
  end;

  TfcxStdDevMeasureProcessor = class(TfcxVarianceMeasureProcessor)
  private
    class function GetItem(AMeasureValue: PfcxMeasureValue): Variant; override;
  end;

  TfcxStdDevSMeasureProcessor = class(TfcxVarianceMeasureProcessor)
  private
    class function GetItem(AMeasureValue: PfcxMeasureValue): Variant; override;
  end;

  TfcxBitMaskMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class function GetSize: Integer; override;
    class function NewMeasureValue(AMeasureField: TfcxMeasureField): PfcxMeasureValue; override;
    class function Check(const AMeasureValue: PfcxMeasureValueWithBitMask; AIndexValue: Integer): Boolean;
  end;

  TfcxCntUniMeasureProcessor = class(TfcxBitMaskMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxListOfUniqueProcessor = class(TfcxBitMaskMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxDistinctSumMeasureProcessor = class(TfcxBitMaskMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxValuesListMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class function GetSize: Integer; override;
    class function NewMeasureValue(AMeasureField: TfcxMeasureField): PfcxMeasureValue; override;
    class procedure Add(const AMeasureValue: PfcxMeasureValueWithValuesList; AIndexValue: Integer);
  end;

  TfcxMedianMeasureProcessor = class(TfcxValuesListMeasureProcessor)
  private
    class procedure Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxWeightedMeanMeasureProcessor = class(TfcxMeasureProcessor)
  private
    class function GetSize: Integer; override;
    class function GetItem(AMeasureValue: PfcxMeasureValue): Variant; override;
    class procedure DestroyMeasure(AMeasureValue: PfcxMeasureValue); override;
    class procedure Func2(const AMeasureValue: PfcxMeasureValue; AValue, AValue2: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField); override;
  end;

  TfcxMeasureProcessorClass = class of TfcxMeasureProcessor;

  TfcxLoadedRange = packed record
    Low: Real;
    High: Real;
    TextColor: LongInt;
    FillColor: LongInt;
  end;

  TfcxCustomHighlight = class(TCollectionItem)
  private
    FApplyTo: TfcxHighlightApplyTo;
    FOnChange: TNotifyEvent;
    FUpdateCount: Integer;
  protected
    procedure AssignTo(Dest: TPersistent); override;
    procedure DoChange;
    procedure SetApplyTo(const Value: TfcxHighlightApplyTo);
  public
    constructor Create(Collection: TCollection); override;

    class function GetCaptionForEditor: String; virtual;
    function CustomDrawn: Boolean; virtual;
    function HideValue: Boolean; virtual;

    procedure BeginUpdate;
    procedure EndUpdate;

    function AsString: String; virtual;
    function AcceptValue(AValue: Variant): Boolean; virtual;

    procedure LoadFromXML(AItem: TfcxXMLItem); virtual;
    procedure SaveToXML(AItem: TfcxXMLItem); virtual;

    procedure LoadRange(ARange: TfcxLoadedRange); virtual;
    procedure LoadRangeXML(AItem: TfcxXMLItem); virtual;

    property ApplyTo: TfcxHighlightApplyTo read FApplyTo write SetApplyTo;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  TfcxCustomHighlightClass = class of TfcxCustomHighlight;

  TfcxCustomHighlights = class(TCollection)
  private
    FOnChange: TNotifyEvent;
    FMeasure: TfcxMeasureField;
    function GetItem(AIndex: Integer): TfcxCustomHighlight;
  protected
    procedure Update(Item: TCollectionItem); override;
  public
    constructor Create(AMeasure: TfcxMeasureField);
    destructor Destroy; override;

    function Add(AClass: TfcxCustomHighlightClass): TfcxCustomHighlight; reintroduce;
    function AddByName(AClassName: String): TfcxCustomHighlight;
    procedure Assign(Source: TPersistent); override;
    procedure SaveToXML(AItem: TfcxXMLItem);
    procedure LoadFromXML(AItem: TfcxXMLItem);
    procedure LoadRangeXML(AItem: TfcxXMLItem);

    function RequestValue(AValue: Pointer): Double; virtual;
    function RequestMinMax(AValue: Pointer; AMax: Boolean; ByCol: Boolean): Double; virtual;
    function RequestPercent(AValue: Pointer; APercent: Double; ByCol: Boolean): Double; virtual;
    function RequestPercentile(AValue: Pointer; APercentile: Double; ByCol: Boolean): Double; virtual;

    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property Items[AIndex: Integer]: TfcxCustomHighlight read GetItem; default;
  end;

// detail (count of records of cell and start index in source records list).
  TfcxCellDetailInfo = record
    Count: Integer;
    Start: Integer;
  end;
  PfcxCellDetailInfoArray = ^_fcxCellDetailInfoArray;
  _fcxCellDetailInfoArray = array[0..0] of TfcxCellDetailInfo;

// common properties for all measures of level in Second axis
  _fcxLevelInCellList = packed record
//used when Simple = False or nil when Simple = True
    IndexList: PfcxIntegerArray;
// measures cell count
    Count: Integer;
    Capacity: Integer;
    FreeIndex: Integer;
    Simple: Boolean;
    CellDetailInfo: PfcxCellDetailInfoArray;
  end;

  PfcxLevelInCellListArray = ^_fcxLevelInCellListArray;
  _fcxLevelInCellListArray = array[0..0] of _fcxLevelInCellList;

  PfcxArrLevelInCellListArray = ^_fcxArrLevelInCellListArray;
  _fcxArrLevelInCellListArray = array[-1..-1] of PfcxLevelInCellListArray;

  PfcxArr2LevelInCellListArray = ^_fcxArr2LevelInCellListArray;
  _fcxArr2LevelInCellListArray = array[-1..-1] of PfcxArrLevelInCellListArray;

// measure properties on levels intersection
  _fcxMeasurePropInLevels = record
    AgrFunc: TfcxAgrFunc; // function
    Distinct: Boolean;
    MeasureProcessorClass: TfcxMeasureProcessorClass;
    Expression: TfcxExpression;
    ScriptFunction: String;
    ScriptOrder: Integer;
    ScriptError: Boolean;
    CachedFunction: Pointer;
    Pass: Integer; // step when calc
    UseTotalToCalc: boolean; // function uses results from next level (base or second see UseTotalOfBaseAxis)
    UseTotalOfBaseAxis: boolean; // use BaseAxis to find next level for calc
    Variance: boolean; // Variance function
    FinalNeed: boolean; // function required Final
    RestrucNeed: Boolean;
    CreateAllCells: Boolean;
  end;
  PfcxMeasurePropInLevelsList = ^_fcxMeasurePropInLevelsList;
  _fcxMeasurePropInLevelsList = array[-1..-1] of _fcxMeasurePropInLevels;
  PfcxMeasurePropInLevelsListArray = ^_fcxMeasurePropInLevelsListArray;
  _fcxMeasurePropInLevelsListArray = array[-1..-1] of PfcxMeasurePropInLevelsList;

  PfcxTotalPositionsInLevelsList = ^_fcxTotalPositionsInLevelsList;
  _fcxTotalPositionsInLevelsList = array[-1..-1] of TfcxTotalPosition;

  { TfcxMeasureField }

  TfcxMeasureField = class(TfcxCommonFieldOfRegion)
  private
    FSliceField: TfcxSliceField;
    FSliceFieldDistinct: TfcxSliceField;
    FSliceFieldExtra: TfcxSliceField;
    FMeasuresContainer: TfcxMeasuresContainer;
    FUCArray: PfcxArrRecPointerArray;
    FUCArrayCount: Integer;
    FVLArray: PfcxPointerArray;
    FVLArrayCount: Integer;
    FMeasurePropInLevelsListArray: PfcxMeasurePropInLevelsListArray;
    FTotalPositionsInLevelsListBase, FTotalPositionsInLevelsListSecond: PfcxTotalPositionsInLevelsList;
    FPassCount: Integer;
    FVarianceExistsArray: PfcxBooleanArray;
    FVarianceExists: Boolean;
    FFinalNeed: Boolean;
    FRestrucNeed: Boolean;
    FRestrucAdditionalNeed: Boolean;

// main measure properties
    FAgrFunc: TfcxAgrFunc; // main function
    FCalcAllCells: Boolean; // for af_Formula
    FExpression: TfcxExpression;
    FFilterExpression: TfcxExpression;
    FTotalsExpression: TfcxExpression;
    FScriptFunction: String; // function name in Slice.Script // for af_Formula and af_FormulaDetail
    FScriptOrder: Integer;                                    // for af_Formula and af_FormulaDetail
    FFilterScriptFunction: String; // function name in Slice.Script
    CachedFilterFunction: Pointer;
    FFilterScriptError: Boolean;

    FTotalPosition: TfcxTotalPosition; // Position of Totals

    FUseDifferentAggForTotals: boolean; // False - use main function FAgrFunc for all totals, True - use function FAgrFuncForTotals for all totals
    FAgrFuncForTotals: TfcxAgrFunc; // function for all totals
    FCalcAllCellsForTotals: Boolean; // for af_Formula
    FScriptFunctionForTotals: String; // function name in Slice.Script // for af_Formula and af_FormulaDetail
    FScriptOrderForTotals: Integer;                                    // for af_Formula and af_FormulaDetail
    FCalcTotalsOnTotals: Boolean; // function uses results from next level as source
    FUseXAxisTotalsAsBase: Boolean; // used when FCalcTotalsOnTotals = True
// conflict resolving
    FConflictResolve: TfcxTotalsConflictResolve; // af_None when Conflict

// List of cells of levels
// PfcxArrPointerArray([base level index][Second level index])[Index in base level][Index in second level]
    FMeasureCells: PfcxArrMinusPointerArrayMinus;
// List of cells of levels for addon totals for each measure
// PfcxArrPointerArray(PfcxPointerArray([base level index][Second level index])[Index in base level][Index in second level]
    FAdditionalTotalsMeasureCellsBase, FAdditionalTotalsMeasureCellsSecond: PfcxArrMinusPointerArrayMinus;
    FHighlights: TfcxCustomHighlights;
//  PfcxArrMeasureCellHighlightsArray([base level index][Second level index])[Index in base level][Index in second level]
    FCellHighlights: PfcxArrMinusPointerArrayMinus;
    FPercentilesCount: Integer;
    FPercentiles: PfcxDoubleArray;
// Lists of Ranks
// PfcxArrIntegerArray([base level index][Second level index])[Index in base level][Index in second level]
    FBaseRanks, FSecondRanks: PfcxArrMinusPointerArrayMinus;
{ for future. do not delete
    FAgrFuncForDimCount: TfcxSmallCount; // count items in FAgrFuncForDimensionArray
    FAgrFuncForDimensionArray: PfcxAgrFuncForDimensionArray;
}
    FVisible: Boolean;
    FDisplayAs: TfcxDisplayAs;
    FHeight: SmallInt;
    FWidth: SmallInt;
    FDistinct: Boolean;
    FDistinctForTotals: Boolean;
    FChangeNullToZero: Boolean;
    procedure CreateMeasurePropArrayS1;
    procedure CreateMeasurePropArrayS2;
    procedure ClearMeasurePropArray;
    procedure ClearCellHighlights;
    procedure ClearRanks;
    procedure AddVLArr(AMeasureValue: PfcxMeasureValueWithValuesList);
    procedure CreateTotalPositionArray;
    procedure ClearTotalPositionArray;
    function GetExpression: String;
    function GetFilterExpression: String;
    function GetTotalsExpression: String;
    procedure SetExpression(const AValue: String);
    procedure SetFilterExpression(const AValue: String);
    procedure SetTotalPosition(const Value: TfcxTotalPosition);
    procedure SetCaptionWidth(const Value: SmallInt); override;
    function GetTotalPositionByLevel(AXAxis: Boolean; ALevelIndex: TfcxSmallCount): TfcxTotalPosition;
    procedure SetTotalsExpression(const AValue: String);
    procedure SetVisible(const Value: Boolean);
    procedure SetDisplayAs(const Value: TfcxDisplayAs);
    procedure SetAgrFunc(const Value: TfcxAgrFunc);
    procedure SetSliceField(const Value: TfcxSliceField);
    procedure SetSliceFieldDistinct(const Value: TfcxSliceField);
    procedure SetSliceFieldExtra(const Value: TfcxSliceField);
    procedure SetHighlights(const Value: TfcxCustomHighlights);
    procedure SetFilterScriptFunction(const Value: String);
    procedure SetScriptFunction(const Value: String);
    procedure SetScriptOrder(const Value: Integer);
    procedure SetAgrFuncForTotals(const Value: TfcxAgrFunc);
    procedure SetUseDifferentAggForTotals(const Value: Boolean);
    procedure SetCalcTotalsOnTotals(const Value: Boolean);
    procedure SetUseXAxisTotalsAsBase(const Value: Boolean);
    procedure SetConflictResolve(const Value: TfcxTotalsConflictResolve);
    function GetDisplayFormat: TfcxFormat; override;
    procedure SetDisplayFormat(const Value: TfcxFormat); override;
    procedure SetCalcAllCells(const Value: Boolean);
    function GetCalculated: Boolean;
    function GetCalculatedTotals: Boolean;
    function CreateAllCells: Boolean;
    function CreateAllCellsForTotals: Boolean;
    procedure SetCalcAllCellsForTotals(const Value: Boolean);
    procedure SetScriptFunctionForTotals(const Value: String);
    procedure SetScriptOrderForTotals(const Value: Integer);
    procedure SetDistinct(const Value: Boolean);
    procedure SetDistinctForTotals(const Value: Boolean);
    procedure SetChangeNullToZero(const Value: Boolean);
    function HasDisplayFormat: Boolean; override;
  protected
    procedure DoHighlightsChange(Sender: TObject);
    function GetExpressions: TfcxExpressions; override;
    function GetWidth: SmallInt; override;
    procedure SetWidth(const Value: SmallInt); override;
    function GetHeight: SmallInt; override;
    procedure SetHeight(const Value: SmallInt); override;
  public
    constructor Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion); overload; override;
    constructor Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion; AAgrFunc: TfcxAgrFunc; AName, ACaption: TfcxString); overload;
    constructor Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion; AAgrFunc: TfcxAgrFunc; ASliceField: TfcxSliceField; AName, ACaption: TfcxString; ADistinct: Boolean = False); overload;
    constructor Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion; AAgrFunc: TfcxAgrFunc; ASliceField, ASliceFieldDistinct: TfcxSliceField; AName, ACaption: TfcxString; ADistinct: Boolean = False); overload;
    constructor Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion; AAgrFunc: TfcxAgrFunc; ASliceField, ASliceFieldDistinct, ASliceFieldExtra: TfcxSliceField; AName, ACaption: TfcxString; ADistinct: Boolean = False); overload;
    destructor Destroy; override;
    procedure SaveToXML(AItem: TfcxXMLItem); override;
    function LoadFromXML(AItem: TfcxXMLItem): boolean; override;
    property AgrFunc: TfcxAgrFunc read FAgrFunc write SetAgrFunc;
    property CalcAllCells: Boolean read FCalcAllCells write SetCalcAllCells;
    property Calculated: Boolean read GetCalculated;
    property CalculatedTotals: Boolean read GetCalculatedTotals;
    property Visible: Boolean read FVisible write SetVisible;
    property DisplayAs: TfcxDisplayAs read FDisplayAs write SetDisplayAs;
    property Highlights: TfcxCustomHighlights read FHighlights write SetHighlights;
    property ScriptFunction: String read FScriptFunction write SetScriptFunction;
    property Expression: String read GetExpression write SetExpression;
    property ScriptOrder: Integer read FScriptOrder write SetScriptOrder;
    property FilterScriptFunction: String read FFilterScriptFunction write SetFilterScriptFunction;
    property FilterExpression: String read GetFilterExpression write SetFilterExpression;
// Base field
    property SliceField: TfcxSliceField read FSliceField write SetSliceField;
// Field for functions with unique
    property SliceFieldDistinct: TfcxSliceField read FSliceFieldDistinct write SetSliceFieldDistinct;
// Field for advanced functions
    property SliceFieldExtra: TfcxSliceField read FSliceFieldExtra write SetSliceFieldExtra;
    property Distinct: Boolean read FDistinct write SetDistinct;
    property DistinctForTotals: Boolean read FDistinctForTotals write SetDistinctForTotals;
    property MeasuresContainer: TfcxMeasuresContainer read FMeasuresContainer;
    // totals
    property UseDifferentAggForTotals: Boolean read FUseDifferentAggForTotals write SetUseDifferentAggForTotals;
    property AgrFuncForTotals: TfcxAgrFunc read FAgrFuncForTotals write SetAgrFuncForTotals;
    property CalcAllCellsForTotals: Boolean read FCalcAllCellsForTotals write SetCalcAllCellsForTotals;
    property TotalsExpression: String read GetTotalsExpression write SetTotalsExpression;
    property ScriptFunctionForTotals: String read FScriptFunctionForTotals write SetScriptFunctionForTotals;
    property ScriptOrderForTotals: Integer read FScriptOrderForTotals write SetScriptOrderForTotals;
    property CalcTotalsOnTotals: Boolean read FCalcTotalsOnTotals write SetCalcTotalsOnTotals;
    property UseXAxisTotalsAsBase: Boolean read FUseXAxisTotalsAsBase write SetUseXAxisTotalsAsBase;
    property ConflictResolve: TfcxTotalsConflictResolve read FConflictResolve write SetConflictResolve;
    property DefaultTotalPosition: TfcxTotalPosition read FTotalPosition write SetTotalPosition;
    property TotalPositionByLevel[AXAxis: Boolean; ALevelIndex: TfcxSmallCount]: TfcxTotalPosition read GetTotalPositionByLevel;
    property ChangeNullToZero: Boolean read FChangeNullToZero write SetChangeNullToZero;
  end;

// List of Fields in Measures
  TfcxMeasureFields = class(TfcxCommonFieldsOfRegion)
  private
    FVisibleCount: TfcxSmallCount;
    function GetItem(Index: Integer): TfcxMeasureField;
  protected
  public
    property VisibleCount: TfcxSmallCount read FVisibleCount;
    property Items[Index: Integer]: TfcxMeasureField read GetItem; default;
  end;

  PfcxMeasureCell = ^TfcxMeasureCell;
  TfcxMeasureCell = record
    Value: Variant;
    StrValue: TfcxString;
    BaseIndex: Integer;
    SecondIndex: Integer;
    MeasureIndex: TfcxSmallCount;
    Alignment: TAlignment;
    IsTotal: Boolean;
    IsGrandTotal: Boolean;
    Row, Col: Integer;
{$IFDEF TRIAL}
    IsTrial: Boolean;
{$ENDIF}
  end;

  TfcxMeasureCellHighlights = record
    MinBase, MaxBase, MinSecond, MaxSecond: Double;
    PercentilesBase, PercentilesSecond: PfcxDoubleArray;
  end;
  PfcxMeasureCellHighlightsArray = ^_fcxMeasureCellHighlightsArray;
  _fcxMeasureCellHighlightsArray = array[0..0] of TfcxMeasureCellHighlights;
  PfcxArrayMeasureCellHighlightsArray = ^_fcxArrayMeasureCellHighlightsArray;
  _fcxArrayMeasureCellHighlightsArray = array[0..0] of PfcxMeasureCellHighlightsArray;

  TfcxMeasureCellDetailRecords = record
    Count: Integer;
    Records: PfcxIntegerArray;
  end;
  PfcxMeasureCellDetailRecordsArray = ^_fcxMeasureCellDetailRecordsArray;
  _fcxMeasureCellDetailRecordsArray = array[0..0] of TfcxMeasureCellDetailRecords;
  PfcxArrayMeasureCellDetailRecordsArray = ^_fcxArrayMeasureCellDetailRecordsArray;
  _fcxArrayMeasureCellDetailRecordsArray = array[0..0] of PfcxMeasureCellDetailRecordsArray;

// Measures Container

  { TfcxMeasuresContainer }

  TfcxMeasuresContainer = class(TfcxSliceContainer)
  private
    FIsCleared: boolean;
    FFinalized: boolean; // True after Final procedure
//    FSlice: TfcxSlice;
// common properties of second axis levels for all measures
// [base level index][Second level index])[Index in base level]
    FLevelsProp: PfcxArr2LevelInCellListArray;
    FMeasureCellDetailRecordsTemp: PfcxArrayMeasureCellDetailRecordsArray;
    FBaseDetailRecords, FSecondDetailRecords: PfcxMeasureCellDetailRecordsArray;

    FCount: Integer; // BaseCount
    FSecondCount: Integer;
    FBaseContainer, FSecondContainer: TfcxAxisContainer;
    FBaseAxisIsX: boolean;
// AxisContainer with "Measures" field or Nil if "Measures" field not in Axises
    FContainer: TfcxFieldsContainer;
// Position "Measures" field in FContainer (-1: in the end of the fields list)
    FPosition: TfcxSmallCount;
// Measure Fields List
    FMeasureFields: TfcxMeasureFields;
    FSaveCountMeasures, FSaveCountBaseLevels, FSaveCountSecondLevels: TfcxSmallCount;
    FSaveGoodRecCount: integer;
    FSaveCountAdditionalTotalsBaseLevels, FSaveCountAdditionalTotalsSecondLevels: PfcxSmallCountArrayMinus;
    FSaveAdditionalTotalsBaseLevels, FSaveAdditionalTotalsSecondLevels: PfcxArrAgrFuncArrayMinus;
    FWidth, FHeight: SmallInt;
    FCaptionWidth: SmallInt;
    FCaption: TfcxString;
    FCollapsed: Boolean;

    procedure Clear;
    procedure ClearMeasure(AMeasureIndex: Integer);
    procedure MeasureDeleted(AMeasureIndex: TfcxSmallCount); // Measure deleted
    procedure MeasureMoved(AFromIndex, AToIndex: TfcxSmallCount); // Measure moved
    function GetCount: TfcxSmallCount;
// FMeasureCells functions
// return index of Cell with AIndex. Cell is created if need.
    function FindByIndex(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; var AResIndex: Integer): Boolean;

// return MeasureValue with AIndex (index in last level of axis) and AMeasureIndex.
    function GetCells(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex: TfcxSmallCount): PfcxMeasureValue;
    function GetCellRanks(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex: TfcxSmallCount; AXAxis: Boolean): Integer;

    procedure ReCreateTotalPositions;
// calc measures
    procedure CreateMatrix;
    procedure CalcRank(AMeasureIndex: TfcxSmallCount);
// calc Highlights
    procedure CalcMaxMins(AMeasureIndex: TfcxSmallCount);
    procedure CalcPercentile(AMeasureIndex: TfcxSmallCount; APercentile: Double);

    function GetCaption: TfcxString;
    function GetCaptionWidth: SmallInt;
    procedure SetCaption(const Value: TfcxString);
    procedure SetCaptionWidth(const Value: SmallInt);
    function GetMeasure(AIndex: TfcxSmallCount): TfcxMeasureField;
    function GetVisibleCount: TfcxSmallCount;
    function GetVisibleMeasures(AVisIndex: Integer): TfcxMeasureField;
    function GetDetailRecordsCount(const ABaseLevel, ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel, AIndexInSecondLevel: Integer): integer;
    function GetDetailRecords(const ABaseLevel, ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel, AIndexInSecondLevel, ARecIndex: Integer): integer;
    procedure SetContainer(const AContainer: TfcxFieldsContainer);
    function GetBaseAdditionalTotalCells(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): PfcxMeasureValue;
    function GetSecondAdditionalTotalCells(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): PfcxMeasureValue;
    procedure SetHeight(const Value: SmallInt);
    procedure SetWidth(const Value: SmallInt);
    procedure SetCollapsed(const Value: Boolean);
    function GetVisibleIndex(AIndex: Integer): integer;
    function AddMeasureInternal(AField: TfcxMeasureField; ADoStopChange: Boolean): Integer;
    procedure InsertMeasureInternal(AField: TfcxMeasureField; AIndex: TfcxSmallCount; ADoStopChange: Boolean);
    procedure DeleteMeasureInternal(AMeasureIndex: TfcxSmallCount; ADoStopChange: Boolean = False);
  public
    constructor Create(ASlice: TfcxSlice; ARegion: TfcxRegionOfField); override;
    destructor Destroy; override;
    function GetAsPlainText(AStartRow, AStopRow, AStartCol, AStopCol: Integer): TfcxString;

    procedure SaveToXML(AItem: TfcxXMLItem); override;
    procedure LoadFromXML(AItem: TfcxXMLItem); override;
// measures count
    property Count: TfcxSmallCount read GetCount;
// visible measures count
    property VisibleCount: TfcxSmallCount read GetVisibleCount;
    property Measures[AIndex: TfcxSmallCount]: TfcxMeasureField read GetMeasure;
    property VisibleMeasures[AVisIndex: Integer]: TfcxMeasureField read GetVisibleMeasures;
    property VisibleIndex[AIndex: Integer]: integer read GetVisibleIndex;

    property Cells[const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex: TfcxSmallCount]: PfcxMeasureValue read GetCells;
    property BaseAdditionalTotalCells[const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount]: PfcxMeasureValue read GetBaseAdditionalTotalCells;
    property SecondAdditionalTotalCells[const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount]: PfcxMeasureValue read GetSecondAdditionalTotalCells;
    property CellRanks[const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex: TfcxSmallCount; AXAxis: Boolean]: Integer read GetCellRanks;
    function AddMeasure(ASliceField: TfcxSliceField; AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc): Integer; overload;
    function AddMeasure(ASliceField, ASliceFieldDistinct, ASliceFieldExtra: TfcxSliceField; AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc): Integer; overload;
    function AddCalcMeasure(AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc; AScriptFunctionName: String; AScriptFunctionCode: TfcxString): Integer; overload;
    function AddCalcMeasure(AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc; AScriptFunctionName: String; AScriptFunctionVarSection, AScriptFunctionCode: TfcxString): Integer; overload;
    function AddMeasure(AField: TfcxMeasureField): Integer; overload;
    procedure InsertMeasure(ASliceField: TfcxSliceField; AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc; AIndex: TfcxSmallCount); overload;
    procedure InsertCalcMeasure(AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc; AScriptFunctionName: String; AScriptFunctionCode: TfcxString; AIndex: TfcxSmallCount); overload;
    procedure InsertCalcMeasure(AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc; AScriptFunctionName: String; AScriptFunctionVarSection, AScriptFunctionCode: TfcxString; AIndex: TfcxSmallCount); overload;
    procedure InsertMeasure(AField: TfcxMeasureField; AIndex: TfcxSmallCount); overload;
    function ChangeCalcMeasureScript(AName: TfcxString; AScriptFunctionName: String; AScriptFunctionCode: TfcxString): Integer; overload;
    function ChangeCalcMeasureScript(AName: TfcxString; AScriptFunctionName: String; AScriptFunctionVarSection, AScriptFunctionCode: TfcxString): Integer; overload;
    procedure AddCalculationScript(ACalcScriptType: TfcxCalcScriptType; AScriptFunctionName: String; AScriptFunctionVarSection, AScriptFunctionCode: TfcxString);
// Delete one measure
    procedure DeleteMeasure(AMeasureIndex: TfcxSmallCount);
// Delete all measures
    procedure DeleteMeasures;
    procedure SetPosition(APosition: TfcxSmallCount);
    function MoveMeasure(AFromIndex, AToIndex: TfcxSmallCount): boolean;
    procedure SetAllVisible;
    procedure InverseVisible;
    procedure SetNoneVisible;
    function InAxis: Boolean;
    procedure Recalculate;

    property Container: TfcxFieldsContainer read FContainer{ write SetContainer};
    property Position: TfcxSmallCount read FPosition;
    property Caption: TfcxString read GetCaption write SetCaption;
    property CaptionWidth: SmallInt read GetCaptionWidth write SetCaptionWidth;
    property MeasureFields: TfcxMeasureFields read FMeasureFields;
    property DetailRecordsCount[const ABaseLevel, ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel, AIndexInSecondLevel: Integer]: integer read GetDetailRecordsCount;
    property DetailRecords[const ABaseLevel, ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel, AIndexInSecondLevel, ARecIndex: Integer]: integer read GetDetailRecords;
    property Width: SmallInt read FWidth write SetWidth;
    property Height: SmallInt read FHeight write SetHeight;
// Level Collapsed (collapsed all nodes in level)
    property Collapsed: Boolean read FCollapsed write SetCollapsed;
    property BaseAxisIsX: boolean read FBaseAxisIsX;
  end;

  TfcxDetailOrderedRecordSetProvider = class(TfcxOrderedRecordSetProvider)
  private
    FSlice: TfcxSlice;
    FPosition: TPoint;
    FBaseLevel, FSecondLevel: TfcxSmallCount;
    FIndexInBaseLevel, FIndexInSecondLevel: Integer;
    function ValidPosition: Boolean;
  protected
    function GetRowIndexDefault(ARow: Integer): Integer; override;
    procedure FillRecordIndex; override;
    function GetRecordCount: Integer; override;
    function CompareUVIndexes(AIndex1, AIndex2: integer): integer; override;
    function GetText(ACol, ARowIndex: Integer): String; override;
  public
    constructor Create(ASlice: TfcxSlice); reintroduce; virtual;
    procedure SetSlice(ASlice: TfcxSlice);
    procedure SetPosition(APosition: TPoint);
    function ValidProperties: Boolean; override;
  end;

  TfcxOnConvertScript = function(Sender: TObject; const AFunctionName, AScript: String): String of Object;

  {$IFDEF DELPHI_16UP}
  [ComponentPlatformsAttribute(pidWin32 or pidWin64 or pidOSX32)]
  {$ENDIF}

  { TfcxSlice }

  TfcxSlice = class(TfcxAbstractSlice)
  private
    FCube: TfcxCube;
    FFilterManager: TfcxFilterManager;
    FChartsManager: TfcxChartsManager;
    FSliceFields: TfcxSliceFields;
    FLoading: Boolean;
    FSliceTimeStat: TfcxSliceTimeStat;
    FSliceTimeStatStart: TfcxSliceTimeStat;

    FXAxisContainer,
    FYAxisContainer: TfcxAxisContainer;
    FMeasuresContainer: TfcxMeasuresContainer;
    FPageContainer: TfcxPageContainer;

// Set of changes in slice
    FChanges: TfcxChangesInSlice;
    FFieldsOrder: TfcxFieldsOrder;
    FOnFieldsListSortCompare: TfcxFieldsListSortCompare;
    FDefaultColWidth: Integer;
    FDefaultRowHeight: Integer;
    FPopupWidthDefault: Integer;
    FPopupShowAvailableDefault: Boolean;
    FAutoUVFilter: Boolean;
// Selected (for sort by total and selected)
    FSelectedMeasure,
    FSelectedColAdditionalTotal,
    FSelectedRowAdditionalTotal,
    FSelectedCol,
    FSelectedRow: Integer;
    FSortSelectionLoaded: boolean;

    FHideColZeros: Boolean;
    FHideRowZeros: Boolean;

    // script
    FInterpreter: IfcInterpreter;
    FMeasures: TPersistent;
    FDimensions: TPersistent;
    FCustomObject: TPersistent;
    FHideTotalForSingle: Boolean;
    FTopNs: TfcxTopNs;
    FSettings: TfcxSliceDefaultSettings;
    FOnScriptError: TfcxOnScriptError;
    FOnConvertScript: TfcxOnConvertScript;
    FClearUnattachedFieldsFilter: Boolean;
    FOnInterpreterCreated: TfcxInterpreterCreated;

    FOnBeforeRemoveSliceField: TfcxSliceFieldRegionChange;
    FOnBeforeAddSliceFieldToRegion: TfcxSliceFieldRegionChange;
    FOnAfterAddSliceFieldToRegion: TfcxSliceFieldRegionChanged;
    FSyncronizeYFieldsWidth: Boolean;

    procedure AddScriptExpressions;
    procedure AddScriptVariables;
    function GetSelectedCol: integer;
    function GetSelectedRow: integer;

    procedure SetFilterManager(const Value: TfcxFilterManager);
    procedure SetCube(const Value: TfcxCube);
    procedure SetFieldsOrder(const Value: TfcxFieldsOrder);

    procedure CubeChanged(Sender: TfcxCube; AChangeAlert: TfcxChangeAlert);
    procedure FilterManagerChanged(Sender: TfcxFilterManager; AChangeAlert: TfcxChangeAlert);
    procedure StopChange(AChanges: TfcxChangesInSlice = []);

    function GetFieldsOfRegion(ARegionOfField: TfcxRegionOfField): TfcxCommonFieldsOfRegion;
    function GetContainerOfRegion(
      ARegionOfField: TfcxRegionOfField): TfcxFieldsContainer;

    function GetColCount: Integer;
    function GetRowCount: Integer;
    procedure SetDefaultColWidth(const Value: Integer);
    procedure SetDefaultRowHeight(const Value: Integer);
    function GetColWidth(AColIndex: integer): Smallint;
    function GetRowHeight(ARowIndex: integer): Smallint;
    procedure SetColWidth(AColIndex: integer; const Value: Smallint);
    procedure SetRowHeight(ARowIndex: integer; const Value: Smallint);
    function GetSliceField(AIndex: integer): TfcxSliceField;
    procedure SetAutoUVFilter(const Value: Boolean);
    function GetSliceFieldCount: Integer;
    function LoadFromOldStream(Version: Word; AStream: TStream): Boolean;
    procedure SetHideColZeros(const Value: Boolean);
    procedure SetHideRowZeros(const Value: Boolean);
    procedure SetHideTotalForSingle(const Value: Boolean);
    procedure ChartDataWithoutMeasure(AChartParam: TfcxChartParam; ADataProc: TfcxChartDataProc);
    procedure ChartDataWithMeasure(AChartParam: TfcxChartParam; ADataProc: TfcxChartDataProc);
    function GetActive: Boolean;
    function GetSliceFieldByName(AName: String): TfcxSliceField;
    procedure SetSettings(const Value: TfcxSliceDefaultSettings);
    procedure SetClearUnattachedFieldsFilter(const Value: Boolean);
    function DoConvertScript(const AFunctionName, AScript: String): String;
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure CreateSlice;
    procedure DestroySlice(ASaveFields: boolean; ASaveLevels: boolean = False);
    function GetScript: TStrings; override;
    procedure SetScript(const Value: TStrings); override;
    function GetScriptLanguage: String; override;
    procedure SetScriptLanguage(const Value: String); override;
    function GetCommonScriptLanguage: String; override;
    procedure SetCommonScriptLanguage(const Value: String); override;
    function GetCommonScript: TStrings; override;
    property PopupWidthDefault: Integer read FPopupWidthDefault write FPopupWidthDefault default 120;
    property PopupShowAvailableDefault: Boolean read FPopupShowAvailableDefault write FPopupShowAvailableDefault default False;
    property SelectedCol: integer read GetSelectedCol;
    property SelectedRow: integer read GetSelectedRow;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Loaded; override;
    procedure SetDefaults; override;
    procedure ApplyDefaultSettings;
    procedure Clear;
    function ResetUnattachedFieldsFilter: boolean;
    procedure InitInterpreter; override;
    function HasMeasure(const MeasureName: String): Boolean; override;
    function HasDimension(const DimensionName: String): Boolean; override;
    function GetCommonScriptChangedTick: Cardinal; override;

    function LoadFromStream(ASliceStream: TStream): Boolean; override;
    procedure SaveToStream(ASliceStream: TStream; AStoreItems: TfcxItemsForStoreWithSlice = []); override;

    function LoadFromFile(AFileName: String): Boolean;
    procedure SaveToFile(AFileName: String; AStoreItems: TfcxItemsForStoreWithSlice = []);

    function LoadFromXML(XMLDoc: TfcxXMLDocument): Boolean;
    procedure SaveToXML(XMLDoc: TfcxXMLDocument; AStoreItems: TfcxItemsForStoreWithSlice = []);

    function ExecuteAction(Action : TBasicAction) : Boolean; override;
    procedure BeginUpdate;
    procedure EndUpdate;

    procedure UpdateDisplayLabel(ACubeField: TObject; const Value: TfcxString); override;
    procedure ResetDisplayLabelOfFields; override;

    procedure InternalSetSelected(ASelectedMeasure, ASelectedCol, ASelectedRow, ASelectedColAdditionalTotal, ASelectedRowAdditionalTotal: Integer); override;
    procedure InternalSetSelectedVis(ASelectedMeasure, ASelectedColVis, ASelectedRowVis: Integer);

    procedure SetTotalPosition(const Value: TfcxTotalPosition);
//    function TESTGetRealIndexOfRecord(AIndex: Integer; AX: boolean) : Integer;
{
    function GetTESTDisplayValueWithTotal(AXIndex, AYIndex: Integer;
      AMeasureIndex: TfcxSmallCount; var ATotal: boolean): TfcxString;
}

//    property TESTDisplayValue[AXIndex, AYIndex: Integer; AMeasureIndex: TfcxSmallCount]: TfcxString read GetTESTDisplayValue;

{
    function AddSliceFieldToRegion(ASliceField: TfcxSliceField; AName: String;
      ACaption: String; ARegion: TfcxRegionOfField; AAgrFunc: TfcxAgrFunc = af_None): Integer;
}
    function CalcFuncForSelectedArea(ASelAgrFunc: TfcxSelAgrFunc; ASelectedArea: TfcxGridSelection): Variant;
    function ColRowToDataCellPos(Const ACol, ARow: Integer; var ABaseLevel, ASecondLevel: TfcxSmallCount;
      var AIndexInBaseLevel, AIndexInSecondLevel: Integer; var AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount): Boolean; overload;
    function ColRowToDataCellPos(Const ACol, ARow: Integer; var ABaseLevel, ASecondLevel: TfcxSmallCount;
      var AIndexInBaseLevel, AIndexInSecondLevel: Integer): Boolean; overload;
// find SliceField in regions
    function FindSliceFieldInRegions(ASliceField: TfcxSliceField; var ARegion: TfcxRegionOfField): integer;
    procedure RemoveSliceFieldFromContainers(ASliceField: TfcxSliceField);

    procedure Transpose;

// value of axis cell Level/IndexInLevel
    function GetXAxisDimValueLI(const ALevel: TfcxSmallCount; const AIndexInLevel: Integer;
      const ADimLevelIndex: TfcxSmallCount): Variant;
    function GetYAxisDimValueLI(const ALevel: TfcxSmallCount; const AIndexInLevel: Integer;
      const ADimLevelIndex: TfcxSmallCount): Variant;
// display value of axis cell Level/IndexInLevel
    function GetXAxisDimCaptionLI(const ALevel: TfcxSmallCount; const AIndexInLevel: Integer;
      const ADimLevelIndex: TfcxSmallCount): TfcxString;
    function GetYAxisDimCaptionLI(const ALevel: TfcxSmallCount; const AIndexInLevel: Integer;
      const ADimLevelIndex: TfcxSmallCount): TfcxString;

// display value of measure cell
    procedure GetMeasureCell(ACol, ARow: Integer; out Result: TfcxMeasureCell);
// value of measure cell Base/Second
    function GetMeasureValueBS(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount): Variant;
// value of measure cell Base/Second with direct second index
    function GetMeasureValueBSDirect(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, ADirectIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount): Variant;
// value of measure cell X/Y
    function GetMeasureValueXY(const AXLevel, AYLevel: TfcxSmallCount;
      const AIndexInXLevel, AIndexInYLevel: Integer;
      const AMeasureIndex, AXAdditionalTotalIndex, AYAdditionalTotalIndex: TfcxSmallCount): Variant;
// value of measure cell Col/Row
    function GetMeasureValue(ACol, ARow: Integer): Variant;
// display value of measure cell Base/Second with direct second index
    function GetMeasureValueCaptionBSDirect(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, ADirectIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount): TfcxString;
// display value of measure cell Base/Second
    function GetMeasureValueCaptionBS(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount): TfcxString;
// display value of measure cell X/Y
    function GetMeasureValueCaptionXY(const AXLevel, AYLevel: TfcxSmallCount;
      const AIndexInXLevel, AIndexInYLevel: Integer;
      const AMeasureIndex, AXAdditionalTotalIndex, AYAdditionalTotalIndex: TfcxSmallCount): TfcxString;
// display value of measure cell Col/Row
    function GetMeasureValueCaption(ACol, ARow: Integer): TfcxString;
// value of measure cell Base/Second with offset on level
    function GetMeasureValueBSWOffsetThrough(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
      const AOffsetBase, AOffsetSecond: Integer): Variant;
// value of measure cell Base/Second with offset on level
    function GetMeasureValueBSWOffset(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
      const AOffsetBaseLevel, AOffsetSecondLevel: TfcxSmallCount;
      const AOffsetBase, AOffsetSecond: Integer): Variant;
// Total value of measure on dimesion
    function GetMeasureTotalValueForDimsBS(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
      ADimNames: String): Variant;
// Total value of measure on dimesion with offset
    function GetMeasureTotalValueForDimsBSOffset(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
      ADimNames: String;
      const AOffsetBaseLevel, AOffsetSecondLevel: TfcxSmallCount;
      const AOffsetBase, AOffsetSecond: Integer; const AThrough: Boolean = False): Variant;
// measure value Base/Second with offset to dimesion value in level
    function GetMeasureValueBSWOffsetOnValue(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
      const AOffsetBaseLevel, AOffsetSecondLevel: TfcxSmallCount;
      const AOffsetValueBase, AOffsetValueSecond: Variant): Variant;
// measure value Base/Second with coordinates
    function GetMeasureValueBSOnWayValues(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      const AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
      const AUseWayOnBaseLevel, AUseWayOnSecondLevel: Boolean;
      const AWayValuesBase, AWayValuesSecond: Array of Variant): Variant;
// detail records
    function GetDetailRecordsCount(ACol, ARow: Integer): integer;
    function GetDetailRecords(ACol, ARow, ARecIndex: Integer): integer;
    function DetailOrderedRecordSetProvider(APosition: TPoint): TfcxDetailOrderedRecordSetProvider;
// set size for all columns
    procedure SetColsWidth(const Value: Smallint);
// set size for all rows
    procedure SetRowsHeight(const Value: Smallint);
    function CubeIsEqual(ACube: TObject): boolean; override;
    procedure ClearFilterManager; override;
    function GetAsPlainText(AStartRow, AStopRow, AStartCol, AStopCol: Integer; ACloneHeaders: Boolean): TfcxString;

    // getting data for Chart
    procedure GetChartData(AChartParam: TfcxChartParam; ADataProc: TfcxChartDataProc);
    function HaveLayout: Boolean;
    property Loading: Boolean read FLoading;
    property RowCount: Integer read GetRowCount;
    property ColCount: Integer read GetColCount;

    property XAxisContainer: TfcxAxisContainer read FXAxisContainer;
    property YAxisContainer: TfcxAxisContainer read FYAxisContainer;
    property PageContainer: TfcxPageContainer read FPageContainer;
    property MeasuresContainer: TfcxMeasuresContainer read FMeasuresContainer;
    property TopNs: TfcxTopNs read FTopNs;

    // height and width of DataZone
    property DefaultColWidth: Integer read FDefaultColWidth write SetDefaultColWidth;
    property DefaultRowHeight: Integer read FDefaultRowHeight write SetDefaultRowHeight;
    property RowHeight[ARowIndex: integer]: Smallint read GetRowHeight write SetRowHeight;
    property ColWidth[AColIndex: integer]: Smallint read GetColWidth write SetColWidth;

// fields of region
    property FieldsOfRegion[ARegionOfField: TfcxRegionOfField]: TfcxCommonFieldsOfRegion read GetFieldsOfRegion;
// container of region
    property ContainerOfRegion[ARegionOfField: TfcxRegionOfField]: TfcxFieldsContainer read GetContainerOfRegion;
// flice fields
    property SliceFields: TfcxSliceFields read FSliceFields;
    property SliceField[AIndex: integer]: TfcxSliceField read GetSliceField;
    property SliceFieldByName[AName: String]: TfcxSliceField read GetSliceFieldByName;
    property SliceFieldCount: Integer read GetSliceFieldCount;
// Hide zero on Row/Col
    property HideRowZeros: Boolean read FHideRowZeros write SetHideRowZeros;
    property HideColZeros: Boolean read FHideColZeros write SetHideColZeros;
    property Interpreter: IfcInterpreter read FInterpreter;
    property Active: Boolean read GetActive;
    property TimeStat: TfcxSliceTimeStat read FSliceTimeStat;
    property ChartsManager: TfcxChartsManager read FChartsManager;
  published
    property Cube: TfcxCube read FCube write SetCube;
    property FieldsOrder: TfcxFieldsOrder read FFieldsOrder write SetFieldsOrder;
    property FilterManager: TfcxFilterManager read FFilterManager write SetFilterManager;
    property ClearUnattachedFieldsFilter: Boolean read FClearUnattachedFieldsFilter write SetClearUnattachedFieldsFilter default False;
    property AutoUVFilter: Boolean read FAutoUVFilter write SetAutoUVFilter default True;
// Total not visible if only one value
    property HideTotalForSingleValue: Boolean read FHideTotalForSingle write SetHideTotalForSingle default True;
// Default Settings
    property Settings: TfcxSliceDefaultSettings read FSettings write SetSettings;
    property SyncronizeYFieldsWidth: Boolean read FSyncronizeYFieldsWidth write FSyncronizeYFieldsWidth default False;
    // events
    property OnFieldsListSortCompare: TfcxFieldsListSortCompare read FOnFieldsListSortCompare write FOnFieldsListSortCompare;
    property OnScriptError: TfcxOnScriptError read FOnScriptError write FOnScriptError;
    property OnConvertScript: TfcxOnConvertScript read FOnConvertScript write FOnConvertScript;
    property OnInterpreterCreated: TfcxInterpreterCreated read FOnInterpreterCreated write FOnInterpreterCreated;
    property OnBeforeRemoveSliceFieldFromRegion: TfcxSliceFieldRegionChange read FOnBeforeRemoveSliceField write FOnBeforeRemoveSliceField;
    property OnBeforeAddSliceFieldToRegion: TfcxSliceFieldRegionChange read FOnBeforeAddSliceFieldToRegion write FOnBeforeAddSliceFieldToRegion;
    property OnAfterAddSliceFieldToRegion: TfcxSliceFieldRegionChanged read FOnAfterAddSliceFieldToRegion write FOnAfterAddSliceFieldToRegion;
  end;

  // Helpers
  TCustomAxisTextBuilder = class
  protected
    FAxisContainer: TfcxAxisContainer;
    FStartCell, FStopCell: Integer;
    FPlainText: TfcxStringList;
    FCloneHeaders: Boolean;
    function PlainTextProc(Sender: TfcxAxisContainer; const ARec: TfcxSliceDrawHeader): Boolean; virtual; abstract;
  public
    constructor Create(AxisContainer: TfcxAxisContainer; StartCell, StopCell: Integer; ACloneHeaders: Boolean);
    destructor Destroy; override;

    function BuildText: TfcxString;
  end;

  TCustomAxisTextBuilderClass = class of TCustomAxisTextBuilder;

  TXAxisPlainTextBuilder = class(TCustomAxisTextBuilder)
  protected
    function PlainTextProc(Sender: TfcxAxisContainer; const ARec: TfcxSliceDrawHeader): Boolean; override;
  end;

  TYAxisPlainTextBuilder = class(TCustomAxisTextBuilder)
  protected
    function PlainTextProc(Sender: TfcxAxisContainer; const ARec: TfcxSliceDrawHeader): Boolean; override;
  end;

  PfcxRegisteredHighlight = ^TfcxRegisteredHighlight;
  TfcxRegisteredHighlight = record
    ClassType: TfcxCustomHighlightClass;
    FrameType: TClass;
  end;

  TfcxRegisteredHighlights = class(TList)
  private
    function GetItem(AIndex: Integer): PfcxRegisteredHighlight;
    procedure SetItem(AIndex: Integer; const Value: PfcxRegisteredHighlight);
  protected
    procedure Notify(Ptr: Pointer; Action: TListNotification); override;
  public
    function Add(AHighlightClass: TfcxCustomHighlightClass; AFrameClass: TClass): PfcxRegisteredHighlight;
    function FindByName(AClassName: String): PfcxRegisteredHighlight;
    function FindItem(AHighlightClass: TfcxCustomHighlightClass): PfcxRegisteredHighlight;
    function FindHighlightClass(AClassName: String): TfcxCustomHighlightClass;
    function FindEditorClass(AHighlightClass: TfcxCustomHighlightClass): TClass;
    property Items[AIndex: Integer]: PfcxRegisteredHighlight read GetItem write SetItem;
  end;

const
  da_Percents: set of TfcxDisplayAs = [da_RowPercentTotal..da_GrandTotalPercent];
  da_Ranks: set of TfcxDisplayAs = [da_RowRank..da_ColRank];

type
  TfcxCreateInrerpter = function(Sender: TObject): IfcInterpreter; 
var
  fcxRegisteredHighlights: TfcxRegisteredHighlights;
  fcxCreateInterpreter: TfcxCreateInrerpter;

procedure fcxRegisterHighlightClass(AClass: TfcxCustomHighlightClass);
procedure fcxRegisterHighlightEditor(AClass: TfcxCustomHighlightClass; AFrame: TClass);

implementation

//VCL uses section
{$IFNDEF FMX}
uses
{$IFDEF fc_debug}
  fcxTypesDebug,
{$ENDIF}
{$IFDEF DELPHI_6UP}
  Variants,
{$ENDIF}
{$IFDEF SHOWAXIS}
  fcxAxis,
{$ENDIF}
  Math, SysUtils, TypInfo, StrUtils,
  fcxStreamUtils,
  fcxRes,
  fcxCodeUtils,
  fcxInterpreter, DateUtils;
//FMX uses
{$ELSE FMX}
uses
  System.Variants, System.Math,
  System.SysUtils, System.TypInfo, System.StrUtils,
  FMX.fcxStreamUtils, FMX.fcxRes, FMX.fcxCodeUtils, FMX.fcxInterpreter;
{$ENDIF FMX}

Type
  HackTfcxFilterManager = class(TfcxFilterManager);
  TfcxSourceHolderHack = class(TfcxSourceHolder);
  TfcxCustomObjectHack = class(TfcxCustomObject);

const
  cfc_SortByValue: set of TfcxTypeSortAxis = [md_tsa_ByTotalValue, md_tsa_BySelected];
  sMeasuresFieldName = '#MEASURES';
  cfcMeasureProcessorMap: array[Boolean, TfcxAgrFunc] of TfcxMeasureProcessorClass =
  (
 {not Distinct}
   (
   { af_None          } TfcxMeasureProcessor,
   { af_Sum           } TfcxSumMeasureProcessor,
   { af_Count         } TfcxCountMeasureProcessor,
   { af_Min           } TfcxMinMeasureProcessor,
   { af_Max           } TfcxMaxMeasureProcessor,
   { af_Avg           } TfcxAvgMeasureProcessor,
   { af_Mul           } TfcxMulMeasureProcessor,
   { af_Variance      } TfcxVarianceMeasureProcessor,
   { af_StdDev        } TfcxStdDevMeasureProcessor,
   { af_VarianceS     } TfcxVarianceSMeasureProcessor,
   { af_StdDevS       } TfcxStdDevSMeasureProcessor,
   { af_Formula       } TfcxFormulaMeasureProcessor,
   { af_CountOfUnique } TfcxCntUniMeasureProcessor,
   { af_FirstValue    } TfcxFirstValueMeasureProcessor,
   { af_ListOfUnique  } TfcxListOfUniqueProcessor,
   { af_LastValue     } TfcxLastValueMeasureProcessor,
   { af_FormulaDetail } TfcxFormulaDetailMeasureProcessor,
   { af_Median        } TfcxMedianMeasureProcessor,
   { af_WeightedMean  } TfcxWeightedMeanMeasureProcessor
    ),
 {Distinct}
   (
   { af_None          } TfcxMeasureProcessor,
   { af_Sum           } TfcxDistinctSumMeasureProcessor,
   { af_Count         } TfcxCntUniMeasureProcessor,
   { af_Min           } TfcxMinMeasureProcessor,
   { af_Max           } TfcxMaxMeasureProcessor,
   { af_Avg           } TfcxAvgMeasureProcessor,
   { af_Mul           } TfcxMulMeasureProcessor,
   { af_Variance      } TfcxVarianceMeasureProcessor,
   { af_StdDev        } TfcxStdDevMeasureProcessor,
   { af_VarianceS     } TfcxVarianceSMeasureProcessor,
   { af_StdDevS       } TfcxStdDevSMeasureProcessor,
   { af_Formula       } TfcxFormulaMeasureProcessor,
   { af_CountOfUnique } TfcxCntUniMeasureProcessor,
   { af_FirstValue    } TfcxFirstValueMeasureProcessor,
   { af_ListOfUnique  } TfcxListOfUniqueProcessor,
   { af_LastValue     } TfcxLastValueMeasureProcessor,
   { af_FormulaDetail } TfcxFormulaDetailMeasureProcessor,
   { af_Median        } TfcxMedianMeasureProcessor,
   { af_WeightedMean  } TfcxWeightedMeanMeasureProcessor
    )
  );

// 0 - empty array, 1 - one subarray, 2 - subarray count = measures count
// 3 - subarray count = addon total count, 4 - subarray count = measures count * addon total count
  cfcSelfPropertiesArrayCount: array[TfcxAxisTreeLevelPosType] of Byte =
  (1, 1, 1, 2, 2, 1, 1);
  cfcTotalSelfPropertiesArrayCount: array[TfcxAxisTreeLevelPosType] of Byte =
  (1, 0, 2, 2, 0, 2, 0);
  cfcAdditionalTotalSelfPropertiesArrayCount: array[TfcxAxisTreeLevelPosType] of Byte =
  (3, 0, 4, 4, 0, 4, 0);
  cfcMeasuresPropertiesArrayCount: array[TfcxAxisTreeLevelPosType] of Byte =
  (0, 0, 0, 0, 0, 2, 2);
// 0 - = 0, 1 - = 1, 2 - = measures count,
  cfcCanCollapseMultiplier: array[TfcxAxisTreeLevelPosType] of Byte =
  (1, 0, 1, 2, 0, 1, 0);
  RegionToChangeTypeMap: array[TfcxRegionOfField] of TfcxChangesInSlice = (
 { rf_Page     } [chs_Page],
 { rf_CapXAx   } [chs_CapXAxis],
 { rf_CapYAx   } [chs_CapYAxis],
 { rf_CapFacts } [chs_CapMeasures],
 { rf_None     } []
  );

function VarToDouble(const V: Variant): Double;
begin
  case TVarData(V).VType of
    varEmpty, varNull, varString, varOleStr: Result := 0
    else
      Result := V;
  end;
end;

function VariantCompareNotNull(AItem1, AItem2: Variant): Integer;
begin
  begin
    if AItem1 > AItem2 then
      Result := 1
    else
      if AItem1 < AItem2 then
        Result := -1
      else
       Result := 0;
  end;
end;

procedure fcxRegisterHighlightClass(AClass: TfcxCustomHighlightClass);
begin
  if not Assigned(fcxRegisteredHighlights.FindItem(AClass)) then
    fcxRegisteredHighlights.Add(AClass, nil)
end;

procedure fcxRegisterHighlightEditor(AClass: TfcxCustomHighlightClass; AFrame: TClass);
var
  Rec: PfcxRegisteredHighlight;
begin
  Rec := fcxRegisteredHighlights.FindItem(AClass);
  if Assigned(Rec) then
    Rec^.FrameType := AFrame
  else
    fcxRegisteredHighlights.Add(AClass, AFrame);
end;


{ TfcxSlice }

constructor TfcxSlice.Create(AOwner: TComponent);
begin
  FSettings := TfcxSliceDefaultSettings.Create;
  inherited;
  FSyncronizeYFieldsWidth := False;
  FSliceTimeStat := TfcxSliceTimeStat.Create;
  FSliceTimeStatStart := TfcxSliceTimeStat.Create;
  FClearUnattachedFieldsFilter := False;
  FLoading := False;
  FDefaultColWidth := fcDefaultSettingsStore.ColWidth;
  FDefaultRowHeight := fcDefaultSettingsStore.RowHeight;
  FPopupWidthDefault := 120;
  FPopupShowAvailableDefault := False;
  FFieldsOrder := fcfloByFieldDisplayLabel;
  FCube := nil;
  FSliceFields := TfcxSliceFields.Create(Self);
  FXAxisContainer := TfcxAxisContainer.Create(Self, rf_CapXAx);
  FYAxisContainer := TfcxAxisContainer.Create(Self, rf_CapYAx);
  FPageContainer := TfcxPageContainer.Create(Self, rf_Page);
  FMeasuresContainer := TfcxMeasuresContainer.Create(Self, rf_CapFacts);
  FTopNs := TfcxTopNs.Create(Self);
  FChanges := [];
  FAutoUVFilter := True;
  FDimensions := CreateDimensions(Self);
  FMeasures := CreateMeasures(Self);
  FCustomObject := CreateCustomObject(Self);
  FInterpreter := fcxCreateInterpreter(Self);
  FHideTotalForSingle := True;
  FilterManager := nil;
  FChartsManager := TfcxChartsManager.Create(Self);
  FSortSelectionLoaded := False;
end;

destructor TfcxSlice.Destroy;
begin
  Cube := nil;
  FilterManager := nil;
  FInterpreter := nil;
  FreeAndNil(FChartsManager);
  FreeAndNil(FCustomObject);
  FreeAndNil(FMeasures);
  FreeAndNil(FDimensions);
  FreeAndNil(FTopNs);
  FreeAndNil(FPageContainer);
  FreeAndNil(FYAxisContainer);
  FreeAndNil(FXAxisContainer);
  FreeAndNil(FMeasuresContainer);
  FreeAndNil(FSliceFields);
  FreeAndNil(FSettings);
  FreeAndNil(FSliceTimeStat);
  FreeAndNil(FSliceTimeStatStart);
  inherited;
end;

procedure TfcxSlice.DestroySlice(ASaveFields: boolean; ASaveLevels: boolean = False);
begin
  FMeasuresContainer.Clear;
  if ASaveFields then
  begin
    FXAxisContainer.ClearAxisTree(ASaveLevels);
    FYAxisContainer.ClearAxisTree(ASaveLevels);
  end
  else
  begin
    FXAxisContainer.ClearAxisTree;
    FYAxisContainer.ClearAxisTree;
    FXAxisContainer.FAxisSourceTree.ClearTree;
    FYAxisContainer.FAxisSourceTree.ClearTree;
    FXAxisContainer.Fields.Clear;
    FYAxisContainer.Fields.Clear;
  end;
  FreeMem(FXAxisContainer.FCoordinate);
  FXAxisContainer.FCoordinate := nil;
  FreeMem(FYAxisContainer.FCoordinate);
  FYAxisContainer.FCoordinate := nil;
  if not ASaveFields then
  begin
    FPageContainer.Fields.Clear;
    FMeasuresContainer.FMeasureFields.Clear;
    FSliceFields.Clear;
    MeasuresContainer.FContainer := FPageContainer;
  end;
end;

function TfcxSlice.DetailOrderedRecordSetProvider(
  APosition: TPoint): TfcxDetailOrderedRecordSetProvider;
begin
  Result := TfcxDetailOrderedRecordSetProvider.Create(Self);
  Result.FPosition := APosition;
  Result.Update;
end;

function TfcxSlice.DoConvertScript(const AFunctionName, AScript: String): String;
begin
  if Assigned(FOnConvertScript) then
    Result := OnConvertScript(Self, AFunctionName, AScript)
  else
  begin
    Result := Format('procedure %s(var Result: Variant);'#$D#$A, [AFunctionName]);
    if (AScript <> '') then
      Result := Result + StringReplace(AScript, 'end.', 'end;', [rfIgnoreCase])
    else
      Result := Result + 'begin end;';
  end;
end;

function TfcxSlice.LoadFromStream(ASliceStream: TStream): Boolean;
const
  XMLSignature: array[0..1] of AnsiChar = '<?';
var
  Version: Word;
  XMLDoc: TfcxXMLDocument;
begin
  Result := False;
  if ASliceStream.Read(Version, SizeOf(Word)) < SizeOf(Word) then
    Exit;
  if Version = word(XMLSignature) then
  begin
// this is XML and should be read throug LoadFromXML
    XMLDoc := TfcxXMLDocument.Create;
    FLoading := True;
    try
      ASliceStream.Position := ASliceStream.Position - 2;
      XMLDoc.LoadFromStream(ASliceStream);
      Result := LoadFromXML(XMLDoc);
    finally
      FLoading := False;
      XMLDoc.Free;
    end;
  end
  else
  begin
    FLoading := True;
    try
      Result := LoadFromOldStream(Version, ASliceStream);
    finally
      FLoading := False;
    end;
  end
end;

function TfcxSlice.LoadFromOldStream(Version: Word; AStream: TStream): Boolean;
var
  i, j, ACount, ACount2, AOrder, ANewIndex, ACalcOrder, AUVIndex: Integer;
  APrec: SmallInt;
  AFieldName, AFieldCaption, AFormula, AFilter, AFieldValue, AFunctionName: String;
  AVisibleInData, AMakeTotal, AExpanded,
  ACalculated: Boolean;
  AAgrFunc: TfcxAgrFunc;
  ADisplayAs: TfcxDisplayAs;
  Alignment: TAlignment;
  Highlights: TfcxCustomHighlights;
  CfgDate: TDateTime;
  AField: TfcxSliceField;

  procedure LoadRangeInfo(Highlights: TfcxCustomHighlights);
  var
    i, c: integer;
    LoadedRange: TfcxLoadedRange;
  begin
    AStream.Read(c, SizeOf(c));
    for i := 0 to c - 1 do
    begin
      if Version < 10 then
      begin
        AStream.Read(LoadedRange, SizeOf(TfcxLoadedRange) - SizeOf(LongInt));
        LoadedRange.FillColor := $1FFFFFFF;
      end
      else
        AStream.Read(LoadedRange, SizeOf(TfcxLoadedRange));

      Highlights.AddByName('TfcxRangeHighlight').LoadRange(LoadedRange);
    end;
  end;

  procedure AddField(AFieldName, AFieldCaption: String; AFieldsContainer: TfcxFieldsContainer;
    AMakeTotal: Boolean; AOrder: Integer; AExpanded: Boolean; AAlignment: TAlignment; AMeasurePosition: integer=-1);
  var
    AField: TfcxSliceField;
    ANewIndex: Integer;
  begin
    AField := SliceFields.ItemByName[AFieldName];
    if Assigned(AField) then
    begin
      ANewIndex := AFieldsContainer.Fields.Count;
      if not AFieldsContainer.InsertField(AField, ANewIndex, AFieldName, AFieldCaption) then
        Exit;
      if ANewIndex >= 0 then
      begin
        if AMakeTotal then
          TfcxAxisField(AFieldsContainer.Fields[ANewIndex]).TotalPosition := fctp_After
        else
          TfcxAxisField(AFieldsContainer.Fields[ANewIndex]).TotalPosition := fctp_Hide;
        TfcxAxisField(AFieldsContainer.Fields[ANewIndex]).FAlignment := AAlignment;
        if AOrder = 1 then
          TfcxAxisField(AFieldsContainer.Fields[ANewIndex]).SortDirection := fcsd_Asc
        else
          TfcxAxisField(AFieldsContainer.Fields[ANewIndex]).SortDirection := fcsd_Desc;

        AField.FCollapsed := not AExpanded;
        AField.DisplayFormat.TypeFormat.Kind := fkText;
      end;
    end
    else
    if AFieldName = sMeasuresFieldName then
    begin
      AFieldCaption := fcxResources.GetAnsi('sMeasures');
      AFieldsContainer.InsertMeasuresField(AMeasurePosition);
    end;
  end;

begin
  FSortSelectionLoaded := False;
  StartChange;
  Clear;
  FFilterManager.ResetAllFilters;
  AStream.Read(CfgDate, SizeOf(TDateTime));

  if Version > 6 then
  begin
    FSelectedMeasure := ReadInteger(AStream);
    FSelectedCol := ReadInteger(AStream);
    FSelectedRow := ReadInteger(AStream);
    FSelectedColAdditionalTotal := -1;
    FSelectedRowAdditionalTotal := -1;
  end;

  ACount := ReadInteger(AStream);
  for i := 0 to ACount - 1 do
  begin
    AFieldName := ReadOldString(AStream);
    AFieldCaption := ReadOldString(AStream);
    if (FCube <> nil) and (AFieldName <> '') then
      AddField(AFieldName, AFieldCaption, PageContainer, True, -1, True, taLeftJustify, i);
  end;
  ACount := ReadInteger(AStream);
  for i := 0 to ACount - 1 do
  begin
    AFieldName := ReadOldString(AStream);
    AFieldCaption := ReadOldString(AStream);
    AMakeTotal := ReadBoolean(AStream);
    AOrder := ReadInteger(AStream);
    AExpanded := ReadBoolean(AStream);
    if Version >= 7 then
      Alignment := TAlignment(ReadByte(AStream))
    else
      Alignment := taLeftJustify;
    if (FCube <> nil) and (AFieldName <> '') then
      AddField(AFieldName, AFieldCaption, XAxisContainer, AMakeTotal, AOrder, AExpanded, Alignment, i);
  end;
  ACount := ReadInteger(AStream);
  for i := 0 to ACount - 1 do
  begin
    AFieldName := ReadOldString(AStream);
    AFieldCaption := ReadOldString(AStream);
    AMakeTotal := ReadBoolean(AStream);
    AOrder := ReadInteger(AStream);
    AExpanded := ReadBoolean(AStream);
    if Version >= 7 then // fpc
      Alignment := TAlignment(ReadByte(AStream))
    else
      Alignment := taLeftJustify;
    if (FCube <> nil) and (AFieldName <> '') then
      AddField(AFieldName, AFieldCaption, YAxisContainer, AMakeTotal, AOrder, AExpanded, Alignment, i);
  end;
// CapFactsFieldsAbs
  ACount := ReadInteger(AStream);
  for i := 0 to ACount - 1 do
  begin
    if Version >= 9 then
      ACalculated := ReadBoolean(AStream)
    else
      ACalculated := False;
    AFieldName := ReadOldString(AStream);
    AFieldCaption := ReadOldString(AStream);
    if (Version >= 3) and (Version < 5) then
      ReadOldString(AStream) else
    if Version >= 5 then
      APrec := ReadInteger(AStream)
    else
      APrec := 2;

    AAgrFunc := TfcxAgrFunc(ReadByte(AStream));
    ADisplayAs := TfcxDisplayAs(ReadByte(AStream));
    AVisibleInData := ReadBoolean(AStream);

    Highlights := TfcxCustomHighlights.Create(nil);

    if Version >= 4 then
      LoadRangeInfo(Highlights);

    if Version >= 7 then // fpc
      Alignment := TAlignment(ReadByte(AStream))
    else
      Alignment := taRightJustify;

    ANewIndex := -1;
    if (AFieldName = '') and (not ACalculated) then
    begin
      if (FCube <> nil) then
        ANewIndex := MeasuresContainer.AddMeasure(nil, AFieldCaption, AFieldCaption, af_Count);
    end
    else
    if not ACalculated then
    begin
      if Version >= 12 then
        AFilter := ReadOldString(AStream)
      else
        AFilter := '';
      if (FCube <> nil) then
      begin
        AField := SliceFields.ItemByName[AFieldName];
        if Assigned(AField) then
          ANewIndex := MeasuresContainer.AddMeasure(AField, AFieldCaption, AFieldCaption, AAgrFunc);
        if AFilter <> '' then
        begin
          AFunctionName := 'func_f' + IntToStr(ANewIndex);
          MeasuresContainer.Measures[ANewIndex].FilterScriptFunction := AFunctionName;
          // add a new formula to the common script
          Script.Insert(0, DoConvertScript(AFunctionName, AFilter));
        end;
      end;
    end else
    begin
      AFormula := ReadOldString(AStream);
      ACalcOrder := ReadInteger(AStream);
      if Version >= 12 then
        AFilter := ReadOldString(AStream)
      else
        AFilter := '';

      ANewIndex := MeasuresContainer.AddMeasure(nil, AFieldCaption, AFieldCaption, af_Formula{ AAgrFunc});
      if ANewIndex >= 0 then
      begin
        AFunctionName := 'func_' + IntToStr(ANewIndex);
        MeasuresContainer.Measures[ANewIndex].ScriptFunction := AFunctionName;
        MeasuresContainer.Measures[ANewIndex].ScriptOrder := ACalcOrder;
        if AAgrFunc <> af_Formula then
        begin
          MeasuresContainer.Measures[ANewIndex].FUseDifferentAggForTotals := True;
          MeasuresContainer.Measures[ANewIndex].FAgrFuncForTotals := AAgrFunc;
        end;

        // add a new formula to the common script
        Script.Insert(0, DoConvertScript(AFunctionName, AFormula))
      end;
      if AFilter <> '' then
      begin
        AFunctionName := 'func_f' + IntToStr(ANewIndex);
        MeasuresContainer.Measures[ANewIndex].FilterScriptFunction := AFunctionName;
        // add a new formula to the common script
        Script.Insert(0, DoConvertScript(AFunctionName, AFilter));
      end;
    end;
    if (FCube <> nil) and (ANewIndex >= 0) then
    begin
      MeasuresContainer.Measures[ANewIndex].Caption := AFieldCaption;
      MeasuresContainer.Measures[ANewIndex].DisplayAs := ADisplayAs;
      MeasuresContainer.Measures[ANewIndex].FVisible := AVisibleInData;
      MeasuresContainer.Measures[ANewIndex].FAlignment := Alignment;
      MeasuresContainer.Measures[ANewIndex].Highlights := Highlights;
      MeasuresContainer.Measures[ANewIndex].DisplayFormat.TypeFormat.Kind := fkNumeric;
      MeasuresContainer.Measures[ANewIndex].DisplayFormat.TypeFormat.FormatStr := '%2.' + IntToStr(APrec) + 'f';
    end;
    FreeAndNil(Highlights);
  end;
// Information about filtered values
  ACount := ReadInteger(AStream);
  for i := 0 to ACount - 1 do
  begin
    ACount2 := ReadInteger(AStream);
    if ACount2 > 0 then
    begin
      FFilterManager.UVFilter.CubeFieldFilter[TfcxCommonField(TfcxHackCube(FCube).FOldLoadedFields[i])].BeginUpdateField;
      for j := 0 to ACount2 - 1 do
      begin
        if (Version <= 7) and (Version <> 6) then
        begin
          AFieldValue := ReadOldString(AStream);
          FFilterManager.UVFilter.CubeFieldFilter[TfcxCommonField(TfcxHackCube(FCube).FOldLoadedFields[i])].FilterByValue[AFieldValue] := False;
        end else
        begin
          AUVIndex := ReadInteger(AStream);
          if AUVIndex < FFilterManager.UVFilter.CubeFieldFilter[TfcxCommonField(TfcxHackCube(FCube).FOldLoadedFields[i])].UVCount then
            FFilterManager.UVFilter.CubeFieldFilter[TfcxCommonField(TfcxHackCube(FCube).FOldLoadedFields[i])].Filter[AUVIndex] := False;
        end;
      end;
      FFilterManager.UVFilter.CubeFieldFilter[TfcxCommonField(TfcxHackCube(FCube).FOldLoadedFields[i])].EndUpdateField;
    end;
  end;
  HideColZeros := ReadBoolean(AStream);
  HideRowZeros := ReadBoolean(AStream);

  if Version >= 2 then
  begin
    XAxisContainer.DefaultTypeSort := TfcxTypeSortAxis(ReadByte(AStream));
    YAxisContainer.DefaultTypeSort := TfcxTypeSortAxis(ReadByte(AStream));
  end;
  XAxisContainer.AxisType := at_Standard;
  YAxisContainer.AxisType := at_Standard;
  XAxisContainer.GrandTotalPosition := fctp_After;
  YAxisContainer.GrandTotalPosition := fctp_After;
  XAxisContainer.AdditionalGrandTotalFunctions := [];
  YAxisContainer.AdditionalGrandTotalFunctions := [];
  StopChange([chs_FiltersChanged, chs_CapXAxis, chs_CapYAxis, chs_Load]);
  Result := True;
end;

procedure TfcxSlice.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) then
    if Assigned(Cube) and (AComponent = Cube) then
      Cube := nil
    else
    if Assigned(FilterManager) and (AComponent = FilterManager) and (FilterManager.Owner <> Self) then
      FilterManager := nil;
end;

procedure TfcxSlice.CreateSlice;
var
  i: Integer;
begin
  DestroySlice(False);
  if Assigned(FCube) then
    for i := 0 to FCube.Fields.Count - 1 do
      FSliceFields.AddCubeField(FCube.Fields.Items[i]);
  if FXAxisContainer.FDefaultTypeSort = md_tsa_BySelected then
    FXAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
  if FYAxisContainer.FDefaultTypeSort = md_tsa_BySelected then
    FYAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
  FXAxisContainer.FAxisSourceTree.FillSourceTree;
  FYAxisContainer.FAxisSourceTree.FillSourceTree;
  FTopNs.FStep := 0;
  FXAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
  FYAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
  FXAxisContainer.CreateAxisTree;
  FYAxisContainer.CreateAxisTree;
  FMeasuresContainer.CreateMatrix;
  FXAxisContainer.FillVisibleArray;
  FYAxisContainer.FillVisibleArray;
  StartChange;
  StopChange([chs_Created]);
end;

procedure TfcxSlice.SaveToStream(ASliceStream: TStream; AStoreItems: TfcxItemsForStoreWithSlice = []);
var
  Doc: TfcxXMLDocument;
begin
  Doc := TfcxXMLDocument.Create;
  Doc.AutoIndent := True;
  try
    SaveToXML(Doc, AStoreItems);
    Doc.SaveToStream(ASliceStream);
  finally
    Doc.Free;
  end;
end;

procedure TfcxSlice.SetCube(const Value: TfcxCube);
begin
  if (Cube <> Value) or ((Cube = nil) and (Value = nil)) then
  begin
    if not (csDestroying in ComponentState) then
      StartChange;

    if Assigned(Cube) then
      Cube.RemoveFreeNotification(Self);
    FCube := Value;
    if Assigned(Cube) then
      Cube.FreeNotification(Self);
    if Assigned(FCube) then
    begin
      if FilterManager.Owner = Self then
        FFilterManager.Cube := FCube
      else
      begin
        if not Assigned(FFilterManager.Cube) then
          FFilterManager.Cube := FCube
        else
        if (FFilterManager.Cube <> FCube) then
          FilterManager := nil;
      end
    end
    else
    begin
      if not (csDestroying in ComponentState) then
      begin
        if FilterManager.Owner = Self then
          FFilterManager.Cube := nil;
      end
      else
        FilterManager := nil;
    end;
    if not (csDestroying in ComponentState) then
    begin
      StopChange([chs_SetCube]);
{
      move to StopChange
      if (FCube <> nil) and (FCube.Active) then
        Recreate else
        DestroyingSlice;
      DoChange(sct_All, []);
      or
      DoChange(sct_Cube);
}
    end;
  end;
end;

function TfcxSlice.GetSelectedCol: integer;
begin
  if XAxisContainer = MeasuresContainer.Container then
    result := XAxisContainer.VisibleAxisNodes.GetVisibleIndex(FSelectedCol, FSelectedMeasure, FSelectedColAdditionalTotal)
  else
    result := XAxisContainer.VisibleAxisNodes.GetVisibleIndex(FSelectedCol, -1, FSelectedColAdditionalTotal);
end;

function TfcxSlice.GetSelectedRow: integer;
begin
  if YAxisContainer = MeasuresContainer.Container then
    result := YAxisContainer.VisibleAxisNodes.GetVisibleIndex(FSelectedRow, FSelectedMeasure, FSelectedRowAdditionalTotal)
  else
    result := YAxisContainer.VisibleAxisNodes.GetVisibleIndex(FSelectedRow, -1, FSelectedRowAdditionalTotal);
end;

procedure TfcxSlice.SetFieldsOrder(const Value: TfcxFieldsOrder);
begin
  if FFieldsOrder <> Value then
  begin
    FSliceFields.SetFieldsOrder(Value);
    FFieldsOrder := Value;
  end
end;

procedure TfcxSlice.SetFilterManager(const Value: TfcxFilterManager);
begin
  if (FFilterManager <> Value) or ((FFilterManager = nil) and (Value = nil)) then
  begin
    if Assigned(Value) then
    begin
      if Assigned(FCube) and Assigned(Value.Cube) and (Value.Cube <> FCube) then
        exit;
    end;
    if not (csDestroying in ComponentState) then
      StartChange;

    if Assigned(FFilterManager) then
    begin
      FFilterManager.ListnersManager.RemoveListner(Self);
      if FFilterManager.Owner = Self then
        FreeAndNil(FFilterManager);
    end;
    FFilterManager := Value;
    if Assigned(FFilterManager) then
    begin
      FFilterManager.ListnersManager.AddListner(Self);
      if not Assigned(FCube) and Assigned(FFilterManager.Cube) then
        Cube := FFilterManager.Cube
      else
      if Assigned(FCube) and not Assigned(FFilterManager.Cube) then
        FFilterManager.Cube := Cube;
    end
    else
    begin
      if not (csDestroying in ComponentState) then
      begin
        FFilterManager := TfcxFilterManager.Create(Self);
        FFilterManager.ListnersManager.AddListner(Self);
        FFilterManager.Cube := Cube;
      end;
    end;

    if not (csDestroying in ComponentState) then
    begin
      StopChange([chs_SetFilterManager]);
{
      move in StopChange
      if (FCube <> nil) and (FCube.Active) then
        Recreate else
        DestroyingSlice;
      DoChange(sct_All, []);
      or
      DoChange(sct_Cube);
}
    end;
  end;
end;

procedure TfcxSlice.StopChange(AChanges: TfcxChangesInSlice = []);
var
  ARegions: TfcxRegionsOfField;
  AXSort, AYSort: boolean;
begin
  if FChangeSemaphore > 0 then
    Exit;
  FChanges := FChanges + AChanges;
  dec(FChangeCount);
  if FChangeCount = 0 then
  begin
    if FChanges = [] then
      exit;
    inc(FChangeSemaphore); // Lock process changes
    ARegions := [];
    FSliceTimeStat.Clear;
    if FTopNs.FActiveCount > 0 then
      if ([chs_CapXAxis, chs_CapYAxis, chs_CapMeasures] * FChanges) <> [] then
        FChanges := FChanges + [chs_TopNChanged];
    if ([chs_SetCube, chs_SetFilterManager, chs_CubeChanged, chs_FilterManagerChanged] * FChanges) <> [] then
    begin
      if (FCube <> nil) and (FCube.Active) then
      begin
        FChanges := FChanges + [chs_Created];
        CreateSlice;
      end
      else
      begin
        DestroySlice(False);
      end
    end
    else
    if (FCube <> nil) and FCube.Active then
    begin
      if ClearUnattachedFieldsFilter then
        if ResetUnattachedFieldsFilter then
          FChanges := FChanges + [chs_FiltersChanged];
      if (([chs_FiltersChanged, chs_TopNChanged] * FChanges) <> []) then
      begin
        DestroySlice(True, (FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
        AXSort := ([chs_CapXAxis] * FChanges) <> [];
        AYSort := ([chs_CapYAxis] * FChanges) <> [];
        FTopNs.Prepare;
        AXSort := AXSort or FTopNs.GetNeedReSortXAxis;
        AYSort := AYSort or FTopNs.GetNeedReSortYAxis;
        if AXSort then
          FXAxisContainer.FAxisSourceTree.FillSourceTree;
        if AYSort then
          FYAxisContainer.FAxisSourceTree.FillSourceTree;
        FXAxisContainer.FAxisSourceTree.FillSourceOrderIndexes((FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
        FYAxisContainer.FAxisSourceTree.FillSourceOrderIndexes((FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
        if (FChanges - [chs_FiltersChanged, chs_TopNChanged]) = [] then
        begin
          if (FXAxisContainer.Fields.Count > 0) and (FYAxisContainer.FDefaultTypeSort = md_tsa_BySelected) then
            FYAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
          if (FYAxisContainer.Fields.Count > 0) and (FXAxisContainer.FDefaultTypeSort = md_tsa_BySelected) then
            FXAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
        end
        else
        begin
          if (FYAxisContainer.FDefaultTypeSort = md_tsa_BySelected) and not (chs_Load in FChanges) then
            FYAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
          if (FXAxisContainer.FDefaultTypeSort = md_tsa_BySelected) and not (chs_Load in FChanges) then
            FXAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
        end;
        FXAxisContainer.CreateAxisTree((FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
        FYAxisContainer.CreateAxisTree((FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
        FSliceTimeStatStart.DataBuildTime := fcxGetTickCount;
        FMeasuresContainer.CreateMatrix;
        FSliceTimeStat.DataBuildTime := FSliceTimeStat.DataBuildTime + fcxGetTickCount - FSliceTimeStatStart.DataBuildTime;
        if FTopNs.FActiveCount > 0 then
        begin
          FTopNs.Calculate;
          if not FTopNs.AsFilter then
          begin
            if FTopNs.NeedSortXAxis then
              FXAxisContainer.FAxisSourceTree.FillSourceTree;
            if FTopNs.NeedSortYAxis then
              FYAxisContainer.FAxisSourceTree.FillSourceTree;
          end;
          FXAxisContainer.FAxisSourceTree.FillSourceOrderIndexes((FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
          FYAxisContainer.FAxisSourceTree.FillSourceOrderIndexes((FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
          if (FChanges - [chs_FiltersChanged]) = [] then
          begin
            if (FXAxisContainer.Fields.Count > 0) and (FYAxisContainer.FDefaultTypeSort = md_tsa_BySelected) then
              FYAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
            if (FYAxisContainer.Fields.Count > 0) and (FXAxisContainer.FDefaultTypeSort = md_tsa_BySelected) then
              FXAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
          end
          else
          begin
            if (FYAxisContainer.FDefaultTypeSort = md_tsa_BySelected) and not (chs_Load in FChanges) then
              FYAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
            if (FXAxisContainer.FDefaultTypeSort = md_tsa_BySelected) and not (chs_Load in FChanges) then
              FXAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
          end;
          FXAxisContainer.CreateAxisTree((FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
          FYAxisContainer.CreateAxisTree((FChanges - [chs_FiltersChanged, chs_TopNChanged]) = []);
          FSliceTimeStatStart.DataBuildTime := fcxGetTickCount;
          FMeasuresContainer.CreateMatrix;
          FSliceTimeStat.DataBuildTime := FSliceTimeStat.DataBuildTime + fcxGetTickCount - FSliceTimeStatStart.DataBuildTime;
        end;
        if chs_Load in FChanges then
        begin
          if FSelectedCol >= FXAxisContainer.FCellsCount then
            FSelectedCol := FXAxisContainer.FCellsCount - 1;
          if FSelectedRow >= FYAxisContainer.FCellsCount then
            FSelectedRow := FYAxisContainer.FCellsCount - 1;
          if FSelectedMeasure >= FMeasuresContainer.Count then
            FSelectedMeasure := FMeasuresContainer.Count - 1;
          if FXAxisContainer.FDefaultTypeSort in cfc_SortByValue then
            YAxisContainer.SetSelected(FXAxisContainer.FDefaultTypeSort);
          if FYAxisContainer.FDefaultTypeSort in cfc_SortByValue then
            XAxisContainer.SetSelected(FYAxisContainer.FDefaultTypeSort);
          FSortSelectionLoaded := False;
        end;
        if FXAxisContainer.FDefaultTypeSort in cfc_SortByValue then
        begin
          FXAxisContainer.FAxisSortTree.SortTree;
        end;
        if FYAxisContainer.FDefaultTypeSort in cfc_SortByValue then
        begin
          FYAxisContainer.FAxisSortTree.SortTree;
        end;
        FXAxisContainer.FillVisibleArray;
        FYAxisContainer.FillVisibleArray;
        ARegions := [rf_CapXAx, rf_CapYAx, rf_CapFacts, rf_Page];
      end
      else
      begin
        AXSort := ([chs_CapXAxis] * FChanges) <> [];
        AYSort := ([chs_CapYAxis] * FChanges) <> [];
        if AXSort or AYSort or (([chs_ExpandXGroup, chs_ExpandYGroup, chs_CapMeasures] * FChanges) <> []) then
        begin
          if AXSort or (([chs_ExpandXGroup, chs_CapMeasures] * FChanges) <> []) then
            if FYAxisContainer.FDefaultTypeSort = md_tsa_BySelected then
            begin
              FYAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
              FChanges := FChanges + [chs_CapYAxis];
            end;
          if AYSort or (([chs_ExpandXGroup, chs_CapMeasures] * FChanges) <> []) then
            if FXAxisContainer.FDefaultTypeSort = md_tsa_BySelected then
            begin
              FXAxisContainer.FDefaultTypeSort := md_tsa_ByAxisValue;
              FChanges := FChanges + [chs_CapXAxis];
            end;
          AXSort := ([chs_CapXAxis] * FChanges) <> [];
          AYSort := ([chs_CapYAxis] * FChanges) <> [];
          FTopNs.Prepare;
          AXSort := AXSort or FTopNs.GetNeedReSortXAxis;
          AYSort := AYSort or FTopNs.GetNeedReSortYAxis;
          if AXSort then
          begin
            FXAxisContainer.FAxisSourceTree.FillSourceTree;
            FXAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
            FXAxisContainer.CreateAxisTree;
          end
          else
          if chs_ExpandXGroup in FChanges then
          begin
            FXAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
            FXAxisContainer.CreateAxisTree;
          end;
          if AYSort then
          begin
            FYAxisContainer.FAxisSourceTree.FillSourceTree;
            FYAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
            FYAxisContainer.CreateAxisTree;
          end
          else
          if chs_ExpandYGroup in FChanges then
          begin
            FYAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
            FYAxisContainer.CreateAxisTree;
          end;
          FSliceTimeStatStart.DataBuildTime := fcxGetTickCount;
          FMeasuresContainer.CreateMatrix;
          FSliceTimeStat.DataBuildTime := FSliceTimeStat.DataBuildTime + fcxGetTickCount - FSliceTimeStatStart.DataBuildTime;
          if FTopNs.FActiveCount > 0 then
          begin
            FTopNs.Calculate;
            if not FTopNs.AsFilter then
            begin
              if FTopNs.NeedSortXAxis then
                FXAxisContainer.FAxisSourceTree.FillSourceTree;
              if FTopNs.NeedSortYAxis then
                FYAxisContainer.FAxisSourceTree.FillSourceTree;
            end;
            if AXSort or FTopNs.NeedSortXAxis then
            begin
              FXAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
              FXAxisContainer.CreateAxisTree;
            end
            else
            if chs_ExpandXGroup in FChanges then
            begin
              FXAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
              FXAxisContainer.CreateAxisTree;
            end;
            if AYSort or FTopNs.NeedSortYAxis then
            begin
              FYAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
              FYAxisContainer.CreateAxisTree;
            end
            else
            if chs_ExpandYGroup in FChanges then
            begin
              FYAxisContainer.FAxisSourceTree.FillSourceOrderIndexes;
              FYAxisContainer.CreateAxisTree;
            end;
            FSliceTimeStatStart.DataBuildTime := fcxGetTickCount;
            FMeasuresContainer.CreateMatrix;
            FSliceTimeStat.DataBuildTime := FSliceTimeStat.DataBuildTime + fcxGetTickCount - FSliceTimeStatStart.DataBuildTime;
          end;

          if chs_Load in FChanges then
          begin
            if FSelectedCol >= FXAxisContainer.FCellsCount then
              FSelectedCol := FXAxisContainer.FCellsCount - 1;
            if FSelectedRow >= FYAxisContainer.FCellsCount then
              FSelectedRow := FYAxisContainer.FCellsCount - 1;
            if FSelectedMeasure >= FMeasuresContainer.Count then
              FSelectedMeasure := FMeasuresContainer.Count - 1;
            if FXAxisContainer.FDefaultTypeSort in cfc_SortByValue then
              YAxisContainer.SetSelected(FXAxisContainer.FDefaultTypeSort);
            if FYAxisContainer.FDefaultTypeSort in cfc_SortByValue then
              XAxisContainer.SetSelected(FYAxisContainer.FDefaultTypeSort);
            FSortSelectionLoaded := False;
          end;
          if FXAxisContainer.FDefaultTypeSort in cfc_SortByValue then
            FXAxisContainer.FAxisSortTree.SortTree;
          if FYAxisContainer.FDefaultTypeSort in cfc_SortByValue then
            FYAxisContainer.FAxisSortTree.SortTree;
          FXAxisContainer.FillVisibleArray;
          FYAxisContainer.FillVisibleArray;

        end
        else
        begin
          if (chs_XTypeSort in FChanges) then
            FXAxisContainer.FAxisSortTree.SortTree;
          if (chs_YTypeSort in FChanges) then
            FYAxisContainer.FAxisSortTree.SortTree;
          if ([chs_XAxisType, chs_YAxisType, chs_XTotalPosition, chs_YTotalPosition] * FChanges) <> [] then
            FMeasuresContainer.ReCreateTotalPositions;
          if ([chs_ExpandXNode, chs_XSortDirection, chs_XAxisType, chs_XTotalPosition, chs_NeedFillVisibleX, chs_XTypeSort] * FChanges) <> [] then
            XAxisContainer.FillVisibleArray;
          if ([chs_ExpandYNode, chs_YSortDirection, chs_YAxisType, chs_YTotalPosition, chs_NeedFillVisibleY, chs_YTypeSort] * FChanges) <> [] then
            YAxisContainer.FillVisibleArray;
        end;

        if chs_Script in FChanges then
        begin
          FInterpreter.NeedCompile;
        end;

        if [chs_ExpandXGroup, chs_CapXAxis, chs_ExpandXNode, chs_ColWidth, chs_XHeight, chs_ChangeAxis, chs_XSortDirection, chs_XAxisType, chs_XTotalPosition, chs_XTypeSort, chs_NeedFillVisibleX] * FChanges <> [] then
          ARegions := ARegions + [rf_CapXAx, rf_CapFacts];
        if [chs_ExpandYGroup, chs_CapYAxis, chs_ExpandYNode, chs_RowHeight, chs_YWidth, chs_ChangeAxis, chs_YSortDirection, chs_YAxisType, chs_YTotalPosition, chs_YTypeSort, chs_NeedFillVisibleY] * FChanges <> [] then
          ARegions := ARegions + [rf_CapYAx, rf_CapFacts];
        if [chs_CapMeasures, chs_MeasuresFieldPosition, chs_Fact] * FChanges <> [] then
          ARegions := ARegions + [rf_CapFacts];
        if [chs_CapMeasures, chs_CapXWidth, chs_XAxis] * FChanges <> [] then
          ARegions := ARegions + [rf_CapXAx];
        if [chs_CapMeasures, chs_CapYWidth, chs_YAxis] * FChanges <> [] then
          ARegions := ARegions + [rf_CapYAx];
        if [chs_CapPage, chs_CapPageWidth, chs_Page] * FChanges <> [] then
          ARegions := ARegions + [rf_Page];
      end;

    end;
    DoChange(TfcxSliceChangeAlert.Create(SliceChangeTypeOfChanges(FChanges), FChanges, ARegions));

    FChanges:=[];
    dec(FChangeSemaphore);
    if Assigned(OnStopChange) then
      OnStopChange(Self);
  end;
end;

procedure TfcxSlice.CubeChanged(Sender: TfcxCube;
  AChangeAlert: TfcxChangeAlert);
var
  i: integer;
  AChanges: TfcxChangesInSlice;
  AIndexOfSliceField: Integer;
begin
  AChanges := [];
  if AChangeAlert is TfcxCubeChangeAlert then
    if TfcxCubeChangeAlert(AChangeAlert).CubeChangeType = ccht_Field then
    begin
      if chc_GroupsChanged in TfcxCubeChangeAlert(AChangeAlert).ChangesInCube then
      begin
        for i := 0 to FXAxisContainer.Fields.Count - 1 do
          if TfcxAxisField(FXAxisContainer.Fields[i]).SliceField is TfcxCommonSliceCubeField then
//            if TfcxCommonSliceCubeField(TfcxAxisField(FXAxisContainer.Fields[i]).SliceField).CubeField.Index = TfcxCubeChangeAlert(AChangeAlert).FieldIndex then
            begin
              AChanges := AChanges + [chs_CapXAxis];
              Break;
            end;
        for i := 0 to FYAxisContainer.Fields.Count - 1 do
          if TfcxAxisField(FYAxisContainer.Fields[i]).SliceField is TfcxCommonSliceCubeField then
//            if TfcxCommonSliceCubeField(TfcxAxisField(FYAxisContainer.Fields[i]).SliceField).CubeField.Index = TfcxCubeChangeAlert(AChangeAlert).FieldIndex then
            begin
              AChanges := AChanges + [chs_CapYAxis];
              Break;
            end;
        if AChanges <> [] then
        begin
          StartChange;
          StopChange(AChanges);
        end;
      end
    end
    else
    if TfcxCubeChangeAlert(AChangeAlert).CubeChangeType = ccht_OneSplit then
    begin
// Added one split
// Need:
// 1. Create and Add new SliceField
      if chc_AddedOneSplit in TfcxCubeChangeAlert(AChangeAlert).ChangesInCube then
      begin
        StartChange;
        FSliceFields.AddCubeField(TfcxCommonField(TfcxCubeSplitsChangeAlert(AChangeAlert).MasterField).Fields.Items[TfcxCubeSplitsChangeAlert(AChangeAlert).SplitFieldIndex]);
        StopChange([chs_XAxis, chs_YAxis, chs_Page, chs_Fact]);
      end
      else
      if chc_DeletedOneSplit in TfcxCubeChangeAlert(AChangeAlert).ChangesInCube then
      begin
        StopChange([chs_FiltersChanged{chs_XAxis, chs_YAxis, chs_Page, chs_Fact}]);
      end
      else
      if chc_DeletingOneSplit in TfcxCubeChangeAlert(AChangeAlert).ChangesInCube then
      begin
        StartChange;
// find SliceField
        AIndexOfSliceField := FSliceFields.IndexOfCubeField(TfcxCommonField(TfcxCubeSplitsChangeAlert(AChangeAlert).MasterField).Fields.Items[TfcxCubeSplitsChangeAlert(AChangeAlert).SplitFieldIndex]);
        if AIndexOfSliceField > -1 then
        begin
          FXAxisContainer.RemoveField(FSliceFields[AIndexOfSliceField]);
          FYAxisContainer.RemoveField(FSliceFields[AIndexOfSliceField]);
          FPageContainer.RemoveField(FSliceFields[AIndexOfSliceField]);
          FSliceFields.Delete(AIndexOfSliceField);
        end;
      end
    end
    else
    if TfcxCubeChangeAlert(AChangeAlert).CubeChangeType = ccht_NeedRedraw then
    begin
      StartChange;
      StopChange([chs_XAxis, chs_YAxis, chs_Page, chs_Fact]);
    end;
(*
  if AChangeAlert is TfcxCubeChangeAlert then
    if TfcxCubeChangeAlert(AChangeAlert).CubeChangeType = ccht_All then
    begin
      StartChange;
      StopChange([chs_CubeChanged]);
    end
    else
    if TfcxCubeChangeAlert(AChangeAlert).CubeChangeType = ccht_FieldList then
    begin
      StartChange;
{
      if chc_AddedFields in TfcxCubeChangeAlert(AChangeAlert).ChangesInCube then
        FieldsAdded
      else
      if chc_DeletedField in TfcxCubeChangeAlert(AChangeAlert).ChangesInCube then
        FieldDeleted(TfcxCubeChangeAlert(AChangeAlert).FieldIndex)
      else
      if chc_ChangedSplit in TfcxCubeChangeAlert(AChangeAlert).ChangesInCube then
        FieldSplitChanged(TfcxCubeChangeAlert(AChangeAlert).FieldIndex);
}
      StopChange([]);
    end;
*)
end;

function TfcxSlice.ExecuteAction(Action: TBasicAction): Boolean;
begin
  if Action is TfcxAction then
  begin
    if TfcxAction(Action).Owner is TfcxCube then
      CubeChanged(TfcxCube(Action.Owner), TfcxAction(Action).ChangeAlert)
    else
    if TfcxAction(Action).Owner is TfcxFilterManager then
      FilterManagerChanged(TfcxFilterManager(Action.Owner), TfcxAction(Action).ChangeAlert);
    Result := True;
  end else
    Result := False;
end;

procedure TfcxSlice.Transpose;
var
  ATempContainer: TfcxAxisContainer;
begin
  StartChange;
  ATempContainer := FXAxisContainer;
  FXAxisContainer := FYAxisContainer;
  FXAxisContainer.FRegion := rf_CapXAx;
  FYAxisContainer := ATempContainer;
  FYAxisContainer.FRegion := rf_CapYAx;
  FMeasuresContainer.FBaseAxisIsX := not FMeasuresContainer.FBaseAxisIsX;
  StopChange([chs_ChangeAxis]);
end;

procedure TfcxSlice.FilterManagerChanged(Sender: TfcxFilterManager;
  AChangeAlert: TfcxChangeAlert);
begin
  if AChangeAlert is TfcxFiltersChangeAlert then
    if TfcxFiltersChangeAlert(AChangeAlert).FiltersChangeType = fcht_All then
    begin
      StartChange;
      StopChange([chs_FilterManagerChanged]);
    end
    else
    if TfcxFiltersChangeAlert(AChangeAlert).FiltersChangeType = fcht_EnabledItems then
    begin
      StartChange;
      StopChange([chs_FiltersChanged]);
    end
    else
    if TfcxFiltersChangeAlert(AChangeAlert).FiltersChangeType = fcht_DisabledItems then
    begin
//      StartChange;
//      StopChange([chs_FilterManagerChanged]);
    end
    else
    if TfcxFiltersChangeAlert(AChangeAlert).FiltersChangeType = fcht_NeedRedraw then
    begin
      StartChange;
      StopChange([chs_XAxis, chs_YAxis, chs_Page, chs_Fact]);
    end
end;

(*
function TfcxSlice.AddSliceFieldToRegion(ASliceField: TfcxSliceField;
  AName, ACaption: String; ARegion: TfcxRegionOfField; AAgrFunc: TfcxAgrFunc = af_None): Integer;
begin
{ TODO -cNeed : Remake.}
  StartChange;
  case ARegion of
    rf_Page: Result := FPageContainer.Fields.Add(TfcxAxisField.Create(FPageContainer.Fields, ASliceField, AName, ACaption));
    rf_CapXAx: Result := FXAxisContainer.Fields.Add(TfcxAxisField.Create(FXAxisContainer.Fields, ASliceField, AName, ACaption));
    rf_CapYAx: Result := FYAxisContainer.Fields.Add(TfcxAxisField.Create(FYAxisContainer.Fields, ASliceField, AName, ACaption));
    rf_CapFacts: Result := FMeasuresContainer.AddMeasure(ASliceField, AName, ACaption, AAgrFunc);
  else
    Result := -1;
  end;
  StopChange(RegionToChangeTypeMap[ARegion]);
end;
*)

function TfcxSlice.GetFieldsOfRegion(ARegionOfField: TfcxRegionOfField): TfcxCommonFieldsOfRegion;
begin
  case ARegionOfField of
    rf_CapFacts: Result := FMeasuresContainer.FMeasureFields;
    rf_CapXAx: Result := FXAxisContainer.Fields;
    rf_CapYAx: Result := FYAxisContainer.Fields;
    rf_Page: Result := FPageContainer.Fields;
  else
    Result := nil;
  end;
end;

{
function TfcxSlice.TESTGetRealIndexOfRecord(AIndex: Integer;
  AX: boolean): Integer;
begin
  if AX then
    Result := FXAxisContainer.FRecsOfAxis.FRecs.BaseIndex[AIndex]
  else
    Result := FYAxisContainer.FRecsOfAxis.FRecs.BaseIndex[AIndex];
end;

function TfcxSlice.GetTESTDisplayValue(AXIndex, AYIndex: Integer;
  AMeasureIndex: TfcxSmallCount): TfcxString;
var
  ATotal: boolean;
begin
  Result := GetTESTDisplayValueWithTotal(AXIndex, AYIndex, AMeasureIndex, ATotal)
end;

function TfcxSlice.GetTESTDisplayValueWithTotal(AXIndex, AYIndex: Integer;
  AMeasureIndex: TfcxSmallCount; var ATotal: boolean): TfcxString;
var
  ABaseIndex, ASecondIndex: Integer;
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  AMeasureValue: PfcxMeasureValue;
begin
  Result := 'error';
  if FMeasuresContainer.FBaseAxisIsX then
  begin
    ABaseIndex := AXIndex;
    ASecondIndex := AYIndex;
  end
  else
  begin
    ABaseIndex := AYIndex;
    ASecondIndex := AXIndex;
  end;
  if (ABaseIndex < 0) or (ABaseIndex > (FMeasuresContainer.FCount - 1)) then
    Exit;
  if (ASecondIndex < 0) or (ASecondIndex > (FMeasuresContainer.FSecondCount - 1)) then
    Exit;
  if (AMeasureIndex < 0) or (AMeasureIndex > (FMeasuresContainer.Count - 1)) then
    Exit;
  AMeasureValue := FMeasuresContainer.Cells[ABaseIndex, ASecondIndex, AMeasureIndex];
  ABaseLevel := FMeasuresContainer.FBaseContainer.FLevelOf[ABaseIndex];
  ASecondLevel := FMeasuresContainer.FSecondContainer.FLevelOf[ASecondIndex];
  Result := inttostr(ABaseLevel) + ':' + inttostr(ASecondLevel) + ':' + inttostr(AMeasureIndex);

  ATotal := (ABaseLevel < (FMeasuresContainer.FBaseContainer.FLevelCount - 1)) or
            (ASecondLevel < (FMeasuresContainer.FSecondContainer.FLevelCount - 1));
  if AMeasureValue = nil then
    Result := Result + ' empty'
  else
  begin
    Result := Result + ' ' + vartostr(cfcMeasureProcessorMap[FMeasuresContainer.FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc].GetItem(AMeasureValue));
  end
end;
}

function TfcxSlice.GetColCount: Integer;
begin
  if Assigned(FXAxisContainer) then
    Result := FXAxisContainer.FVisibleAxisNodes.Count
  else
    Result := 0;
end;

function TfcxSlice.GetRowCount: Integer;
begin
  if Assigned(FYAxisContainer) then
    Result := FYAxisContainer.FVisibleAxisNodes.Count
  else
    Result := 0;
end;

procedure TfcxSlice.GetMeasureCell(ACol, ARow: Integer; out Result: TfcxMeasureCell);
var
  ATotalIndexBaseLevel, ATotalIndexSecondLevel: Integer;
  ATotalBaseLevel, ATotalSecondLevel: TfcxSmallCount;
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  AIndexInBaseLevel, AIndexInSecondLevel: Integer;
  AMeasureValue, ATotalMeasureValue: PfcxMeasureValue;
  AAgrFunc: TfcxAgrFunc;
  ATotalValue: Variant;
  AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
begin
  FillChar(Result, SizeOf(Result), 0);
  Result.Row := ARow;
  Result.Col := ACol;
  Result.Alignment := taRightJustify;
  if FMeasuresContainer.FIsCleared or (FMeasuresContainer.FContainer = FPageContainer) or (FMeasuresContainer.VisibleCount = 0) then
  begin
    Result.MeasureIndex := -1;
    Exit;
  end;
  Result.StrValue := 'error';
  if not ColRowToDataCellPos(ACol, ARow, ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex) then
    Exit;
{$IFDEF TRIAL}
  Result.IsTrial := Random(3) = 1;
{$ENDIF}
  Result.MeasureIndex := AMeasureIndex;
  if FMeasuresContainer.FBaseAxisIsX then
  begin
    Result.BaseIndex := FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol];
    Result.SecondIndex := FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow];
  end
  else
  begin
    Result.BaseIndex := FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow];
    Result.SecondIndex := FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol];
  end;

  if ABaseAdditionalTotalIndex <> -1 then
    if ASecondAdditionalTotalIndex <> -1 then
      AMeasureValue := nil
    else
      AMeasureValue := FMeasuresContainer.BaseAdditionalTotalCells[ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, Result.MeasureIndex, ABaseAdditionalTotalIndex]
  else
  if ASecondAdditionalTotalIndex <> -1 then
    AMeasureValue := FMeasuresContainer.SecondAdditionalTotalCells[ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, Result.MeasureIndex, ASecondAdditionalTotalIndex]
  else
    AMeasureValue := FMeasuresContainer.Cells[ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, Result.MeasureIndex];
  {$ifdef fc_debug}
  Result.StrValue := inttostr(ABaseLevel) + ':' + inttostr(ASecondLevel) + ':' + inttostr(Result.MeasureIndex);
  {$endif}

  Result.IsTotal := (ABaseLevel < (FMeasuresContainer.FBaseContainer.FLevelCount - 1)) or
                    (ASecondLevel < (FMeasuresContainer.FSecondContainer.FLevelCount - 1));
  Result.IsGrandTotal := (ABaseLevel = -1) and (FMeasuresContainer.FBaseContainer.FLevelCount > 0) or
                         (ASecondLevel = -1) and (FMeasuresContainer.FSecondContainer.FLevelCount > 0);
  Result.Alignment := FMeasuresContainer.FMeasureFields[Result.MeasureIndex].Alignment;
{$IFDEF TRIAL}
  if Result.IsTrial then
  begin
    Result.Value := TrialString;
    Result.StrValue := TrialString;
    Exit;
  end;
{$ENDIF}
  if (AMeasureValue = nil) and not FMeasuresContainer.FMeasureFields[Result.MeasureIndex].ChangeNullToZero then
  begin
    Result.Value := Null;
    Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}'';
  end
  else
  begin
    if ABaseAdditionalTotalIndex <> -1 then
      AAgrFunc := FMeasuresContainer.FSaveAdditionalTotalsBaseLevels[ABaseLevel, ABaseAdditionalTotalIndex]
    else
    if ASecondAdditionalTotalIndex <> -1 then
      AAgrFunc := FMeasuresContainer.FSaveAdditionalTotalsSecondLevels[ASecondLevel, ASecondAdditionalTotalIndex]
    else
      AAgrFunc := FMeasuresContainer.FMeasureFields[Result.MeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc;
    if (AMeasureValue <> nil) then
      Result.Value := AMeasureValue.Value
    else
      Result.Value := 0;
    if TVarData(Result.Value).VType > 1 then
      if (FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayAs = da_Value) or (ABaseAdditionalTotalIndex <> -1) or (ASecondAdditionalTotalIndex <> -1) then
        if AAgrFunc <> af_ListOfUnique then
          Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayFormat.FormatData(Result.Value, nil)
        else
          Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}Result.Value
      else
      if FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayAs in da_Percents then
      begin
        ATotalIndexBaseLevel := AIndexInBaseLevel;
        ATotalIndexSecondLevel := AIndexInSecondLevel;
        ATotalBaseLevel := ABaseLevel;
        ATotalSecondLevel := ASecondLevel;
// get total
        if FMeasuresContainer.FBaseAxisIsX then
        begin
          case FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayAs of
            da_RowPercentLevel:
              begin
                if ATotalBaseLevel <> - 1 then
                begin
                  Dec(ATotalBaseLevel);
                  ATotalIndexBaseLevel := FXAxisContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent;
                end;
              end;
            da_RowPercentTotal:
              begin
                ATotalBaseLevel := -1;
                ATotalIndexBaseLevel := 0;
              end;
            da_ColPercentLevel:
              begin
                if ATotalSecondLevel <> -1 then
                begin
                  Dec(ATotalSecondLevel);
                  ATotalIndexSecondLevel := FYAxisContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent;
                end;
              end;
            da_ColPercentTotal:
              begin
                ATotalSecondLevel := -1;
                ATotalIndexSecondLevel := 0;
              end;
            da_GrandTotalPercent:
              begin
                ATotalSecondLevel := -1;
                ATotalIndexSecondLevel := 0;
                ATotalBaseLevel := -1;
                ATotalIndexBaseLevel := 0;
              end;
          end;
        end
        else
        begin
          case FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayAs of
            da_RowPercentLevel:
              begin
                if ATotalSecondLevel <> -1 then
                begin
                  Dec(ATotalSecondLevel);
                  ATotalIndexSecondLevel := FXAxisContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent;
                end;
              end;
            da_RowPercentTotal:
              begin
                ATotalSecondLevel := -1;
                ATotalIndexSecondLevel := 0;
              end;
            da_ColPercentLevel:
              begin
                if ATotalBaseLevel <> -1 then
                begin
                  Dec(ATotalBaseLevel);
                  ATotalIndexBaseLevel := FYAxisContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent;
                end;
              end;
            da_ColPercentTotal:
              begin
                ATotalBaseLevel := -1;
                ATotalIndexBaseLevel := 0;
              end;
            da_GrandTotalPercent:
              begin
                ATotalSecondLevel := -1;
                ATotalIndexSecondLevel := 0;
                ATotalBaseLevel := -1;
                ATotalIndexBaseLevel := 0;
              end;
          end;
        end;
        ATotalMeasureValue := FMeasuresContainer.Cells[ATotalBaseLevel, ATotalSecondLevel, ATotalIndexBaseLevel, ATotalIndexSecondLevel,  Result.MeasureIndex];
        if (ATotalMeasureValue = nil) and not FMeasuresContainer.FMeasureFields[Result.MeasureIndex].ChangeNullToZero then
        begin
          Result.Value := Null;
          Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}'';
        end
        else
        begin
          if (ATotalMeasureValue <> nil) then
            ATotalValue := ATotalMeasureValue.Value
          else
            ATotalValue := 0;
          try
            if ATotalValue = 0 then
              Result.Value := 0
            else
              Result.Value := Result.Value / ATotalValue;
            Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}Format('%2.2n', [Extended(Result.Value * 100)]) + '%'
          except
            Result.Value := Null;
            Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}fcxResources.GetAnsi('SError');
          end;
        end
      end
      else
      if FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayAs in da_Ranks then
      begin
        Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}IntToStr(FMeasuresContainer.CellRanks[ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, Result.MeasureIndex, FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayAs = da_RowRank])
      end
      else
//      if FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayAs = da_Value then
        Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}FMeasuresContainer.FMeasureFields[Result.MeasureIndex].DisplayFormat.FormatData(Result.Value, nil)
    else
      Result.StrValue := {$ifdef fc_debug}Result.StrValue + ' ' + {$endif}'';
  end;
end;

procedure TfcxSlice.SetDefaultColWidth(const Value: Integer);
begin
  if FDefaultColWidth <> Value then
  begin
    StartChange;
    FDefaultColWidth := Value;
    StopChange([chs_DefaultColWidth]);
  end;
end;

procedure TfcxSlice.SetDefaultRowHeight(const Value: Integer);
begin
  if FDefaultRowHeight <> Value then
  begin
    StartChange;
    FDefaultRowHeight := Value;
    StopChange([chs_DefaultRowHeight]);
  end;
end;

function TfcxSlice.GetColWidth(AColIndex: integer): Smallint;
begin
  Result := FXAxisContainer.CellSize[AColIndex]
end;

function TfcxSlice.GetRowHeight(ARowIndex: integer): Smallint;
begin
  Result := FYAxisContainer.CellSize[ARowIndex]
end;

procedure TfcxSlice.SetColWidth(AColIndex: integer;
  const Value: Smallint);
begin
  FXAxisContainer.CellSize[AColIndex] := Value
end;

procedure TfcxSlice.SetRowHeight(ARowIndex: integer;
  const Value: Smallint);
begin
  FYAxisContainer.CellSize[ARowIndex] := Value
end;

procedure TfcxSlice.RemoveSliceFieldFromContainers(ASliceField: TfcxSliceField);
begin
  StartChange;
  XAxisContainer.RemoveField(ASliceField);
  YAxisContainer.RemoveField(ASliceField);
  PageContainer.RemoveField(ASliceField);
  StopChange([]);
end;

procedure TfcxSlice.ResetDisplayLabelOfFields;
var
  I: Integer;
begin
  StartChange;
  for I := 0 to SliceFields.Count - 1 do
    if SliceFields[I] is TfcxCommonSliceCubeField then
      SliceFields[I].Caption := TfcxCommonSliceCubeField(SliceFields[I]).CubeField.CubeFieldDisplayLabel;
  StopChange([]);
end;

function TfcxSlice.ResetUnattachedFieldsFilter: boolean;
var
  I: integer;
begin
  Result := False;
  for I := 0 to SliceFields.Count - 1 do
    if SliceFields[i].UVFilteredValuesCount > 0 then
      if not (XAxisContainer.ContainField(SliceFields[i]) or
         YAxisContainer.ContainField(SliceFields[i]) or
         PageContainer.ContainField(SliceFields[i])) then
      begin
        SliceFields[i].SetAllFilter;
        Result := True;
      end;
end;

procedure TfcxSlice.ApplyDefaultSettings;
var
  I: Integer;
begin
  if not Assigned(XAxisContainer) then
    Exit;
  BeginUpdate;
  XAxisContainer.GrandTotalPosition := Settings.XAxisSettings.GrandTotalPosition;
  XAxisContainer.ShowTotalAs := Settings.XAxisSettings.ShowTotalAs;
  YAxisContainer.GrandTotalPosition := Settings.YAxisSettings.GrandTotalPosition;
  YAxisContainer.ShowTotalAs := Settings.YAxisSettings.ShowTotalAs;
  for I := 0 to XAxisContainer.Fields.Count - 1 do
  begin
    XAxisContainer.Fields[I].TotalPosition := Settings.AxisFieldSettings.TotalPosition;
    XAxisContainer.Fields[I].SortDirection := Settings.AxisFieldSettings.SortDirection;
  end;
  for I := 0 to YAxisContainer.Fields.Count - 1 do
  begin
    YAxisContainer.Fields[I].TotalPosition := Settings.AxisFieldSettings.TotalPosition;
    YAxisContainer.Fields[I].SortDirection := Settings.AxisFieldSettings.SortDirection;
  end;
  for I := 0 to PageContainer.Fields.Count - 1 do
  begin
    PageContainer.Fields[I].TotalPosition := Settings.AxisFieldSettings.TotalPosition;
    PageContainer.Fields[I].SortDirection := Settings.AxisFieldSettings.SortDirection;
  end;
  EndUpdate;
end;

procedure TfcxSlice.BeginUpdate;
begin
  StartChange;
end;

procedure TfcxSlice.EndUpdate;
begin
  StopChange([]);
end;

function TfcxSlice.GetSliceField(AIndex: integer): TfcxSliceField;
begin
  Result := FSliceFields[AIndex]
end;

procedure TfcxSlice.SetAutoUVFilter(const Value: Boolean);
begin
  FAutoUVFilter := Value;
end;

function TfcxSlice.GetSliceFieldCount: Integer;
begin
  Result := FSliceFields.Count;
end;

procedure TfcxSlice.Loaded;
begin
  inherited;
  ApplyDefaultSettings;
  if Assigned(FOnInterpreterCreated) then
    Interpreter.DoInterpreterCreated(Self, OnInterpreterCreated);
end;

function TfcxSlice.LoadFromFile(AFileName: String): Boolean;
var
  Stream: TStream;
begin
  Stream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
  try
    Result := LoadFromStream(Stream);
  finally
    Stream.Free;
  end;
end;

function TfcxSlice.LoadFromXML(XMLDoc: TfcxXMLDocument): Boolean;

  procedure LoadSelection(Item: TfcxXMLItem);
  begin
    FSelectedCol := Item.IntProp['col'];
    FSelectedRow := Item.IntProp['row'];
    FSelectedMeasure := Item.IntProp['measure'];
    FSelectedColAdditionalTotal := -1;
    FSelectedRowAdditionalTotal := -1;
  end;

  procedure LoadSelectionForSort(Item: TfcxXMLItem);
  begin
    XAxisContainer.FSelectedLevelIndex := Item.IntProp['xlevelindex'];
    XAxisContainer.FSelectedIndexInLevel := Item.IntProp['xindexinlevel'];
    XAxisContainer.FSelectedMeasureIndex := Item.IntProp['xmeasureindex'];
    XAxisContainer.FSelectedAdditionalTotalIndex := Item.IntProp['xadditionaltotalindex'];
    YAxisContainer.FSelectedLevelIndex := Item.IntProp['ylevelindex'];
    YAxisContainer.FSelectedIndexInLevel := Item.IntProp['yindexinlevel'];
    YAxisContainer.FSelectedMeasureIndex := Item.IntProp['ymeasureindex'];
    YAxisContainer.FSelectedAdditionalTotalIndex := Item.IntProp['yadditionaltotalindex'];
    FSortSelectionLoaded := True;
  end;

  function LoadField(Item: TfcxXMLItem; AFieldsContainer: TfcxFieldsContainer; AMeasurePosition: integer): Integer;
  var
    AField: TfcxSliceField;
  begin
    if Item.Prop['field_name'] = sMeasuresFieldName then
    begin
      AFieldsContainer.InsertMeasuresField(AMeasurePosition);
      MeasuresContainer.FAlignment := TAlignment(GetEnumValue(TypeInfo(TAlignment), Item.Prop['alignment']));
{
      MeasuresContainer.SetContainer(FieldsOfRegion[ARegion].Container as TfcxFieldsContainer);
      MeasuresContainer.SetPosition(AMeasurePosition);
}
      Result := -1;
    end
    else
    begin
      AField := SliceFields.ItemByName[Item.Prop['field_name']];
      if Assigned(AField) then
      begin
        // todo: Prop['filter']
        Result := AFieldsContainer.Fields.Count;
        if Item.Prop['rfname'] = '' then
          AFieldsContainer.InsertField(AField, Result, Item.Prop['caption'], Item.Prop['caption'])
        else
          AFieldsContainer.InsertField(AField, Result, Item.Prop['rfname'], Item.Prop['caption'])
      end
      else
        Result := -1;
    end;
  end;

  function LoadMeasure(Item: TfcxXMLItem; AAgrFunc: TfcxAgrFunc): Integer;
  var
    AField: TfcxSliceField;
  begin
    AField := SliceFields.ItemByName[Item.Prop['field_name']];
    if AField = nil then
      if Item.Prop['field_name'] <> '' then
      begin
        result := -1;
        exit;
      end;
    if Item.Prop['rfname'] = '' then
      Result := MeasuresContainer.AddMeasure(AField, Item.Prop['caption'], Item.Prop['caption'], AAgrFunc)
    else
      Result := MeasuresContainer.AddMeasure(AField, Item.Prop['rfname'], Item.Prop['caption'], AAgrFunc);
  end;

  procedure LoadPageFields(Item: TfcxXMLItem);
  var
    i: integer;
  begin
    for i := 0 to Item.Count - 1 do
      LoadField(Item[i], PageContainer, i);
  end;

  procedure LoadAxisFields(Item: TfcxXMLItem; AFieldsContainer: TfcxFieldsContainer);
  var
    AField: TfcxAxisField;
    i, idx: integer;
  begin
    for i := 0 to Item.Count - 1 do
    begin
      idx := LoadField(Item[i], AFieldsContainer, i);
      if idx <> -1 then
      begin
        AField := AFieldsContainer.FFields[idx] as TfcxAxisField;
        if Item[i].IntProp['order'] = 1 then
          AField.SortDirection := fcsd_Asc
        else
          AField.SortDirection := fcsd_Desc;
        if Item[i].BoolProp['make_total'] then
          AField.TotalPosition := fctp_After
        else
          AField.TotalPosition := fctp_Hide;
        AField.SliceField.FCollapsed := not Item[i].BoolProp['expanded'];
        AField.FAlignment := TAlignment(GetEnumValue(TypeInfo(TAlignment), Item[i].Prop['alignment']));
      end
    end;
    TfcxAxisContainer(AFieldsContainer).AxisType := at_Standard;
    TfcxAxisContainer(AFieldsContainer).AdditionalGrandTotalFunctions := [];
  end;

  procedure LoadMeasureFields(Item: TfcxXMLItem);
  var
    i, idx: integer;
    AgrFunc: TfcxAgrFunc;
    FunctionName, Formula: String;
  begin
    for i := 0 to Item.Count - 1 do
    begin
      with Item[i] do
      begin
        AgrFunc := TfcxAgrFunc(GetEnumValue(TypeInfo(TfcxAgrFunc), Prop['aggregate']));
        if BoolProp['calculated'] then
          idx := LoadMeasure(Item[i], af_Formula)
        else
          idx := LoadMeasure(Item[i], AgrFunc);
        if idx <> -1 then
        begin
          if BoolProp['calculated'] then
          begin
            FunctionName := 'func_' + IntToStr(idx);
            MeasuresContainer.Measures[idx].ScriptFunction := FunctionName;
            MeasuresContainer.Measures[idx].ScriptOrder := IntProp['calculation_order'];
            if AgrFunc <> af_Formula then
            begin
              MeasuresContainer.Measures[idx].FUseDifferentAggForTotals := True;
              MeasuresContainer.Measures[idx].FAgrFuncForTotals := AgrFunc;
            end;
            // add a new formula to the common script
            Formula := Prop['formula'];
            Script.Insert(0, DoConvertScript(FunctionName, Formula));
          end;
          if Prop['filter'] <> '' then
          begin
            FunctionName := 'func_f' + IntToStr(idx);
            MeasuresContainer.Measures[idx].FilterScriptFunction := FunctionName;
            // add a new formula to the common script
            Formula := Prop['filter'];
            Script.Insert(0, DoConvertScript(FunctionName, Formula));
          end;
          MeasuresContainer.Measures[idx].FDisplayAs := TfcxDisplayAs(GetEnumValue(TypeInfo(TfcxDisplayAs), Prop['display']));
          MeasuresContainer.Measures[idx].FVisible := BoolProp['visible'];
          MeasuresContainer.Measures[idx].FAlignment := TAlignment(GetEnumValue(TypeInfo(TAlignment), Prop['alignment']));
          if Find('display_format') <> -1 then
            MeasuresContainer.Measures[idx].DisplayFormat.FromXML(FindItem('display_format'));
          {if Find('percent_format') <> -1 then
            MeasuresContainer.Measures[idx].PercentFormat.FromXML(FindItem('percent_format'))
          else
            MeasuresContainer.Measures[idx].PercentFormat.Assign(FCube.DefaultFormat.PercentFormat);}
          MeasuresContainer.Measures[idx].Highlights.LoadRangeXML(FindItem('ranges'));
        end;
      end;
    end;
  end;

  procedure LoadFields(Item: TfcxXMLItem);
  var
    i, j, id, AIndex: integer;
    s: String;
    SaveFiltersByValue: Boolean;
    SaveFiltersEnabledValues{, AInversed}: Boolean;
    AfcVarType: TfcxVarType;
    AVarValue: Variant;
  begin
    for i := 0 to Item.Count - 1 do
      if Item[i].Name = 'field' then
      begin
        s := Item[i].Prop['name'];
        if (s <> '') and SliceFields.Find(s, AIndex) then
        begin
          SliceFields[AIndex].SetCaption(Item[i].Prop['caption']);
        end
        else
          Continue;
        if Item[i].PropExists('Width') then
          SliceFields[AIndex].Width := Item[i].IntProp['Width'];
        if Item[i].PropExists('Height') then
          SliceFields[AIndex].Height := Item[i].IntProp['Height'];
        if Item[i].PropExists('PopupWidth') then
          SliceFields[AIndex].PopupWidth := Item[i].IntProp['PopupWidth'];
        if Item[i].PropExists('PopupShowAvailable') then
          SliceFields[AIndex].PopupShowAvailable := Item[i].BoolProp['PopupShowAvailable'];

        if Item[i].PropExists('SaveFiltersByValue') then
          SaveFiltersByValue := Item[i].BoolProp['SaveFiltersByValue']
        else
          SaveFiltersByValue := False;
        if Item[i].PropExists('SaveFiltersEnabledValues') then
          SaveFiltersEnabledValues := Item[i].BoolProp['SaveFiltersEnabledValues']
        else
          SaveFiltersEnabledValues := False;
        if Item[i].PropExists('fcVarType') then
          AfcVarType := TfcxVarType(GetEnumValue(TypeInfo(TfcxVarType), Item[i].Prop['fcVarType']))
        else
          AfcVarType := fcvtString;
//        AInversed := False;
        if SaveFiltersByValue then
        begin
          if SaveFiltersEnabledValues then
            SliceFields[AIndex].InverseFilter;
          for j := 0 to Item[i].Count - 1 do
          begin
            case AfcVarType of
              fcvtDate:
                AVarValue := Item[i][j].DateProp['value'];
              fcvtOrdinal:
                AVarValue := Item[i][j].IntProp['value'];
              fcvtFloat:
                AVarValue := Item[i][j].FloatProp['value'];
            else
              AVarValue := Item[i][j].Prop['value'];
            end;
            SliceFields[AIndex].UVFilterOfValue[AVarValue] := SaveFiltersEnabledValues;
          end
        end
        else
        begin
          SliceFields[AIndex].BeginUpdateFieldFilter;
          if SaveFiltersEnabledValues then
            SliceFields[AIndex].InverseFilter;
          for j := 0 to Item[i].Count - 1 do
          begin
            id := Item[i][j].IntProp['id'];
            if id < SliceFields[AIndex].UVCount then
              SliceFields[AIndex].UVFilterOf[id] := SaveFiltersEnabledValues;
          end;
          SliceFields[AIndex].EndUpdateFieldFilter;
        end;
      end;
  end;

  procedure LoadOptions(Item: TfcxXMLItem; version: Integer);
  begin
    if version = 1 then
    begin
      XAxisContainer.DefaultTypeSort := TfcxTypeSortAxis(GetEnumValue(TypeInfo(TfcxTypeSortAxis), Item.Prop['sort_x']));
      YAxisContainer.DefaultTypeSort := TfcxTypeSortAxis(GetEnumValue(TypeInfo(TfcxTypeSortAxis), Item.Prop['sort_y']));
      HideColZeros := Item.BoolProp['hide_col_zeros'];
      HideRowZeros := Item.BoolProp['hide_row_zeros'];
    end
    else
    begin
      HideColZeros := Item.BoolProp['HideColZeros'];
      HideRowZeros := Item.BoolProp['HideRowZeros'];
    end;
{
    if Item.PropExists('PopUpFieldListWidth') then
      PopUpFieldListWidth := Item.IntProp['PopUpFieldListWidth']
    if Item.PropExists('FieldsOrder') then
      FieldsOrder := TfcxFieldsOrder(GetEnumValue(TypeInfo(TfcxFieldsOrder), Item.Prop['FieldsOrder']));
    if Item.PropExists('ShowSplitFieldsInFieldList') then
      ShowSplitFieldsInFieldList := TfcxShowSplitFields(GetEnumValue(TypeInfo(TfcxShowSplitFields), Item.Prop['ShowSplitFieldsInFieldList']));
}
  end;

  procedure LoadScript(Item: TfcxXMLItem);
  var
    Language: String;
  begin
    Language := Item.Prop['ScriptLanguage'];
    if Interpreter.SupportsScriptLanguage(Language) then
    begin
      ScriptLanguage := Language;
      Script.Text := Item.Prop['Code'];
    end;
  end;

  procedure LoadSizes(Item: TfcxXMLItem);
{  var
    i: integer;
    SubItem: TfcxXMLItem;}
  begin
{
    SubItem := Item.FindItem('y_sizes');
    for i := 0 to SubItem.Count - 1 do
    begin
      YAxisColWidth[SubItem[i].IntProp['item']] := SubItem[i].IntProp['width'];
    end;
    SubItem := Item.FindItem('x_sizes');

//    for i := 0 to SubItem.Count - 1 do
//    begin
//      XAxisColWidth[SubItem[i].IntProp['item']] := SubItem[i].IntProp['width'];
//    end;

    // obsolete: remove in 2.0
    if (FCube <> nil) then
    begin
      if Active then
      begin
        Build;
        for i := 0 to SubItem.Count - 1 do
        begin
          XAxisColWidth[SubItem[i].IntProp['item']] := SubItem[i].IntProp['width'];
        end;
      end;
    end;
}
  end;

  procedure LoadCharts(Item: TfcxXMLItem);
  begin
    ChartsManager.LoadFromXMLItem(Item);
  end;

  procedure LoadFromOldXML;
  var
    i: integer;
    XMLItem: TfcxXMLItem;
  begin
    for i := 0 to XMLDoc.Root.Count - 1 do
    begin
      XMLItem := XMLDoc.Root[i];
      if XMLItem.Name = 'selection' then
        LoadSelection(XMLItem) else
      if XMLItem.Name = 'page_fields' then
        LoadPageFields(XMLItem) else
      if XMLItem.Name = 'xaxis_fields' then
        LoadAxisFields(XMLItem, XAxisContainer) else
      if XMLItem.Name = 'yaxis_fields' then
        LoadAxisFields(XMLItem, YAxisContainer) else
      if XMLItem.Name = 'measure_fields' then
        LoadMeasureFields(XMLItem) else
      if XMLItem.Name = 'fields' then
        LoadFields(XMLItem) else
      if XMLItem.Name = 'options' then
        LoadOptions(XMLItem, 1) else
      if XMLItem.Name = 'sizes' then
        LoadSizes(XMLItem) else
      if XMLItem.Name = 'charts' then
        LoadCharts(XMLItem);
    end;
  end;

var
  XMLItem: TfcxXMLItem;
  I: integer;
begin
  if XMLDoc.Root.Name <> 'slice' then
  begin
    Result := False;
    exit;
  end;
  FSortSelectionLoaded := False;
  StartChange;
  Clear;
  if not XMLDoc.Root.PropExists('version') or
     (XMLDoc.Root.IntProp['version'] < 2) then
    LoadFromOldXML
  else
  begin
    for I := 0 to XMLDoc.Root.Count - 1 do
    begin
      XMLItem := XMLDoc.Root[i];
      if XMLItem.Name = 'selection' then
        LoadSelection(XMLItem) else
      if XMLItem.Name = 'sortselection' then
        LoadSelectionForSort(XMLItem) else
      if XMLItem.Name = 'page_fields' then
        PageContainer.LoadFromXML(XMLItem) else
      if XMLItem.Name = 'xaxis_fields' then
        XAxisContainer.LoadFromXML(XMLItem) else
      if XMLItem.Name = 'yaxis_fields' then
        YAxisContainer.LoadFromXML(XMLItem) else
      if XMLItem.Name = 'measure_fields' then
        MeasuresContainer.LoadFromXML(XMLItem) else
      if XMLItem.Name = 'topn' then
        TopNs.LoadFromXML(XMLItem) else
      if XMLItem.Name = 'fields' then
        SliceFields.LoadFromXML(XMLItem) else
      if XMLItem.Name = 'options' then
        LoadOptions(XMLItem, 2) else
      if XMLItem.Name = 'Script' then
        LoadScript(XMLItem) else
      if XMLItem.Name = 'groups' then
        fCube.LoadGroupsFromXMLItem(XMLItem) else
      if XMLItem.Name = 'filters' then
        FilterManager.LoadFromXMLItem(XMLItem) else
      if XMLItem.Name = 'charts' then
        ChartsManager.LoadFromXMLItem(XMLItem) else
    end;
  end;
  StopChange([chs_FiltersChanged, chs_CapXAxis, chs_CapYAxis, chs_Load]);
  Result := True;
end;

procedure TfcxSlice.SaveToFile(AFileName: String; AStoreItems: TfcxItemsForStoreWithSlice = []);
var
  Stream: TStream;
begin
  Stream := TFileStream.Create(AFileName, fmCreate);
  try
    SaveToStream(Stream, AStoreItems);
  finally
    Stream.Free;
  end;
end;

procedure TfcxSlice.SaveToXML(XMLDoc: TfcxXMLDocument; AStoreItems: TfcxItemsForStoreWithSlice = []);

  procedure SaveSelection(AItem: TfcxXMLItem);
  begin
    AItem.Name := 'selection';
    AItem.IntProp['col'] := FSelectedCol;
    AItem.IntProp['row'] := FSelectedRow;
    AItem.IntProp['measure'] := FSelectedMeasure;
  end;

  procedure SaveSelectionForSort(AItem: TfcxXMLItem);
  begin
    AItem.Name := 'sortselection';
    AItem.IntProp['xlevelindex'] := XAxisContainer.FSelectedLevelIndex;
    AItem.IntProp['xindexinlevel'] := XAxisContainer.FSelectedIndexInLevel;
    AItem.IntProp['xmeasureindex'] := XAxisContainer.FSelectedMeasureIndex;
    AItem.IntProp['xadditionaltotalindex'] := XAxisContainer.FSelectedAdditionalTotalIndex;
    AItem.IntProp['ylevelindex'] := YAxisContainer.FSelectedLevelIndex;
    AItem.IntProp['yindexinlevel'] := YAxisContainer.FSelectedIndexInLevel;
    AItem.IntProp['ymeasureindex'] := YAxisContainer.FSelectedMeasureIndex;
    AItem.IntProp['yadditionaltotalindex'] := YAxisContainer.FSelectedAdditionalTotalIndex;
  end;

  procedure SaveOptions(AItem: TfcxXMLItem);
  begin
    AItem.Name := 'options';
    AItem.BoolProp['HideColZeros'] := HideColZeros;
    AItem.BoolProp['HideRowZeros'] := HideRowZeros;
  end;

  procedure SaveScript(AItem: TfcxXMLItem);
  begin
    AItem.Name := 'Script';
    AItem.Prop['ScriptLanguage'] := ScriptLanguage;
    AItem.Prop['Code'] := Script.Text;
  end;

begin
  XMLDoc.Root.Name := 'slice';
  XMLDoc.Root.IntProp['version'] := 2;
  XMLDoc.Root.Prop['timestamp'] := DateTimeToStr(Now);
  SaveScript(XMLDoc.Root.Add);
  SliceFields.SaveToXML(XMLDoc.Root.Add);
  PageContainer.SaveToXML(XMLDoc.Root.Add);
  XAxisContainer.SaveToXML(XMLDoc.Root.Add);
  YAxisContainer.SaveToXML(XMLDoc.Root.Add);
  MeasuresContainer.SaveToXML(XMLDoc.Root.Add);
  TopNs.SaveToXML(XMLDoc.Root.Add);
  SaveSelection(XMLDoc.Root.Add);
  SaveSelectionForSort(XMLDoc.Root.Add);
  SaveOptions(XMLDoc.Root.Add);
  if fcxiss_Groups in AStoreItems then
    fCube.SaveGroupsToXMLItem(XMLDoc.Root.Add);
  if fcxiss_Filters in AStoreItems then
    FilterManager.SaveToXMLItem(XMLDoc.Root.Add);
  if fcxiss_Charts in AStoreItems then
    ChartsManager.SaveToXMLItem(XMLDoc.Root.Add);
end;

procedure TfcxSlice.Clear;
begin
  StartChange;
  DestroySlice(False);
  CreateSlice;
  Interpreter.Clear;
  fcxEmptyCode(Script, ScriptLanguage);
  StopChange([chs_CapXAxis, chs_CapYAxis, chs_CapPage])
end;

procedure TfcxSlice.InternalSetSelected(ASelectedMeasure, ASelectedCol, ASelectedRow, ASelectedColAdditionalTotal, ASelectedRowAdditionalTotal: Integer);
begin
  FSelectedColAdditionalTotal := ASelectedColAdditionalTotal;
  FSelectedRowAdditionalTotal := ASelectedRowAdditionalTotal;
  FSelectedMeasure := ASelectedMeasure;
  FSelectedCol := ASelectedCol;
  FSelectedRow := ASelectedRow;
end;

procedure TfcxSlice.InternalSetSelectedVis(ASelectedMeasure, ASelectedColVis, ASelectedRowVis: Integer);
begin
  if not Assigned(FXAxisContainer.FVisibleAxisNodes.FIndexes) then
    if not Assigned(FYAxisContainer.FVisibleAxisNodes.FIndexes) then
      InternalSetSelected(ASelectedMeasure, -1, -1, -1, -1)
    else
      InternalSetSelected(ASelectedMeasure, -1, FYAxisContainer.FVisibleAxisNodes.FIndexes[ASelectedRowVis], -1, FYAxisContainer.FVisibleAxisNodes.FAdditionalTotalsIndexes[ASelectedRowVis])
  else
    if not Assigned(FYAxisContainer.FVisibleAxisNodes.FIndexes) then
      InternalSetSelected(ASelectedMeasure, FXAxisContainer.FVisibleAxisNodes.FIndexes[ASelectedColVis], -1, FXAxisContainer.FVisibleAxisNodes.FAdditionalTotalsIndexes[ASelectedColVis], -1)
    else
      InternalSetSelected(ASelectedMeasure, FXAxisContainer.FVisibleAxisNodes.FIndexes[ASelectedColVis], FYAxisContainer.FVisibleAxisNodes.FIndexes[ASelectedRowVis], FXAxisContainer.FVisibleAxisNodes.FAdditionalTotalsIndexes[ASelectedColVis], FYAxisContainer.FVisibleAxisNodes.FAdditionalTotalsIndexes[ASelectedRowVis]);
end;

procedure TfcxSlice.SetHideColZeros(const Value: Boolean);
begin
  if FHideColZeros <> Value then
  begin
    StartChange;
    FHideColZeros := Value;
    if not Value then
      FXAxisContainer.ClearHideZeros;
    StopChange([chs_NeedFillVisibleX]);
  end;
end;

procedure TfcxSlice.SetHideRowZeros(const Value: Boolean);
begin
  if FHideRowZeros <> Value then
  begin
    StartChange;
    FHideRowZeros := Value;
    if not Value then
      FYAxisContainer.ClearHideZeros;
    StopChange([chs_NeedFillVisibleY]);
  end;
end;

procedure TfcxSlice.SetScript(const Value: TStrings);
begin
  Interpreter.Script.Assign(Value);
end;

function TfcxSlice.GetScript: TStrings;
begin
  Result := Interpreter.Script;
end;

procedure TfcxSlice.AddScriptVariables;
begin
  Interpreter.AddVariable('Dimensions', 'TDimensions', PtrUInt(FDimensions));
  Interpreter.AddVariable('Measures', 'TMeasures', PtrUInt(FMeasures));
  Interpreter.AddVariable('SliceFields', 'TfcxSliceFields', PtrUInt(FSliceFields));
  Interpreter.AddVariable('CustomObject', 'TfcxCustomObject', PtrUInt(FCustomObject));
end;

procedure TfcxSlice.AddScriptExpressions;
var
  I, J: Integer;
  Expressions: TfcxExpressions;
begin
  // add measures
  for I := 0 to MeasuresContainer.MeasureFields.Count - 1 do
  begin
    Expressions := MeasuresContainer.MeasureFields[I].GetExpressions;
    for J := 0 to Length(Expressions) - 1 do
      Interpreter.AddExpression(Expressions[J]);
  end;
  // add dimensions
  for I := 0 to PageContainer.Fields.Count - 1 do
  begin
    Expressions := PageContainer.Fields[I].GetExpressions;
    for J := 0 to Length(Expressions) - 1 do
      Interpreter.AddExpression(Expressions[J]);
  end;
  for I := 0 to XAxisContainer.Fields.Count - 1 do
  begin
    Expressions := XAxisContainer.Fields[I].GetExpressions;
    for J := 0 to Length(Expressions) - 1 do
      Interpreter.AddExpression(Expressions[J]);
  end;
  for I := 0 to YAxisContainer.Fields.Count - 1 do
  begin
    Expressions := YAxisContainer.Fields[I].GetExpressions;
    for J := 0 to Length(Expressions) - 1 do
      Interpreter.AddExpression(Expressions[J]);
  end;
  // add totals
  Expressions := XAxisContainer.GetExpressions;
  for J := 0 to Length(Expressions) - 1 do
    Interpreter.AddExpression(Expressions[J]);
  Expressions := YAxisContainer.GetExpressions;
  for J := 0 to Length(Expressions) - 1 do
    Interpreter.AddExpression(Expressions[J]);
end;

procedure TfcxSlice.InitInterpreter;
begin
  AddScriptVariables;
  AddScriptExpressions;
end;

function TfcxSlice.HasDimension(const DimensionName: String): Boolean;
begin
  Result :=
    (PageContainer.Fields.IndexByName[DimensionName] >= 0) or
    (XAxisContainer.Fields.IndexByName[DimensionName] >= 0) or
    (YAxisContainer.Fields.IndexByName[DimensionName] >= 0);
end;

function TfcxSlice.HasMeasure(const MeasureName: String): Boolean;
begin
  Result := FMeasuresContainer.MeasureFields.IndexByName[MeasureName] >= 0;
end;

function TfcxSlice.GetCommonScript: TStrings;
begin
  Result := FFilterManager.GetCommonScript;
end;

function TfcxSlice.GetCommonScriptChangedTick: Cardinal;
begin
  Result := FFilterManager.GetCommonScriptChangedTick;
end;

function TfcxSlice.GetScriptLanguage: String;
begin
  Result := Interpreter.ScriptLanguage;
end;

procedure TfcxSlice.SetScriptLanguage(const Value: String);
begin
  inherited;
  Interpreter.ScriptLanguage := Value;
end;

function TfcxSlice.GetCommonScriptLanguage: String;
begin
  Result := FFilterManager.ScriptLanguage;
end;

procedure TfcxSlice.SetCommonScriptLanguage(const Value: String);
begin
  FFilterManager.ScriptLanguage := Value;
end;

function TfcxSlice.CalcFuncForSelectedArea(ASelAgrFunc: TfcxSelAgrFunc; ASelectedArea: TfcxGridSelection): Variant;
var
  AVisCol, AVisRow: integer;
  MeasureValue: PfcxMeasureValue;
begin
  MeasureValue := cfcMeasureProcessorMap[False, ASelAgrFunc].NewMeasureValue(nil);
  for AVisCol := ASelectedArea.Rect.Left to ASelectedArea.Rect.Right do
  begin
    for AVisRow := ASelectedArea.Rect.Top to ASelectedArea.Rect.Bottom do
      cfcMeasureProcessorMap[False, ASelAgrFunc].Func(MeasureValue, GetMeasureValue(AVisCol, AVisRow), 0, 0, nil);
  end;
  Result := cfcMeasureProcessorMap[False, ASelAgrFunc].GetItem(MeasureValue);
  cfcMeasureProcessorMap[False, ASelAgrFunc].DestroyMeasure(MeasureValue);
end;

function TfcxSlice.GetMeasureValue(ACol, ARow: Integer): Variant;
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  AIndexInBaseLevel, AIndexInSecondLevel: Integer;
  AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
begin
  if ColRowToDataCellPos(ACol, ARow, ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex) then
    Result := GetMeasureValueBS(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex)
  else
    Result := Unassigned;
end;

function TfcxSlice.GetDetailRecords(ACol, ARow, ARecIndex: Integer): integer;
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  AIndexInBaseLevel, AIndexInSecondLevel: Integer;
begin
  Result := -1;
  if not ColRowToDataCellPos(ACol, ARow, ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel) then
    Exit;
  Result := FMeasuresContainer.DetailRecords[ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, ARecIndex];
end;

function TfcxSlice.GetDetailRecordsCount(ACol, ARow: Integer): integer;
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  AIndexInBaseLevel, AIndexInSecondLevel: Integer;
begin
  Result := 0;
  if not ColRowToDataCellPos(ACol, ARow, ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel) then
    Exit;
  Result := FMeasuresContainer.DetailRecordsCount[ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel];
end;

function TfcxSlice.CubeIsEqual(ACube: TObject): boolean;
begin
  Result := (ACube = FCube);
end;

procedure TfcxSlice.ClearFilterManager;
begin
  FilterManager := nil;
end;

function TfcxSlice.ColRowToDataCellPos(const ACol, ARow: Integer;
  var ABaseLevel, ASecondLevel: TfcxSmallCount; var AIndexInBaseLevel, AIndexInSecondLevel: Integer;
  var AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount): Boolean;
begin
  Result := False;
  if FMeasuresContainer.FIsCleared or (FMeasuresContainer.FContainer = FPageContainer) then
    Exit;
  if (ACol < 0) or (ACol > (FXAxisContainer.FVisibleAxisNodes.FCount - 1)) then
    Exit;
  if (ARow < 0) or (ARow > (FYAxisContainer.FVisibleAxisNodes.FCount - 1)) then
    Exit;
  if FMeasuresContainer.FBaseAxisIsX then
  begin
    ABaseLevel := FXAxisContainer.FLevelOf[FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol]];
    ASecondLevel := FYAxisContainer.FLevelOf[FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow]];
    AIndexInBaseLevel := FXAxisContainer.FIndexInLevel[FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol]];
    AIndexInSecondLevel := FYAxisContainer.FIndexInLevel[FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow]];
    ABaseAdditionalTotalIndex := FXAxisContainer.FVisibleAxisNodes.FAdditionalTotalsIndexes[ACol];
    ASecondAdditionalTotalIndex := FYAxisContainer.FVisibleAxisNodes.FAdditionalTotalsIndexes[ARow];
  end
  else
  begin
    ASecondLevel := FXAxisContainer.FLevelOf[FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol]];
    ABaseLevel := FYAxisContainer.FLevelOf[FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow]];
    AIndexInSecondLevel := FXAxisContainer.FIndexInLevel[FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol]];
    AIndexInBaseLevel := FYAxisContainer.FIndexInLevel[FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow]];
    ASecondAdditionalTotalIndex := FXAxisContainer.FVisibleAxisNodes.FAdditionalTotalsIndexes[ACol];
    ABaseAdditionalTotalIndex := FYAxisContainer.FVisibleAxisNodes.FAdditionalTotalsIndexes[ARow];
  end;
  if FMeasuresContainer.Count > 1 then
    if FMeasuresContainer.FContainer = FXAxisContainer then
      AMeasureIndex := FXAxisContainer.FVisibleAxisNodes.FMeasureIndexes[ACol]
    else
    if FMeasuresContainer.FContainer = FYAxisContainer then
      AMeasureIndex := FYAxisContainer.FVisibleAxisNodes.FMeasureIndexes[ARow]
    else
      AMeasureIndex := 0
  else
    AMeasureIndex := 0;
  Result := True;
end;

function TfcxSlice.ColRowToDataCellPos(const ACol, ARow: Integer;
  var ABaseLevel, ASecondLevel: TfcxSmallCount; var AIndexInBaseLevel,
  AIndexInSecondLevel: Integer): Boolean;
begin
  Result := False;
  if FMeasuresContainer.FIsCleared then
    Exit;
  if (ACol < 0) or (ACol > (FXAxisContainer.FVisibleAxisNodes.FCount - 1)) then
    Exit;
  if (ARow < 0) or (ARow > (FYAxisContainer.FVisibleAxisNodes.FCount - 1)) then
    Exit;
  if FMeasuresContainer.FBaseAxisIsX then
  begin
    ABaseLevel := FXAxisContainer.FLevelOf[FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol]];
    ASecondLevel := FYAxisContainer.FLevelOf[FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow]];
    AIndexInBaseLevel := FXAxisContainer.FIndexInLevel[FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol]];
    AIndexInSecondLevel := FYAxisContainer.FIndexInLevel[FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow]];
  end
  else
  begin
    ASecondLevel := FXAxisContainer.FLevelOf[FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol]];
    ABaseLevel := FYAxisContainer.FLevelOf[FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow]];
    AIndexInSecondLevel := FXAxisContainer.FIndexInLevel[FXAxisContainer.FVisibleAxisNodes.FIndexes[ACol]];
    AIndexInBaseLevel := FYAxisContainer.FIndexInLevel[FYAxisContainer.FVisibleAxisNodes.FIndexes[ARow]];
  end;
  Result := True;
end;

procedure TfcxSlice.SetHideTotalForSingle(const Value: Boolean);
begin
  if FHideTotalForSingle <> Value then
  begin
    StartChange;
    FHideTotalForSingle := Value;
    StopChange([chs_NeedFillVisibleX, chs_NeedFillVisibleY]);
  end;
end;

procedure TfcxSlice.SetClearUnattachedFieldsFilter(const Value: Boolean);
begin
  if FClearUnattachedFieldsFilter <> Value then
  begin
    StartChange;
    FClearUnattachedFieldsFilter := Value;
    StopChange([chs_FiltersChanged]);
  end;
end;

procedure TfcxSlice.SetColsWidth(const Value: Smallint);
begin
  if (Value = 0) or (Value = DefaultColWidth) then
    FXAxisContainer.SetCellsSize(0)
  else
    FXAxisContainer.SetCellsSize(Value)
end;

procedure TfcxSlice.SetRowsHeight(const Value: Smallint);
begin
  if (Value = 0) or (Value = DefaultRowHeight) then
    FYAxisContainer.SetCellsSize(0)
  else
    FYAxisContainer.SetCellsSize(Value)
end;

function TfcxSlice.FindSliceFieldInRegions(ASliceField: TfcxSliceField;
  var ARegion: TfcxRegionOfField): integer;
begin
  ARegion := rf_None;
  Result := FPageContainer.Fields.FindSliceField(ASliceField);
  if Result > -1 then
  begin
    ARegion := rf_Page;
    Exit;
  end;
  Result := FXAxisContainer.Fields.FindSliceField(ASliceField);
  if Result > -1 then
  begin
    ARegion := rf_CapXAx;
    Exit;
  end;
  Result := FYAxisContainer.Fields.FindSliceField(ASliceField);
  if Result > -1 then
  begin
    ARegion := rf_CapYAx;
    Exit;
  end;
end;

procedure TfcxSlice.GetChartData(AChartParam: TfcxChartParam; ADataProc: TfcxChartDataProc);
begin
  if not Assigned(FCube) or not FCube.Active then
    exit;
  if FMeasuresContainer.Container = nil then
    exit;
  if FFilterManager.GoodRecCount = 0 then
    exit;
  case AChartParam.TypeChartData of
    tcd_ByAxisAxis:
      ChartDataWithoutMeasure(AChartParam, ADataProc);
    tcd_ByAxisMeasures, tcd_ByMeasuresAxis:
      ChartDataWithMeasure(AChartParam, ADataProc);
  end;
end;

procedure TfcxSlice.ChartDataWithMeasure(AChartParam: TfcxChartParam;
  ADataProc: TfcxChartDataProc);
var
  RowDataAxis: boolean; // true - x
  RowFieldCount : integer;
  AAxis : TfcxAxisContainer;
  AData1: array of String;
  AData2: array of String;
  AValues: array of Variant;
  AIndexLevel: array of integer;
  i, j: integer;
  AMeasureIndexes: array of integer;
  AMeasuresVisibleCount: integer;

  procedure ProcessLevel(ASTNode: PfcxSTNode; ADLevel: TfcxSmallCount);
  var
    ANodeIndex, ACountChild, AIndexWithOrd, j: Integer;
    AAscOrder: boolean;
  begin
    with AAxis do
    begin
      AAscOrder := Fields[AAxis.FAxisTree[ADLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc;
      ACountChild := FAxisTree[ADLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ADLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
      for ANodeIndex := 0 to ACountChild - 1 do
      begin
        if AAscOrder then
          AIndexWithOrd := ANodeIndex
        else
          AIndexWithOrd := ACountChild - ANodeIndex - 1;
        if FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
  // Group
          AData1[ADLevel+1] := Fields[FAxisTree[ADLevel + 1].RegionFieldIndex].FSliceField.GroupCaption[FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.Index]
        else
        if FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
  // others
          AData1[ADLevel+1] := fcxResources.GetAnsi('sOther')
        else
          AData1[ADLevel+1] := Fields[FAxisTree[ADLevel + 1].RegionFieldIndex].FSliceField.UVCaption[FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.Index];
        if ADLevel < (RowFieldCount - 2) then
          ProcessLevel(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1)
        else
        begin
          for j := 0 to AMeasuresVisibleCount - 1 do
          begin
            if AAxis = FMeasuresContainer.FBaseContainer then
              AValues[j] := GetMeasureValueBS(RowFieldCount-1, -1, ASTNode.Nodes[AIndexWithOrd].FIndex, 0, AMeasureIndexes[j], -1, -1)
            else
              AValues[j] := GetMeasureValueBS(-1, RowFieldCount-1, 0, ASTNode.Nodes[AIndexWithOrd].FIndex, AMeasureIndexes[j], -1, -1);
          end;
          case AChartParam.TypeChartData of
            tcd_ByAxisMeasures:
              ADataProc(Self, AChartParam.TypeChartData, AData1, AData2, AValues);
            tcd_ByMeasuresAxis:
              ADataProc(Self, AChartParam.TypeChartData, AData2, AData1, AValues);
          end;
        end;
      end;

    end;
  end;

begin
  if AChartParam.TypeChartData = tcd_ByAxisMeasures then
  begin
    RowDataAxis := (AChartParam.CategoriesAxis = ar_ColAxis);
    RowFieldCount := AChartParam.CategoriesFieldCount;
  end
  else
  begin
    RowDataAxis := (AChartParam.SeriesAxis = ar_ColAxis);
    RowFieldCount := AChartParam.SeriesFieldCount;
  end;

  if RowFieldCount = 0 then
    SetLength(AData1, 1)
  else
    SetLength(AData1, RowFieldCount);
  AMeasuresVisibleCount := MeasuresContainer.VisibleCount;
  SetLength(AData2, AMeasuresVisibleCount);
  SetLength(AValues, AMeasuresVisibleCount);
  SetLength(AMeasureIndexes, AMeasuresVisibleCount);
  for i := 0 to AMeasuresVisibleCount - 1 do
  begin
    AData2[i] := MeasuresContainer.VisibleMeasures[i].Caption;
    AMeasureIndexes[i] := MeasuresContainer.VisibleMeasures[i].Index;
  end;

  Setlength(AIndexLevel, RowFieldCount);
  for i := 0 to RowFieldCount - 1 do
    AIndexLevel[i] := 0;

  if RowDataAxis then
    AAxis := XAxisContainer
  else
    AAxis := YAxisContainer;
  if RowFieldCount > AAxis.LevelCount then
    Exit;
  if RowFieldCount = 0 then
  begin
    AData1[0] := 'Total';
    for j := 0 to AMeasuresVisibleCount - 1 do
      AValues[j] := GetMeasureValueBS(-1, -1, 0, 0, AMeasureIndexes[j], -1, -1);
    case AChartParam.TypeChartData of
      tcd_ByAxisMeasures:
        ADataProc(Self, AChartParam.TypeChartData, AData1, AData2, AValues);
      tcd_ByMeasuresAxis:
        ADataProc(Self, AChartParam.TypeChartData, AData2, AData1, AValues);
    end;
  end
  else
  begin
    ProcessLevel(AAxis.FAxisSortTree.FRootNode, -1);
  end;
end;

procedure TfcxSlice.ChartDataWithoutMeasure(AChartParam: TfcxChartParam;
  ADataProc: TfcxChartDataProc);
var
  AMeasuresVisibleCount: integer;
  ASeriesFieldCount, ACategoriesFieldCount: integer;
  ASeriesAxis, ACategoriesAxis: TfcxAxisContainer;
  ASeriesIndexInLevel: array of integer;
  AData1: array of String;
  AData2: array of String;
  AValues: array of Variant;
  ASeriesIndex: integer;
  AMeasureIndex: integer;

  procedure MakeSeries(ASTNode: PfcxSTNode; ADLevel: TfcxSmallCount);
  var
    ANodeIndex, ACountChild, AIndexWithOrd: Integer;
    AAscOrder: boolean;
  begin
    with ASeriesAxis do
    begin
      AAscOrder := Fields[FAxisTree[ADLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc;
      ACountChild := FAxisTree[ADLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ADLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
      for ANodeIndex := 0 to ACountChild - 1 do
      begin
        if AAscOrder then
          AIndexWithOrd := ANodeIndex
        else
          AIndexWithOrd := ACountChild - ANodeIndex - 1;
        if FAxisTree[ADLevel + 1].nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
  // Group
          AData2[ADLevel+1] := Fields[FAxisTree[ADLevel + 1].RegionFieldIndex].FSliceField.GroupCaption[FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.Index]
        else
        if FAxisTree[ADLevel + 1].nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
  // others
          AData2[ADLevel+1] := fcxResources.GetAnsi('sOther')
        else
          AData2[ADLevel+1] := Fields[FAxisTree[ADLevel + 1].RegionFieldIndex].FSliceField.UVCaption[FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.Index];
        if ADLevel < (ASeriesFieldCount - 2) then
          MakeSeries(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1)
        else
        begin
          SetLength(ASeriesIndexInLevel, Length(ASeriesIndexInLevel) + 1);
          ASeriesIndexInLevel[High(ASeriesIndexInLevel)] := ASTNode.Nodes[AIndexWithOrd].FIndex;
          ADataProc(Self, AChartParam.TypeChartData, AData1, AData2, AValues);
        end;
      end;
    end;
  end;

  procedure FillSeries(ASTNode: PfcxSTNode; ADLevel: TfcxSmallCount);
  var
    ANodeIndex, ACountChild, AIndexWithOrd: Integer;
    AAscOrder: boolean;
    ASeriesIndex: integer;
  begin
    with ACategoriesAxis do
    begin
      AAscOrder := Fields[FAxisTree[ADLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc;
      ACountChild := FAxisTree[ADLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ADLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
      for ANodeIndex := 0 to ACountChild - 1 do
      begin
        if AAscOrder then
          AIndexWithOrd := ANodeIndex
        else
          AIndexWithOrd := ACountChild - ANodeIndex - 1;
        if FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
  // Group
          AData1[ADLevel+1] := Fields[FAxisTree[ADLevel + 1].RegionFieldIndex].FSliceField.GroupCaption[FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.Index]
        else
        if FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
  // others
          AData1[ADLevel+1] := fcxResources.GetAnsi('sOther')
        else
          AData1[ADLevel+1] := Fields[FAxisTree[ADLevel + 1].RegionFieldIndex].FSliceField.UVCaption[FAxisTree[ADLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.Index];
        if ADLevel < (ACategoriesFieldCount - 2) then
          FillSeries(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1)
        else
        begin
          for ASeriesIndex := 0 to High(ASeriesIndexInLevel) do
          begin
            if ACategoriesAxis = FMeasuresContainer.FBaseContainer then
              AValues[0] := GetMeasureValueBS(ACategoriesFieldCount - 1, ASeriesFieldCount - 1, ASTNode.Nodes[AIndexWithOrd].FIndex, ASeriesIndexInLevel[ASeriesIndex], AMeasureIndex, -1, -1)
            else
              AValues[0] := GetMeasureValueBS(ASeriesFieldCount - 1, ACategoriesFieldCount - 1, ASeriesIndexInLevel[ASeriesIndex], ASTNode.Nodes[AIndexWithOrd].FIndex, AMeasureIndex, -1, -1);
            ADataProc(Self, AChartParam.TypeChartData, AData1, AData2, AValues, ASeriesIndex);
          end;
        end;
      end;
    end;
  end;

begin
// check SeriesAxis <> CategoriesAxis
  if (AChartParam.SeriesAxis = AChartParam.CategoriesAxis) then
    exit;
  AMeasuresVisibleCount := MeasuresContainer.VisibleCount;
  if AChartParam.MeasureFieldIndex >= AMeasuresVisibleCount then
    exit;
  if AChartParam.CategoriesAxis = ar_ColAxis then
  begin
    ASeriesAxis := YAxisContainer;
    ACategoriesAxis := XAxisContainer;
  end
  else
  begin
    ASeriesAxis := XAxisContainer;
    ACategoriesAxis := YAxisContainer;
  end;
  ASeriesFieldCount := AChartParam.SeriesFieldCount;
  ACategoriesFieldCount := AChartParam.CategoriesFieldCount;
  if ASeriesFieldCount > ASeriesAxis.LevelCount then
    exit;
  if ACategoriesFieldCount > ACategoriesAxis.LevelCount then
    exit;
  if ACategoriesFieldCount = 0 then
    SetLength(AData1, 1)
  else
    SetLength(AData1, ACategoriesFieldCount);
  if ASeriesFieldCount = 0 then
    SetLength(AData2, 1)
  else
    SetLength(AData2, ASeriesFieldCount);
  SetLength(AValues, 1);
  SetLength(ASeriesIndexInLevel, 0);
  AMeasureIndex := MeasuresContainer.VisibleMeasures[AChartParam.MeasureFieldIndex].Index;

// Collect inf about series
  if ASeriesFieldCount = 0 then
  begin
    AData2[0] := 'Total';
    SetLength(ASeriesIndexInLevel, 1);
    ASeriesIndexInLevel[0] := 0;
    ADataProc(Self, AChartParam.TypeChartData, AData1, AData2, AValues);
  end
  else
  begin
    MakeSeries(ASeriesAxis.FAxisSortTree.FRootNode, -1);
  end;
// fill series
  SetLength(AData2, 0);
  if ACategoriesFieldCount = 0 then
  begin
    AData1[0] := 'Total';
    for ASeriesIndex := 0 to High(ASeriesIndexInLevel) do
    begin
      if ACategoriesAxis = FMeasuresContainer.FBaseContainer then
        AValues[0] := GetMeasureValueBS(-1, ASeriesFieldCount - 1, 0, ASeriesIndexInLevel[ASeriesIndex], AMeasureIndex, -1, -1)
      else
        AValues[0] := GetMeasureValueBS(ASeriesFieldCount - 1, -1, ASeriesIndexInLevel[ASeriesIndex], 0, AMeasureIndex, -1, -1);
      ADataProc(Self, AChartParam.TypeChartData, AData1, AData2, AValues, ASeriesIndex);
    end;
  end
  else
  begin
    FillSeries(ACategoriesAxis.FAxisSortTree.FRootNode, -1);
  end;
end;

function TfcxSlice.GetMeasureValueBS(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  AIndexInSecondLevel: Integer; const AMeasureIndex,
  ABaseAdditionalTotalIndex,
  ASecondAdditionalTotalIndex: TfcxSmallCount): Variant;
var
  ADirectIndexInSecondLevel: Integer;
begin
  if ABaseAdditionalTotalIndex <> -1 then
    if ASecondAdditionalTotalIndex <> -1 then
    begin
      Result := Unassigned;
      Exit;
    end;
  if FMeasuresContainer.FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or FMeasuresContainer.FMeasureFields[AMeasureIndex].CreateAllCells then
    ADirectIndexInSecondLevel := AIndexInSecondLevel
  else
  if not FMeasuresContainer.FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, ADirectIndexInSecondLevel) then
  begin
    Result := Unassigned;
    Exit;
  end;
  Result := GetMeasureValueBSDirect(ABaseLevel, ASecondLevel,
    AIndexInBaseLevel, ADirectIndexInSecondLevel,
    AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex)
end;

function TfcxSlice.GetMeasureValueBSDirect(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  ADirectIndexInSecondLevel: Integer; const AMeasureIndex,
  ABaseAdditionalTotalIndex,
  ASecondAdditionalTotalIndex: TfcxSmallCount): Variant;
var
  AMeasureValue: PfcxMeasureValue;
  AgrFunc: TfcxAgrFunc;
begin
  if ABaseAdditionalTotalIndex <> -1 then
    if ASecondAdditionalTotalIndex <> -1 then
    begin
      Result := Unassigned;
      Exit;
    end
    else
    begin
      AMeasureValue := PfcxArrPointerArray(PfcxPointerArray(FMeasuresContainer.FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[ABaseAdditionalTotalIndex])[AIndexInBaseLevel][ADirectIndexInSecondLevel];
      AgrFunc := FMeasuresContainer.FSaveAdditionalTotalsBaseLevels[ABaseLevel, ABaseAdditionalTotalIndex];
    end
  else
  if ASecondAdditionalTotalIndex <> -1 then
  begin
    AMeasureValue := PfcxArrPointerArray(PfcxPointerArray(FMeasuresContainer.FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[ASecondAdditionalTotalIndex])[AIndexInBaseLevel][ADirectIndexInSecondLevel];
    AgrFunc := FMeasuresContainer.FSaveAdditionalTotalsSecondLevels[ASecondLevel, ASecondAdditionalTotalIndex];
  end
  else
  begin
    AMeasureValue := PfcxArrPointerArray(FMeasuresContainer.FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ADirectIndexInSecondLevel];
    AgrFunc := FMeasuresContainer.FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc;
  end;
  if (AMeasureValue <> nil) or FMeasuresContainer.FMeasureFields[AMeasureIndex].ChangeNullToZero then
  begin
    if AMeasureValue <> nil then
      if FMeasuresContainer.FFinalized then
        Result := AMeasureValue.Value
      else
        Result := cfcMeasureProcessorMap[False, AgrFunc].GetItem(AMeasureValue)
    else
      Result := 0;
  end
  else
    Result := Unassigned;
end;

function TfcxSlice.GetMeasureValueXY(const AXLevel, AYLevel: TfcxSmallCount;
  const AIndexInXLevel, AIndexInYLevel: Integer;
  const AMeasureIndex, AXAdditionalTotalIndex,
  AYAdditionalTotalIndex: TfcxSmallCount): Variant;
begin
  if FMeasuresContainer.FBaseAxisIsX then
    Result := GetMeasureValueBS(AXLevel, AYLevel, AIndexInXLevel, AIndexInYLevel, AMeasureIndex, AXAdditionalTotalIndex, AYAdditionalTotalIndex)
  else
    Result := GetMeasureValueBS(AYLevel, AXLevel, AIndexInYLevel, AIndexInXLevel, AMeasureIndex, AYAdditionalTotalIndex, AXAdditionalTotalIndex)
end;

function TfcxSlice.GetActive: Boolean;
begin
  Result := Assigned(FCube) and FCube.Active
end;

function TfcxSlice.HaveLayout: Boolean;
begin
  Result := (XAxisContainer.RealLevelCount > 0) or (YAxisContainer.RealLevelCount > 0)
end;

function TfcxSlice.GetMeasureValueCaptionBSDirect(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  ADirectIndexInSecondLevel: Integer; const AMeasureIndex,
  ABaseAdditionalTotalIndex,
  ASecondAdditionalTotalIndex: TfcxSmallCount): TfcxString;
var
  AMeasureValue, ATotalMeasureValue: PfcxMeasureValue;
  AgrFunc: TfcxAgrFunc;
  AValue, ATotalValue: Variant;
  AIndexInSecondLevel, ATotalIndexBaseLevel, ATotalIndexSecondLevel: Integer;
  ATotalBaseLevel, ATotalSecondLevel: TfcxSmallCount;
begin
  if ABaseAdditionalTotalIndex <> -1 then
    if ASecondAdditionalTotalIndex <> -1 then
    begin
      Result := Unassigned;
      Exit;
    end
    else
    begin
      AMeasureValue := PfcxArrPointerArray(PfcxPointerArray(FMeasuresContainer.FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[ABaseAdditionalTotalIndex])[AIndexInBaseLevel][ADirectIndexInSecondLevel];
      AgrFunc := FMeasuresContainer.FSaveAdditionalTotalsBaseLevels[ABaseLevel, ABaseAdditionalTotalIndex]
    end
  else
  if ASecondAdditionalTotalIndex <> -1 then
  begin
    AMeasureValue := PfcxArrPointerArray(PfcxPointerArray(FMeasuresContainer.FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[ASecondAdditionalTotalIndex])[AIndexInBaseLevel][ADirectIndexInSecondLevel];
    AgrFunc := FMeasuresContainer.FSaveAdditionalTotalsSecondLevels[ASecondLevel, ASecondAdditionalTotalIndex]
  end
  else
  begin
    AMeasureValue := PfcxArrPointerArray(FMeasuresContainer.FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ADirectIndexInSecondLevel];
    AgrFunc := FMeasuresContainer.FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc;
  end;
  if (AMeasureValue = nil) and not FMeasuresContainer.FMeasureFields[AMeasureIndex].ChangeNullToZero then
    Result := ''
  else
  begin
    if MeasuresContainer.FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or FMeasuresContainer.FMeasureFields[AMeasureIndex].CreateAllCells then
      AIndexInSecondLevel := ADirectIndexInSecondLevel
    else
      AIndexInSecondLevel := MeasuresContainer.FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].IndexList[ADirectIndexInSecondLevel];
    if AMeasureValue <> nil then
      if FMeasuresContainer.FFinalized then
        AValue := AMeasureValue.Value
      else
        AValue := cfcMeasureProcessorMap[False, AgrFunc].GetItem(AMeasureValue)
    else
      AValue := 0;
    if TVarData(AValue).VType > 1 then
      if (FMeasuresContainer.FMeasureFields[AMeasureIndex].DisplayAs = da_Value) or (ABaseAdditionalTotalIndex <> -1) or (ASecondAdditionalTotalIndex <> -1) then
        if AgrFunc <> af_ListOfUnique then
          Result := FMeasuresContainer.FMeasureFields[AMeasureIndex].DisplayFormat.FormatData(AValue, nil)
        else
          Result := AValue
      else
      if FMeasuresContainer.FMeasureFields[AMeasureIndex].DisplayAs in da_Percents then
      begin
        ATotalIndexBaseLevel := AIndexInBaseLevel;
        ATotalIndexSecondLevel := AIndexInSecondLevel;
        ATotalBaseLevel := ABaseLevel;
        ATotalSecondLevel := ASecondLevel;
// get total
        if FMeasuresContainer.FBaseAxisIsX then
        begin
          case FMeasuresContainer.FMeasureFields[AMeasureIndex].DisplayAs of
            da_RowPercentLevel:
              begin
                if ATotalBaseLevel <> - 1 then
                begin
                  Dec(ATotalBaseLevel);
                  ATotalIndexBaseLevel := FXAxisContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent;
                end;
              end;
            da_RowPercentTotal:
              begin
                ATotalBaseLevel := -1;
                ATotalIndexBaseLevel := 0;
              end;
            da_ColPercentLevel:
              begin
                if ATotalSecondLevel <> -1 then
                begin
                  Dec(ATotalSecondLevel);
                  ATotalIndexSecondLevel := FYAxisContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent;
                end;
              end;
            da_ColPercentTotal:
              begin
                ATotalSecondLevel := -1;
                ATotalIndexSecondLevel := 0;
              end;
            da_GrandTotalPercent:
              begin
                ATotalSecondLevel := -1;
                ATotalIndexSecondLevel := 0;
                ATotalBaseLevel := -1;
                ATotalIndexBaseLevel := 0;
              end;
          end;
        end
        else
        begin
          case FMeasuresContainer.FMeasureFields[AMeasureIndex].DisplayAs of
            da_RowPercentLevel:
              begin
                if ATotalSecondLevel <> -1 then
                begin
                  Dec(ATotalSecondLevel);
                  ATotalIndexSecondLevel := FXAxisContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent;
                end;
              end;
            da_RowPercentTotal:
              begin
                ATotalSecondLevel := -1;
                ATotalIndexSecondLevel := 0;
              end;
            da_ColPercentLevel:
              begin
                if ATotalBaseLevel <> -1 then
                begin
                  Dec(ATotalBaseLevel);
                  ATotalIndexBaseLevel := FYAxisContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent;
                end;
              end;
            da_ColPercentTotal:
              begin
                ATotalBaseLevel := -1;
                ATotalIndexBaseLevel := 0;
              end;
            da_GrandTotalPercent:
              begin
                ATotalSecondLevel := -1;
                ATotalIndexSecondLevel := 0;
                ATotalBaseLevel := -1;
                ATotalIndexBaseLevel := 0;
              end;
          end;
        end;
        ATotalMeasureValue := FMeasuresContainer.Cells[ATotalBaseLevel, ATotalSecondLevel, ATotalIndexBaseLevel, ATotalIndexSecondLevel,  AMeasureIndex];
        if (ATotalMeasureValue = nil) and not FMeasuresContainer.FMeasureFields[AMeasureIndex].ChangeNullToZero then
        begin
          Result := '';
        end
        else
        begin
          if (ATotalMeasureValue <> nil) then
            if FMeasuresContainer.FFinalized then
              ATotalValue := ATotalMeasureValue.Value
            else
              ATotalValue := cfcMeasureProcessorMap[False, FMeasuresContainer.FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ATotalBaseLevel][ATotalSecondLevel].AgrFunc].GetItem(ATotalMeasureValue)
          else
            ATotalValue := 0;
          try
            if ATotalValue = 0 then
              Result := '0%'
            else
              Result := Format('%2.2n', [Extended(AValue / ATotalValue * 100)]) + '%';
          except
            Result := fcxResources.GetAnsi('SError');
          end;
        end
      end
      else
      if FMeasuresContainer.FMeasureFields[AMeasureIndex].DisplayAs in da_Ranks then
      begin
        Result := IntToStr(FMeasuresContainer.CellRanks[ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AMeasureIndex, FMeasuresContainer.FMeasureFields[AMeasureIndex].DisplayAs = da_RowRank])
      end
      else
        Result := FMeasuresContainer.FMeasureFields[AMeasureIndex].DisplayFormat.FormatData(AValue, nil)
    else
      Result := '';
  end;
end;

function TfcxSlice.GetMeasureValueCaptionBS(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  AIndexInSecondLevel: Integer; const AMeasureIndex,
  ABaseAdditionalTotalIndex,
  ASecondAdditionalTotalIndex: TfcxSmallCount): TfcxString;
var
  ADirectIndexInSecondLevel: Integer;
begin
  if FMeasuresContainer.FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or FMeasuresContainer.FMeasureFields[AMeasureIndex].CreateAllCells then
    ADirectIndexInSecondLevel := AIndexInSecondLevel
  else
  if not FMeasuresContainer.FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, ADirectIndexInSecondLevel) then
  begin
    Result := '';
    Exit;
  end;
  Result := GetMeasureValueCaptionBSDirect(ABaseLevel, ASecondLevel,
    AIndexInBaseLevel, ADirectIndexInSecondLevel,
    AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex)
end;

function TfcxSlice.GetMeasureValueCaption(ACol, ARow: Integer): TfcxString;
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  AIndexInBaseLevel, AIndexInSecondLevel: Integer;
  AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
begin
  if ColRowToDataCellPos(ACol, ARow, ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex) then
    Result := GetMeasureValueCaptionBS(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex)
  else
    Result := '';
end;

function TfcxSlice.GetMeasureValueCaptionXY(const AXLevel,
  AYLevel: TfcxSmallCount; const AIndexInXLevel, AIndexInYLevel: Integer;
  const AMeasureIndex, AXAdditionalTotalIndex,
  AYAdditionalTotalIndex: TfcxSmallCount): TfcxString;
begin
  if FMeasuresContainer.FBaseAxisIsX then
    Result := GetMeasureValueCaptionBS(AXLevel, AYLevel, AIndexInXLevel, AIndexInYLevel, AMeasureIndex, AXAdditionalTotalIndex, AYAdditionalTotalIndex)
  else
    Result := GetMeasureValueCaptionBS(AYLevel, AXLevel, AIndexInYLevel, AIndexInXLevel, AMeasureIndex, AYAdditionalTotalIndex, AXAdditionalTotalIndex)
end;

function TfcxSlice.GetMeasureValueBSWOffset(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  AIndexInSecondLevel: Integer; const AMeasureIndex, ABaseAdditionalTotalIndex,
  ASecondAdditionalTotalIndex: TfcxSmallCount; const AOffsetBaseLevel,
  AOffsetSecondLevel: TfcxSmallCount; const AOffsetBase, AOffsetSecond: Integer
  ): Variant;
var
  ANewIndexInBaseLevel, ANewIndexInSecondLevel: integer;
begin
  if AOffsetBase = 0 then
    ANewIndexInBaseLevel := AIndexInBaseLevel
  else
    ANewIndexInBaseLevel := FMeasuresContainer.FBaseContainer.CellIndexWithOffset(ABaseLevel, AOffsetBaseLevel, AIndexInBaseLevel, AOffsetBase);
  if AOffsetSecond = 0 then
    ANewIndexInSecondLevel := AIndexInSecondLevel
  else
    ANewIndexInSecondLevel := FMeasuresContainer.FSecondContainer.CellIndexWithOffset(ASecondLevel, AOffsetSecondLevel, AIndexInSecondLevel, AOffsetSecond);
  if (ANewIndexInBaseLevel <> -1) and (ANewIndexInSecondLevel <> -1) then
    Result := GetMeasureValueBS(ABaseLevel, ASecondLevel, ANewIndexInBaseLevel, ANewIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex)
  else
    Result := Unassigned;
end;

function TfcxSlice.GetMeasureTotalValueForDimsBS(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  AIndexInSecondLevel: Integer; const AMeasureIndex,
  ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
  ADimNames: String): Variant;
var
  AStrTemp, ADimNameTemp: String;
  ADelimPos, ADimFieldIndex: Integer;
  ATargetBaseLevel, ATargetSecondLevel, i: TfcxSmallCount;
  AIndexInTargetBaseLevel, AIndexInTargetSecondLevel: Integer;
begin
  if (ABaseAdditionalTotalIndex <> -1) or (ASecondAdditionalTotalIndex <> -1) then
  begin
    Result := Unassigned;
    Exit;
  end;
  AStrTemp := ADimNames;
  ATargetBaseLevel := -1;
  ATargetSecondLevel := -1;
  while AStrTemp <> '' do
  begin
    ADelimPos := Pos(',', AStrTemp);
    if ADelimPos = 0 then
      ADimNameTemp := AStrTemp
    else
      ADimNameTemp := Trim(copy(AStrTemp, 1, ADelimPos - 1));
    ADimFieldIndex := FMeasuresContainer.FBaseContainer.Fields.IndexByName[ADimNameTemp];
    if ADimFieldIndex = -1 then
    begin
      ADimFieldIndex := FMeasuresContainer.FSecondContainer.Fields.IndexByName[ADimNameTemp];
      if ADimFieldIndex <> -1 then
        if ATargetSecondLevel < ADimFieldIndex then
          ATargetSecondLevel := ADimFieldIndex;
    end
    else
      if ATargetBaseLevel < ADimFieldIndex then
        ATargetBaseLevel := ADimFieldIndex;
    if ADelimPos = 0 then
      AStrTemp := ''
    else
      AStrTemp := trim(copy(AStrTemp, ADelimPos + 1, Length(AStrTemp)));
  end;
  if ATargetBaseLevel > ABaseLevel then
    ATargetBaseLevel := ABaseLevel;
  if ATargetSecondLevel > ASecondLevel then
    ATargetSecondLevel := ASecondLevel;
  AIndexInTargetBaseLevel := AIndexInBaseLevel;
  AIndexInTargetSecondLevel := AIndexInSecondLevel;
  if (ATargetBaseLevel <> ABaseLevel) or (ATargetSecondLevel <> ASecondLevel) then
  begin
    for i := ABaseLevel downto ATargetBaseLevel + 1 do
      AIndexInTargetBaseLevel := FMeasuresContainer.FBaseContainer.FAxisTree[i].nodes[AIndexInTargetBaseLevel].IndParent;
    for i := ASecondLevel downto ATargetSecondLevel + 1 do
      AIndexInTargetSecondLevel := FMeasuresContainer.FSecondContainer.FAxisTree[i].nodes[AIndexInTargetSecondLevel].IndParent;
  end;
  Result := GetMeasureValueBS(ATargetBaseLevel, ATargetSecondLevel, AIndexInTargetBaseLevel, AIndexInTargetSecondLevel,
    AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex);
end;

function TfcxSlice.GetXAxisDimValueLI(const ALevel: TfcxSmallCount;
  const AIndexInLevel: Integer;
  const ADimLevelIndex: TfcxSmallCount): Variant;
var
  AIndexInDimLevel, i: Integer;
begin
  if (ALevel < ADimLevelIndex) then
  begin
    Result := Unassigned;
    Exit;
  end;
  AIndexInDimLevel := AIndexInLevel;
  for i := ALevel downto ADimLevelIndex + 1 do
    AIndexInDimLevel := FXAxisContainer.FAxisTree[i].nodes[AIndexInDimLevel].IndParent;
  Result := FXAxisContainer.GetValueLI(ADimLevelIndex, AIndexInDimLevel)
end;

function TfcxSlice.GetYAxisDimValueLI(const ALevel: TfcxSmallCount;
  const AIndexInLevel: Integer;
  const ADimLevelIndex: TfcxSmallCount): Variant;
var
  AIndexInDimLevel, i: Integer;
begin
  if (ALevel < ADimLevelIndex) then
  begin
    Result := Unassigned;
    Exit;
  end;
  AIndexInDimLevel := AIndexInLevel;
  for i := ALevel downto ADimLevelIndex + 1 do
    AIndexInDimLevel := FYAxisContainer.FAxisTree[i].nodes[AIndexInDimLevel].IndParent;
  Result := FYAxisContainer.GetValueLI(ADimLevelIndex, AIndexInDimLevel)
end;

function TfcxSlice.GetXAxisDimCaptionLI(const ALevel: TfcxSmallCount;
  const AIndexInLevel: Integer;
  const ADimLevelIndex: TfcxSmallCount): TfcxString;
var
  AIndexInDimLevel, i: Integer;
begin
  if (ALevel < ADimLevelIndex) then
  begin
    Result := Unassigned;
    Exit;
  end;
  AIndexInDimLevel := AIndexInLevel;
  for i := ALevel downto ADimLevelIndex + 1 do
    AIndexInDimLevel := FXAxisContainer.FAxisTree[i].nodes[AIndexInDimLevel].IndParent;
  Result := FXAxisContainer.GetDisplayValueLI(ADimLevelIndex, AIndexInDimLevel)
end;

function TfcxSlice.GetYAxisDimCaptionLI(const ALevel: TfcxSmallCount;
  const AIndexInLevel: Integer;
  const ADimLevelIndex: TfcxSmallCount): TfcxString;
var
  AIndexInDimLevel, i: Integer;
begin
  if (ALevel < ADimLevelIndex) then
  begin
    Result := Unassigned;
    Exit;
  end;
  AIndexInDimLevel := AIndexInLevel;
  for i := ALevel downto ADimLevelIndex + 1 do
    AIndexInDimLevel := FYAxisContainer.FAxisTree[i].nodes[AIndexInDimLevel].IndParent;
  Result := FYAxisContainer.GetDisplayValueLI(ADimLevelIndex, AIndexInDimLevel)
end;

function TfcxSlice.GetMeasureValueBSWOffsetOnValue(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  AIndexInSecondLevel: Integer; const AMeasureIndex, ABaseAdditionalTotalIndex,
  ASecondAdditionalTotalIndex: TfcxSmallCount; const AOffsetBaseLevel,
  AOffsetSecondLevel: TfcxSmallCount; const AOffsetValueBase,
  AOffsetValueSecond: Variant): Variant;
var
  ANewIndexInBaseLevel, ANewIndexInSecondLevel: integer;
begin
  if vartype(AOffsetValueBase) = varEmpty then
    ANewIndexInBaseLevel := AIndexInBaseLevel
  else
    ANewIndexInBaseLevel := FMeasuresContainer.FBaseContainer.CellIndexWithOffsetOnValue(ABaseLevel, AOffsetBaseLevel, AIndexInBaseLevel, AOffsetValueBase);
  if vartype(AOffsetValueSecond) = varEmpty then
    ANewIndexInSecondLevel := AIndexInSecondLevel
  else
    ANewIndexInSecondLevel := FMeasuresContainer.FSecondContainer.CellIndexWithOffsetOnValue(ASecondLevel, AOffsetSecondLevel, AIndexInSecondLevel, AOffsetValueSecond);
  if (ANewIndexInBaseLevel <> -1) and (ANewIndexInSecondLevel <> -1) then
    Result := GetMeasureValueBS(ABaseLevel, ASecondLevel, ANewIndexInBaseLevel, ANewIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex)
  else
    Result := Unassigned;
end;

function TfcxSlice.GetMeasureValueBSWOffsetThrough(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  AIndexInSecondLevel: Integer; const AMeasureIndex, ABaseAdditionalTotalIndex,
  ASecondAdditionalTotalIndex: TfcxSmallCount; const AOffsetBase,
  AOffsetSecond: Integer): Variant;
var
  ANewIndexInBaseLevel, ANewIndexInSecondLevel: integer;
begin
  if AOffsetBase = 0 then
    ANewIndexInBaseLevel := AIndexInBaseLevel
  else
    ANewIndexInBaseLevel := FMeasuresContainer.FBaseContainer.CellIndexWithOffsetThrough(ABaseLevel, AIndexInBaseLevel, AOffsetBase);
  if AOffsetSecond = 0 then
    ANewIndexInSecondLevel := AIndexInSecondLevel
  else
    ANewIndexInSecondLevel := FMeasuresContainer.FSecondContainer.CellIndexWithOffsetThrough(ASecondLevel, AIndexInSecondLevel, AOffsetSecond);
  if (ANewIndexInBaseLevel <> -1) and (ANewIndexInSecondLevel <> -1) then
    Result := GetMeasureValueBS(ABaseLevel, ASecondLevel, ANewIndexInBaseLevel, ANewIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex)
  else
    Result := Unassigned;
end;

function TfcxSlice.GetAsPlainText(AStartRow, AStopRow, AStartCol, AStopCol: Integer; ACloneHeaders: Boolean): TfcxString;
var
  L, Y: TfcxStringList;
  I, C: Integer;
  Offset: TfcxString;
begin
  L := TfcxStringList.Create;
  Y := TfcxStringList.Create;
  Y.QuoteChar := '"';
  try
    L.DelimitedText := XAxisContainer.GetAsPlainText(AStartCol, AStopCol, False)  + L.Delimiter + MeasuresContainer.GetAsPlainText(AStartRow, AStopRow, AStartCol, AStopCol);
    Offset := '';
    for I := 0 to YAxisContainer.VisibleLevelCount - 1 do
      Offset := Offset + #9;
    C := XAxisContainer.VisibleLevelCount;
    for I := 0 to C - 1 do
      L[I] := Offset + L[I];
    Y.DelimitedText := YAxisContainer.GetAsPlainText(AStartRow, AStopRow, False);
    for I := 0 to Y.Count - 1 do
      if (C + I) < L.Count then
        L[C + I] := Y[I] + #9 + L[C + I];
    Result := L.Text;
  finally
    Y.Free;
    L.Free;
  end;
end;

function TfcxSlice.GetContainerOfRegion(
  ARegionOfField: TfcxRegionOfField): TfcxFieldsContainer;
begin
  case ARegionOfField of
    rf_CapXAx: Result := FXAxisContainer;
    rf_CapYAx: Result := FYAxisContainer;
    rf_Page: Result := FPageContainer;
  else
    Result := nil;
  end;
end;

function TfcxSlice.GetSliceFieldByName(AName: String): TfcxSliceField;
begin
  Result := FSliceFields.ItemByName[AName]
end;

procedure TfcxSlice.SetSettings(const Value: TfcxSliceDefaultSettings);
begin
  FSettings.Assign(Value);
  ApplyDefaultSettings;
end;

procedure TfcxSlice.SetTotalPosition(const Value: TfcxTotalPosition);
begin
  BeginUpdate;
  XAxisContainer.SetTotalPosition(Value);
  YAxisContainer.SetTotalPosition(Value);
  EndUpdate;
end;

procedure TfcxSlice.SetDefaults;
begin
  inherited;
  Settings := fcDefaultSettingsStore.SliceSettings;
end;

function TfcxSlice.GetMeasureValueBSOnWayValues(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  AIndexInSecondLevel: Integer; const AMeasureIndex,
  ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
  const AUseWayOnBaseLevel, AUseWayOnSecondLevel: Boolean;
  const AWayValuesBase, AWayValuesSecond: array of Variant): Variant;
var
  ANewBaseLevel, ANewSecondLevel: integer;
  ANewIndexInBaseLevel, ANewIndexInSecondLevel: integer;
begin
  if not AUseWayOnBaseLevel then
  begin
    ANewBaseLevel := ABaseLevel;
    ANewIndexInBaseLevel := AIndexInBaseLevel;
  end
  else
  begin
    ANewBaseLevel := Length(AWayValuesBase) - 1;
    ANewIndexInBaseLevel := FMeasuresContainer.FBaseContainer.CellIndexWithValues(AWayValuesBase);
  end;
  if not AUseWayOnSecondLevel then
  begin
    ANewSecondLevel := ASecondLevel;
    ANewIndexInSecondLevel := AIndexInSecondLevel
  end
  else
  begin
    ANewSecondLevel := Length(AWayValuesSecond) - 1;
    ANewIndexInSecondLevel := FMeasuresContainer.FSecondContainer.CellIndexWithValues(AWayValuesSecond);
  end;
  if (ANewIndexInBaseLevel <> -1) and (ANewIndexInSecondLevel <> -1)
    and (ANewBaseLevel < FMeasuresContainer.FBaseContainer.LevelCount)
    and (ANewSecondLevel < FMeasuresContainer.FSecondContainer.LevelCount) then
    Result := GetMeasureValueBS(ANewBaseLevel, ANewSecondLevel, ANewIndexInBaseLevel, ANewIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex)
  else
    Result := Unassigned;
end;

procedure TfcxSlice.UpdateDisplayLabel(ACubeField: TObject; const Value: TfcxString);
var
  Index: Integer;
begin
  Index := SliceFields.IndexOfCubeField(TfcxCommonField(ACubeField));
  if Index > -1 then
    if SliceFields[Index].Caption = TfcxCommonField(ACubeField).CubeFieldDisplayLabel then
      SliceFields[Index].Caption := Value;
end;

function TfcxSlice.GetMeasureTotalValueForDimsBSOffset(const ABaseLevel,
  ASecondLevel: TfcxSmallCount; const AIndexInBaseLevel,
  AIndexInSecondLevel: Integer; const AMeasureIndex,
  ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex: TfcxSmallCount;
  ADimNames: String; const AOffsetBaseLevel,
  AOffsetSecondLevel: TfcxSmallCount; const AOffsetBase,
  AOffsetSecond: Integer; const AThrough: Boolean = False): Variant;
var
  AStrTemp, ADimNameTemp: String;
  ADelimPos, ADimFieldIndex: Integer;
  ATargetBaseLevel, ATargetSecondLevel, i: TfcxSmallCount;
  AIndexInTargetBaseLevel, AIndexInTargetSecondLevel: Integer;
begin
  if (AOffsetBase = 0) and (AOffsetSecond = 0) then
  begin
    Result := GetMeasureTotalValueForDimsBS(ABaseLevel, ASecondLevel, AIndexInBaseLevel,
      AIndexInSecondLevel, AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex, ADimNames);
    exit;
  end;
  if (ABaseAdditionalTotalIndex <> -1) or (ASecondAdditionalTotalIndex <> -1) then
  begin
    Result := Unassigned;
    Exit;
  end;
  AStrTemp := ADimNames;
  ATargetBaseLevel := -1;
  ATargetSecondLevel := -1;
  while AStrTemp <> '' do
  begin
    ADelimPos := Pos(',', AStrTemp);
    if ADelimPos = 0 then
      ADimNameTemp := AStrTemp
    else
      ADimNameTemp := Trim(copy(AStrTemp, 1, ADelimPos - 1));
    ADimFieldIndex := FMeasuresContainer.FBaseContainer.Fields.IndexByName[ADimNameTemp];
    if ADimFieldIndex = -1 then
    begin
      ADimFieldIndex := FMeasuresContainer.FSecondContainer.Fields.IndexByName[ADimNameTemp];
      if ADimFieldIndex <> -1 then
        if ATargetSecondLevel < ADimFieldIndex then
          ATargetSecondLevel := ADimFieldIndex;
    end
    else
      if ATargetBaseLevel < ADimFieldIndex then
        ATargetBaseLevel := ADimFieldIndex;
    if ADelimPos = 0 then
      AStrTemp := ''
    else
      AStrTemp := trim(copy(AStrTemp, ADelimPos + 1, Length(AStrTemp)));
  end;
  if ATargetBaseLevel > ABaseLevel then
    ATargetBaseLevel := ABaseLevel;
  if ATargetSecondLevel > ASecondLevel then
    ATargetSecondLevel := ASecondLevel;
  if (ATargetBaseLevel <> ABaseLevel) and (AOffsetBase <> 0) then
  begin
    Result := Unassigned;
    Exit;
  end;
  if (ATargetSecondLevel <> ASecondLevel) and (AOffsetSecond <> 0) then
  begin
    Result := Unassigned;
    Exit;
  end;
  AIndexInTargetBaseLevel := AIndexInBaseLevel;
  AIndexInTargetSecondLevel := AIndexInSecondLevel;
  if ATargetBaseLevel <> ABaseLevel then
    for i := ABaseLevel downto ATargetBaseLevel + 1 do
      AIndexInTargetBaseLevel := FMeasuresContainer.FBaseContainer.FAxisTree[i].nodes[AIndexInTargetBaseLevel].IndParent;
  if ATargetSecondLevel <> ASecondLevel then
    for i := ASecondLevel downto ATargetSecondLevel + 1 do
      AIndexInTargetSecondLevel := FMeasuresContainer.FSecondContainer.FAxisTree[i].nodes[AIndexInTargetSecondLevel].IndParent;
  if AOffsetBase <> 0 then
    if AThrough and (ABaseLevel = AOffsetBaseLevel) then
      AIndexInTargetBaseLevel := FMeasuresContainer.FBaseContainer.CellIndexWithOffsetThrough(ABaseLevel, AIndexInBaseLevel, AOffsetBase)
    else
      AIndexInTargetBaseLevel := FMeasuresContainer.FBaseContainer.CellIndexWithOffset(ABaseLevel, AOffsetBaseLevel, AIndexInBaseLevel, AOffsetBase);
  if AOffsetSecond <> 0 then
    if AThrough and (ASecondLevel = AOffsetSecondLevel) then
      AIndexInTargetSecondLevel := FMeasuresContainer.FSecondContainer.CellIndexWithOffsetThrough(ASecondLevel, AIndexInSecondLevel, AOffsetSecond)
    else
      AIndexInTargetSecondLevel := FMeasuresContainer.FSecondContainer.CellIndexWithOffset(ASecondLevel, AOffsetSecondLevel, AIndexInSecondLevel, AOffsetSecond);
  if (AIndexInTargetBaseLevel < 0) or (AIndexInTargetSecondLevel < 0) then
  begin
    Result := Unassigned;
    Exit;
  end;
  Result := GetMeasureValueBS(ATargetBaseLevel, ATargetSecondLevel, AIndexInTargetBaseLevel, AIndexInTargetSecondLevel,
    AMeasureIndex, ABaseAdditionalTotalIndex, ASecondAdditionalTotalIndex);
end;

{ TfcxSliceFields }

function TfcxSliceFields.AddCubeField(ACubeField: TfcxCommonField): Integer;
begin
  Result := FCount;
  ProcessField(ACubeField);
end;

function TfcxSliceFields.BuildTree: TfcxTree;
  function AddField(ATree: TfcxTree; AField: TfcxSliceField): PfcxTreeNode;
  var
    ParentField: TfcxSliceField;
    ParentNode: PfcxTreeNode;
  begin
    if AField is TfcxCommonSliceCubeField then
      ParentField := ItemByCubeField[TfcxCommonSliceCubeField(AField).CubeField.MasterField]
    else
      ParentField := nil;
    if Assigned(ParentField) then
    begin
      ParentNode := ATree.FindByData(ParentField);
      if Not Assigned(ParentNode) then
        ParentNode := AddField(ATree, ParentField);
      Result := ATree.AddChild(ParentNode)
    end
    else
      Result := ATree.AddChild(nil);
    Result^.Data := AField;
  end;
var
  i: integer;
  Field: TfcxSliceField;
begin
  Result := TfcxTree.Create;
  for i := 0 to Count - 1 do
  begin
    Field := Items[BaseIndex[i]];
    if not Assigned(Result.FindByData(Field)) then
      AddField(Result, Field);
  end;
end;

procedure TfcxSliceFields.Clear;
begin
  inherited;
end;

function TfcxSliceFields.CompareByCaption(const AIndex1,
  AIndex2: Integer): Integer;
begin
  Result := fcStrCompare(Items[AIndex1].Caption, Items[AIndex2].Caption);
end;

function TfcxSliceFields.CompareByIndexInDataSet(const AIndex1,
  AIndex2: Integer): Integer;
begin
  Result := fcxIntCompare(AIndex1, AIndex2);
{ TODO -cNeed : Get IndexInDataSet.}
//  Result := CompareInt(Items[AIndex1].IndexInDataSet, Items[AIndex2].IndexInDataSet);
end;

function TfcxSliceFields.CompareByName(const AIndex1,
  AIndex2: Integer): Integer;
begin
  Result := fcStrCompare(Items[AIndex1].FieldName, Items[AIndex2].FieldName);
end;

function TfcxSliceFields.CompareCustom(const AIndex1,
  AIndex2: Integer): Integer;
begin
  if Assigned(FSlice.OnFieldsListSortCompare) then
    Result := FSlice.OnFieldsListSortCompare(Items[AIndex1].FieldName, Items[AIndex2].FieldName)
  else
    Result := fcxIntCompare(AIndex1, AIndex2);
end;

constructor TfcxSliceFields.Create(ASlice: TfcxSlice);
begin
  Create(True);
  FSlice := ASlice;
  AutoFree := True;
  if Assigned(FSlice) then
    SetFieldsOrder(FSlice.FieldsOrder);
end;

procedure TfcxSliceFields.DeleteCubeField(ACubeField: TfcxCommonField);
begin
  Delete(IndexOfCubeField(ACubeField));
end;

function TfcxSliceFields.Find(const S: String; out AIndex: Integer): Boolean;
begin
  AIndex := IndexOfField(S);
  Result := AIndex <> -1;
end;

function TfcxSliceFields.GetItem(Index: Integer): TfcxSliceField;
begin
  Result := inherited Items[Index];
end;

function TfcxSliceFields.GetItemByCaption(AIndex: String): TfcxSliceField;
var
  i: Integer;
begin
  Result := nil;
  for i := 0 to Count - 1 do
    if AnsiCompareText(Items[i].Caption, AIndex) = 0 then
    begin
      Result := Items[i];
      break;
    end;
end;

function TfcxSliceFields.GetItemByCubeField(ACubeField: TfcxCommonField): TfcxSliceField;
var
  Index: Integer;
begin
  if ACubeField = nil then
    Result := nil
  else
  begin
    Index := IndexOfCubeField(ACubeField);
    if Index = -1 then
      Result := nil
    else
      Result := Items[Index];
  end;
end;

function TfcxSliceFields.GetItemByName(AIndex: String): TfcxSliceField;
var
  Index: Integer;
begin
  Index := IndexOfField(AIndex);
  if Index = -1 then
    Result := nil
  else
    Result := Items[Index];
end;

function TfcxSliceFields.GetItemsByOrder(Order: Integer): TfcxSliceField;
begin
  Result := inherited ItemsByOrder[Order];
end;

function TfcxSliceFields.IndexOfCubeField(ACubeField: TfcxCommonField): Integer;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    if (Items[I] is TfcxCommonSliceCubeField) and (TfcxCommonSliceCubeField(Items[I]).FCubeField = ACubeField) then
    begin
      Result := I;
      Exit;
    end;
  Result := -1;
end;

function TfcxSliceFields.IndexOfField(s: String): Integer;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    if AnsiCompareText(Items[i].FieldName, s) = 0 then
    begin
      Result := I;
      Exit;
    end;
  Result := -1;
end;

function TfcxSliceFields.InternalAddCubeField(
  ACubeField: TfcxCommonField): Integer;
begin
  if ACubeField is TfcxCommonDatePathField then
    Result := Add(TfcxSliceDateSplitCubeField.Create(FSlice, ACubeField))
  else
  if ACubeField is TfcxCommonTimePathField then
    Result := Add(TfcxSliceTimeSplitCubeField.Create(FSlice, ACubeField))
  else
  if ACubeField is TfcxAttributeField then
    Result := Add(TfcxSliceAttributeCubeField.Create(FSlice, ACubeField))
  else
    Result := Add(TfcxCommonUVSliceCubeField.Create(FSlice, ACubeField));
end;

procedure TfcxSliceFields.LoadFromXML(AItem: TfcxXMLItem);
var
  I: Integer;
  Field: TfcxSliceField;
begin
  if AItem.Name <> 'fields' then
    Exit;
  for I := 0 to AItem.Count - 1 do
  begin
    Field := ItemByName[AItem[I].Prop['name']];
    if Assigned(Field) then
      Field.LoadFromXML(AItem[I]);
  end;
end;

procedure TfcxSliceFields.ProcessField(ACubeField: TfcxCommonField);
var
  j: Integer;
begin
  InternalAddCubeField(ACubeField);
  if ACubeField.Fields <> nil then
    for j := 0 to ACubeField.Fields.Count - 1 do
      ProcessField(ACubeField.Fields.Items[j]);
end;

procedure TfcxSliceFields.SaveToXML(AItem: TfcxXMLItem);
var
  I: Integer;
begin
  AItem.Name := 'fields';
  for I := 0 to Count - 1 do
    Items[I].SaveToXML(AItem.Add);
end;

procedure TfcxSliceFields.SetFieldsOrder(const Value: TfcxFieldsOrder);
begin
  case Value of
    fcfloByDataSet:
      Compare := CompareByIndexInDataSet;
    fcfloByFieldDisplayLabel:
      Compare := CompareByCaption;
    fcfloByFieldName:
      Compare := CompareByName;
    fcfloCustom:
      Compare := CompareCustom;
  end;
end;

{ TfcxSliceField }

constructor TfcxSliceField.Create(ASlice: TfcxSlice);
begin
  inherited Create;
  FSlice := ASlice;
  FUVFilterSupport := True;
  FPopupWidth := ASlice.PopupWidthDefault;
  FPopupShowAvailable := ASlice.PopupShowAvailableDefault;
  FShowAllValues := False;
end;

function TfcxSliceField.GetCanGroup: Boolean;
begin
  Result := False;
end;

function TfcxSliceField.GetCaption: TfcxString;
begin
  Result := FCaption;
  if Result = '' then
    Result := FieldName;
end;

function TfcxSliceField.GetHasGroups: Boolean;
begin
  Result := False;
end;

function TfcxSliceField.GetUVSingleIndex: Integer;
begin
  Result := -1;
end;

function TfcxSliceField.GetUVCaption(AUVIndex: integer): TfcxString;
begin
  Result := '';
end;

function TfcxSliceField.GetUVCount: integer;
begin
  Result := 0;
end;

function TfcxSliceField.GetUVFilterActive: boolean;
begin
  Result := False;
end;

function TfcxSliceField.GetUVFilteredValuesCount: integer;
begin
  Result := 0;
end;

function TfcxSliceField.GetUVFilterOf(AUVIndex: integer): boolean;
begin
  Result := False;
end;

function TfcxSliceField.GetUVFilterType: TfcxUVFilterType;
begin
  Result := uvft_Set;
end;

function TfcxSliceField.GetUVIndexByOrder(AOrder: integer): integer;
begin
  Result := AOrder
end;

procedure TfcxSliceField.InverseFilter;
begin
end;

procedure TfcxSliceField.SetAllFilter;
begin
end;

procedure TfcxSliceField.SetCanGroup(const Value: Boolean);
begin
//
end;

procedure TfcxSliceField.SetCaption(const Value: TfcxString);
var
  IndexInRegion: integer;
begin
  if FCaption <> Value then
  begin
    Slice.StartChange;
    IndexInRegion := Slice.PageContainer.Fields.FindSliceField(Self);
    if IndexInRegion > -1 then
      if Slice.PageContainer.Fields[IndexInRegion].Caption = FCaption then
        Slice.PageContainer.Fields[IndexInRegion].Caption := Value;
    IndexInRegion := Slice.XAxisContainer.Fields.FindSliceField(Self);
    if IndexInRegion > -1 then
      if Slice.XAxisContainer.Fields[IndexInRegion].Caption = FCaption then
        Slice.XAxisContainer.Fields[IndexInRegion].Caption := Value;
    IndexInRegion := Slice.YAxisContainer.Fields.FindSliceField(Self);
    if IndexInRegion > -1 then
      if Slice.YAxisContainer.Fields[IndexInRegion].Caption = FCaption then
        Slice.YAxisContainer.Fields[IndexInRegion].Caption := Value;
    FCaption := Value;
    if FSlice.FieldsOrder = fcfloByFieldDisplayLabel then
      FSlice.FSliceFields.ReIndex;
    Slice.StopChange([]);
  end;
end;

procedure TfcxSliceField.SetNoneFilter;
begin
end;

procedure TfcxSliceField.SetUVSingleIndex(const Value: Integer);
begin
//
end;

procedure TfcxSliceField.SetUVCaption(AUVIndex: integer;
  const Value: TfcxString);
begin
//
end;

procedure TfcxSliceField.SetUVFilterOf(AUVIndex: integer;
  const Value: boolean);
begin
//
end;

procedure TfcxSliceField.SetUVFilterType(const Value: TfcxUVFilterType);
begin
//
end;

function TfcxSliceField.GetUVOrderByIndex(AIndex: integer): integer;
begin
  Result := AIndex
end;

procedure TfcxSliceField.BeginUpdateFieldFilter;
begin

end;

function TfcxSliceField.EndUpdateFieldFilter: boolean;
begin
  Result := True
end;

procedure TfcxSliceField.RollBackFieldFilter;
begin

end;

function TfcxSliceField.GetGroupCount: Integer;
begin
  Result := 0;
end;

function TfcxSliceField.GetGroupCaption(AGroupIndex: integer): TfcxString;
begin
  Result := '';
end;

procedure TfcxSliceField.SetGroupCaption(AGroupIndex: integer; const Value: TfcxString);
begin
//
end;

function TfcxSliceField.GetGroupIndexByOrder(AOrder: integer): integer;
begin
  Result := AOrder;
end;

function TfcxSliceField.GetGroupUVCount(AGroupIndex: integer): integer;
begin
  Result := 0;
end;

function TfcxSliceField.GetUVCaptionInGroup(AGroupIndex, AOrder: integer): TfcxString;
begin
  Result := '';
end;

function TfcxSliceField.GetNonGroupUVCount: integer;
begin
  Result := 0;
end;

function TfcxSliceField.GetUVCaptionInNonGroups(AOrder: integer): TfcxString;
begin
  Result := '';
end;

function TfcxSliceField.GetGroupManager: TfcxCommonGroupManager;
begin
  Result := nil;
end;

function TfcxSliceField.GetUniqueValues: TfcxBaseUniqueValues;
begin
  Result := nil;
end;

procedure TfcxSliceField.SetHeight(const Value: SmallInt);
begin
  if Value = FSlice.DefaultRowHeight then
  begin
    if FHeight <> 0 then
    begin
      FHeight := 0;
      FSlice.StartChange;
      FSlice.StopChange([chs_XHeight]);
    end
  end
  else
  begin
    if FHeight <> Value then
    begin
      FHeight := Value;
      FSlice.StartChange;
      FSlice.StopChange([chs_XHeight]);
    end
  end
end;

procedure TfcxSliceField.SetWidth(const Value: SmallInt);
begin
  if Value = FSlice.DefaultColWidth then
  begin
    if FWidth <> 0 then
    begin
      FWidth := 0;
      FSlice.StartChange;
      FSlice.StopChange([chs_YWidth]);
    end
  end
  else
  begin
    if FWidth <> Value then
    begin
      FWidth := Value;
      FSlice.StartChange;
      FSlice.StopChange([chs_YWidth]);
    end
  end
end;

procedure TfcxSliceField.SetPopupWidth(const Value: SmallInt);
begin
  FPopupWidth := Value;
end;

function TfcxSliceField.GetUVFilterOfValue(AVarValue: Variant): boolean;
begin
  Result := False;
end;

procedure TfcxSliceField.SetUVFilterOfValue(AVarValue: Variant;
  const Value: boolean);
begin
//
end;

function TfcxSliceField.GetNonGroupUVIndexByOrder(AOrder: integer): integer;
begin
  Result := -1;
end;

function TfcxSliceField.GetGroupUVIndexByOrder(AGroupIndex,
  AOrder: integer): integer;
begin
  Result := -1;
end;

function TfcxSliceField.GetUVFilterStateOfGroup(AGroupIndex: integer): TfcxCheckState;
begin
  Result := csGrayed;
end;

procedure TfcxSliceField.SetUVFilterStateOfGroup(AGroupIndex: integer; const Value: TfcxCheckState);
begin
//  if Value in [cbUnchecked, cbChecked] then
end;

procedure TfcxSliceField.SetUVGroupSingleIndex(const Value: Integer);
begin
//
end;

function TfcxSliceField.GetSplitManager: TfcxSplitManager;
begin
  Result := nil;
end;

procedure TfcxSliceField.SaveToXML(AItem: TfcxXMLItem);
begin
  AItem.Name := 'field';
  AItem.Prop['name'] := FieldName;
  AItem.Prop['caption'] := Caption;
  AItem.IntProp['Width'] := Width;
  AItem.IntProp['Height'] := Height;
  AItem.IntProp['PopupWidth'] := PopupWidth;
  AItem.BoolProp['PopupShowAvailable'] := PopupShowAvailable;
  AItem.BoolProp['Collapsed'] := FCollapsed;
  AItem.BoolProp['CollapsedSubGroup'] := FCollapsedSubGroup;
  if HasDisplayFormat then
  begin
    AItem := AItem.Add;
    AItem.Name := 'display_format';
    DisplayFormat.ToXML(AItem);
  end;
end;

procedure TfcxSliceField.LoadFromXML(AItem: TfcxXMLItem);
var
  I: integer;
  ADisplayFormat: TfcxFormat;
begin
  if AItem.Name <> 'field' then
    Exit;
  Caption := AItem.Prop['caption'];
  Width := AItem.IntProp['Width'];
  Height := AItem.IntProp['Height'];
  PopupWidth := AItem.IntProp['PopupWidth'];
  PopupShowAvailable := AItem.BoolProp['PopupShowAvailable'];
  if AItem.PropExists('Collapsed') then
  begin
    FCollapsed := AItem.BoolProp['Collapsed'];
    FCollapsedSubGroup  := AItem.BoolProp['CollapsedSubGroup'];
  end;
  for I := 0 to AItem.Count - 1 do
    if AItem[I].Name = 'display_format' then
    begin
      ADisplayFormat := TfcxFormat.Create;
      ADisplayFormat.FromXML(AItem[I]);
      DisplayFormat := ADisplayFormat;
      ADisplayFormat.Free;
    end
end;

function TfcxSliceField.GetCurrentCaption: String;
begin
  if FSlice.SliceFields.FRow <> -1 then
    Result := GetUVCaptionFromRec(FSlice.SliceFields.FRow)
  else
    Result := '';
end;

function TfcxSliceField.GetCurrentValue: Variant;
begin
  if FSlice.SliceFields.FRow <> -1 then
    Result := GetUVValueFromRec(FSlice.SliceFields.FRow)
  else
    Result := UnAssigned;
end;

function TfcxSliceField.HasDisplayFormat: Boolean;
begin
  Result := False;
end;

procedure TfcxSliceField.SetHeightSubGroup(const Value: SmallInt);
begin
  if Value = FSlice.DefaultRowHeight then
  begin
    if FHeightSubGroup <> 0 then
    begin
      FHeightSubGroup := 0;
      FSlice.StartChange;
      FSlice.StopChange([chs_XHeight]);
    end
  end
  else
  begin
    if FHeightSubGroup <> Value then
    begin
      FHeightSubGroup := Value;
      FSlice.StartChange;
      FSlice.StopChange([chs_XHeight]);
    end
  end
end;

procedure TfcxSliceField.SetWidthSubGroup(const Value: SmallInt);
begin
  if Value = FSlice.DefaultColWidth then
  begin
    if FWidthSubGroup <> 0 then
    begin
      FWidthSubGroup := 0;
      FSlice.StartChange;
      FSlice.StopChange([chs_YWidth]);
    end
  end
  else
  begin
    if FWidthSubGroup <> Value then
    begin
      FWidthSubGroup := Value;
      FSlice.StartChange;
      FSlice.StopChange([chs_YWidth]);
    end
  end
end;

procedure TfcxSliceField.SetCollapsed(const Value: Boolean);
var
  ARegion: TfcxRegionOfField;
  AIndex: integer;
begin
  if FCollapsed <> Value then
  begin
    FCollapsed := Value;
    AIndex := FSlice.FindSliceFieldInRegions(Self, ARegion);
    if (AIndex > -1) then
    begin
      if ARegion = rf_CapXAx then
        FSlice.FXAxisContainer.SetExpandedLevelInternal(AIndex, False, not Value)
      else
      if ARegion = rf_CapYAx then
        FSlice.FYAxisContainer.SetExpandedLevelInternal(AIndex, False, not Value)
    end;
  end
end;

procedure TfcxSliceField.SetCollapsedSubGroup(const Value: Boolean);
var
  ARegion: TfcxRegionOfField;
  AIndex: integer;
begin
  if FCollapsedSubGroup <> Value then
  begin
    FCollapsedSubGroup := Value;
    AIndex := FSlice.FindSliceFieldInRegions(Self, ARegion);
    if (AIndex > -1) then
    begin
      if ARegion = rf_CapXAx then
      begin
        if FSlice.FXAxisContainer.FAxisSourceTree.FSourceTreeLevels[FSlice.FXAxisContainer.LevelOfRegionFieldAbs[AIndex]].FGSubLevelVisible then
          FSlice.FXAxisContainer.SetExpandedLevelInternal(AIndex + 1, False, not Value)
      end
      else
      if ARegion = rf_CapYAx then
      begin
        if FSlice.FYAxisContainer.FAxisSourceTree.FSourceTreeLevels[FSlice.FYAxisContainer.LevelOfRegionFieldAbs[AIndex]].FGSubLevelVisible then
          FSlice.FYAxisContainer.SetExpandedLevelInternal(AIndex + 1, False, not Value)
      end;
    end;
  end
end;

procedure TfcxSliceField.SetShowAllValues(const Value: Boolean);
var
  ARegion: TfcxRegionOfField;
  AIndex: integer;
begin
  if FShowAllValues <> Value then
  begin
    FShowAllValues := Value;
    AIndex := FSlice.FindSliceFieldInRegions(Self, ARegion);
    if (AIndex > -1) then
    begin
      if ARegion = rf_CapXAx then
      begin
        FSlice.StartChange;
        FSlice.StopChange([chs_CapXAxis]);
      end
      else
      if ARegion = rf_CapYAx then
      begin
        FSlice.StartChange;
        FSlice.StopChange([chs_CapYAxis]);
      end
    end;
  end
end;

function TfcxSliceField.GetDisplayFormat: TfcxFormat;
begin
  Result := nil;
end;

procedure TfcxSliceField.SetDisplayFormat(const Value: TfcxFormat);
begin
//
end;

procedure TfcxSliceField.SetRangeFilter(ARange: TfcxRanges);
begin
//
end;

function TfcxSliceField.GetDataType: TfcxDataType;
begin
  Result := fcdt_NotImplemented;
end;

function TfcxSliceField.GetUVAvailable(AUVIndex: integer): boolean;
begin
  Result := True;
end;

function TfcxSliceField.GetUVAvailableCount: integer;
begin
  Result := UVCount;
end;

procedure TfcxSliceField.SetPopupShowAvailable(const Value: Boolean);
begin
  FPopupShowAvailable := Value;
end;

function TfcxSliceField.BuildUVTree(ShowAvailable: Boolean): TfcxTree;
var
  I, J, Index: Integer;
  Node, Child: PfcxTreeNode;
begin
  Result := TfcxTree.Create;
  if HasGroups then
  begin
    for I := 0 to GroupCount - 1 do
    begin
      Node := Result.AddChild(nil);
      Node.Data := Pointer(I);
      Node.Tag := uvtGroup;
      for J := 0 to GroupUVCount[I] - 1 do
      begin
        Child := Result.AddChild(Node);
        Child.Data := Pointer(J);
        Child.Tag := uvtGroupUV;
      end;
    end;
    for I := 0 to NonGroupUVCount - 1 do
    begin
      Node := Result.AddChild(nil);
      Node.Data := Pointer(I);
      Node.Tag := uvtNonGroupUV;
    end;
  end
  else
  begin
    for I := 0 to UVCount - 1 do
    begin
      Index := UVIndexByOrder[I];
      if not ShowAvailable or UVAvailable[Index] then
      begin
        Node := Result.AddChild(nil);
        Node.Data := Pointer(Index);
        Node.Tag := uvtUV;
      end;
    end;
  end;
end;

function TfcxSliceField.GetUVValue(AUVIndex: integer): Variant;
begin
  Result := Unassigned;
end;

{ TfcxSliceMeasuresField }

constructor TfcxSliceMeasuresField.Create(ASlice: TfcxSlice);
begin
  inherited;
  FUVFilterSupport := False;
end;

function TfcxSliceMeasuresField.GetUVFilterType: TfcxUVFilterType;
begin
  Result := uvft_None
end;

{ TfcxAxisContainer }

constructor TfcxAxisContainer.Create(ASlice: TfcxSlice; ARegion: TfcxRegionOfField);
begin
  inherited;
//  FSlice := ASlice;
{
  if AXAxis then
    FRegion := rf_CapXAx
  else
    FRegion := rf_CapYAx;
}
  if ARegion = rf_CapXAx then
  begin
    FGrandTotalPosition := FSlice.Settings.XAxisSettings.GrandTotalPosition;
    FShowTotalAs := FSlice.Settings.XAxisSettings.ShowTotalAs;
  end
  else
  begin
    FGrandTotalPosition := FSlice.Settings.YAxisSettings.GrandTotalPosition;
    FShowTotalAs := FSlice.Settings.YAxisSettings.ShowTotalAs;
  end;
  FUseGrandTotalPositionFromMeasure := False;
{
  FCoordinate := nil;
  FLevelCount := 0;
  FRealLevelCount := 0;
  FCellsCount := 0;
  FAxisTree := nil;
}
  FAxisTree := nil;
  FAxisSortTree := TfcxAxisSortTree.Create(Self);
  FVisibleAxisNodes := TfcxVisibleAxisNodes.Create(Self);
//  FFieldsOfRegion := TfcxAxisFields.Create(Self);
  FAxisSourceTree := TfcxAxisSourceTree.Create(Self);
  FNoFields := True;
  FMeasuresLevel := -1;
  FMeasuresLevelVisibleIndex := -1;
  FAxisType := at_Standard;
  FAdditionalGrandTotalFunctions := [];
  FCountAdditionalGrandTotalFunctions := 0;
  FAdditionalGrandTotalScriptFunction := '';
  FAdditionalGrandTotalExpression.UseFields := False;
  FAdditionalGrandTotalExpression.Text := '';
end;

destructor TfcxAxisContainer.Destroy;
begin
  ClearAxisTree;
  FreeMem(FCoordinate);
  FreeAndNil(FAxisSourceTree);
//  FFieldsOfRegion.Free;
  FreeAndNil(FVisibleAxisNodes);
  FreeAndNil(FAxisSortTree);
  inherited;
end;

procedure TfcxAxisContainer.SetCountInLevel(ALevel: TfcxSmallCount;
  const Value: Integer);
begin
  if FAxisTree[ALevel].Count <> Value then
  begin
    if Value > FAxisTree[ALevel].Capacity then
    begin
      ReallocMem(FAxisTree[ALevel].Nodes, Value * SizeOf(_fcxAxisTreeNodeList));
      FAxisTree[ALevel].Capacity := Value;
    end;
    FAxisTree[ALevel].Count := Value;
  end;
end;

function TfcxAxisContainer.GetDimCaption(ALevel: TfcxSmallCount;
  AVisIndex: Integer): TfcxString;
var
  AIndexInDimLevel, i, Index: integer;
  AIndexInLevel: Integer;
begin
  if FSlice.FMeasuresContainer.FContainer = Self then
  begin
    if ALevel = MeasuresLevel then
    begin
      Result := FSlice.FMeasuresContainer.Measures[VisibleAxisNodes.MeasureIndex[AVisIndex]].Caption;
      exit;
    end
    else
    if ALevel > MeasuresLevel then
      Dec(ALevel);
  end;
  Index := VisibleAxisNodes.Index[AVisIndex];
  if FLevelOf[Index] = -1 then
    Result := fcxResources.GetAnsi('sGrandTotal')
  else
  if FLevelOf[Index] < ALevel then
  begin
    AIndexInLevel := FIndexInLevel[Index];
    case ShowTotalAs of
      sta_AsParentValue:
        Result := GetDisplayValueLI(ALevel - 1, AIndexInLevel);
      sta_AsTotalWithParentValue:
        Result := fcxResources.GetAnsi('sTotal') + ' ' + GetDisplayValueLI(ALevel - 1, AIndexInLevel);
    else
      Result := fcxResources.GetAnsi('sTotal');
    end;
  end
  else
  begin
    AIndexInDimLevel := FIndexInLevel[Index];
    for i := FLevelOf[Index] downto ALevel + 1 do
      AIndexInDimLevel := FAxisTree[i].nodes[AIndexInDimLevel].IndParent;
    Result := GetDisplayValueLI(ALevel, AIndexInDimLevel);
  end;
end;

function TfcxAxisContainer.GetDimValue(ALevel: TfcxSmallCount;
  AVisIndex: Integer): variant;
var
  AIndexInDimLevel, i, Index: integer;
begin
  if FSlice.FMeasuresContainer.FContainer = Self then
  begin
    if ALevel = MeasuresLevel then
    begin
      Result := FSlice.FMeasuresContainer.Measures[VisibleAxisNodes.MeasureIndex[AVisIndex]].Caption;
      exit;
    end
    else
    if ALevel > MeasuresLevel then
      Dec(ALevel);
  end;
  Index := VisibleAxisNodes.Index[AVisIndex];
  if FLevelOf[Index] < ALevel then
  begin
    Result := Unassigned;
    Exit;
  end;
  AIndexInDimLevel := FIndexInLevel[Index];
  for i := FLevelOf[Index] downto ALevel + 1 do
    AIndexInDimLevel := FAxisTree[i].nodes[AIndexInDimLevel].IndParent;
  Result := GetValueLI(ALevel, AIndexInDimLevel);
end;

function TfcxAxisContainer.GetDimValueID(ALevel: TfcxSmallCount;
  AVisIndex: Integer): integer;
var
  AIndexInDimLevel, i, Index: integer;
begin
  if FSlice.FMeasuresContainer.FContainer = Self then
  begin
    if ALevel = MeasuresLevel then
    begin
      Result := -1;
      exit;
    end
    else
    if ALevel > MeasuresLevel then
      Dec(ALevel);
  end;
  Index := VisibleAxisNodes.Index[AVisIndex];
  if FLevelOf[Index] < ALevel then
  begin
    Result := -1;
    Exit;
  end;
  AIndexInDimLevel := FIndexInLevel[Index];
  for i := FLevelOf[Index] downto ALevel + 1 do
    AIndexInDimLevel := FAxisTree[i].nodes[AIndexInDimLevel].IndParent;
  Result := GetValueIDLI(ALevel, AIndexInDimLevel);
end;

function TfcxAxisContainer.GetDisplayValue(ALevel: TfcxSmallCount; AIndex: Integer): TfcxString;
var
  AIndexInLevel: Integer;
begin
  AIndexInLevel := FIndexInLevel[AIndex];
  if FLevelOf[AIndex] = -1 then
    Result := fcxResources.GetAnsi('sGrandTotal')
  else
  if FLevelOf[AIndex] < ALevel then
  begin
    case ShowTotalAs of
      sta_AsParentValue:
        Result := GetDisplayValueLI(ALevel - 1, AIndexInLevel);
      sta_AsTotalWithParentValue:
        Result := fcxResources.GetAnsi('sTotal') + ' ' + GetDisplayValueLI(ALevel - 1, AIndexInLevel);
    else
      Result := fcxResources.GetAnsi('sTotal');
    end;
  end
  else
    Result := GetDisplayValueLI(ALevel, AIndexInLevel);
end;

function TfcxAxisContainer.GetDimValuesID(ALevel: TfcxSmallCount; AStartVisIndex, AEndVisIndex: Integer): TIntegerDynArray;
var
  I, J: Integer;
  UvList: TList;
  valueID, valueID2: integer;
begin
  // 1. Add uv indices to list without doubles
  UvList := TList.Create;
  try
    if (AStartVisIndex <= AEndVisIndex) and (AStartVisIndex > -1) then
    begin
      for I := AStartVisIndex to AEndVisIndex do
      begin
        valueID := DimValueID[ALevel, I];
        if valueID > -1 then
          UvList.Add(Pointer(valueID))
        else
        if valueID < -1 then
        begin
          // group
          for J := 0 to LevelInfo[ALevel].RegionField.SliceField.GroupManager.CountInGroup[-valueID-2] - 1 do
          begin
            valueID2 := LevelInfo[ALevel].RegionField.SliceField.GroupManager.UVIndexInGroup[-valueID-2, J];
            if valueID2 > -1 then
              UvList.Add(Pointer(valueID2))
          end;
        end;
      end;
      if UvList.Count > 0 then
      begin
        SetLength(Result, UvList.Count);
        for I := 0 to UvList.Count - 1 do
          Result[I] := PtrInt(UvList.Items[I]);
      end;
    end;
  finally
    UvList.Free;
  end;
end;

procedure TfcxAxisContainer.ClearAxisTree(ASaveLevels: boolean = False);
var
  i: TfcxSmallCount;
begin
  FSlice.FMeasuresContainer.Clear;
  FVisibleAxisNodes.Clear;
  FAxisSortTree.Clear;
// ??? !!! often called. Check.
  FreeMem(FLevelOf);
  FLevelOf := nil;

  FreeMem(FIndexInLevel);
  FIndexInLevel := nil;

  FreeMem(FIndexOfParent);
  FIndexOfParent := nil;

  if FAxisTree <> nil then
  begin
    if ASaveLevels then
    begin
      for i := -1 to FLevelCount - 1 do
      begin
        FreeMem(FAxisTree[i].Nodes);
        FAxisTree[i].Nodes := nil;
        FreeMem(FAxisTree[i].IndexInLastLevel);
        FAxisTree[i].IndexInLastLevel := nil;
        FAxisTree[i].Count := 0;
        FAxisTree[i].Capacity := 0;
        FAxisTree[i].CountWithTotals := 0;
      end;
    end
    else
    begin
      for i := -1 to FLevelCount - 1 do
      begin
        FreeMem(FAxisTree[i].Nodes);
        FreeMem(FAxisTree[i].IndexInLastLevel);
      end;
      FreeMem(FAxisTree);
      FAxisTree := Nil;
    end
  end;
  FLevelCount := 0;
  FVisibleLevelCount := 0;
  FRealLevelCount := 0;
  FCellsCount := 0;
  FMeasuresLevel := -1;
  FMeasuresLevelVisibleIndex := -1;
end;

procedure TfcxAxisContainer.MeasuresInserted;
begin
  FAxisSourceTree.ResetLevelsType;
  ResetLevelsType;
end;

procedure TfcxAxisContainer.MeasuresDeleted;
begin
  FAxisSourceTree.ResetLevelsType;
  ResetLevelsType;
end;

procedure TfcxAxisContainer.MeasuresMoved(AFromIndex: TfcxSmallCount);
begin
  FAxisSourceTree.ResetLevelsType;
  ResetLevelsType;
end;

procedure TfcxAxisContainer.FillVisibleArray;
begin
  if (FSlice.FXAxisContainer = Self) and FSlice.FHideColZeros then
    MakeHideZeros
  else
  if (FSlice.FYAxisContainer = Self) and FSlice.FHideRowZeros then
    MakeHideZeros;
  if (FSlice.FMeasuresContainer.FContainer = Self) then
    FillVisibleArrayWM
  else
    FillVisibleArrayWOM;
  FVisibleLevelCount := GetVisibleLevelCount;
  FMeasuresLevelVisibleIndex := GetMeasuresLevelVisibleIndexInternal;
end;

procedure TfcxAxisContainer.FillVisibleArrayWOM;
  function FillVisibleFromNodeWOM(ASTNode: PfcxSTNode; ADLevel: TfcxSmallCount; ATotalPosition: TfcxTotalPosition; ACheckOnly: boolean): boolean;
  var
    ANodeIndex, ACountChild, AIndexWithOrd, i: Integer;
    AAscOrder: boolean;
    ANextTotalPosition: TfcxTotalPosition;
    ATotalVisible: Boolean;
    ASelfVisible: Boolean;
    ATotalVisIndex, AVisibledChildCount: integer;
    ANeedHideTotal: Boolean;
    AResTotVis: Boolean;
  begin
    Result := False;
    AResTotVis := False;
// iterate children. for visible run FVisibleAxisNodes.AddWOM(

// total before
    ATotalVisible := False;
    ATotalVisIndex := -1;
    if (ATotalPosition = fctp_Before) or (FAxisType = at_Tree) then // if total before
    begin
      if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
      begin
{-oleg 2017-03-22
        if not ACheckOnly then
          FAxisTree[ASTNode.FLevel + 1].Visible := True;
        Result := True;
}
        if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FGSubLevelVisible)
           and
           ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType in [fcATNT_SimpleValue, fcATNT_OtherValue])
           or
           ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType = fcATNT_GroupValue)
           and
           ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0))) then
  // collapsed group or single with expanded sublevel
        begin
  // do not add total
        end
        else
        begin
  // total in next level is visible
  // check hide
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
          begin
//+oleg 2017-03-22
            if not ACheckOnly then
              FAxisTree[ASTNode.FLevel + 1].Visible := True;
            AResTotVis := True;
//oleg 2017-03-22
            if not ACheckOnly then
              FVisibleAxisNodes.AddWOM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex]);
            ATotalVisIndex := FVisibleAxisNodes.Count - 1;
            ATotalVisible := True;
            if not ACheckOnly then
              for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                FVisibleAxisNodes.AddWOM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], i)
          end;
        end;
      end;
    end;
    AVisibledChildCount := 0;
    if not ((ASTNode.FLevel = -1)
       and
       ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) <> 0)) then
// cell is not collapsed grand total
    begin
      AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
      ANextTotalPosition := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].FTotalPosition; // total position (in child)
      ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
      for ANodeIndex := 0 to ACountChild - 1 do
      begin
        ASelfVisible := False;
        if AAscOrder then
          AIndexWithOrd := ANodeIndex
        else
          AIndexWithOrd := ACountChild - ANodeIndex - 1;
        if (ADLevel = (FLevelCount - 1)) or
           ((FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType <> fcATNT_EmptyValue) and
            ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ASTNode.FLevel + 1, 0).State and stDimCollapsed) = stDimCollapsed) and
            ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0)) then // cell is not empty and collapsed, but cell is not expanded group
        begin
// cell is in last level or collapsed
// check hide
// !!! error if all cells in level = level + 2 are hidden. need deep check.
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
          begin
            if (ADLevel <> (FLevelCount - 1)) then
              ASelfVisible := FillVisibleFromNodeWOM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ANextTotalPosition, True)
            else
              ASelfVisible := True;
            if not ACheckOnly then
              if ASelfVisible then
                FVisibleAxisNodes.AddWOM(FAxisTree[ASTNode.FLevel + 1].IndexInLastLevel[ASTNode.Nodes[AIndexWithOrd].FIndex]);
              if ASelfVisible then
                inc(AVisibledChildCount);
          end;
        end
        else
        begin
// process child
// check hide
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
          begin
            ASelfVisible := FillVisibleFromNodeWOM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ANextTotalPosition, ACheckOnly);
            if ASelfVisible then
              inc(AVisibledChildCount);
          end;
        end;
        if not ACheckOnly then
          if ASelfVisible then
          begin
            FAxisTree[ASTNode.FLevel + 1].Visible := True;
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State or stVisible
          end
          else
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and not stVisible;
        Result := Result or ASelfVisible;
      end;
    end;
    ANeedHideTotal := (FSlice.HideTotalForSingleValue and (FAxisType <> at_Tree) and (AVisibledChildCount = 1) and (CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] = 0));
    if not ANeedHideTotal then
    begin
      ANeedHideTotal := (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) <> 0;
      Result := Result or AResTotVis; // +oleg 2017-03-22
    end;
    if ANeedHideTotal and (ATotalVisIndex > -1) then
    begin
      if not ACheckOnly then
        FVisibleAxisNodes.DeleteWOM(ATotalVisIndex);
      ATotalVisible := False;
    end
    else
    if not ANeedHideTotal then
      if (ATotalPosition = fctp_After) and (FAxisType <> at_Tree) then // if total after
      begin
{-oleg 2017-03-22
        if not ACheckOnly then
          FAxisTree[ASTNode.FLevel + 1].Visible := True;
        Result := True;
}
        if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FGSubLevelVisible)
           and
           ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType in [fcATNT_SimpleValue, fcATNT_OtherValue])
           or
           ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType = fcATNT_GroupValue)
           and
           ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0))) then
// collapsed group or single with expanded sublevel
        begin
// do not add total
        end
        else
        begin
// total in next level is visible
// check hide
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
          begin
//+oleg 2017-03-22
            if not ACheckOnly then
              FAxisTree[ASTNode.FLevel + 1].Visible := True;
            Result := True;
//oleg 2017-03-22
            ATotalVisible := True;
            if not ACheckOnly then
              FVisibleAxisNodes.AddWOM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex]);
            if not ACheckOnly then
              for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                FVisibleAxisNodes.AddWOM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], i)
          end;
        end;
      end;
    if not ACheckOnly then
      if ATotalVisible then
      begin
        FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State or stVisible;
        if ASTNode.FLevel = -1 then
          for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State or stVisible
      end
      else
      begin
        FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and not stVisible;
        for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
          FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and not stVisible
      end
  end;

var
  ALevelIndex: TfcxSmallCount;
begin
  FVisibleAxisNodes.Clear;
  FMeasuresLevel := -1;
  FMeasuresLevelVisibleIndex := -1;
//  FAxisSourceTree.ClearPropertiesByMask(byte(not byte(stVisible)));
  ALevelIndex := 0;
  if FLevelCount > 0 then
    FAxisTree[ALevelIndex].Visible := True;
  for ALevelIndex := 1 to FLevelCount - 1 do
    FAxisTree[ALevelIndex].Visible := False;
  if FAxisSourceTree.FGoodRecordCount = 0 then
    Exit;
  if FLevelCount > 0 then
    FillVisibleFromNodeWOM(FAxisSortTree.FRootNode, 0, FGrandTotalPosition, False)
  else
  begin
    FVisibleAxisNodes.AddWOM(FAxisTree[-1].IndexInLastLevel[0]);
    FAxisTree[-1].Visible := True;
    FAxisSourceTree.FSourceTreeLevels[-1].FPropSelf[0][0].State
          := FAxisSourceTree.FSourceTreeLevels[-1].FPropSelf[0][0].State or stVisible
  end;
  FVisibleAxisNodes.FillVisIndexes;
end;

procedure TfcxAxisContainer.FillVisibleArrayWM;
var
  ATotalPositions: Array of Array of TfcxTotalPosition;
  AExistNonCollapsedTotal: Boolean;

  function FillVisibleFromNodeWM(ASTNode: PfcxSTNode; ADLevel, ADRealLevel, ACurMeasureIndex: TfcxSmallCount; ACheckOnly: boolean): boolean;
  var
    ANodeIndex, ACountChild, AIndexWithOrd, i: Integer;
    AAscOrder: boolean;
    ATotalVisible: Boolean;
    ASelfVisible: Boolean;
    AMeasureIndex: TfcxSmallCount;
    ATotalVisIndex, AVisibledChildCount: integer;
    ANeedHideTotal: Boolean;
    ATotalVisIndexes: Array of Integer;
    ATotalVisibles: Array of Boolean;
    AResTotVis: boolean;
  begin
    SetLength(ATotalVisIndexes, FSlice.FMeasuresContainer.Count);
    SetLength(ATotalVisibles, FSlice.FMeasuresContainer.Count);
    Result := False;
    AResTotVis := False;
// iterate children. for visible run FVisibleAxisNodes.AddWM(
    if FMeasuresLevel = ADRealLevel then
    begin
// measures level. total not draw. draw measures name only.
      for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
        if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
        begin
          ASelfVisible := False;
          if not ((ASTNode.FLevel <> -1)
             and
            ((FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][0].State and stDimCollapsed) <> 0)) then // grand total is not collapsed
            if (ADRealLevel = (FRealLevelCount - 1)) or
               ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType <> fcATNT_EmptyValue) and
               (((CellPropertiesByNodeSelf(ASTNode.FIndex, ASTNode.FLevel, 0).State and stDimCollapsed) = stDimCollapsed) or
                ((CellPropertiesByNodeMeasures(ASTNode.FIndex, ASTNode.FLevel, AMeasureIndex).State and stDimCollapsed) = stDimCollapsed))) then // collapsed
            begin
    // cell is in last level or collapsed
// check hide
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
              begin
                if (ADRealLevel <> (FRealLevelCount - 1)) then
                  ASelfVisible := FillVisibleFromNodeWM(ASTNode, ADLevel + 1, ADRealLevel + 1, AMeasureIndex, True)
                else
                  ASelfVisible := True;
                if not ACheckOnly then
                  if ASelfVisible then
                    FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex);
              end;
            end
            else
            begin
    // process child
// check hide
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
                ASelfVisible := FillVisibleFromNodeWM(ASTNode, ADLevel + 1, ADRealLevel + 1, AMeasureIndex, ACheckOnly);
            end;

          if not ACheckOnly then
            if ASelfVisible then
            begin
              FAxisTree[ASTNode.FLevel].Visible := True;
              FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                  := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State or stVisible
            end
            else
              FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                  := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and not stVisible;

          Result := Result or ASelfVisible;
        end;
    end
    else
    if FMeasuresLevel > ADRealLevel then // above measures
    begin
      for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
      begin
        ATotalVisIndexes[AMeasureIndex] := -1;
        ATotalVisibles[AMeasureIndex] := False;
        if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
        begin
          if ATotalPositions[AMeasureIndex, ADLevel] = fctp_Before then // if total before
          begin
            if (ASTNode.FLevel = -1)
               or
              ((FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][0].State and stDimCollapsed) = 0) then // grand total cell or grand total is not collapsed
            begin
{-oleg 2017-03-22
              if not ACheckOnly then
                FAxisTree[ADLevel].Visible := True;
              Result AResTotVis := True;
}
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FGSubLevelVisible)
                 and
                 ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType in [fcATNT_SimpleValue, fcATNT_OtherValue])
                 or
                 ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType = fcATNT_GroupValue)
                 and
                 ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0))) then
        // collapsed group or single with expanded sublevel
              begin
        // do not add total
              end
              else
              begin
        // total in next level is visible
// check hide
                if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
                begin
//+oleg 2017-03-22
                  if not ACheckOnly then
                    FAxisTree[ADLevel].Visible := True;
                  AResTotVis := True;
//oleg 2017-03-22
                  ATotalVisibles[AMeasureIndex] := True;
                  if not ACheckOnly then
                    FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex);
                  ATotalVisIndexes[AMeasureIndex] := FVisibleAxisNodes.Count - 1;
                  if not ACheckOnly then
                    for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                      FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex, i)
                end;
              end;
            end
          end
          else
          if (ATotalPositions[AMeasureIndex, ADLevel] = fctp_Hide) and
             ((CellPropertiesByNodeSelf(ASTNode.FIndex, ASTNode.FLevel, 0).State and stDimCollapsed) <> 0) and
             ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0) then // cell is collapsed and group
          begin
        // total in next level is visible
// check hide
            if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
            begin
              ATotalVisibles[AMeasureIndex] := True;
              if not ACheckOnly then
                FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex);
              ATotalVisIndexes[AMeasureIndex] := FVisibleAxisNodes.Count - 1;
              if not ACheckOnly then
                FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State or stVisible;
              {Result}AResTotVis := True;
              if not ACheckOnly then
                for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                  FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex, i)
            end;
          end;
        end;
      end;
      AVisibledChildCount := 0;
      if AExistNonCollapsedTotal then
  // exists noncollapsed grand totals
      if (FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType = fcATNT_EmptyValue) or
         ((CellPropertiesByNodeSelf(ASTNode.FIndex, ASTNode.FLevel, 0).State and stDimCollapsed) = 0) or
         ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stGExpanded) <> 0) then // expanded cell or group
      begin
        begin
          AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
          ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
          for ANodeIndex := 0 to ACountChild - 1 do
          begin
            ASelfVisible := False;
            if AAscOrder then
              AIndexWithOrd := ANodeIndex
            else
              AIndexWithOrd := ACountChild - ANodeIndex - 1;
            if (ADRealLevel + 1) = FMeasuresLevel then // next level is measures
            begin
// check hide
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
              begin
                ASelfVisible := FillVisibleFromNodeWM(ASTNode.Nodes[AIndexWithOrd], ADLevel, ADRealLevel + 1, ACurMeasureIndex, ACheckOnly);
                if ASelfVisible then
                  inc(AVisibledChildCount);
              end
            end
            else
            if ((FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType <> fcATNT_EmptyValue) and
                ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ASTNode.FLevel + 1, 0).State and stDimCollapsed) = stDimCollapsed) and
                ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0)) then // cell is not empty and collapsed, but cell is not expanded group
            begin
// cell is collapsed
// check hide
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
              begin
                ASelfVisible := FillVisibleFromNodeWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, ACurMeasureIndex, True);
                if ASelfVisible then
                begin
                  inc(AVisibledChildCount);
                  for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
                  begin
                    ATotalVisible := False;
                    if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
                    begin
                      if ((FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][0].State and stDimCollapsed) = 0) then // grand total is not collapsed
                      begin
  //                      FAxisTree[ADLevel+1].Visible := True;
  // check hide
                        if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
                        begin
                          ATotalVisible := True;
                          if not ACheckOnly then
                            FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel + 1].IndexInLastLevel[ASTNode.Nodes[AIndexWithOrd].FIndex], AMeasureIndex);
                        end;
                      end;
                    end;
                    if not ACheckOnly then
                      if ATotalVisible then
                        FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel+1].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel+1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State
                              := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel+1].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel+1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State or stVisible
                      else
                        FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel+1].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel+1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State
                              := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel+1].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel+1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and not stVisible;
                  end;
                end;
              end;
            end
            else
            begin
// process child
// check hide ??
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
              begin
                ASelfVisible := FillVisibleFromNodeWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, ACurMeasureIndex, ACheckOnly);
                if ASelfVisible then
                  inc(AVisibledChildCount);
              end
            end;
            if not ACheckOnly then
              if ASelfVisible then
              begin
                FAxisTree[ASTNode.FLevel + 1].Visible := True;
                FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State or stVisible
              end
              else
                FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[0][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and not stVisible;
            Result := Result or ASelfVisible;
          end;
        end;
      end;

      ANeedHideTotal := (FSlice.HideTotalForSingleValue and (FAxisType <> at_Tree) and (AVisibledChildCount = 1) and (CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] = 0));
      if not ANeedHideTotal then
      begin
        for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
        begin
          ANeedHideTotal := True;
          if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
            if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
            begin
              ANeedHideTotal := False;
              Break;
            end
        end;
        Result := Result or AResTotVis; // +oleg 2017-03-22
      end;
      if ANeedHideTotal then
      begin
        for AMeasureIndex := FSlice.FMeasuresContainer.Count - 1 downto 0 do
        begin
          if not ACheckOnly then
            if ATotalVisIndexes[AMeasureIndex] > -1 then
            begin
              FVisibleAxisNodes.DeleteWM(ATotalVisIndexes[AMeasureIndex]);
            end;
          ATotalVisibles[AMeasureIndex] := False;
        end;
      end
      else
      if not ANeedHideTotal then
      begin
        for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
        begin
          if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
          begin
            if ATotalPositions[AMeasureIndex, ADLevel] = fctp_After then // if total after
            begin
              ATotalVisibles[AMeasureIndex] := False;
              if not ((ASTNode.FLevel <> -1)
                 and
                ((FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][0].State and stDimCollapsed) <> 0)) then // grand total is not collapsed
              begin
{-oleg 2017-03-22
                if not ACheckOnly then
                  FAxisTree[ADLevel].Visible := True;
                Result := True;
}
                if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FGSubLevelVisible)
                   and
                   ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType in [fcATNT_SimpleValue, fcATNT_OtherValue])
                   or
                   ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType = fcATNT_GroupValue)
                   and
                   ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0))) then
          // collapsed group or single with expanded sublevel
                begin
          // do not add total
                end
                else
                begin
          // total in next level is visible
  // check hide
                  if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
                  begin
//+oleg 2017-03-22
                    if not ACheckOnly then
                      FAxisTree[ADLevel].Visible := True;
                    Result := True;
//oleg 2017-03-22
                    ATotalVisibles[AMeasureIndex] := True;
                    if not ACheckOnly then
                      FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex);
                    if not ACheckOnly then
                      for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                        FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex, i)
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
      for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
      begin
        if not ACheckOnly then
          if ATotalVisibles[AMeasureIndex] then
          begin
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                  := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State or stVisible;
            for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
              FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] + i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] + i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State or stVisible
          end
          else
          begin
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                  := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and not stVisible;
            for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
              FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] + i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] + i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and not stVisible
        end
      end;
    end
    else // under measures
    begin
      AMeasureIndex := ACurMeasureIndex;
      ATotalVisible := False;
      ATotalVisIndex := -1;
      if ATotalPositions[AMeasureIndex, ADLevel] = fctp_Before then // if total before
      begin
        if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
        begin
{-oleg 2017-03-22
          if not ACheckOnly then
            FAxisTree[ADLevel].Visible := True;
          Result AResTotVis := True;
}
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FGSubLevelVisible)
             and
             ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType in [fcATNT_SimpleValue, fcATNT_OtherValue])
             or
             ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType = fcATNT_GroupValue)
             and
             ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0))) then
    // collapsed group or single with expanded sublevel
          begin
    // do not add total
          end
          else
          begin
    // total in next level is visible
  // check hide
            if (FMeasuresLevel - 1) = ASTNode.FLevel then
            begin
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
              begin
//+oleg 2017-03-22
                if not ACheckOnly then
                  FAxisTree[ADLevel].Visible := True;
                AResTotVis := True;
//oleg 2017-03-22
                if not ACheckOnly then
                  FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex);
                ATotalVisIndex := FVisibleAxisNodes.Count - 1;
                ATotalVisible := True;
                if not ACheckOnly then
                  for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                    FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex, i)
              end;
            end
            else
            begin
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
              begin
//+oleg 2017-03-22
                if not ACheckOnly then
                  FAxisTree[ADLevel].Visible := True;
                AResTotVis := True;
//oleg 2017-03-22
                if not ACheckOnly then
                  FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex);
                ATotalVisIndex := FVisibleAxisNodes.Count - 1;
                ATotalVisible := True;
                if not ACheckOnly then
                  for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                    FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex, i)
              end;
            end
          end;
        end;
      end;

      AVisibledChildCount := 0;
      AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
      ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
      for ANodeIndex := 0 to ACountChild - 1 do
      begin
        ASelfVisible := False;
        if AAscOrder then
          AIndexWithOrd := ANodeIndex
        else
          AIndexWithOrd := ACountChild - ANodeIndex - 1;

        if (ADLevel = (FLevelCount - 1)) or
           ((FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType <> fcATNT_EmptyValue) and
            ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ASTNode.FLevel + 1, AMeasureIndex).State and stDimCollapsed) = stDimCollapsed) and
            ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0)) then // collapsed, but is not expanded group
        begin
// cell is in last level or collapsed
// check hide
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
          begin
            if (ADLevel <> (FLevelCount - 1)) then
              ASelfVisible := FillVisibleFromNodeWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, ACurMeasureIndex, True)
            else
              ASelfVisible := True;
            if not ACheckOnly then
              if ASelfVisible then
                FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel + 1].IndexInLastLevel[ASTNode.Nodes[AIndexWithOrd].FIndex], ACurMeasureIndex);
              if ASelfVisible then
                inc(AVisibledChildCount);
          end;
        end
        else
        begin
// process child
// check hide
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
          begin
            ASelfVisible := FillVisibleFromNodeWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, ACurMeasureIndex, ACheckOnly);
            if ASelfVisible then
              inc(AVisibledChildCount);
          end
        end;
        if not ACheckOnly then
          if ASelfVisible then
          begin
            FAxisTree[ASTNode.FLevel + 1].Visible := True;
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State or stVisible
          end
          else
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and not stVisible;
        Result := Result or ASelfVisible;
      end;

      ANeedHideTotal := (FSlice.HideTotalForSingleValue and (FAxisType <> at_Tree) and (AVisibledChildCount = 1) and (CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] = 0));
      if not ANeedHideTotal then
        ANeedHideTotal := (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) <> 0;
      if ANeedHideTotal and (ATotalVisIndex > -1) then
      begin
        if not ACheckOnly then
          FVisibleAxisNodes.DeleteWM(ATotalVisIndex);
        ATotalVisible := False;
      end
      else
      if not ANeedHideTotal then
      begin
        Result := Result or AResTotVis; // +oleg 2017-03-22
        if ATotalPositions[AMeasureIndex, ADLevel] = fctp_After then // if total after
        begin
          ATotalVisible := False;
{-oleg 2017-03-22
          if not ACheckOnly then
            FAxisTree[ADLevel].Visible := True;
          Result AResTotVis := True;
}
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FGSubLevelVisible)
             and
             ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType in [fcATNT_SimpleValue, fcATNT_OtherValue])
             or
             ((FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.NodeType = fcATNT_GroupValue)
             and
             ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0))) then
// collapsed group or single with expanded sublevel
          begin
// do not add total
          end
          else
          begin
// total in next level is visible
// check hide
            if (FMeasuresLevel - 1) = ASTNode.FLevel then
            begin
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
              begin
//+oleg 2017-03-22
                if not ACheckOnly then
                  FAxisTree[ADLevel].Visible := True;
                Result := True;
//oleg 2017-03-22
                ATotalVisible := True;
                if not ACheckOnly then
                  FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex);
                if not ACheckOnly then
                  for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                    FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex, i)
              end;
            end
            else
            begin
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stZeroOrHidden) = 0 then
              begin
//+oleg 2017-03-22
                if not ACheckOnly then
                  FAxisTree[ADLevel].Visible := True;
                Result := True;
//oleg 2017-03-22
                ATotalVisible := True;
                if not ACheckOnly then
                  FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex);
                if not ACheckOnly then
                  for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                    FVisibleAxisNodes.AddWM(FAxisTree[ASTNode.FLevel].IndexInLastLevel[ASTNode.FIndex], AMeasureIndex, i)
              end;
            end;
          end;
        end;
      end;
      if not ACheckOnly then
        if ATotalVisible then
        begin
          FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State or stVisible;
          for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] + i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] + i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State or stVisible
        end
        else
        begin
          FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and not stVisible;
          for i := 0 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
            FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] + i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State
                := FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] + i][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and not stVisible
        end;
    end
  end;
var
  ALevelIndex, AMeasureIndex: TfcxSmallCount;
  AXAxis: Boolean;
begin
  FVisibleAxisNodes.Clear;
  FMeasuresLevel := FAxisSourceTree.FMeasuresLevel;
//  FAxisSourceTree.ClearPropertiesByMask(byte(not byte(stVisible)));
  for ALevelIndex := 0 to FLevelCount - 1 do
    FAxisTree[ALevelIndex].Visible := False;
  if (FAxisSourceTree.FGoodRecordCount = 0) or (FSlice.FMeasuresContainer.VisibleCount = 0) then
    Exit;
  if FRealLevelCount > 0 then
  begin
    AXAxis := (Self = FSlice.FXAxisContainer);
    SetLength(ATotalPositions, FSlice.FMeasuresContainer.Count, FRealLevelCount);
    for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
      if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
      begin
        if FAxisType = at_Tree then
          ATotalPositions[AMeasureIndex, 0] := fctp_Before
        else
        if FUseGrandTotalPositionFromMeasure then
          ATotalPositions[AMeasureIndex, 0] := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].TotalPositionByLevel[AXAxis, -1] // position total from measure
        else
          ATotalPositions[AMeasureIndex, 0] := FGrandTotalPosition; // grand total position
        for ALevelIndex := 1 to FLevelCount - 1 do
        begin
          if FAxisType = at_Tree then
            ATotalPositions[AMeasureIndex, ALevelIndex] := fctp_Before
          else
          if Fields[FAxisTree[ALevelIndex - 1].RegionFieldIndex].FUseTotalPositionFromMeasure then
            ATotalPositions[AMeasureIndex, ALevelIndex] := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].TotalPositionByLevel[AXAxis, ALevelIndex - 1] // position total from measure
          else
            ATotalPositions[AMeasureIndex, ALevelIndex] := Fields[FAxisTree[ALevelIndex - 1].RegionFieldIndex].FTotalPosition; // total position
        end;
      end;
    if FLevelCount = 0 then
      AExistNonCollapsedTotal := True
    else
    begin
      AExistNonCollapsedTotal := False;
      for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
        if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
          if ((FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][0].State and stDimCollapsed) = 0) then // grand total is not collapsed
            begin
              AExistNonCollapsedTotal := True;
              Break;
            end;
    end;
    if FMeasuresLevel = 0 then
      FillVisibleFromNodeWM(FAxisSortTree.FRootNode, -1, 0, -1, False)
    else
      FillVisibleFromNodeWM(FAxisSortTree.FRootNode, 0, 0, -1, False);
  end;
  FVisibleAxisNodes.FillVisIndexes;
end;

function TfcxAxisContainer.TraverseAxis(AStartLevel,
  AStopLevel: TfcxSmallCount; AStartCell: Integer;
  ADrawProc: TfcxSliceAxisDrawCellProc): TfcxSliceDrawHeaderResult;
begin
  FillChar(Result, SizeOf(Result), 0);
  if AStartLevel > AStopLevel then
    Exit;
  case FAxisType of
    at_Tree: Result := TraverseAxis_Tree(AStartCell, ADrawProc);
    at_Standard: Result := TraverseAxis_Grid(AStartLevel, AStopLevel, AStartCell, ADrawProc);
  end;
end;

procedure TfcxAxisContainer.MeasureDeleted(AMeasureIndex: TfcxSmallCount);
begin
  FAxisSourceTree.ResetLevelsType;
  ResetLevelsType;
end;

procedure TfcxAxisContainer.MeasureInserted(AMeasureIndex: TfcxSmallCount);
begin
  FAxisSourceTree.ResetLevelsType;
  ResetLevelsType;
end;

procedure TfcxAxisContainer.MeasureMoved(AFromIndex, AToIndex: TfcxSmallCount);
var
  ALevelIndex, j, i: TfcxSmallCount;
  AAxisTreeNodePropertiesList: PfcxAxisTreeNodePropertiesList;
  AAxisTreeNodeSizesList: PfcxAxisTreeNodeSizesList;
  ACountAdditionalTotalFunctionsInLevel: TfcxSmallCount;
begin
  for ALevelIndex := -1 to FLevelCount - 1 do
  begin
    ACountAdditionalTotalFunctionsInLevel := CountAdditionalTotalFunctionsInLevel[ALevelIndex];
    if cfcSelfPropertiesArrayCount[FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelPosType] = 2 then
    begin
      if AFromIndex > AToIndex then
      begin
        AAxisTreeNodePropertiesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AFromIndex];
        for j := AFromIndex downto AToIndex + 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[j - 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AToIndex] := AAxisTreeNodePropertiesList;
      end
      else
      begin
        AAxisTreeNodePropertiesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AFromIndex];
        for j := AFromIndex to AToIndex - 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[j + 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AToIndex] := AAxisTreeNodePropertiesList;
      end;
    end;
    if cfcTotalSelfPropertiesArrayCount[FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelPosType] = 2 then
    begin
      if AFromIndex > AToIndex then
      begin
        AAxisTreeNodePropertiesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AFromIndex];
        for j := AFromIndex downto AToIndex + 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[j - 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AToIndex] := AAxisTreeNodePropertiesList;
      end
      else
      begin
        AAxisTreeNodePropertiesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AFromIndex];
        for j := AFromIndex to AToIndex - 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[j + 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AToIndex] := AAxisTreeNodePropertiesList;
      end;
    end;
    if ACountAdditionalTotalFunctionsInLevel > 0 then
      if cfcAdditionalTotalSelfPropertiesArrayCount[FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelPosType] = 4 then
      begin
        for i := 0 to ACountAdditionalTotalFunctionsInLevel - 1 do
        begin
          if AFromIndex > AToIndex then
          begin
            AAxisTreeNodePropertiesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AFromIndex * ACountAdditionalTotalFunctionsInLevel + i];
            for j := AFromIndex downto AToIndex + 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[j * ACountAdditionalTotalFunctionsInLevel + i] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[(j - 1) * ACountAdditionalTotalFunctionsInLevel + i];
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AToIndex * ACountAdditionalTotalFunctionsInLevel + i] := AAxisTreeNodePropertiesList;
            AAxisTreeNodeSizesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[AFromIndex * ACountAdditionalTotalFunctionsInLevel + i];
            for j := AFromIndex downto AToIndex + 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[j * ACountAdditionalTotalFunctionsInLevel + i] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[(j - 1) * ACountAdditionalTotalFunctionsInLevel + i];
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[AToIndex * ACountAdditionalTotalFunctionsInLevel + i] := AAxisTreeNodeSizesList;
            AAxisTreeNodeSizesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[AFromIndex * ACountAdditionalTotalFunctionsInLevel + i];
            for j := AFromIndex downto AToIndex + 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[j * ACountAdditionalTotalFunctionsInLevel + i] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[(j - 1) * ACountAdditionalTotalFunctionsInLevel + i];
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[AToIndex * ACountAdditionalTotalFunctionsInLevel + i] := AAxisTreeNodeSizesList;
          end
          else
          begin
            AAxisTreeNodePropertiesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AFromIndex * ACountAdditionalTotalFunctionsInLevel + i];
            for j := AFromIndex to AToIndex - 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[j * ACountAdditionalTotalFunctionsInLevel + i] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[(j + 1) * ACountAdditionalTotalFunctionsInLevel + i];
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AToIndex * ACountAdditionalTotalFunctionsInLevel + i] := AAxisTreeNodePropertiesList;
            AAxisTreeNodeSizesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[AFromIndex * ACountAdditionalTotalFunctionsInLevel + i];
            for j := AFromIndex to AToIndex - 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[j * ACountAdditionalTotalFunctionsInLevel + i] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[(j + 1)  * ACountAdditionalTotalFunctionsInLevel + i];
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[AToIndex * ACountAdditionalTotalFunctionsInLevel + i] := AAxisTreeNodeSizesList;
            AAxisTreeNodeSizesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[AFromIndex * ACountAdditionalTotalFunctionsInLevel + i];
            for j := AFromIndex to AToIndex - 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[j * ACountAdditionalTotalFunctionsInLevel + i] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[(j + 1) * ACountAdditionalTotalFunctionsInLevel + i];
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[AToIndex * ACountAdditionalTotalFunctionsInLevel + i] := AAxisTreeNodeSizesList;
          end;
        end;
      end;

    if cfcMeasuresPropertiesArrayCount[FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelPosType] = 2 then
    begin
      if AFromIndex > AToIndex then
      begin
        AAxisTreeNodePropertiesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[AFromIndex];
        for j := AFromIndex downto AToIndex + 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[j - 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[AToIndex] := AAxisTreeNodePropertiesList;
      end
      else
      begin
        AAxisTreeNodePropertiesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[AFromIndex];
        for j := AFromIndex to AToIndex - 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[j + 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[AToIndex] := AAxisTreeNodePropertiesList;
      end;
    end;
    if FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FSizeCount > 1 then
    begin
      if AFromIndex > AToIndex then
      begin
        AAxisTreeNodeSizesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[AFromIndex];
        for j := AFromIndex downto AToIndex + 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[j - 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[AToIndex] := AAxisTreeNodeSizesList;
        AAxisTreeNodeSizesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[AFromIndex];
        for j := AFromIndex downto AToIndex + 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[j - 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[AToIndex] := AAxisTreeNodeSizesList;
      end
      else
      begin
        AAxisTreeNodeSizesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[AFromIndex];
        for j := AFromIndex to AToIndex - 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[j + 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[AToIndex] := AAxisTreeNodeSizesList;
        AAxisTreeNodeSizesList := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[AFromIndex];
        for j := AFromIndex to AToIndex - 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[j] := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[j + 1];
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[AToIndex] := AAxisTreeNodeSizesList;
      end;
    end;
  end;
end;

procedure TfcxAxisContainer.SetExpanded(ARealLevel: TfcxSmallCount;
  ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean);
var
  ALevel: TfcxSmallCount;
begin
  ALevel := ARealLevel;
  if FMeasuresLevel <> -1 then // axis with measures
  begin
    if FMeasuresLevel <= ARealLevel then
      dec(ALevel);
    if (ALevel <> ARealLevel) or (ALevel = -1) then
      SetExpandedInternal(ALevel, ARealLevel, ANodeIndex, AMeasureIndex, Value, False)
    else
      SetExpandedInternal(ALevel, ARealLevel, ANodeIndex, 0, Value, False);
  end
  else
    SetExpandedInternal(ALevel, ARealLevel, ANodeIndex, 0, Value, False);
end;

procedure TfcxAxisContainer.SetExpandedInternal(ALevel, ARealLevel: TfcxSmallCount;
  ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean; ANonChange: boolean);
{
const
  Changes: array[Boolean] of TfcxTypeOfChange =
  (
    tch_ExpandYAx,
    tch_ExpandXAx
  );
}
{
var
  i: integer;
  v: Boolean;
}
begin
  if ANonChange then
    inc(FSlice.FChangeSemaphore);
  case FAxisTree[ALevel].LevelPosType of
//    fcATLPT_WMeasures_BaseLevel_LastLevel: ;
//    fcATLPT_WMeasures_NotBaseLevel_LastLevel: ;
//    fcATLPT_WOMeasures_LastLevel: ;
    fcATLPT_WMeasures_BaseLevel_NotLastLevel:
      begin
        if ALevel <> ARealLevel then
        begin // measures level
          if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) <> Value then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State xor stDimCollapsed;
            if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
// ??             dec(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount)
            else
// ??             inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount)
            ;
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_ExpandXNode])
              else
                FSlice.StopChange([chs_ExpandYNode]);
          end;
        end
        else
        begin
          if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) <> Value then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
             := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State xor stDimCollapsed;
            if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
              dec(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount)
            else
              inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount);
            CheckVisibleLevelCollapsedReal(ARealLevel);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_ExpandXNode])
              else
                FSlice.StopChange([chs_ExpandYNode]);
          end;
        end;
      end;
    fcATLPT_WMeasures_NotBaseLevel_High_NotLastLevel:
      begin
        if ALevel = -1 then // grand total
        begin
          if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) <> Value then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State xor stDimCollapsed;
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_ExpandXNode])
              else
                FSlice.StopChange([chs_ExpandYNode]);
          end;
        end
        else
          if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) <> Value then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State xor stDimCollapsed;
            if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
              dec(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount)
            else
              inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount);
            CheckVisibleLevelCollapsedReal(ARealLevel);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_ExpandXNode])
              else
                FSlice.StopChange([chs_ExpandYNode]);
          end;
      end;
    fcATLPT_WMeasures_NotBaseLevel_Low_NotLastLevel:
      begin
        if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) <> Value then
        begin
          if not ANonChange then
            FSlice.StartChange;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
            := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State xor stDimCollapsed;
          if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
            dec(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount)
          else
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount);
          CheckVisibleLevelCollapsedReal(ARealLevel);
          if not ANonChange then
            if FSlice.XAxisContainer = Self then
              FSlice.StopChange([chs_ExpandXNode])
            else
              FSlice.StopChange([chs_ExpandYNode]);
        end;
      end;
    fcATLPT_WOMeasures_NotLastLevel:
      begin
        if ALevel = -1 then // grand total
        begin
          if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) <> Value then
          begin
            if not ANonChange then
              FSlice.StartChange;

            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State xor stDimCollapsed;

            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_ExpandXNode])
              else
                FSlice.StopChange([chs_ExpandYNode]);
          end;
        end
        else
        begin
          if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) <> Value then
          begin
            if not ANonChange then
              FSlice.StartChange;

            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State xor stDimCollapsed;
            if ((FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
              dec(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount)
            else
              inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FCollapsedNodeCount);
            CheckVisibleLevelCollapsedReal(ALevel);

            if FAxisSourceTree.FSourceTreeLevels[ALevel].FLevelType = fcATLT_HasGroup then
            begin
              if FAxisSourceTree.FSourceTreeLevels[ALevel].FGSubLevelVisible then
                if FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.NodeType in [fcATNT_SimpleValue, fcATNT_OtherValue] then
                begin
// simple(only one) (in base level) change in child in sublevel
                  FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State xor stDimCollapsed;
                  if ((FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
                    dec(FAxisSourceTree.FSourceTreeLevels[ALevel+1].FCollapsedNodeCount)
                  else
                    inc(FAxisSourceTree.FSourceTreeLevels[ALevel+1].FCollapsedNodeCount);
                  CheckVisibleLevelCollapsedReal(ALevel+1);
                end
                else
                if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0 then
                begin
                  FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State xor stDimCollapsed;
                  if ((FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
                    dec(FAxisSourceTree.FSourceTreeLevels[ALevel+1].FCollapsedNodeCount)
                  else
                    inc(FAxisSourceTree.FSourceTreeLevels[ALevel+1].FCollapsedNodeCount);
                  CheckVisibleLevelCollapsedReal(ALevel+1);
                end;
            end
            else
            if FAxisSourceTree.FSourceTreeLevels[ALevel].FLevelType = fcATLT_SubGroup then
            begin
              if FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
              begin
// emty(continues of only one) (in sublevel) change in parent
                FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State
                  := FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State xor stDimCollapsed;
                if ((FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
                  dec(FAxisSourceTree.FSourceTreeLevels[ALevel-1].FCollapsedNodeCount)
                else
                  inc(FAxisSourceTree.FSourceTreeLevels[ALevel-1].FCollapsedNodeCount);
                CheckVisibleLevelCollapsedReal(ALevel-1);
              end;
            end;
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_ExpandXNode])
              else
                FSlice.StopChange([chs_ExpandYNode]);
          end;
        end;
      end
  end;
  if ANonChange then
    dec(FSlice.FChangeSemaphore);
end;

procedure TfcxAxisContainer.ResetLevelsType;
var
  ALevelIndex: TfcxSmallCount;
begin
  FMeasuresLevel := FAxisSourceTree.FMeasuresLevel;
  FMeasuresLevelVisibleIndex := GetMeasuresLevelVisibleIndexInternal;
{
  if FSlice.FMeasuresContainer.FContainer = Self then
  begin
    if (FSlice.FMeasuresContainer.FPosition = -1) or (FSlice.FMeasuresContainer.FPosition > FLevelCount) then
      FMeasuresLevel := FLevelCount
    else
      FMeasuresLevel := FSlice.FMeasuresContainer.FPosition;
  end
  else
  begin
    FMeasuresLevel := -1;
  end;
}
  for ALevelIndex := -1 to FLevelCount - 1 do
  begin
    FAxisTree[ALevelIndex].LevelPosType := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelPosType
  end;
end;

function TfcxAxisContainer.GetCellSize(AVisCellIndex: integer): Smallint;
var
  ALevel, AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount;
  AIndexInLevel, AIndexInProp: Integer;
begin
  if not VisIndexToTreeCellPos(AVisCellIndex, ALevel, AIndexInLevel, AMeasureIndex, AAdditionalTotalIndex) then
    if FSlice.FXAxisContainer = Self then
      Result := FSlice.DefaultColWidth
    else
      Result := FSlice.DefaultRowHeight
  else
  if FSlice.FXAxisContainer = Self then
  begin
    AIndexInProp := FAxisTree[ALevel].Nodes[AIndexInLevel].SSelfNode.IndexInProp;
    if AAdditionalTotalIndex < 0 then
      Result := FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp]
    else
      Result := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp];
    if Result = 0 then
      if FMeasuresLevel = -1 then
        Result := FSlice.DefaultColWidth
      else
      begin
        Result := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Width;
        if Result = 0 then
          Result := FSlice.DefaultColWidth;
      end
  end
  else
  begin
    AIndexInProp := FAxisTree[ALevel].Nodes[AIndexInLevel].SSelfNode.IndexInProp;
    if AAdditionalTotalIndex < 0 then
      Result := FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp]
    else
      Result := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp];
    if Result = 0 then
      if FMeasuresLevel = -1 then
        Result := FSlice.DefaultRowHeight
      else
      begin
        Result := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Height;
        if Result = 0 then
          Result := FSlice.DefaultRowHeight;
      end
  end;
end;

procedure TfcxAxisContainer.SetCellSize(AVisCellIndex: integer; const Value: Smallint);
var
  AChanged: boolean;
  ALevel, AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount;
  AIndexInLevel, ADefaultSize, AIndexInProp: Integer;
begin
  if VisIndexToTreeCellPos(AVisCellIndex, ALevel, AIndexInLevel, AMeasureIndex, AAdditionalTotalIndex) then
  begin
    AIndexInProp := FAxisTree[ALevel].Nodes[AIndexInLevel].SSelfNode.IndexInProp;
    if FSlice.FXAxisContainer = Self then
    begin
      if FMeasuresLevel = -1 then
        ADefaultSize := FSlice.DefaultColWidth
      else
      begin
        ADefaultSize := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Width;
        if ADefaultSize = 0 then
          ADefaultSize := FSlice.DefaultColWidth;
      end;
      if Value = ADefaultSize then
      begin
        if AAdditionalTotalIndex < 0 then
        begin
          AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp] <> 0;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp] := 0;
        end
        else
        begin
          AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] <> 0;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] := 0;
        end
      end
      else
      begin
        if AAdditionalTotalIndex < 0 then
        begin
          AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp] <> Value;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp] := Value
        end
        else
        begin
          AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] <> Value;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] := Value;
        end;
      end;
      if AChanged then
      begin
        FSlice.StartChange;
        FSlice.StopChange([chs_ColWidth]);
      end;
    end
    else
    begin
      if FMeasuresLevel = -1 then
        ADefaultSize := FSlice.DefaultRowHeight
      else
      begin
        ADefaultSize := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Height;
        if ADefaultSize = 0 then
          ADefaultSize := FSlice.DefaultRowHeight;
      end;
      if Value = ADefaultSize then
      begin
        if AAdditionalTotalIndex < 0 then
        begin
          AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp] <> 0;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp] := 0;
        end
        else
        begin
          AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] <> 0;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] := 0;
        end
      end
      else
      begin
        if AAdditionalTotalIndex < 0 then
        begin
          AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp] <> Value;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp] := Value
        end
        else
        begin
          AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] <> Value;
          FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] := Value;
        end;
      end;
      if AChanged then
      begin
        FSlice.StartChange;
        FSlice.StopChange([chs_RowHeight]);
      end
    end;
  end;
end;

procedure TfcxAxisContainer.SetGrandTotalPosition(
  const Value: TfcxTotalPosition);
begin
  if FGrandTotalPosition <> Value then
  begin
    FSlice.StartChange;
    FGrandTotalPosition := Value;
    if Self = FSlice.FXAxisContainer then
      FSlice.StopChange([chs_XTotalPosition])
    else
      FSlice.StopChange([chs_YTotalPosition]);
  end;
end;

procedure TfcxAxisContainer.SetUseGrandTotalPositionFromMeasure(
  const Value: Boolean);
begin
  if FUseGrandTotalPositionFromMeasure <> Value then
  begin
    FSlice.StartChange;
    FUseGrandTotalPositionFromMeasure := Value;
    if Self = FSlice.FXAxisContainer then
      FSlice.StopChange([chs_XTotalPosition])
    else
      FSlice.StopChange([chs_YTotalPosition]);
  end;
end;

function TfcxAxisContainer.GetLevelSize(AAbsLevelIndex: TfcxSmallCount): Smallint;
begin
  if AAbsLevelIndex < 0 then
  begin
    Result := 0;
    Exit;
  end;
  if FSlice.FXAxisContainer = Self then
  begin
    if (FMeasuresLevel = -1) or (AAbsLevelIndex < FMeasuresLevel) then
      if FAxisTree[AAbsLevelIndex].LevelType <> fcATLT_SubGroup then
        Result := Fields[FAxisTree[AAbsLevelIndex].RegionFieldIndex].Height
      else
        Result := Fields[FAxisTree[AAbsLevelIndex].RegionFieldIndex].HeightSubGroup
    else
    if AAbsLevelIndex = FMeasuresLevel then
      Result := FSlice.FMeasuresContainer.Height
    else
      if FAxisTree[AAbsLevelIndex - 1].LevelType <> fcATLT_SubGroup then
        Result := Fields[FAxisTree[AAbsLevelIndex - 1].RegionFieldIndex].Height
      else
        Result := Fields[FAxisTree[AAbsLevelIndex - 1].RegionFieldIndex].HeightSubGroup;
    if Result = 0 then
      Result := FSlice.DefaultRowHeight;
  end
  else
  begin
    if (FMeasuresLevel = -1) or (AAbsLevelIndex < FMeasuresLevel) then
      if FAxisTree[AAbsLevelIndex].LevelType <> fcATLT_SubGroup then
        Result := Fields[FAxisTree[AAbsLevelIndex].RegionFieldIndex].Width
      else
        Result := Fields[FAxisTree[AAbsLevelIndex].RegionFieldIndex].WidthSubGroup
    else
    if AAbsLevelIndex = FMeasuresLevel then
      Result := FSlice.FMeasuresContainer.Width
    else
      if FAxisTree[AAbsLevelIndex - 1].LevelType <> fcATLT_SubGroup then
        Result := Fields[FAxisTree[AAbsLevelIndex - 1].RegionFieldIndex].Width
      else
        Result := Fields[FAxisTree[AAbsLevelIndex - 1].RegionFieldIndex].WidthSubGroup;
    if Result = 0 then
      Result := FSlice.DefaultColWidth;
  end;
end;

function TfcxAxisContainer.GetVisibleLevelSize(AVisLevelIndex: TfcxSmallCount): Smallint;
begin
  Result := LevelSize[IndexOfVisibleLevel[AVisLevelIndex]];
end;

procedure TfcxAxisContainer.SetLevelSize(AAbsLevelIndex: TfcxSmallCount; const Value: Smallint);
var
  Field: TfcxAxisField;
begin
  if AAbsLevelIndex < 0 then
    Exit;
  if FSlice.FXAxisContainer = Self then
    if (FMeasuresLevel = -1) or (AAbsLevelIndex < FMeasuresLevel) then
    begin
      Field := Fields[FAxisTree[AAbsLevelIndex].RegionFieldIndex];
      if FAxisTree[AAbsLevelIndex].LevelType <> fcATLT_SubGroup then
        Field.Height := Value
      else
        Field.HeightSubGroup := Value
    end
    else
    if AAbsLevelIndex = FMeasuresLevel then
      FSlice.FMeasuresContainer.Height := Value
    else
    begin
      Field := Fields[FAxisTree[AAbsLevelIndex - 1].RegionFieldIndex];
      if FAxisTree[AAbsLevelIndex - 1].LevelType <> fcATLT_SubGroup then
        Field.Height := Value
      else
        Field.HeightSubGroup := Value
    end
  else
    if (FMeasuresLevel = -1) or (AAbsLevelIndex < FMeasuresLevel) then
    begin
      Field := Fields[FAxisTree[AAbsLevelIndex].RegionFieldIndex];
      if FAxisTree[AAbsLevelIndex].LevelType <> fcATLT_SubGroup then
      begin
        Field.Width := Value;
        if Slice.SyncronizeYFieldsWidth then
          Field.CaptionWidth := Value;
      end
      else
        Field.WidthSubGroup := Value;
    end
    else
    if AAbsLevelIndex = FMeasuresLevel then
    begin
      FSlice.FMeasuresContainer.Width := Value;
      if Slice.SyncronizeYFieldsWidth then
        FSlice.FMeasuresContainer.CaptionWidth := Value;
    end
    else
    begin
      Field := Fields[FAxisTree[AAbsLevelIndex - 1].RegionFieldIndex];
      if FAxisTree[AAbsLevelIndex - 1].LevelType <> fcATLT_SubGroup then
      begin
        Field.Width := Value;
        if Slice.SyncronizeYFieldsWidth then
          Field.CaptionWidth := Value;
      end
      else
        Field.WidthSubGroup := Value;
    end;
end;

procedure TfcxAxisContainer.SetVisibleLevelSize(AVisLevelIndex: TfcxSmallCount; const Value: Smallint);
begin
  LevelSize[IndexOfVisibleLevel[AVisLevelIndex]] := Value;
end;

function TfcxAxisContainer.GetCellSizeByNode(ALevel: TfcxSmallCount; AIndexInProp: integer;
  AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): Smallint;
begin
  if AMeasureIndex < 0 then
    AMeasureIndex := 0;
  if FSlice.FXAxisContainer = Self then
  begin
    if AAdditionalTotalIndex < 0 then
      Result := FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp]
    else
      Result := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp];
    if Result = 0 then
      if FMeasuresLevel = -1 then
        Result := FSlice.DefaultColWidth
      else
      begin
        Result := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Width;
        if Result = 0 then
          Result := FSlice.DefaultColWidth;
      end
  end
  else
  begin
    if AAdditionalTotalIndex < 0 then
      Result := FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp]
    else
      Result := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp];
    if Result = 0 then
      if FMeasuresLevel = -1 then
        Result := FSlice.DefaultRowHeight
      else
      begin
        Result := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Height;
        if Result = 0 then
          Result := FSlice.DefaultRowHeight;
      end
  end;
end;

procedure TfcxAxisContainer.SetCellSizeByNode(ALevel: TfcxSmallCount; AIndexInProp: integer;
  AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount; const Value: Smallint);
var
  AChanged: boolean;
  ADefaultSize: integer;
begin
  if AMeasureIndex < 0 then
    AMeasureIndex := 0;
  if FSlice.FXAxisContainer = Self then
  begin
    if FMeasuresLevel = -1 then
      ADefaultSize := FSlice.DefaultColWidth
    else
    begin
      ADefaultSize := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Width;
      if ADefaultSize = 0 then
        ADefaultSize := FSlice.DefaultColWidth;
    end;
    if Value = ADefaultSize then
    begin
      if AAdditionalTotalIndex < 0 then
      begin
        AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp] <> 0;
        FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp] := 0;
      end
      else
      begin
        AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] <> 0;
        FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] := 0;
      end;
    end
    else
    begin
      if AAdditionalTotalIndex < 0 then
      begin
        AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp] <> Value;
        FAxisSourceTree.FSourceTreeLevels[ALevel].FWidths[AMeasureIndex][AIndexInProp] := Value
      end
      else
      begin
        AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] <> Value;
        FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalWidths[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] := Value;
      end;
    end;
    if AChanged then
    begin
      FSlice.StartChange;
      FSlice.StopChange([chs_ColWidth]);
    end;
  end
  else
  begin
    if FMeasuresLevel = -1 then
      ADefaultSize := FSlice.DefaultRowHeight
    else
    begin
      ADefaultSize := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Height;
      if ADefaultSize = 0 then
        ADefaultSize := FSlice.DefaultRowHeight;
    end;
    if Value = ADefaultSize then
    begin
      if AAdditionalTotalIndex < 0 then
      begin
        AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp] <> 0;
        FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp] := 0;
      end
      else
      begin
        AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] <> 0;
        FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] := 0;
      end
    end
    else
    begin
      if AAdditionalTotalIndex < 0 then
      begin
        AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp] <> Value;
        FAxisSourceTree.FSourceTreeLevels[ALevel].FHeights[AMeasureIndex][AIndexInProp] := Value;
      end
      else
      begin
        AChanged := FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] <> Value;
        FAxisSourceTree.FSourceTreeLevels[ALevel].FAdditionalTotalHeights[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ALevel] + AAdditionalTotalIndex][AIndexInProp] := Value;
      end
    end;
    if AChanged then
    begin
      FSlice.StartChange;
      FSlice.StopChange([chs_RowHeight]);
    end
  end;
end;

function TfcxAxisContainer.GetVisibleNodeCount: Integer;
begin
  Result := FVisibleAxisNodes.Count;
end;

function TfcxAxisContainer.GetMeasuresLevel: TfcxSmallCount;
begin
  Result := FMeasuresLevel;
end;

function TfcxAxisContainer.GetLevelCount: TfcxSmallCount;
begin
  Result := FLevelCount;
end;

procedure TfcxAxisContainer.CreateAxisTree(ALevelsSaved: Boolean = False);

  procedure FillAxisTree;
  var
    ALevelIndex: TfcxSmallCount;
    AStep: TfcxTraverseStep;
    ANode: PfcxSourceTreeNode;
    ANodeIndexArray: Array of integer;
    ANodeArray: Array of PfcxSourceTreeNode;
    AIndexArray, AGoodCountArray: Array of integer;
    i: integer;
  begin
    if (FLevelCount = 0) or (FAxisSourceTree.FFullRecordCount = 0) then
    begin
      ALevelIndex := 0;
      with FAxisTree[ALevelIndex - 1].Nodes[0] do
      begin
        SSelfNode := @(PfcxSourceTreeNodeArray(FAxisSourceTree.FRootNodeNew.Nodes)[0]);
        IndParent := -1;
        IndFirst := -1;
        IndLast := -1;
      end;
//      FillChar(FCoordinate^, FAxisSourceTree.FGoodRecordCount * SizeOf(Integer), 0);
      exit;
    end;
    SetLength(ANodeArray, FLevelCount + 1);
    SetLength(ANodeIndexArray, FLevelCount + 1);
    SetLength(AIndexArray, FLevelCount + 1);
    SetLength(AGoodCountArray, FLevelCount + 1);
    ANodeArray[0] := @(PfcxSourceTreeNodeArray(FAxisSourceTree.FRootNodeNew.Nodes)[0]);
    ANodeIndexArray[0] := 0;
    for ALevelIndex := 0 to FLevelCount do
    begin
      AGoodCountArray[ALevelIndex] := 0;
      AIndexArray[ALevelIndex] := -1;
    end;
    ALevelIndex := 0;
    AStep := fcts_Down;
    while True do
    begin
      case AStep of
        fcts_Next, fcts_Down:
          begin
// proc begin
            begin
              ANode := ANodeArray[ALevelIndex];
              AGoodCountArray[ALevelIndex] := 0;
              if ALevelIndex = FLevelCount then
              begin
                if FAxisSourceTree.FFullRecordCount = FAxisSourceTree.FGoodRecordCount then
                begin
                  for i := 0 to ANode.Count - 1 do
                    FCoordinate[FAxisSourceTree.FSourceOrderIndexes[PfcxIntegerArray(ANode.Nodes)[i]]] := AIndexArray[FLevelCount] + 1;
                  AGoodCountArray[ALevelIndex] := AGoodCountArray[ALevelIndex] + ANode.Count;
                end
                else
                  for i := 0 to ANode.Count - 1 do
                  begin
                    if FSlice.FTopNs.GoodTopNRec[FSlice.FilterManager.GoodIndex[PfcxIntegerArray(ANode.Nodes)[i]]] > -1 then
                    begin
                      FCoordinate[FAxisSourceTree.FSourceOrderIndexes[FSlice.FTopNs.GoodTopNRec[FSlice.FilterManager.GoodIndex[PfcxIntegerArray(ANode.Nodes)[i]]]]] := AIndexArray[FLevelCount] + 1;
                      inc(AGoodCountArray[ALevelIndex]);
                    end
                  end;
                if AGoodCountArray[ALevelIndex] > 0 then
                  inc(AGoodCountArray[ALevelIndex - 1])
              end;

              if AIndexArray[ALevelIndex] = (FAxisTree[ALevelIndex - 1].Count - 1) then
              begin
// no more rows? ! Can be if filters!
// check
                Break;
              end;
              if (ALevelIndex <> FLevelCount) or (AGoodCountArray[ALevelIndex] > 0) then
              begin
                AIndexArray[ALevelIndex] := AIndexArray[ALevelIndex] + 1;
                FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].SSelfNode := ANodeArray[ALevelIndex];
                if ALevelIndex = 0 then
                begin
                  FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent := -1;
                end
                else
                begin
                  FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent := AIndexArray[ALevelIndex - 1];
                  if FAxisTree[ALevelIndex - 2].Nodes[FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent].IndFirst = -1 then
                    FAxisTree[ALevelIndex - 2].Nodes[FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent].IndFirst := AIndexArray[ALevelIndex];
                  FAxisTree[ALevelIndex - 2].Nodes[FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent].IndLast := AIndexArray[ALevelIndex];
                end;
                FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndFirst := -1;
                FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndLast := -1;
              end;
            end;
// proc end
            if ALevelIndex = FLevelCount then // last level
            begin
              if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
              begin
                AStep := fcts_Up;
                dec(ALevelIndex);
              end
              else
              begin
                AStep := fcts_Next;
                ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
                ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
              end
            end
            else
            begin
              AStep := fcts_Down;
              inc(ALevelIndex);
              ANodeIndexArray[ALevelIndex] := 0;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end;
          end;
        fcts_Up:
          begin
            if AGoodCountArray[ALevelIndex] = 0 then
            begin
              if ALevelIndex <> 0 then
              begin
                if FAxisTree[ALevelIndex - 2].Nodes[FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent].IndFirst = AIndexArray[ALevelIndex] then
                begin
                  FAxisTree[ALevelIndex - 2].Nodes[FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent].IndFirst := -1;
                  FAxisTree[ALevelIndex - 2].Nodes[FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent].IndLast := -1;
                end
                else
                  FAxisTree[ALevelIndex - 2].Nodes[FAxisTree[ALevelIndex - 1].Nodes[AIndexArray[ALevelIndex]].IndParent].IndLast := AIndexArray[ALevelIndex] - 1;
              end;
              AIndexArray[ALevelIndex] := AIndexArray[ALevelIndex] - 1;
            end
            else
              if ALevelIndex <> 0 then
                inc(AGoodCountArray[ALevelIndex - 1]);
            if ALevelIndex = 0 then  // can stop - on this level ALWAYS only one element
            begin
              Break;
            end
            else
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end
          end;
      end;
    end;
  end;

  procedure FillIndexes;
  var
    ACellIndex: integer;
    procedure FillIndexes(ALevelIndex: TfcxSmallCount; AIndex: Integer);
    var
      i: integer;
    begin
      if FAxisTree[ALevelIndex].Nodes[AIndex].IndFirst = -1 then
        Exit;
      for i := FAxisTree[ALevelIndex].Nodes[AIndex].IndFirst to FAxisTree[ALevelIndex].Nodes[AIndex].IndLast do
      begin
        FLevelOf[ACellIndex] := ALevelIndex + 1;
        FIndexInLevel[ACellIndex] := i;
{
        if ALevelIndex = (FLevelCount - 2) then
          FIndexOfParent[ACellIndex] := FAxisTree[ALevelIndex].IndexInLastLevel[AIndex]
        else
          FIndexOfParent[ACellIndex] := ACellIndex;
}
//        FIndexOfParent[ACellIndex] := AIndexInLevel[ALevelIndex + 1];
        FAxisTree[ALevelIndex + 1].IndexInLastLevel[i] := ACellIndex;
        inc(ACellIndex);
        if ALevelIndex < (FLevelCount - 2) then
        begin
//          AIndexInLevel[ALevelIndex + 2] := ACellIndex - 1;
          FillIndexes(ALevelIndex + 1, i);
        end;
      end;
    end;
  begin
    ACellIndex := 0;
    FLevelOf[ACellIndex] := -1;
    FIndexInLevel[ACellIndex] := 0;
    FIndexOfParent[ACellIndex] := -1;
    FAxisTree[-1].IndexInLastLevel[0] := ACellIndex;
    inc(ACellIndex);
    if (FLevelCount > 0) then
      FillIndexes(-1, 0);
    for ACellIndex := 1 to FCellsCount - 1 do
      FIndexOfParent[ACellIndex] := FAxisTree[FLevelOf[ACellIndex] - 1].IndexInLastLevel[FAxisTree[FLevelOf[ACellIndex]].Nodes[FIndexInLevel[ACellIndex]].IndParent]
  end;

var
  ALevelIndex: TfcxSmallCount;
  i: integer;
  AIndexInLevel: PfcxIntegerArray;
begin
  ClearAxisTree(ALevelsSaved);
  FLevelCount := FAxisSourceTree.FLevelCount;
  FMeasuresLevel := FAxisSourceTree.FMeasuresLevel;
  FMeasuresLevelVisibleIndex := FMeasuresLevel;
  FRealLevelCount := FLevelCount;
  if (FSlice.FMeasuresContainer.FContainer = Self) then
    inc(FRealLevelCount);
  FVisibleLevelCount := FRealLevelCount;
  FNoFields := (FLevelCount = 0);
  if not ALevelsSaved then
  begin
    GetMem(FAxisTree, (FLevelCount + 1) * SizeOf(_fcxAxisTree));
    FillChar(FAxisTree^, (FLevelCount + 1) * SizeOf(_fcxAxisTree), 0);
    for ALevelIndex := -1 to FLevelCount - 1 do
    begin
      if ALevelIndex <> -1 then
        FAxisTree[ALevelIndex].RegionFieldIndex := FAxisSourceTree.FFieldIndexOfLevel[ALevelIndex]
      else
        FAxisTree[ALevelIndex].RegionFieldIndex := ALevelIndex;
      FAxisTree[ALevelIndex].LevelPosType := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelPosType;
      FAxisTree[ALevelIndex].LevelType := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelType;
      FAxisTree[ALevelIndex].GSubLevelVisible := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FGSubLevelVisible;
      FAxisTree[ALevelIndex].Visible := True;
      if FAxisTree[ALevelIndex].RegionFieldIndex > -1 then
      begin
        if FAxisTree[ALevelIndex].LevelType <> fcATLT_SubGroup then
        begin
          FAxisTree[ALevelIndex].HasGroups := Fields[FAxisTree[ALevelIndex].RegionFieldIndex].HasGroups;
          FAxisTree[ALevelIndex].IsGroupMembers := (Fields[FAxisTree[ALevelIndex].RegionFieldIndex] is TfcxAxisGroupMemberField);
        end;
      end;
      SetCountInLevel(ALevelIndex, FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FGoodNodeCount);
      FAxisTree[ALevelIndex].CountWithTotals := FAxisTree[ALevelIndex].Count;
      if ALevelIndex = -1 then
      begin
        GetMem(FAxisTree[ALevelIndex].IndexInLastLevel, FAxisTree[ALevelIndex].Count * SizeOf(Integer));
        if FAxisTree[ALevelIndex].Count > 0 then
          FAxisTree[ALevelIndex].IndexInLastLevel[0] := 0;
      end;
    end;
  end
  else
  begin
    for ALevelIndex := -1 to FLevelCount - 1 do
    begin
      SetCountInLevel(ALevelIndex, FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FGoodNodeCount);
      FAxisTree[ALevelIndex].CountWithTotals := FAxisTree[ALevelIndex].Count;
      if ALevelIndex = -1 then
      begin
        GetMem(FAxisTree[ALevelIndex].IndexInLastLevel, FAxisTree[ALevelIndex].Count * SizeOf(Integer));
        if FAxisTree[ALevelIndex].Count > 0 then
          FAxisTree[ALevelIndex].IndexInLastLevel[0] := 0;
      end;
    end;
  end;
  FillChar(FCoordinate^, FAxisSourceTree.FGoodRecordCount * SizeOf(Integer), 0);
  FillAxisTree;

  FCellsCount := 1;
  for i := 0 to FLevelCount - 1 do
    FCellsCount := FCellsCount + FAxisTree[i].Count;


  GetMem(FLevelOf, FCellsCount * SizeOf(TfcxSmallCount));
  GetMem(FIndexInLevel, FCellsCount * SizeOf(Integer));
  GetMem(FIndexOfParent, FCellsCount * SizeOf(Integer));
// temp to calc
  GetMem(AIndexInLevel, FLevelCount * SizeOf(Integer));
  for i := 0 to FLevelCount - 1 do
  begin
    FAxisTree[i].CountWithTotals := FAxisTree[i].Count + FAxisTree[i-1].CountWithTotals;
    GetMem(FAxisTree[i].IndexInLastLevel, FAxisTree[i].Count * SizeOf(Integer));
    AIndexInLevel[i] := 0;
  end;

  if FCellsCount > 0 then
    FillIndexes;
  FreeMem(AIndexInLevel);
  FAxisSortTree.FillSortTree;
//need test 11-2015
  FAxisSortTree.SortTree;
{$IFDEF SHOWAXIS}
  if FSlice.XAxisContainer = Self then
    ShowAxis('X Axis', Self)
  else
    ShowAxis('Y Axis', Self)
{$ENDIF}
end;

function TfcxAxisContainer.CellPropertiesByNodeMeasures(ANodeIndex: integer;
  ANodeLevelIndex,
  AMeasureIndex: TfcxSmallCount): _fcxAxisTreeNodeProperties;
begin
  Result := FAxisSourceTree.FSourceTreeLevels[ANodeLevelIndex].FPropMeasures[AMeasureIndex][FAxisTree[ANodeLevelIndex].Nodes[ANodeIndex].SSelfNode.IndexInProp];
end;

function TfcxAxisContainer.CellPropertiesByNodeSelf(ANodeIndex: integer;
  ANodeLevelIndex,
  AMeasureIndex: TfcxSmallCount): _fcxAxisTreeNodeProperties;
begin
  Result := FAxisSourceTree.FSourceTreeLevels[ANodeLevelIndex].FPropSelf[AMeasureIndex][FAxisTree[ANodeLevelIndex].Nodes[ANodeIndex].SSelfNode.IndexInProp];
end;

function TfcxAxisContainer.CellPropertiesByNodeTotalSelf(
  ANodeIndex: integer; ANodeLevelIndex,
  AMeasureIndex: TfcxSmallCount): _fcxAxisTreeNodeProperties;
begin
  Result := FAxisSourceTree.FSourceTreeLevels[ANodeLevelIndex].FPropTotalSelf[AMeasureIndex][FAxisTree[ANodeLevelIndex].Nodes[ANodeIndex].SSelfNode.IndexInProp];
end;

function TfcxAxisContainer.CellPropertiesByNodeAdditionalTotalSelf(
  ANodeIndex: integer; ANodeLevelIndex, AMeasureIndex,
  AAdditionalTotalIndex: TfcxSmallCount): _fcxAxisTreeNodeProperties;
begin
  Result := FAxisSourceTree.FSourceTreeLevels[ANodeLevelIndex].FPropAdditionalTotalSelf[AMeasureIndex * CountAdditionalTotalFunctionsInLevel[ANodeLevelIndex] + AAdditionalTotalIndex][FAxisTree[ANodeLevelIndex].Nodes[ANodeIndex].SSelfNode.IndexInProp];
end;

function TfcxAxisContainer.GetVisibleLevelCount: TfcxSmallCount;
var
  ALevelIndex: TfcxSmallCount;
begin
  if (FSlice.FMeasuresContainer.FContainer = Self) then
    Result := 1
  else
    Result := 0;
  for ALevelIndex := 0 to FLevelCount - 1 do
    if FAxisTree[ALevelIndex].Visible then
      inc(Result)
    else
      break;
end;

procedure TfcxAxisContainer.SetExpandedLevel(ARealLevel: TfcxSmallCount;
  const Value: Boolean);
begin
  if (FSlice.FMeasuresContainer.FContainer = Self) then
    if ARealLevel = -1 then
      SetExpandedLevelInternal(ARealLevel, False, Value)
    else
    if FMeasuresLevel = ARealLevel then
      SetExpandedLevelInternal(ARealLevel - 1, True, Value)
    else
    if FMeasuresLevel < ARealLevel then
      SetExpandedLevelInternal(ARealLevel - 1, False, Value)
    else
      SetExpandedLevelInternal(ARealLevel, False, Value)
  else
    if ARealLevel = -1 then
      SetExpandedLevelInternal(ARealLevel, False, Value)
    else
      SetExpandedLevelInternal(ARealLevel, False, Value);
(*
  if (FSlice.FMeasuresContainer.FContainer = Self) then
  begin
    if ARealLevel = -1 then
    begin
// grand total
      if FLevelCount > 0 then
//        if not (FAxisTree[ARealLevel + 1].Visible and Value) then
        begin
          FSlice.StartChange;
          if Value then
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
              for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelfCount - 1 do
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelf[AMeasureIndex][AIndex].State :=
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelf[AMeasureIndex][AIndex].State
                  and not stDimCollapsed
          else
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
              for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelfCount - 1 do
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelf[AMeasureIndex][AIndex].State :=
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelf[AMeasureIndex][AIndex].State
                  or stDimCollapsed;
          if FSlice.XAxisContainer = Self then
            FSlice.StopChange([chs_ExpandXNode])
          else
            FSlice.StopChange([chs_ExpandYNode]);
        end
    end
    else
    if FMeasuresLevel = ARealLevel then
    begin
// measures level
      if ARealLevel < FLevelCount then
      begin
//        if not (FAxisTree[ARealLevel].Visible and Value) then
        begin
          FSlice.StartChange;
          if Value then
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FFullNodeCount - 1 do
              for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropMeasuresCount - 1 do
                FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropMeasures[AMeasureIndex][AIndex].State :=
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropMeasures[AMeasureIndex][AIndex].State
                  and not stDimCollapsed
          else
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FFullNodeCount - 1 do
              for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropMeasuresCount - 1 do
                FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropMeasures[AMeasureIndex][AIndex].State :=
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropMeasures[AMeasureIndex][AIndex].State
                  or stDimCollapsed;
          if FSlice.XAxisContainer = Self then
            FSlice.StopChange([chs_ExpandXNode])
          else
            FSlice.StopChange([chs_ExpandYNode]);
        end;
// ?? need set flag of collasing level
      end
    end
    else
    if FMeasuresLevel < ARealLevel then
    begin
// under measures
      if ARealLevel < FLevelCount then
//        if ((FAxisTree[ARealLevel-1].LevelType <> fcATLT_HasGroup) and not (FAxisTree[ARealLevel].Visible and Value)) or
//           ((FAxisTree[ARealLevel-1].LevelType = fcATLT_HasGroup) and (ARealLevel < (FLevelCount - 1)) and not (FAxisTree[ARealLevel + 1].Visible and Value)) then
//        if not (FAxisTree[ARealLevel].Visible and Value) then
        begin
          FSlice.StartChange;
          if Value then
          begin
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FFullNodeCount - 1 do
              for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelfCount - 1 do
                FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelf[AMeasureIndex][AIndex].State :=
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelf[AMeasureIndex][AIndex].State
                  and not stDimCollapsed;
            FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FCollapsedNodeCount := 0;
            CheckLevelCollapsedReal(ARealLevel - 1);
            if (FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FLevelType = fcATLT_SubGroup) or
               ((FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FLevelType = fcATLT_HasGroup) and
                FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FGSubLevelVisible
               )
               then
            begin
              if FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FLevelType = fcATLT_SubGroup then
                dec(ARealLevel)
              else
                inc(ARealLevel);
              for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FFullNodeCount - 1 do
                for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelfCount - 1 do
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelf[AMeasureIndex][AIndex].State :=
                    FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelf[AMeasureIndex][AIndex].State
                    and not stDimCollapsed;
              FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FCollapsedNodeCount := 0;
              CheckLevelCollapsedReal(ARealLevel - 1);
            end;
          end
          else
          begin
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FFullNodeCount - 1 do
              for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelfCount - 1 do
                FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelf[AMeasureIndex][AIndex].State :=
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelf[AMeasureIndex][AIndex].State
                  or stDimCollapsed;
            FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FCollapsedNodeCount := FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FGoodNodeCount * FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FCanCollapseNodeMultiplier;
            CheckLevelCollapsedReal(ARealLevel - 1);
            if (FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FLevelType = fcATLT_SubGroup) or
               ((FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FLevelType = fcATLT_HasGroup) and
                FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FGSubLevelVisible
               )
               then
            begin
              if FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FLevelType = fcATLT_SubGroup then
                dec(ARealLevel)
              else
                inc(ARealLevel);
              for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FFullNodeCount - 1 do
                for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelfCount - 1 do
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelf[AMeasureIndex][AIndex].State :=
                    FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FPropSelf[AMeasureIndex][AIndex].State
                    or stDimCollapsed;
              FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FCollapsedNodeCount := FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FGoodNodeCount * FAxisSourceTree.FSourceTreeLevels[ARealLevel - 1].FCanCollapseNodeMultiplier;
              CheckLevelCollapsedReal(ARealLevel - 1);
            end;
          end;
          if FSlice.XAxisContainer = Self then
            FSlice.StopChange([chs_ExpandXNode])
          else
            FSlice.StopChange([chs_ExpandYNode]);
        end
    end
    else
    begin
// above measures
      if ARealLevel < FLevelCount then
//        if not (FAxisTree[ARealLevel + 1].Visible and Value) then
        begin
          FSlice.StartChange;
          if Value then
          begin
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State :=
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State
                and not stDimCollapsed;
            FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCollapsedNodeCount := 0;
            CheckLevelCollapsedReal(ARealLevel);
            if (FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_SubGroup) or
               ((FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_HasGroup) and
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FGSubLevelVisible
               )
               then
            begin
              if FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_SubGroup then
                dec(ARealLevel)
              else
                inc(ARealLevel);
              for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State :=
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State
                  and not stDimCollapsed;
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCollapsedNodeCount := 0;
              CheckLevelCollapsedReal(ARealLevel);
            end;
          end
          else
          begin
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State :=
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State
                or stDimCollapsed;
            FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCollapsedNodeCount := FAxisSourceTree.FSourceTreeLevels[ARealLevel].FGoodNodeCount * FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCanCollapseNodeMultiplier;
            CheckLevelCollapsedReal(ARealLevel);
            if (FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_SubGroup) or
               ((FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_HasGroup) and
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FGSubLevelVisible
               )
               then
            begin
              if FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_SubGroup then
                dec(ARealLevel)
              else
                inc(ARealLevel);
              for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State :=
                  FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State
                  and not stDimCollapsed;
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCollapsedNodeCount := FAxisSourceTree.FSourceTreeLevels[ARealLevel].FGoodNodeCount * FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCanCollapseNodeMultiplier;
              CheckLevelCollapsedReal(ARealLevel);
            end;
          end;
          if FSlice.XAxisContainer = Self then
            FSlice.StopChange([chs_ExpandXNode])
          else
            FSlice.StopChange([chs_ExpandYNode]);
        end
    end
  end
  else
  begin
    if ARealLevel = -1 then
    begin
// grand total
      if FLevelCount > 0 then
        begin
          FSlice.StartChange;
          if Value then
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelf[0][AIndex].State :=
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelf[0][AIndex].State
                and not stDimCollapsed
          else
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelf[0][AIndex].State :=
                FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropTotalSelf[0][AIndex].State
                or stDimCollapsed;
          if FSlice.XAxisContainer = Self then
            FSlice.StopChange([chs_ExpandXNode])
          else
            FSlice.StopChange([chs_ExpandYNode]);
        end
    end
    else
    if ARealLevel < (FLevelCount - 1) then
//      if ((FAxisTree[ARealLevel].LevelType <> fcATLT_HasGroup) and not (FAxisTree[ARealLevel + 1].Visible and Value)) or
//         ((FAxisTree[ARealLevel].LevelType = fcATLT_HasGroup) and (ARealLevel < (FLevelCount - 2)) and not (FAxisTree[ARealLevel + 2].Visible and Value)) then
      begin
        FSlice.StartChange;
        if Value then
        begin
// expand
          for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
            FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State := FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State
              and not stDimCollapsed;
          FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCollapsedNodeCount := 0;
          CheckLevelCollapsedReal(ARealLevel);
          if (FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_SubGroup) or
             ((FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_HasGroup) and
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FGSubLevelVisible
             )
             then
          begin
            if FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_SubGroup then
              dec(ARealLevel)
            else
              inc(ARealLevel);
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State := FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State
                and not stDimCollapsed;
            FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCollapsedNodeCount := 0;
            CheckLevelCollapsedReal(ARealLevel);
          end;
        end
        else
        begin
// collaps
          for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
            FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State := FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State
              or stDimCollapsed;
          FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCollapsedNodeCount := FAxisSourceTree.FSourceTreeLevels[ARealLevel].FGoodNodeCount * FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCanCollapseNodeMultiplier;
          CheckLevelCollapsedReal(ARealLevel);
          if (FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_SubGroup) or
             ((FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_HasGroup) and
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FGSubLevelVisible
             )
             then
          begin
            if FAxisSourceTree.FSourceTreeLevels[ARealLevel].FLevelType = fcATLT_SubGroup then
              dec(ARealLevel)
            else
              inc(ARealLevel);
            for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ARealLevel].FFullNodeCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State := FAxisSourceTree.FSourceTreeLevels[ARealLevel].FPropSelf[0][AIndex].State
                or stDimCollapsed;
            FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCollapsedNodeCount := FAxisSourceTree.FSourceTreeLevels[ARealLevel].FGoodNodeCount * FAxisSourceTree.FSourceTreeLevels[ARealLevel].FCanCollapseNodeMultiplier;
            CheckLevelCollapsedReal(ARealLevel);
          end
        end;
        if FSlice.XAxisContainer = Self then
          FSlice.StopChange([chs_ExpandXNode])
        else
          FSlice.StopChange([chs_ExpandYNode]);
      end
  end;
*)
end;

function TfcxAxisContainer.GetLevelInfo(AAbsLevelIndex: TfcxSmallCount): TfcxAxisLevelInfo;
begin
  if (FSlice.FMeasuresContainer.FContainer = Self) then
    if FMeasuresLevel = AAbsLevelIndex then
    begin
      Result.IsVisible := True;
      Result.IsMeasure := True;
      Result.RegionField := nil;
      Result.LevelType := fcATLT_Simple;
      Result.Level := -2;
    end
    else
    if FMeasuresLevel < AAbsLevelIndex then
    begin
      Result.IsVisible := FAxisTree[AAbsLevelIndex - 1].Visible;
      Result.IsMeasure := False;
      Result.RegionField := Fields[FAxisTree[AAbsLevelIndex - 1].RegionFieldIndex];
      Result.LevelType := FAxisTree[AAbsLevelIndex - 1].LevelType;
      Result.Level := AAbsLevelIndex - 1;
    end
    else
    begin
      Result.IsVisible := FAxisTree[AAbsLevelIndex].Visible;
      Result.IsMeasure := False;
      Result.RegionField := Fields[FAxisTree[AAbsLevelIndex].RegionFieldIndex];
      Result.LevelType := FAxisTree[AAbsLevelIndex].LevelType;
      Result.Level := AAbsLevelIndex;
    end
  else
  begin
    Result.IsVisible := FAxisTree[AAbsLevelIndex].Visible;
    Result.IsMeasure := False;
    Result.RegionField := Fields[FAxisTree[AAbsLevelIndex].RegionFieldIndex];
    Result.LevelType := FAxisTree[AAbsLevelIndex].LevelType;
    Result.Level := AAbsLevelIndex;
  end;
  if Result.LevelType = fcATLT_SubGroup then
    Result.SubLevel := 1
  else
    Result.SubLevel := 0;
end;

function TfcxAxisContainer.GetIndexOfTotalInLevel(AIndex: integer;
  ATargetLevel: TfcxSmallCount): integer;
begin
  Result := AIndex;
  while FLevelOf[Result] > ATargetLevel do
    Result := FIndexOfParent[Result];
end;

procedure TfcxAxisContainer.SetExpandedGroup(ARealLevel: TfcxSmallCount;
  ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean);
begin
  FAxisSourceTree.SetGroupExpanded(ARealLevel, ANodeIndex, AMeasureIndex, Value);
end;

function TfcxAxisContainer.GetLevelOfRegionField(
  ARegionFieldIndex: integer): TfcxSmallCount;
var
  ALevelIndex: TfcxSmallCount;
begin
  for ALevelIndex := 0 to FLevelCount - 1 do
    if FAxisTree[ALevelIndex].RegionFieldIndex = ARegionFieldIndex then
    begin
      Result := ALevelIndex;
      if FMeasuresLevel <> -1 then
        if FMeasuresLevel <= Result then
          inc(Result);
      exit;
    end;
  Result := -1;
end;

procedure TfcxAxisContainer.SetDefaultTypeSort(const Value: TfcxTypeSortAxis);
begin
  case Value of
    md_tsa_BySelected:
      begin
        FSlice.StartChange;
        FDefaultTypeSort := Value;
        if FSlice.XAxisContainer = Self then
        begin
          FSlice.YAxisContainer.SetSelected(Value);
          FSlice.StopChange([chs_XTypeSort,chs_YAxis])
        end
        else
        begin
          FSlice.XAxisContainer.SetSelected(Value);
          FSlice.StopChange([chs_YTypeSort,chs_XAxis]);
        end;
      end;
    md_tsa_ByTotalValue:
      begin
        FSlice.StartChange;
        FDefaultTypeSort := Value;
        if FSlice.XAxisContainer = Self then
        begin
          FSlice.YAxisContainer.SetSelected(Value);
          FSlice.StopChange([chs_XTypeSort,chs_YAxis])
        end
        else
        begin
          FSlice.XAxisContainer.SetSelected(Value);
          FSlice.StopChange([chs_YTypeSort,chs_XAxis]);
        end;
      end;
    else
      if FDefaultTypeSort <> Value then
      begin
        FSlice.StartChange;
        FDefaultTypeSort := Value;
        if FSlice.XAxisContainer = Self then
          FSlice.StopChange([chs_XTypeSort,chs_YAxis])
        else
          FSlice.StopChange([chs_YTypeSort,chs_XAxis]);
      end;
  end;
end;

procedure TfcxAxisContainer.MakeHideZeros;
var
  AMeasureIndex, ABaseLevel, ASecondLevel: TfcxSmallCount;
  ASecondIndex, AIndexInSecondLevel, AIndexInBaseLevel: integer;
  AIsZero: Boolean;
  AValue: Variant;
  ASecondCount: integer;
begin
  ClearHideZeros;
  if (FSlice.MeasuresContainer.FContainer <> FSlice.FXAxisContainer) and
     (FSlice.MeasuresContainer.FContainer <> FSlice.FYAxisContainer) then
    Exit;
  if FSlice.MeasuresContainer.FBaseContainer = Self then
  begin
    if FSlice.MeasuresContainer.FContainer = Self then
    begin
// measures in this axis
      for ABaseLevel := -1 to FSlice.FMeasuresContainer.FSaveCountBaseLevels - 1 do
      begin
        for AIndexInBaseLevel := 0 to FSlice.MeasuresContainer.FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
        begin
          for AMeasureIndex := 0 to FSlice.MeasuresContainer.FSaveCountMeasures - 1 do
          begin
            AIsZero := True;
            if FSlice.MeasuresContainer.Measures[AMeasureIndex].Visible then
              for ASecondLevel := -1 to FSlice.MeasuresContainer.FSaveCountSecondLevels - 1 do
              begin
                if FSlice.MeasuresContainer.Measures[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                  ASecondCount := FSlice.MeasuresContainer.FSecondContainer.FAxisTree[ASecondLevel].Count
                else
                  ASecondCount := FSlice.MeasuresContainer.FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Count;
                for ASecondIndex := 0 to ASecondCount - 1 do
                begin
                  AValue := FSlice.GetMeasureValueBSDirect(ABaseLevel, ASecondLevel, AIndexInBaseLevel, ASecondIndex, AMeasureIndex, -1, -1);
                  if vartype(AValue) <> varEmpty then
                  begin
                    if (TVarData(AValue).VType = varOleStr) or (TVarData(AValue).VType = varString) {$IFDEF Delphi_12UP}or (TVarData(AValue).VType = varUString){$ENDIF} or not ((TVarData(AValue).VType <= 1) or (AValue = 0)) then
                    begin
                      AIsZero := False;
                      break;
                    end
                  end;
                end;
                if not AIsZero then
                  break;
              end;
            if FMeasuresLevel = (ABaseLevel + 1) then
            begin
              if AIsZero then
// need hide
                FAxisSourceTree.FSourceTreeLevels[ABaseLevel].FPropMeasures[AMeasureIndex][FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].SSelfNode.IndexInProp].State :=
                  FAxisSourceTree.FSourceTreeLevels[ABaseLevel].FPropMeasures[AMeasureIndex][FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].SSelfNode.IndexInProp].State or stZero;
            end
            else
            if FMeasuresLevel > ABaseLevel then
            begin
              if AIsZero then
// need hide
                FAxisSourceTree.FSourceTreeLevels[ABaseLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].SSelfNode.IndexInProp].State :=
                  FAxisSourceTree.FSourceTreeLevels[ABaseLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].SSelfNode.IndexInProp].State or stZero;
            end
            else
            begin
              if AIsZero then
// need hide
                FAxisSourceTree.FSourceTreeLevels[ABaseLevel].FPropSelf[AMeasureIndex][FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].SSelfNode.IndexInProp].State :=
                  FAxisSourceTree.FSourceTreeLevels[ABaseLevel].FPropSelf[AMeasureIndex][FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].SSelfNode.IndexInProp].State or stZero;
            end
          end;
        end;
      end;
    end
    else
    begin
// measures in other axis
      for ABaseLevel := -1 to FSlice.FMeasuresContainer.FSaveCountBaseLevels - 1 do
      begin
        for AIndexInBaseLevel := 0 to FSlice.MeasuresContainer.FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
        begin
// process one row
          AIsZero := True;
          for ASecondLevel := -1 to FSlice.MeasuresContainer.FSaveCountSecondLevels - 1 do
          begin
            for AMeasureIndex := 0 to FSlice.MeasuresContainer.FSaveCountMeasures - 1 do
            begin
              if FSlice.MeasuresContainer.Measures[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                ASecondCount := FSlice.MeasuresContainer.FSecondContainer.FAxisTree[ASecondLevel].Count
              else
                ASecondCount := FSlice.MeasuresContainer.FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Count;
              for ASecondIndex := 0 to ASecondCount - 1 do
                if FSlice.MeasuresContainer.Measures[AMeasureIndex].Visible then
                begin
                  AValue := FSlice.GetMeasureValueBSDirect(ABaseLevel, ASecondLevel, AIndexInBaseLevel, ASecondIndex, AMeasureIndex, -1, -1);
                  if vartype(AValue) <> varEmpty then
                  begin
                    if (TVarData(AValue).VType = varOleStr) or (TVarData(AValue).VType = varString) {$IFDEF Delphi_12UP}or (TVarData(AValue).VType = varUString){$ENDIF} or not ((TVarData(AValue).VType <= 1) or (AValue = 0)) then
                    begin
                      AIsZero := False;
                      break;
                    end
                  end;
                end;
              if not AIsZero then
                break;
            end;
            if not AIsZero then
              break;
          end;
          if AIsZero then
// need hide
            FAxisSourceTree.FSourceTreeLevels[ABaseLevel].FPropSelf[0][FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].SSelfNode.IndexInProp].State :=
              FAxisSourceTree.FSourceTreeLevels[ABaseLevel].FPropSelf[0][FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].SSelfNode.IndexInProp].State or stZero;
        end
      end;
    end;
  end
  else
  begin
    if FSlice.MeasuresContainer.FContainer = Self then
    begin
// measures in this axis
      for ASecondIndex := 0 to FSlice.MeasuresContainer.FSecondCount - 1 do
      begin
        for AMeasureIndex := 0 to FSlice.MeasuresContainer.FSaveCountMeasures - 1 do
        begin
// process one row
          AIsZero := True;
          ASecondLevel := FLevelOf[ASecondIndex];
          AIndexInSecondLevel := FIndexInLevel[ASecondIndex];
          if FSlice.MeasuresContainer.Measures[AMeasureIndex].Visible then
          begin
            for ABaseLevel := -1 to FSlice.FMeasuresContainer.FSaveCountBaseLevels - 1 do
            begin
              for AIndexInBaseLevel := 0 to FSlice.MeasuresContainer.FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
              begin
                AValue := FSlice.GetMeasureValueBS(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AMeasureIndex, -1, -1);
                if vartype(AValue) <> varEmpty then
                begin
                  if (TVarData(AValue).VType = varOleStr) or (TVarData(AValue).VType = varString) {$IFDEF Delphi_12UP}or (TVarData(AValue).VType = varUString){$ENDIF} or not ((TVarData(AValue).VType <= 1) or (AValue = 0)) then
                  begin
                    AIsZero := False;
                    break;
                  end
                end;
              end;
              if not AIsZero then
                break;
            end;
          end;
          if FMeasuresLevel = (ASecondLevel + 1) then
          begin
            if AIsZero then
// need hide
              FAxisSourceTree.FSourceTreeLevels[ASecondLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].SSelfNode.IndexInProp].State :=
                FAxisSourceTree.FSourceTreeLevels[ASecondLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].SSelfNode.IndexInProp].State or stZero;
          end
          else
          if FMeasuresLevel > ASecondLevel then
          begin
            if AIsZero then
// need hide
              FAxisSourceTree.FSourceTreeLevels[ASecondLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].SSelfNode.IndexInProp].State :=
                FAxisSourceTree.FSourceTreeLevels[ASecondLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].SSelfNode.IndexInProp].State or stZero;
          end
          else
          begin
            if AIsZero then
// need hide
              FAxisSourceTree.FSourceTreeLevels[ASecondLevel].FPropSelf[AMeasureIndex][FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].SSelfNode.IndexInProp].State :=
                FAxisSourceTree.FSourceTreeLevels[ASecondLevel].FPropSelf[AMeasureIndex][FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].SSelfNode.IndexInProp].State or stZero;
          end
        end;
      end;
    end
    else
    begin
// measures in other axis
      for ASecondIndex := 0 to FSlice.MeasuresContainer.FSecondCount - 1 do
      begin
// process one row
        AIsZero := True;
        ASecondLevel := FLevelOf[ASecondIndex];
        AIndexInSecondLevel := FIndexInLevel[ASecondIndex];
        for ABaseLevel := -1 to FSlice.FMeasuresContainer.FSaveCountBaseLevels - 1 do
        begin
          for AIndexInBaseLevel := 0 to FSlice.MeasuresContainer.FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
          begin
            for AMeasureIndex := 0 to FSlice.MeasuresContainer.FSaveCountMeasures - 1 do
              if FSlice.MeasuresContainer.Measures[AMeasureIndex].Visible then
              begin
                AValue := FSlice.GetMeasureValueBS(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AMeasureIndex, -1, -1);
                if vartype(AValue) <> varEmpty then
                begin
                  if (TVarData(AValue).VType = varOleStr) or (TVarData(AValue).VType = varString) {$IFDEF Delphi_12UP}or (TVarData(AValue).VType = varUString){$ENDIF} or not ((TVarData(AValue).VType <= 1) or (AValue = 0)) then
                  begin
                    AIsZero := False;
                    break;
                  end
                end;
              end;
            if not AIsZero then
              break;
          end;
          if not AIsZero then
            break;
        end;
        if AIsZero then
// need hide
          FAxisSourceTree.FSourceTreeLevels[ASecondLevel].FPropSelf[0][FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].SSelfNode.IndexInProp].State :=
            FAxisSourceTree.FSourceTreeLevels[ASecondLevel].FPropSelf[0][FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].SSelfNode.IndexInProp].State or stZero;
      end;
    end
  end
end;

procedure TfcxAxisContainer.ClearHideZeros;
begin
  FAxisSourceTree.ClearPropertiesByMask(Byte(not stZero));
end;

procedure TfcxAxisContainer.SetAxisType(const Value: TfcxAxisType);
begin
  if FAxisType <> Value then
  begin
{ TODO -cNeed : reaction.}
    FSlice.StartChange;
    FAxisType := Value;
    if Self = FSlice.XAxisContainer then
      FSlice.StopChange([chs_XAxisType])
    else
      FSlice.StopChange([chs_YAxisType]);
  end
end;

procedure TfcxAxisContainer.SetShowTotalAs(const Value: TfcxShowTotalAs);
begin
  if FShowTotalAs <> Value then
  begin
{ TODO -cNeed : reaction.}
    FSlice.StartChange;
    FShowTotalAs := Value;
    if Self = FSlice.XAxisContainer then
      FSlice.StopChange([chs_XAxis])
    else
      FSlice.StopChange([chs_YAxis]);
  end;
end;

procedure TfcxAxisContainer.SetTotalPosition(const Value: TfcxTotalPosition);
var
  AFieldIndex: integer;
begin
  Slice.BeginUpdate;
  GrandTotalPosition := Value;
  for AFieldIndex := 0 to FFields.Count - 1 do
    FFields[AFieldIndex].TotalPosition := Value;
  Slice.EndUpdate;
end;

procedure TfcxAxisContainer.SetHidden(ANodeLevel: TfcxSmallCount;
  ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; ATotalCell: Boolean;
  AAdditionalTotalIndex: integer);
//var
//  ARealLevel: TfcxSmallCount;
begin
//  ARealLevel := ANodeLevel;
  if FMeasuresLevel <> -1 then // axis with measures
  begin
{
    if FMeasuresLevel <= ANodeLevel then
      inc(ARealLevel)
    else
}
    if AMeasureIndex = -1 then
      SetHiddenInternal(ANodeLevel, ANodeLevel, ANodeIndex, 0, ATotalCell, False)
    else
      if ATotalCell and (FMeasuresLevel > ANodeLevel) then
        SetHiddenInternal(ANodeLevel, ANodeLevel, ANodeIndex, AMeasureIndex, ATotalCell, False)
      else
        SetHiddenInternal(ANodeLevel, ANodeLevel+1, ANodeIndex, AMeasureIndex, ATotalCell, False)
{
    if AMeasureIndex <> -1 then
      inc(ARealLevel);
    if (ANodeLevel <> ARealLevel) or (ANodeLevel = -1) then
      SetHiddenInternal(ANodeLevel, ARealLevel, ANodeIndex, AMeasureIndex, ATotalCell, False)
    else
      SetHiddenInternal(ANodeLevel, ARealLevel, ANodeIndex, 0, ATotalCell, False);
}
  end
  else
    SetHiddenInternal(ANodeLevel, ANodeLevel, ANodeIndex, 0, ATotalCell, False);
end;

procedure TfcxAxisContainer.SetHiddenInternal(ALevel,
  ARealLevel: TfcxSmallCount; ANodeIndex: integer;
  AMeasureIndex: TfcxSmallCount; ATotalCell: Boolean; ANonChange: boolean);
begin
  if ANonChange then
    inc(FSlice.FChangeSemaphore); 
  case FAxisTree[ALevel].LevelPosType of
    fcATLPT_WMeasures_NotBaseLevel_LastLevel:
      begin
        if ATotalCell then
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end
        else
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end;
      end;
    fcATLPT_WMeasures_BaseLevel_LastLevel:
      begin
        if ALevel <> ARealLevel then
        begin // measures level
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end
        else
        begin
          if ATotalCell then
          begin
            if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
            begin
              if not ANonChange then
                FSlice.StartChange;
              FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
               := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
              inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
              if not ANonChange then
                if FSlice.XAxisContainer = Self then
                  FSlice.StopChange([chs_NeedFillVisibleX])
                else
                  FSlice.StopChange([chs_NeedFillVisibleY]);
            end;
          end
          else
          begin
            if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
            begin
              if not ANonChange then
                FSlice.StartChange;
              FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
               := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
              inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
              if not ANonChange then
                if FSlice.XAxisContainer = Self then
                  FSlice.StopChange([chs_NeedFillVisibleX])
                else
                  FSlice.StopChange([chs_NeedFillVisibleY]);
            end;
          end;
        end;
      end;
    fcATLPT_WOMeasures_LastLevel:
      begin
        if ATotalCell then
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;

            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
  {
            if FAxisSourceTree.FSourceTreeLevels[ALevel].FLevelType = fcATLT_HasGroup then
            begin
              if FAxisSourceTree.FSourceTreeLevels[ALevel].FGSubLevelVisible then
                if FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.NodeType = fcATNT_SimpleValue then
  // simple(only one) (in base level) change in child in sublevel
                  FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State or stHidden
                else
                if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0 then
                  FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State or stHidden
            end
            else
            if FAxisSourceTree.FSourceTreeLevels[ALevel].FLevelType = fcATLT_SubGroup then
            begin
              if FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
  // emty(continues of only one) (in sublevel) change in parent
                FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State
                  := FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State or stHidden;
            end;
  }
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end
        end
        else
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;

            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
  {
            if FAxisSourceTree.FSourceTreeLevels[ALevel].FLevelType = fcATLT_HasGroup then
            begin
              if FAxisSourceTree.FSourceTreeLevels[ALevel].FGSubLevelVisible then
                if FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.NodeType = fcATNT_SimpleValue then
  // simple(only one) (in base level) change in child in sublevel
                  FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State or stHidden
                else
                if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0 then
                  FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State or stHidden
            end
            else
            if FAxisSourceTree.FSourceTreeLevels[ALevel].FLevelType = fcATLT_SubGroup then
            begin
              if FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
  // emty(continues of only one) (in sublevel) change in parent
                FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State
                  := FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State or stHidden;
            end;
  }
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end;
      end;
    fcATLPT_WMeasures_BaseLevel_NotLastLevel:
      begin
        if ALevel <> ARealLevel then
        begin // measures level
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropMeasures[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end
        else
        begin
          if ATotalCell then
          begin
            if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
            begin
              if not ANonChange then
                FSlice.StartChange;
              FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
               := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
              inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
              if not ANonChange then
                if FSlice.XAxisContainer = Self then
                  FSlice.StopChange([chs_NeedFillVisibleX])
                else
                  FSlice.StopChange([chs_NeedFillVisibleY]);
            end;
          end
          else
          begin
            if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
            begin
              if not ANonChange then
                FSlice.StartChange;
              FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
               := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
              inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
              if not ANonChange then
                if FSlice.XAxisContainer = Self then
                  FSlice.StopChange([chs_NeedFillVisibleX])
                else
                  FSlice.StopChange([chs_NeedFillVisibleY]);
            end;
          end
        end;
      end;
    fcATLPT_WMeasures_NotBaseLevel_High_NotLastLevel:
      begin
        if ATotalCell then
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end
        else
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end;
      end;
    fcATLPT_WMeasures_NotBaseLevel_Low_NotLastLevel:
      begin
        if ATotalCell then
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end
        else
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;
            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end;
      end;
    fcATLPT_WOMeasures_NotLastLevel:
      begin
        if ATotalCell then
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;

            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropTotalSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);

            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end
        else
        begin
          if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stHidden) = 0 then
          begin
            if not ANonChange then
              FSlice.StartChange;

            FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State
              := FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State or stHidden;
            inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            if FAxisSourceTree.FSourceTreeLevels[ALevel].FLevelType = fcATLT_HasGroup then
            begin
              if FAxisSourceTree.FSourceTreeLevels[ALevel].FGSubLevelVisible then
                if FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.NodeType in [fcATNT_SimpleValue, fcATNT_OtherValue] then
// simple(only one) (in base level) change in child in sublevel
                  FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State or stHidden
                else
                if (FAxisSourceTree.FSourceTreeLevels[ALevel].FPropSelf[0][FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.IndexInProp].State and stGExpanded) = 0 then
                  FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State
                    := FAxisSourceTree.FSourceTreeLevels[ALevel+1].FPropSelf[0][FAxisTree[ALevel+1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndFirst].SSelfNode.IndexInProp].State or stHidden;
                inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            end
            else
            if FAxisSourceTree.FSourceTreeLevels[ALevel].FLevelType = fcATLT_SubGroup then
            begin
              if FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
// emty(continues of only one) (in sublevel) change in parent
                FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State
                  := FAxisSourceTree.FSourceTreeLevels[ALevel-1].FPropSelf[0][FAxisTree[ALevel-1].Nodes[FAxisTree[ALevel].Nodes[ANodeIndex].IndParent].SSelfNode.IndexInProp].State or stHidden;
                inc(FAxisSourceTree.FSourceTreeLevels[ALevel].FHiddenCount);
            end;
            if not ANonChange then
              if FSlice.XAxisContainer = Self then
                FSlice.StopChange([chs_NeedFillVisibleX])
              else
                FSlice.StopChange([chs_NeedFillVisibleY]);
          end;
        end;
      end
  end;
  if ANonChange then
    dec(FSlice.FChangeSemaphore);
end;

procedure TfcxAxisContainer.ShowAllHidden;
begin
  ShowAllHiddenInternal(False);
end;

function TfcxAxisContainer.GetHiddenCount: Integer;
var
  ALevelIndex: TfcxSmallCount;
begin
  Result := 0;
  for ALevelIndex := -1 to FLevelCount - 1 do
    Result := Result + FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHiddenCount;
end;

procedure TfcxAxisContainer.ShowAllHiddenInternal(ANonChange: boolean);
var
  ALevelIndex: TfcxSmallCount;
begin
  if ANonChange then
    inc(FSlice.FChangeSemaphore) 
  else
    FSlice.StartChange;
  FAxisSourceTree.ClearPropertiesByMask(Byte(not stHidden));
  for ALevelIndex := -1 to FLevelCount - 1 do
    FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHiddenCount := 0;
  if not ANonChange then
    if FSlice.XAxisContainer = Self then
      FSlice.StopChange([chs_NeedFillVisibleX])
    else
      FSlice.StopChange([chs_NeedFillVisibleY])
  else
    dec(FSlice.FChangeSemaphore);
end;

procedure TfcxAxisContainer.SaveToXML(AItem: TfcxXMLItem);
var
  AItem1, AItem2: TfcxXMLItem;
  i: integer;
begin
  inherited;
  AItem.Prop['GrandTotalPosition'] := GetEnumName(TypeInfo(TfcxTotalPosition), Ord(GrandTotalPosition));
  AItem.BoolProp['UseGrandTotalPositionFromMeasure'] := UseGrandTotalPositionFromMeasure;
  AItem.Prop['DefaultTypeSort'] := GetEnumName(TypeInfo(TfcxTypeSortAxis), Ord(DefaultTypeSort));
  AItem.Prop['AxisType'] := GetEnumName(TypeInfo(TfcxAxisType), Ord(AxisType));
// AdditionalGrandTotals
  AItem1 := AItem.Add;
  AItem1.Name := 'AdditionalGrandTotals';
  AItem1.Prop['AdditionalGrandTotalScriptFunction'] := AdditionalGrandTotalScriptFunction;
  for i := 0 to FCountAdditionalGrandTotalFunctions - 1 do
  begin
    AItem2 := AItem1.Add;
    AItem2.Name := 'Total';
    AItem2.Prop['AgrFunc'] := GetEnumName(TypeInfo(TfcxAgrFunc), Ord(AdditionalGrandTotalFunction[i]));
  end;
end;

procedure TfcxAxisContainer.LoadFromXML(AItem: TfcxXMLItem);
var
  i, j: integer;
  AAdditionalGrandTotalFunctions: TfcxSetAgrFunc;
begin
  inherited;
  GrandTotalPosition := TfcxTotalPosition(GetEnumValue(TypeInfo(TfcxTotalPosition), AItem.Prop['GrandTotalPosition']));
  UseGrandTotalPositionFromMeasure := AItem.BoolProp['UseGrandTotalPositionFromMeasure'];
  DefaultTypeSort := TfcxTypeSortAxis(GetEnumValue(TypeInfo(TfcxTypeSortAxis), AItem.Prop['DefaultTypeSort']));
  AxisType := TfcxAxisType(GetEnumValue(TypeInfo(TfcxAxisType), AItem.Prop['AxisType']));
  AdditionalGrandTotalFunctions := [];
  for i := 0 to AItem.Count - 1 do
  begin
    if AItem[i].Name = 'AdditionalGrandTotals' then
    begin
      AdditionalGrandTotalScriptFunction := AItem[i].Prop['AdditionalGrandTotalScriptFunction'];
      AAdditionalGrandTotalFunctions := [];
      for j := 0 to AItem[i].Count - 1 do
        AAdditionalGrandTotalFunctions := AAdditionalGrandTotalFunctions + [TfcxAgrFunc(GetEnumValue(TypeInfo(TfcxAgrFunc), AItem[i][j].Prop['AgrFunc']))];
      AdditionalGrandTotalFunctions := AAdditionalGrandTotalFunctions;
      break;
    end
  end;
end;

procedure TfcxAxisContainer.SetAdditionalGrandTotalExpression(const Value: String);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangesInSlice =
  (
    [chs_None],
    [chs_CapXAxis, chs_Script, chs_CapMeasures],
    [chs_CapYAxis, chs_Script, chs_CapMeasures],
    [chs_None],
    [chs_None]
  );
begin
  if FAdditionalGrandTotalExpression.Text <> Value then
  begin
    FSlice.StartChange;
    FAdditionalGrandTotalExpression.Text := Value;
    FSlice.StopChange(cChanges[FRegion]);
  end;
end;

procedure TfcxAxisContainer.SetAdditionalGrandTotalFunctions(const Value: TfcxSetAgrFunc);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_None,
    chs_CapXAxis,
    chs_CapYAxis,
    chs_None,
    chs_None
  );
var
  AAgrFunc: TfcxAgrFunc;
begin
  if FAdditionalGrandTotalFunctions <> Value then
  begin
    FSlice.StartChange;
    FAdditionalGrandTotalFunctions := Value;
    FCountAdditionalGrandTotalFunctions := 0;
    for AAgrFunc := Low(TfcxAgrFunc) to High(TfcxAgrFunc) do
      if AAgrFunc in FAdditionalGrandTotalFunctions then
        inc(FCountAdditionalGrandTotalFunctions);
    FSlice.StopChange([cChanges[FRegion], chs_CapMeasures]);
  end;
end;

function TfcxAxisContainer.GetAdditionalGrandTotalExpression: String;
begin
  Result := FAdditionalGrandTotalExpression.Text;
end;

function TfcxAxisContainer.GetAdditionalGrandTotalFunction(AAdditionalTotalIndex: TfcxSmallCount): TfcxAgrFunc;
var
  AAgrFunc: TfcxAgrFunc;
begin
  for AAgrFunc := Low(TfcxAgrFunc) to High(TfcxAgrFunc) do
  begin
    if AAgrFunc in FAdditionalGrandTotalFunctions then
      dec(AAdditionalTotalIndex);
    if AAdditionalTotalIndex < 0 then
    begin
      Result := AAgrFunc;
      Exit;
    end;
  end;
  Result := af_None;
end;

function TfcxAxisContainer.VisIndexToTreeCellPos(const AVisIndex: Integer;
  var ALevel: TfcxSmallCount; var AIndexInLevel: Integer;
  var AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): Boolean;
begin
  Result := False;
  if (AVisIndex < 0) or (AVisIndex > (FVisibleAxisNodes.FCount - 1)) then
    Exit;
  ALevel := FLevelOf[FVisibleAxisNodes.FIndexes[AVisIndex]];
  AIndexInLevel := FIndexInLevel[FVisibleAxisNodes.FIndexes[AVisIndex]];
  AAdditionalTotalIndex := FVisibleAxisNodes.FAdditionalTotalsIndexes[AVisIndex];
  AMeasureIndex := 0;
  if FVisibleAxisNodes.FMeasureIndexes <> nil{(FSlice.FMeasuresContainer.FContainer = Self) and (FSlice.FMeasuresContainer.Count > 1)} then
    AMeasureIndex := FVisibleAxisNodes.FMeasureIndexes[AVisIndex];
  Result := True;
end;

function TfcxAxisContainer.GetCountAdditionalTotalFunctionsInLevel(
  ALevel: TfcxSmallCount): TfcxSmallCount;
begin
  if ALevel = -1 then
    Result := CountGrandAdditionalTotalFunctions
  else
    Result := Fields[FAxisSourceTree.FFieldIndexOfLevel[ALevel]].CountAdditionalTotalFunctions;
end;

function TfcxAxisContainer.GetAdditionalTotalFunctionInLevel(ALevel, AAdditionalTotalIndex: TfcxSmallCount): TfcxAgrFunc;
begin
  if ALevel = -1 then
    Result := AdditionalGrandTotalFunction[AAdditionalTotalIndex]
  else
    Result := Fields[FAxisSourceTree.FFieldIndexOfLevel[ALevel]].AdditionalTotalFunction[AAdditionalTotalIndex];
end;

function TfcxAxisContainer.CheckSelected(
  ATypeSort: TfcxTypeSortAxis): boolean;
begin
  if ATypeSort = md_tsa_BySelected then
  begin
    Result := (FSelectedLevelIndex < FLevelCount) and (FSelectedMeasureIndex < FSlice.FMeasuresContainer.FSaveCountMeasures) and
      (FSelectedAdditionalTotalIndex < CountAdditionalTotalFunctionsInLevel[FSelectedLevelIndex]) and  (FSelectedIndexInLevel < FAxisTree[FSelectedLevelIndex].Count);
  end
  else
  begin
    Result := (FSelectedMeasureIndex < FSlice.FMeasuresContainer.FSaveCountMeasures);
  end
end;

procedure TfcxAxisContainer.SetSelected(ATypeSort: TfcxTypeSortAxis);
begin
  if not Slice.FSortSelectionLoaded then
    if ATypeSort = md_tsa_BySelected then
    begin
      FSelectedMeasureIndex := FSlice.FSelectedMeasure;
      if Self = FSlice.YAxisContainer then
      begin
        FSelectedLevelIndex := FLevelOf[FSlice.FSelectedRow];
        FSelectedIndexInLevel := FIndexInLevel[FSlice.FSelectedRow];
      end
      else
      begin
        FSelectedLevelIndex := FLevelOf[FSlice.FSelectedCol];
        FSelectedIndexInLevel := FIndexInLevel[FSlice.FSelectedCol];
      end;
      FSelectedAdditionalTotalIndex := FSlice.FSelectedRowAdditionalTotal;
    end
    else
    begin
      FSelectedMeasureIndex := FSlice.FSelectedMeasure;
    end
end;

procedure TfcxAxisContainer.SelectedMeasureMoved(AFromIndex,
  AToIndex: TfcxSmallCount);
begin
  if (FSlice.FXAxisContainer = Self) and (FSlice.FYAxisContainer.FDefaultTypeSort in cfc_SortByValue) or
     (FSlice.FYAxisContainer = Self) and (FSlice.FXAxisContainer.FDefaultTypeSort in cfc_SortByValue) then
    if FSlice.FYAxisContainer.FDefaultTypeSort in cfc_SortByValue  then
      if AFromIndex < AToIndex then
      begin
        if (FSelectedMeasureIndex = AFromIndex) then
          FSelectedMeasureIndex := AToIndex
        else
        if (FSelectedMeasureIndex > AFromIndex) and
          (FSelectedMeasureIndex <= AToIndex) then
          dec(FSelectedMeasureIndex)
      end
      else
      begin
        if (FSelectedMeasureIndex = AFromIndex) then
          FSelectedMeasureIndex := AToIndex
        else
        if (FSelectedMeasureIndex < AFromIndex) and
          (FSelectedMeasureIndex >= AToIndex) then
          inc(FSelectedMeasureIndex)
      end
end;

procedure TfcxAxisContainer.SelectedMeasureDeleted(
  AMeasureIndex: TfcxSmallCount);
begin
  if FSlice.FXAxisContainer = Self then
  begin
    if FSlice.FYAxisContainer.FDefaultTypeSort in cfc_SortByValue  then
      if FSelectedMeasureIndex = AMeasureIndex then
        FSlice.FYAxisContainer.DefaultTypeSort := md_tsa_ByAxisValue
      else
      if FSelectedMeasureIndex > AMeasureIndex then
        Dec(FSelectedMeasureIndex);
  end
  else
  begin
    if FSlice.FXAxisContainer.FDefaultTypeSort in cfc_SortByValue  then
      if FSelectedMeasureIndex = AMeasureIndex then
        FSlice.FXAxisContainer.DefaultTypeSort := md_tsa_ByAxisValue
      else
      if FSelectedMeasureIndex > AMeasureIndex then
        Dec(FSelectedMeasureIndex);
  end
end;

function TfcxAxisContainer.TraverseAxis_Grid(AStartLevel,
  AStopLevel: TfcxSmallCount; AStartCell: Integer;
  ADrawProc: TfcxSliceAxisDrawCellProc): TfcxSliceDrawHeaderResult;
var
  i: integer;
  AXAxis: Boolean;
  AVisibleLevelCount: TfcxSmallCount;
  AArrayStartIndexInLevel: PfcxIntegerArrayMinus;
  AStartMeasureIndex: TfcxSmallCount;
  AStartAdditionalTotalIndex: TfcxSmallCount;
  AArrayCellTypeInLevel: PfcxTypeOfCellInAxisArray;
  AArrayCellsCount: PfcxDinamicPosArray;
  ATotalPositions: Array of Array of TfcxTotalPosition;
  ATerminate: Boolean;
  ATypeSort: TfcxTypeSortAxis;

  procedure DrawAxisCell(ASTNode: PfcxSTNode; ADLevel: TfcxSmallCount; ATotalPosition: TfcxTotalPosition);
  var
    ANodeIndex, ACountChild, AIndexWithOrd: Integer;
    AAscOrder: boolean;
//    ASize, ALastCellSize: integer;
    ADRec: TfcxSliceDrawHeader;

// draw total cell. size 1 cell, on all levels from current to last
// draw collapsed cell, and last level cell
    procedure DrawAxisCellInternalLevelLong(ANodeLevelInternal: TfcxSmallCount; ANodeIndexInternal: integer; AAdditionalTotalIndex: integer = -1);
    var
      i1: integer;
      ATermination: boolean;
    begin
      ADRec.TreeRect.Level := ADLevel;
      ADRec.TreeRect.Cell  := AArrayCellsCount[ADLevel].Pos;
      ADRec.TreeRect.SizeLevel := AVisibleLevelCount - ADLevel;
      ADRec.TreeRect.SizeCell  := 1;

      ADRec.Cell      := FAxisTree[ANodeLevelInternal].IndexInLastLevel[ANodeIndexInternal];
      ADRec.Level     := ADLevel;
      ADRec.MeasureIndex := -1;
      ADRec.TotalIndex := AAdditionalTotalIndex;
      ADRec.NodeLevel  := ANodeLevelInternal;
      ADRec.NodeIndex  := ANodeIndexInternal;
      if ANodeLevelInternal >= 0 then
        ADRec.Alignment  := Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].Alignment
      else
        ADRec.Alignment  := Alignment;

      if pca_LastLevel in ADRec.CellProperties then
      begin
        case ATypeSort of
          md_tsa_BySelected:
            begin
              if (ADRec.NodeLevel = FSelectedLevelIndex) and (ADRec.NodeIndex = FSelectedIndexInLevel) and (ADRec.TotalIndex = FSelectedAdditionalTotalIndex) then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end;
          md_tsa_ByTotalValue:
            begin
              if pca_GrandTotal in ADRec.CellProperties then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end
        end;
      end;

      if (ADRec.CellProperties * [pca_GrandTotal, pca_StartTotal]) = [] then
        ADRec.ValueIndex := FAxisTree[ANodeLevelInternal].Nodes[ANodeIndexInternal].SSelfNode.Index
      else
        ADRec.ValueIndex := -2;

      ADRec.Text := GetDisplayValue(ADLevel, ADRec.Cell);
      {$ifdef fc_debug}
//      ADRec.Text := ADRec.Text + fcGetNameCellProperties(ADRec.CellProperties);
      {$endif}

      // Additional Total
      if AAdditionalTotalIndex >= 0 then
      begin
        if FLevelOf[ADRec.Cell] = -1 then
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[AdditionalGrandTotalFunction[AAdditionalTotalIndex]])
        else
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].AdditionalTotalFunction[AAdditionalTotalIndex]]);
      end;
      AArrayCellsCount[ADLevel].Pos := ADRec.TreeRect.Cell + ADRec.TreeRect.SizeCell;
      ATermination := ADrawProc(Self, ADRec);
      ATerminate := ATerminate or ATermination;
      for i1 := ADLevel + 1 to FRealLevelCount - 1 do
// set start offset value of cell for all levels under
        AArrayCellsCount[i1] := AArrayCellsCount[ADLevel];
    end;

// draw simple cell. size is count of children, on need levels
    procedure DrawAxisCellInternalCellLong(ANodeIndexInternal: integer; ASizeByLevels: integer = 1);
    var
      ATermination: boolean;
    begin
      ADRec.TreeRect.Level := ADLevel;
      ADRec.TreeRect.Cell  := AArrayCellsCount[ADLevel].Pos;
      ADRec.TreeRect.SizeLevel := ASizeByLevels;
      ADRec.TreeRect.SizeCell  := AArrayCellsCount[ADLevel + ASizeByLevels].Pos - AArrayCellsCount[ADLevel].Pos;

      ADRec.Cell      := FAxisTree[ADLevel].IndexInLastLevel[ANodeIndexInternal];
      ADRec.Level     := ADLevel;
      ADRec.MeasureIndex := -1;
      ADRec.TotalIndex := -1;
      ADRec.NodeLevel  := ADLevel;
      ADRec.NodeIndex  := ANodeIndexInternal;
      if ADLevel >= 0 then
        ADRec.Alignment := Fields[FAxisTree[ADLevel].RegionFieldIndex].Alignment
      else
        ADRec.Alignment := Alignment;

      if (ADRec.CellProperties * [pca_GrandTotal, pca_StartTotal]) = [] then
        ADRec.ValueIndex := FAxisTree[ADLevel].Nodes[ANodeIndexInternal].SSelfNode.Index
      else
        ADRec.ValueIndex := -2;
      ADRec.Text := GetDisplayValue(ADLevel, ADRec.Cell);
      {$ifdef fc_debug}
//      ADRec.Text := ADRec.Text + fcGetNameCellProperties(ADRec.CellProperties);
      {$endif}

      AArrayCellsCount[ADLevel].Pos := ADRec.TreeRect.Cell + ADRec.TreeRect.SizeCell;
      ATermination := ADrawProc(Self, ADRec);
      ATerminate := ATerminate or ATermination;
    end;

  var
    i: integer;
    ANeedDrawChild: Boolean; // False - not process children, set start flag; True - process children
    ACellDrawType: integer; // 0 - not draw, 1 - on all levels under, 2 - cell on N levels
    ANLevels: TfcxSmallCount;
    AExtra: integer;
    AMeasureIndex, AParentMeasureIndex: TfcxSmallCount;
    ADRealLevel: TfcxSmallCount;
    AIsNonCollapsedGrandTotal: Boolean;

  begin 
    ADRealLevel := ADLevel;
// iterate all children, call DrawAxisCell for each visible
// draw with type rules
// iterate and process cell children
    AMeasureIndex := 0;
    AParentMeasureIndex := 0;
// total before
    if ATotalPosition = fctp_Before then // if total before
// !!! check
    if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
    begin
// visible cell.
// check on start
      if AArrayStartIndexInLevel[ADLevel] = -2 then
// if level in insignificant end, then set start flag
        for i := ADLevel to FLevelCount - 1 do
          AArrayStartIndexInLevel[i] := -1;
      if AArrayStartIndexInLevel[ADLevel] = -1 then
      begin
// draw total
// determine type
        if ADLevel = 0 then
// grand total
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][ASTNode.FIndex].State and stDimCollapsed) = stDimCollapsed then
// collapsed (grand total)
            ADRec.CellProperties := [pca_GrandTotal, pca_Collapsed, pca_LastLevel]
          else
// expanded (grand total)
            ADRec.CellProperties := [pca_GrandTotal, pca_Expanded, pca_LastLevel]
        else
// simple total
          ADRec.CellProperties := [pca_StartTotal, pca_LastLevel];
        if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) // subgroup level
           and
           ((CellPropertiesByNodeSelf(ASTNode.FIndex, ADLevel - 1, AParentMeasureIndex).State and stGExpanded) = 0) then// group collapsed
// if subgroup level and group is collapsed, not draw cell.
        else
        begin
// draw cell on all levels under
// additional totals
          for i := -1 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
            if AStartAdditionalTotalIndex <= i then
            begin
              DrawAxisCellInternalLevelLong(ASTNode.FLevel, ASTNode.FIndex, i);
              if ATerminate then
                Break;
            end;
          AStartAdditionalTotalIndex := -2;
        end;
// check on stop
        if ATerminate then
// need end!
          Exit;
      end;
    end;
    AIsNonCollapsedGrandTotal := (ADLevel <> 0);
    if ADLevel = 0 then
//      if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][ASTNode.FIndex].State and stVisible) = stVisible) then
        if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][ASTNode.FIndex].State and stDimCollapsed) = 0 then
          AIsNonCollapsedGrandTotal := True;
    if AIsNonCollapsedGrandTotal then
    begin
      AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
      ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
// process children
      for ANodeIndex := 0 to ACountChild - 1 do
      begin
// get index by sort
        if AAscOrder then
          AIndexWithOrd := ANodeIndex
        else
          AIndexWithOrd := ACountChild - ANodeIndex - 1;
        if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible then
// visible cell.
        begin
          if AArrayStartIndexInLevel[ADLevel] <> -1 then
          begin
// no start flag, find start cell
            if AArrayStartIndexInLevel[ADLevel] = ASTNode.Nodes[AIndexWithOrd].FIndex then
// start index equal, set start flag
              AArrayStartIndexInLevel[ADLevel] := -1
            else
// stat index is not equal, skip child
              continue;
          end;
// determine type cell and need process children
          ADRec.CellProperties := [];
// default
// process children
          ANeedDrawChild := True;
// on all levels under
          ACellDrawType := 1;
// on one level (for type 2)
          ANLevels := 1;
// no additional operations
          AExtra := 0;
// call children draw, if need
          if ADRealLevel < (FRealLevelCount - 1) then
          begin
// not last level
            if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
            begin
// group level (not last level)
              if (FAxisTree[ADLevel + 1].LevelType = fcATLT_SubGroup) then
              begin
// has subgroup level (group level (not last level))
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                begin
// group value (has subgroup level (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stGExpanded) = 0) then
                  begin
// group collapsed (group value (has subgroup level (group level (not last level))))
                    if ADRealLevel = (FRealLevelCount - 2) then
                    begin
// prevlast level (group collapsed (group value (has subgroup level (group level (not last level)))))
// collapsed group of last level-1, after it there is nothing
                      ANeedDrawChild := False;
// draw self on all levels under
                      ADRec.CellProperties := [pca_GCollapsed, pca_LastLevel];
                    end
                    else
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// expanded cell of collapsed group.
// process children
// and draw self (on 2 levels)
                      ADRec.CellProperties := [pca_GCollapsed, pca_Expanded];
                      ACellDrawType := 2;
                      ANLevels := 2;
                    end
                    else
                    begin
// collapsed cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// collapsed cell of collapsed group.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ADRec.CellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                    end
                  end
                  else
                  begin
// group expanded (group value (has subgroup level (group level (not last level))))
// expanded group.
// process children
// draw self on 1 level
                    ADRec.CellProperties := [pca_GExpanded];
                    ACellDrawType := 2;
                  end
                end
                else
                begin
// single - single (has subgroup level (group level (not last level)))
                  if ADRealLevel = (FRealLevelCount - 2) then
                  begin
// prevlast level (single - single (has subgroup level (group level (not last level))))
// cell of single and after it there is nothing
                    ANeedDrawChild := False;
// draw self on all levels under
                    ADRec.CellProperties := [pca_GSingle, pca_LastLevel];
                  end
                  else
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// expanded cell of single.
// process children
// draw self on 2 levels
                    ADRec.CellProperties := [pca_GSingle, pca_Expanded];
                    ACellDrawType := 2;
                    ANLevels := 2;
                  end
                  else
                  begin
// collapsed cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// collapsed cell of single.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ADRec.CellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
              else
              begin
// no sublevel of group (group level (not last level))
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                begin
// group value (no sublevel of group (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (group value (no sublevel of group (group level (not last level))))
// expanded cell of collapsed group.
// process children
// draw self on 1 level
                    ADRec.CellProperties := [pca_GCollapsed, pca_Expanded];
                    ACellDrawType := 2;
                  end
                  else
                  begin
// collapsed cell (group value (no sublevel of group (group level (not last level))))
// collapsed cell of collapsed group.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ADRec.CellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                  end
                end
                else
                begin
// single (no sublevel of group (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (single (no sublevel of group (group level (not last level))))
// expanded cell of single.
// process children
// draw self on 1 level
                    ADRec.CellProperties := [pca_GSingle, pca_Expanded];
                    ACellDrawType := 2;
                  end
                  else
                  begin
// collapsed cell (single (no sublevel of group (group level (not last level))))
// collapsed cell of single.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ADRec.CellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
            end
            else
            if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) then
            begin
//subgroup level (not last level)
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
              begin
// continuation of collapsed group (subgroup level (not last level))
// continuation of single (subgroup level (not last level))
// process children
// not draw self, set coordinates after children draw
                ACellDrawType := 0;
                AExtra := 1;
              end
              else
              begin
// cell of detail of expanded group (subgroup level (not last level))
                if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                begin
// expanded cell (cell of detail of expanded group (subgroup level (not last level)))
// expanded cell of detail of expanded group.
// process children
// draw self on 1 level
                  ADRec.CellProperties := [pca_Simple, pca_Expanded];
                  ACellDrawType := 2;
                end
                else
                begin
// collapsed cell (cell of detail of expanded group (subgroup level (not last level)))
// collapsed cell of detail of expanded group.
                  ANeedDrawChild := False;
// draw self on all levels under
                  ADRec.CellProperties := [pca_Simple, pca_Collapsed, pca_LastLevel];
                end;
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
              end
            end
            else
// simple level (not last level)
            if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
            begin
// expanded cell (simple level (not last level))
// expanded cell.
// process children
// draw self on 1 level
              ADRec.CellProperties := [pca_Expanded];
              ACellDrawType := 2;
            end
            else
            begin
// collapsed cell (simple level (not last level))
// collapsed cell.
              ANeedDrawChild := False;
// draw self on all levels under
              ADRec.CellProperties := [pca_Collapsed, pca_LastLevel];
            end
          end
          else
          begin
// last level.
// draw self on all levels under
            ANeedDrawChild := False;
// determine type
            if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
// group level (last level)
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
// group value (group level (last level))
                ADRec.CellProperties := [pca_GCollapsed, pca_LastLevel]
              else
              begin
// single (group level (last level))
                ADRec.CellProperties := [pca_GSingle, pca_LastLevel];
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
              end
            else
            begin
// simple cell (group level (last level))
              ADRec.CellProperties := [pca_Simple, pca_LastLevel];
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
            end
          end;

          if ANeedDrawChild then
          begin
// process children
            DrawAxisCell(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].FTotalPosition);
          end
          else
// set started flag on next level (because not process children)
            for i := ADLevel + 1 to FLevelCount - 1 do
              AArrayStartIndexInLevel[i] := -1;

          if AExtra = 1 then
          begin
            AArrayCellsCount[ADLevel].Pos     := AArrayCellsCount[ADLevel + 1].Pos;
          end;
// draw
          case ACellDrawType of
            1: DrawAxisCellInternalLevelLong(ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex);
            2: DrawAxisCellInternalCellLong(ASTNode.Nodes[AIndexWithOrd].FIndex, ANLevels);
          end;
          if ATerminate then
// if stop flag, then end process
            Exit;
        end;
      end;
    end;
// total after
    if ATotalPosition = fctp_After then // if total after
// !!! check
    if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
    begin
// visible cell.
// check on start
      if AArrayStartIndexInLevel[ADLevel] = -2 then
// if level in insignificant end, then set start flag
        for i := ADLevel to FLevelCount - 1 do
          AArrayStartIndexInLevel[i] := -1;
      if AArrayStartIndexInLevel[ADLevel] = -1 then
      begin
// draw total
// determine type
        if ADLevel = 0 then
// grand total
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = stDimCollapsed then
// collapsed (grand total)
            ADRec.CellProperties := [pca_GrandTotal, pca_Collapsed, pca_LastLevel]
          else
// expanded (grand total)
            ADRec.CellProperties := [pca_GrandTotal, pca_Expanded, pca_LastLevel]
        else
// simple total
          ADRec.CellProperties := [pca_StartTotal, pca_LastLevel];
        if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) // subgroup level
           and
           ((CellPropertiesByNodeSelf(ASTNode.FIndex, ADLevel - 1, AParentMeasureIndex).State and stGExpanded) = 0) then// group collapsed
// if subgroup level and group is collapsed, not draw cell.
        else
        begin
// draw cell on all levels under
// additional totals
          for i := -1 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
            if AStartAdditionalTotalIndex <= i then
            begin
              DrawAxisCellInternalLevelLong(ASTNode.FLevel, ASTNode.FIndex, i);
              if ATerminate then
                break;
            end;
          AStartAdditionalTotalIndex := -2;
        end;
        if ATerminate then
// need end!
          Exit;
      end;
    end;
  end;

  procedure DrawAxisCellWM(ASTNode: PfcxSTNode; ADLevel, ADRealLevel, ADMeasureIndex: TfcxSmallCount; AMakeMeasuresTotal: Boolean = False);
  var
    ANodeIndex, ACountChild, AIndexWithOrd: Integer;
    AAscOrder: boolean;
//    ASize, ALastCellSize: integer;
    ADRec: TfcxSliceDrawHeader;

    procedure DrawAxisCellWMInternalLevelLong(ANodeLevelInternal: TfcxSmallCount; ANodeIndexInternal: integer; AMeasureIndex: TfcxSmallCount = -1; AAdditionalTotalIndex: TfcxSmallCount = -1);
    var
      i1: integer;
      ATermination: boolean;
    begin
      ADRec.TreeRect.Level := ADRealLevel;
      ADRec.TreeRect.Cell  := AArrayCellsCount[ADRealLevel].Pos;
      ADRec.TreeRect.SizeLevel := AVisibleLevelCount - ADRealLevel;
      ADRec.TreeRect.SizeCell  := 1;

      ADRec.Cell      := FAxisTree[ANodeLevelInternal].IndexInLastLevel[ANodeIndexInternal];
      ADRec.Level     := ADLevel;
      ADRec.MeasureIndex := AMeasureIndex; // ??
      ADRec.TotalIndex := AAdditionalTotalIndex;
      ADRec.NodeLevel  := ANodeLevelInternal;
      ADRec.NodeIndex  := ANodeIndexInternal;
      ADRec.Alignment  := Alignment;

      if pca_LastLevel in ADRec.CellProperties then
      begin
        case ATypeSort of
          md_tsa_BySelected:
            begin
              if (ADRec.MeasureIndex = FSelectedMeasureIndex) and (ADRec.NodeLevel = FSelectedLevelIndex) and (ADRec.NodeIndex = FSelectedIndexInLevel) and (ADRec.TotalIndex = FSelectedAdditionalTotalIndex) then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end;
          md_tsa_ByTotalValue:
            begin
              if FMeasuresLevel = 0 then
              begin
                if (ADRec.MeasureIndex = FSelectedMeasureIndex) and (ADRec.NodeLevel = -1) then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
              end
              else
                if (pca_GrandTotal in ADRec.CellProperties) and (ADRec.MeasureIndex = FSelectedMeasureIndex) then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end
        end;
      end;

      if (AMeasureIndex <> -1) and (ADRealLevel <= FMeasuresLevel) then
      begin
        ADRec.Text := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Caption;
        ADRec.ValueIndex := -2;
        ADRec.Alignment := FSlice.FMeasuresContainer.Alignment;
      end
      else
      begin
        if ANodeLevelInternal >= 0 then
          ADRec.Alignment := Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].Alignment
        else
          ADRec.Alignment := Alignment;
        if (ADRec.CellProperties * [pca_GrandTotal, pca_StartTotal]) = [] then
          ADRec.ValueIndex := FAxisTree[ANodeLevelInternal].Nodes[ANodeIndexInternal].SSelfNode.Index
        else
          ADRec.ValueIndex := -2;
        ADRec.Text := GetDisplayValue(ADLevel, ADRec.Cell);
      end;

      {$ifdef fc_debug}
//      ADRec.Text := ADRec.Text + fcGetNameCellProperties(ADRec.CellProperties);
      {$endif}
      // Additional Total
      if AAdditionalTotalIndex >= 0 then
      begin
        if FLevelOf[ADRec.Cell] = -1 then
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[AdditionalGrandTotalFunction[AAdditionalTotalIndex]])
        else
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].AdditionalTotalFunction[AAdditionalTotalIndex]]);
      end;

      AArrayCellsCount[ADRealLevel].Pos := ADRec.TreeRect.Cell + ADRec.TreeRect.SizeCell;
      ATermination := ADrawProc(Self, ADRec);
      ATerminate := ATerminate or ATermination;
      for i1 := ADRealLevel + 1 to FRealLevelCount - 1 do
// set start offset value of cell for all levels under
        AArrayCellsCount[i1] := AArrayCellsCount[ADRealLevel];
    end;

// draw simple cell. size is count of children, on need levels
    procedure DrawAxisCellWMInternalCellLong(ANodeIndexInternal: integer; ASizeByLevels: integer = 1; AMeasureIndex: TfcxSmallCount = -1);
    var
      ATermination: boolean;
    begin
      ADRec.TreeRect.Level := ADRealLevel;
      ADRec.TreeRect.Cell  := AArrayCellsCount[ADRealLevel].Pos;
      ADRec.TreeRect.SizeLevel := ASizeByLevels;
      ADRec.TreeRect.SizeCell  := AArrayCellsCount[ADRealLevel + ASizeByLevels].Pos - AArrayCellsCount[ADRealLevel].Pos;

      ADRec.Cell      := FAxisTree[ADLevel].IndexInLastLevel[ANodeIndexInternal];
      ADRec.Level     := ADLevel;
      ADRec.NodeLevel  := ADLevel;
      ADRec.NodeIndex  := ANodeIndexInternal;
      ADRec.Alignment  := Alignment;
      ADRec.TotalIndex := -1;

      if ADRealLevel = FMeasuresLevel then
      begin
        ADRec.MeasureIndex := AMeasureIndex; // ??
        ADRec.Text := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Caption;
        ADRec.ValueIndex := -2;
        ADRec.Alignment := FSlice.FMeasuresContainer.Alignment;
      end
      else
      begin
        if ADLevel >= 0 then
          ADRec.Alignment  := Fields[FAxisTree[ADLevel].RegionFieldIndex].Alignment
        else
          ADRec.Alignment  := Alignment;
        if (ADRec.CellProperties * [pca_GrandTotal, pca_StartTotal]) = [] then
          ADRec.ValueIndex := FAxisTree[ADLevel].Nodes[ANodeIndexInternal].SSelfNode.Index
        else
          ADRec.ValueIndex := -2;
        ADRec.MeasureIndex := ADMeasureIndex; // ??
        ADRec.Text := GetDisplayValue(ADLevel, ADRec.Cell);
      end;
      {$ifdef fc_debug}
//      ADRec.Text := ADRec.Text + fcGetNameCellProperties(ADRec.CellProperties);
      {$endif}

      AArrayCellsCount[ADRealLevel].Pos := ADRec.TreeRect.Cell + ADRec.TreeRect.SizeCell;
      ATermination := ADrawProc(Self, ADRec);
      ATerminate := ATerminate or ATermination;
    end;

  var
    ASaveStartMeasureIndex, AMeasureIndex, AParentMeasureIndex: TfcxSmallCount;
    i: integer;
    ANeedDrawChild: Boolean;
    ANextLevelIsMeasures: Boolean;
    ANeedDrawMeasures: Boolean;
    ACellDrawType: integer; // 0 - not draw, 1 - on all levels under, 2 - cell on N levels
    ANLevels: TfcxSmallCount;
    AExtra: integer;
    ADrawMeasureIndex: TfcxSmallCount; // index of measures for draw
    AChildMeasureIndex: TfcxSmallCount; // index of measures for process children
    AIsNonCollapsedGrandTotal: Boolean;

  begin
// iterate all children, call DrawAxisCell for each visible
// draw with type rules
// total before
    if AMakeMeasuresTotal then
    begin
// request on draw measures for collapsed cell
// totals not draw. draw measures names only.
      for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
        if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
        begin
          if ((FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][FAxisTree[-1].Nodes[0].SSelfNode.IndexInProp].State and stDimCollapsed) = 0) then
          begin
// measure is not collapsed.
// check on start
            if AStartMeasureIndex <> -1 then
              if AStartMeasureIndex = AMeasureIndex then
                AStartMeasureIndex := -1
              else
                continue;
            for i := ADLevel to FLevelCount - 1 do
              AArrayStartIndexInLevel[i] := -1;
// measure cell. draw self
// determine type
            ADRec.CellProperties := [pca_Simple, pca_LastLevel, pca_MeasureCell];
            DrawAxisCellWMInternalLevelLong(ADLevel, ASTNode.FIndex, AMeasureIndex);
            if ATerminate then
              Exit;
          end;
        end;
    end
    else
    if FMeasuresLevel = ADRealLevel then
    begin
// measures level. total not draw. draw measures name only.
// iterate measures
      if AStartMeasureIndex = -1 then
// begin from first
        ASaveStartMeasureIndex := 0
      else
      begin
// begin from started
        ASaveStartMeasureIndex := AStartMeasureIndex;
        AStartMeasureIndex := -1;
      end;
      for AMeasureIndex := ASaveStartMeasureIndex to FSlice.FMeasuresContainer.Count - 1 do
      begin
        if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
        begin
// visible measure
          if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
          begin
// visible cell
// check on start
            if AArrayStartIndexInLevel[ADLevel] <> -1 then
// no start flag on level based for measures. skip.
              continue;
            ADRec.CellProperties := [pca_MeasureCell];
// default
// process children
            ANeedDrawChild := True;
// on all levels under
            ACellDrawType := 1;
// on one level (for type 2)
            ANLevels := 1;
// index of measure for draw
            ADrawMeasureIndex := AMeasureIndex;
// index of measures for process children
            AChildMeasureIndex := AMeasureIndex;
// call children draw, if need
            if ADRealLevel < (FRealLevelCount - 1) then
            begin
// not last level
              if ((CellPropertiesByNodeMeasures(ASTNode.FIndex, ASTNode.FLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
              begin
// expanded cell (not last level)
// expanded cell.
// process children (for this measure)
// draw cell on one level
                ADRec.CellProperties := [pca_Expanded, pca_MeasureCell];
                ACellDrawType := 2;
              end
              else
              begin
// collapsed cell (not last level)
                ANeedDrawChild := False;
// draw cell on all levels down
                ADRec.CellProperties := [pca_Collapsed, pca_LastLevel, pca_MeasureCell];
              end
            end
            else
            begin
// cell in last level
              ANeedDrawChild := False;
// draw cell on all levels down
              ADRec.CellProperties := [pca_Simple, pca_LastLevel, pca_MeasureCell];
            end;
            if ANeedDrawChild then
            begin
// process children
              DrawAxisCellWM(ASTNode, ADLevel + 1, ADRealLevel + 1, AChildMeasureIndex)
            end
            else
// set started flag on next level (because not process children)
              for i := ADLevel + 1 to FLevelCount - 1 do
                AArrayStartIndexInLevel[i] := -1;
// draw
            case ACellDrawType of
              1: DrawAxisCellWMInternalLevelLong(ADLevel, ASTNode.FIndex, ADrawMeasureIndex);
              2: DrawAxisCellWMInternalCellLong(ASTNode.FIndex, ANLevels, ADrawMeasureIndex);
            end;
            if ATerminate then
              Exit;
          end;
        end;
      end;
    end
    else
    if FMeasuresLevel > ADRealLevel then
    begin
// above measures
// total before
// iterate all measures, because totals need for each
      AParentMeasureIndex := 0;
      AIsNonCollapsedGrandTotal := (ADLevel <> 0);
      if ADLevel = 0 then
        for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
        begin
          if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
//??            if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0 then
              begin
                AIsNonCollapsedGrandTotal := True;
                Break;
              end;
        end;
      for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
      begin
        if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
        begin
// visible measure
          if ATotalPositions[AMeasureIndex, ADLevel] = fctp_Before then // if total before
            if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
            begin
// visible cell.
// check on start
              if (AArrayStartIndexInLevel[ADLevel] = -2) and
                 ((AStartMeasureIndex = -1) or (AStartMeasureIndex = AMeasureIndex)) then
              begin
                AStartMeasureIndex := -1;
                for i := ADLevel to FLevelCount - 1 do
                  AArrayStartIndexInLevel[i] := -1;
              end;
              if AArrayStartIndexInLevel[ADLevel] = -1 then
              begin
// draw total
// determine type
                if ADLevel = 0 then
// grand total
                  if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = stDimCollapsed then
// collapsed (grand total)
                    ADRec.CellProperties := [pca_GrandTotal, pca_Collapsed, pca_LastLevel]
                  else
// expanded (grand total)
                    ADRec.CellProperties := [pca_GrandTotal, pca_Expanded, pca_LastLevel]
                else
// simple total
                  ADRec.CellProperties := [pca_StartTotal, pca_LastLevel];
                if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) // subgroup level
                   and
                   ((CellPropertiesByNodeSelf(ASTNode.FIndex, ADLevel - 1, AParentMeasureIndex).State and stGExpanded) = 0) then// group collapsed
// if subgroup level and group is collapsed, not draw cell.
                else
                begin
// draw cell on all levels under
// additional totals
                  for i := -1 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                    if AStartAdditionalTotalIndex <= i then
                    begin
                      DrawAxisCellWMInternalLevelLong(ASTNode.FLevel, ASTNode.FIndex, AMeasureIndex, i);
                      if ATerminate then
                        Break;
                    end;
                  AStartAdditionalTotalIndex := -2;
                end;
                if ATerminate then
// need end!
                  Exit;
              end;
            end;
        end;
      end;
      if AIsNonCollapsedGrandTotal then
      begin
        AMeasureIndex := 0;
        AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
        ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
// process children
        for ANodeIndex := 0 to ACountChild - 1 do
        begin
          if AAscOrder then
            AIndexWithOrd := ANodeIndex
          else
            AIndexWithOrd := ACountChild - ANodeIndex - 1;
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible then
// visible cell.
          begin
            if AArrayStartIndexInLevel[ADLevel] <> -1 then
            begin
// no start flag, find start cell
              if AArrayStartIndexInLevel[ADLevel] = ASTNode.Nodes[AIndexWithOrd].FIndex then
// start index equal, set start flag
                AArrayStartIndexInLevel[ADLevel] := -1
              else
// stat index is not equal, skip child
                continue;
            end;
// determine type cell and need process children
            ADRec.CellProperties := [];
// default
// process children
            ANeedDrawChild := True;
// next level is not measures
            ANextLevelIsMeasures := False;
// not run draw on next level
            ANeedDrawMeasures := False;
// on needed level count
            ACellDrawType := 2;
// on one level (for type 2)
            ANLevels := 1;
// index of measure for draw
            ADrawMeasureIndex := -1;
// no additional operations
            AExtra := 0;
// call children draw, if need
// not last level - because measures down
            if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
            begin
// group level (not last level)
              if (FAxisTree[ADLevel + 1].LevelType = fcATLT_SubGroup) then
              begin
// has subgroup level (group level (not last level))
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                begin
// group value (has subgroup level (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stGExpanded) = 0) then
                  begin
// group collapsed (group value (has subgroup level (group level (not last level))))
                    if ADLevel = (FRealLevelCount - 3) then
                    begin
// prevprevlast level (group collapsed (group value (has subgroup level (group level (not last level)))))
// collapsed group of last level-2, after it there is nothing But need run draw measures
                      ANeedDrawChild := False;
                      ANeedDrawMeasures := True;
// draw self on 1 level
                      ADRec.CellProperties := [pca_GCollapsed];
                    end
                    else
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (not prevprevlast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// expanded cell of collapsed group.
// process children
// and draw self (on 2 levels)
                      ANLevels := 2;
                      if (FMeasuresLevel = (ADRealLevel + 2)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level (after subgroup) is measures and it is last
                        ADRec.CellProperties := [pca_GCollapsed]
                      else
                        ADRec.CellProperties := [pca_GCollapsed, pca_Expanded];
                    end
                    else
                    begin
// collapsed cell (not prevprevlast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// collapsed cell of collapsed group. Need run draw measures.
                      ANeedDrawChild := False;
                      ANeedDrawMeasures := True;
// draw self on 1 level
                      if (FMeasuresLevel = (ADRealLevel + 2)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level (after subgroup) is measures and it is last
                        ADRec.CellProperties := [pca_GCollapsed]
                      else
                        ADRec.CellProperties := [pca_GCollapsed, pca_Collapsed];
                    end
                  end
                  else
                  begin
// group expanded (group value (has subgroup level (group level (not last level))))
// expanded group.
// process children
// draw self on 1 level
                    ADRec.CellProperties := [pca_GExpanded];
                  end
                end
                else
                begin
// single - single (has subgroup level (group level (not last level)))
                  if ADLevel = (FRealLevelCount - 3) then
                  begin
// prevprevlast level (single - single (has subgroup level (group level (not last level))))
// cell of single and after it there is nothing Need run draw measures.
                    ANeedDrawChild := False;
                    ANeedDrawMeasures := True;
// draw self on 1 level
                    ADRec.CellProperties := [pca_GSingle];
                  end
                  else
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (not prevprevlast level (single - single (has subgroup level (group level (not last level)))))
// expanded cell of single.
// process children
// draw self on 2 levels
                    ANLevels := 2;
                    if (FMeasuresLevel = (ADRealLevel + 2)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                      ADRec.CellProperties := [pca_GSingle]
                    else
                      ADRec.CellProperties := [pca_GSingle, pca_Expanded];
                  end
                  else
                  begin
// collapsed cell (not prevprevlast level (single - single (has subgroup level (group level (not last level)))))
// collapsed cell of single. Need run draw measures.
                    ANeedDrawChild := False;
                    ANeedDrawMeasures := True;
// draw self on 1 level
                    if (FMeasuresLevel = (ADRealLevel + 2)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                      ADRec.CellProperties := [pca_GSingle]
                    else
                      ADRec.CellProperties := [pca_GSingle, pca_Collapsed];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
              else
              begin
// no sublevel of group (group level (not last level))
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                begin
// group value (no sublevel of group (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (group value (no sublevel of group (group level (not last level))))
// expanded cell of collapsed group.
// process children
                    ANextLevelIsMeasures := ((ADRealLevel + 1) = FMeasuresLevel);
// draw self on 1 level
                    if (FMeasuresLevel = (ADRealLevel + 1)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                      ADRec.CellProperties := [pca_GCollapsed]
                    else
                      ADRec.CellProperties := [pca_GCollapsed, pca_Expanded];
                  end
                  else
                  begin
// collapsed cell (group value (no sublevel of group (group level (not last level))))
// collapsed cell of collapsed group.  Need run draw measures.
                    ANeedDrawChild := False;
                    ANeedDrawMeasures := True;
// draw self on 1 level
                    if (FMeasuresLevel = (ADRealLevel + 1)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                      ADRec.CellProperties := [pca_GCollapsed]
                    else
                      ADRec.CellProperties := [pca_GCollapsed, pca_Collapsed];
                  end
                end
                else
                begin
// single (no sublevel of group (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then// expanded cell
                  begin
// expanded cell (single (no sublevel of group (group level (not last level))))
// expanded cell of single.
// process children
                    ANextLevelIsMeasures := ((ADRealLevel + 1) = FMeasuresLevel);
// draw self on 1 level
                    if (FMeasuresLevel = (ADRealLevel + 1)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                      ADRec.CellProperties := [pca_GSingle]
                    else
                      ADRec.CellProperties := [pca_GSingle, pca_Expanded];
                  end
                  else
                  begin
// collapsed cell (single (no sublevel of group (group level (not last level))))
// collapsed cell of single.  Need run draw measures.
                    ANeedDrawChild := False;
                    ANeedDrawMeasures := True;
// draw self on 1 level
                    if (FMeasuresLevel = (ADRealLevel + 1)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                      ADRec.CellProperties := [pca_GSingle]
                    else
                      ADRec.CellProperties := [pca_GSingle, pca_Collapsed];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
            end
            else
            if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) then
            begin
//subgroup level (not last level)
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
              begin
// continuation of collapsed group (subgroup level (not last level))
// continuation of single (subgroup level (not last level))
// process children
                ANextLevelIsMeasures := ((ADRealLevel + 1) = FMeasuresLevel);
// not draw self, set coordinates after children draw
                ACellDrawType := 0;
                AExtra := 1;
              end
              else
              begin
// cell of detail of expanded group (subgroup level (not last level))
                if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                begin
// expanded cell (cell of detail of expanded group (subgroup level (not last level)))
// expanded cell of detail of expanded group.
// process children
                  ANextLevelIsMeasures := ((ADRealLevel + 1) = FMeasuresLevel);
// draw self on 1 level
                  if (FMeasuresLevel = (ADRealLevel + 1)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                    ADRec.CellProperties := [pca_Simple]
                  else
                    ADRec.CellProperties := [pca_Simple, pca_Expanded];
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
                end
                else
                begin
// collapsed cell (cell of detail of expanded group (subgroup level (not last level)))
// collapsed cell of detail of expanded group. Need run draw measures.
                  ANeedDrawChild := False;
                  ANeedDrawMeasures := True;
// draw self on 1 level
                  if (FMeasuresLevel = (ADRealLevel + 1)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                    ADRec.CellProperties := [pca_Simple]
                  else
                    ADRec.CellProperties := [pca_Simple, pca_Collapsed];
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
            end
            else
// simple level (not last level)
            if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
            begin
// expanded cell (simple level (not last level))
// expanded cell.
// process children
              ANextLevelIsMeasures := ((ADRealLevel + 1) = FMeasuresLevel);
// draw self on 1 level
              if (FMeasuresLevel = (ADRealLevel + 1)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                ADRec.CellProperties := [pca_Simple]
              else
                ADRec.CellProperties := [pca_Simple, pca_Expanded];
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
            end
            else
            begin
// collapsed cell (simple level (not last level))
// collapsed cell. Need run draw measures.
              ANeedDrawChild := False;
              ANeedDrawMeasures := True;
// draw self on 1 level
              if (FMeasuresLevel = (ADRealLevel + 1)) and (FRealLevelCount = (FMeasuresLevel + 1)) then
// next level is measures and it is last
                ADRec.CellProperties := [pca_Simple]
              else
                ADRec.CellProperties := [pca_Simple, pca_Collapsed];
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
            end;

            if ANeedDrawChild then
            begin
// process children
              if ANextLevelIsMeasures then
                DrawAxisCellWM(ASTNode.Nodes[AIndexWithOrd], ADLevel, ADRealLevel + 1, ADMeasureIndex)
              else
                DrawAxisCellWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, ADMeasureIndex)
            end
            else
            if ANeedDrawMeasures then
              DrawAxisCellWM(ASTNode.Nodes[AIndexWithOrd], {ALevelForDrawMeasures}ADLevel + 1, ADRealLevel + 1, ADMeasureIndex, True)
            else
// set started flag on next level (because not process children)
              for i := ADLevel + 1 to FLevelCount - 1 do
                AArrayStartIndexInLevel[i] := -1;

            if AExtra = 1 then
            begin
              AArrayCellsCount[ADLevel].Pos     := AArrayCellsCount[ADLevel + 1].Pos;
            end;
// draw
            case ACellDrawType of
              1: DrawAxisCellWMInternalLevelLong(ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex, ADrawMeasureIndex);
              2: DrawAxisCellWMInternalCellLong(ASTNode.Nodes[AIndexWithOrd].FIndex, ANLevels, ADrawMeasureIndex);
            end;
            if ATerminate then
// if stop flag, then end process
              Exit;
          end;
        end;
      end;
// total after
      for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
        if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
        begin
// visible measure
          if ATotalPositions[AMeasureIndex, ADLevel] = fctp_After then // if total after
            if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
            begin
// visible cell.
// check on start
              if (AArrayStartIndexInLevel[ADLevel] = -2) and
                 ((AStartMeasureIndex = -1) or (AStartMeasureIndex = AMeasureIndex)) then
              begin
                AStartMeasureIndex := -1;
                for i := ADLevel to FLevelCount - 1 do
                  AArrayStartIndexInLevel[i] := -1;
              end;
              if AArrayStartIndexInLevel[ADLevel] = -1 then
              begin
// draw total
// determine type
                if ADLevel = 0 then
// grand total
                  if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = stDimCollapsed then
// collapsed (grand total)
                    ADRec.CellProperties := [pca_GrandTotal, pca_Collapsed, pca_LastLevel]
                  else
// expanded (grand total)
                    ADRec.CellProperties := [pca_GrandTotal, pca_Expanded, pca_LastLevel]
                else
// simple total
                  ADRec.CellProperties := [pca_StartTotal, pca_LastLevel];
                if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) // subgroup level
                   and
                   ((CellPropertiesByNodeSelf(ASTNode.FIndex, ADLevel - 1, AParentMeasureIndex).State and stGExpanded) = 0) then// group collapsed
// if subgroup level and group is collapsed, not draw cell.
                else
                begin
// draw cell on all levels under
// additional totals
                  for i := -1 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
                    if AStartAdditionalTotalIndex <= i then
                    begin
                      DrawAxisCellWMInternalLevelLong(ASTNode.FLevel, ASTNode.FIndex, AMeasureIndex, i);
                      if ATerminate then
                        break;
                    end;
                  AStartAdditionalTotalIndex := -2;
                end;
                if ATerminate then
// need end!
                  Exit;
              end;
            end;
        end;
    end
    else
    begin
// under measures
      AMeasureIndex := ADMeasureIndex;
      AParentMeasureIndex := ADMeasureIndex;
// total before
      if ATotalPositions[AMeasureIndex, ADLevel] = fctp_Before then // if total before
// !!! check
      if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
      begin
// visible cell.
// check on start
        if (AArrayStartIndexInLevel[ADLevel] = -2) and
           ((AStartMeasureIndex = -1) or (AStartMeasureIndex = AMeasureIndex)) then
        begin
// if level in insignificant end and mesure is started, then set start flag
          AStartMeasureIndex := -1;
          for i := ADLevel to FLevelCount - 1 do
            AArrayStartIndexInLevel[i] := -1;
        end;
        if AArrayStartIndexInLevel[ADLevel] = -1 then
        begin
// draw total
// determine type
          ADRec.CellProperties := [pca_StartTotal, pca_LastLevel];
          if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) // subgroup level
             and
             ((CellPropertiesByNodeSelf(ASTNode.FIndex, ADLevel - 1, AParentMeasureIndex).State and stGExpanded) = 0) then// group collapsed
// if subgroup level and group is collapsed, not draw cell.
          else
          begin
// draw cell on all levels under
// additional totals
            for i := -1 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
              if AStartAdditionalTotalIndex <= i then
              begin
                DrawAxisCellWMInternalLevelLong(ASTNode.FLevel, ASTNode.FIndex, AMeasureIndex, i);
                if ATerminate then
                  break;
              end;
            AStartAdditionalTotalIndex := -2;
          end;
          if ATerminate then
// need end!
            Exit;
        end;
      end;

      AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
      ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
// process children
      for ANodeIndex := 0 to ACountChild - 1 do
      begin
        if AAscOrder then
          AIndexWithOrd := ANodeIndex
        else
          AIndexWithOrd := ACountChild - ANodeIndex - 1;
        if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible then
// visible cell.
        begin
          if AArrayStartIndexInLevel[ADLevel] <> -1 then
          begin
// no start flag, find start cell
            if AArrayStartIndexInLevel[ADLevel] = ASTNode.Nodes[AIndexWithOrd].FIndex then
// start index equal, set start flag
              AArrayStartIndexInLevel[ADLevel] := -1
            else
// stat index is not equal, skip child
              continue;
          end;
// call children draw, if need
// determine type cell and need process children
          ADRec.CellProperties := [];
// default
// process children
          ANeedDrawChild := True;
// on all levels under
          ACellDrawType := 1;
// on one level (for type 2)
          ANLevels := 1;
// index of measure for draw
          ADrawMeasureIndex := AMeasureIndex;
// index of measures for process children
          AChildMeasureIndex := AMeasureIndex;
// no additional operations
          AExtra := 0;
// call children draw, if need
          if ADRealLevel < (FRealLevelCount - 1) then
          begin
// not last level
            if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
            begin
// group level (not last level)
              if (FAxisTree[ADLevel + 1].LevelType = fcATLT_SubGroup) then
              begin
// has subgroup level (group level (not last level))
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                begin
// group value (has subgroup level (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stGExpanded) = 0) then
                  begin
// group collapsed (group value (has subgroup level (group level (not last level))))
                    if ADRealLevel = (FRealLevelCount - 2) then
                    begin
// prevlast level (group collapsed (group value (has subgroup level (group level (not last level)))))
// collapsed group of last level-1, after it there is nothing
                      ANeedDrawChild := False;
// draw self on all levels under
                      ADRec.CellProperties := [pca_GCollapsed, pca_LastLevel];
                    end
                    else
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// expanded cell of collapsed group.
// process children
//                  ANeedDrawChild := True;
// and draw self (on 2 levels)
                      ADRec.CellProperties := [pca_GCollapsed, pca_Expanded];
                      ACellDrawType := 2;
                      ANLevels := 2;
                      ADrawMeasureIndex := -1;
                    end
                    else
                    begin
// collapsed cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// collapsed cell of collapsed group.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ADRec.CellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                    end
                  end
                  else
                  begin
// group expanded (group value (has subgroup level (group level (not last level))))
// expanded group.
// process children
//                  ANeedDrawChild := True;
// draw self on 1 level
                    ADRec.CellProperties := [pca_GExpanded];
                    ACellDrawType := 2;
                    ADrawMeasureIndex := -1;
                  end
                end
                else
                begin
// single - single (has subgroup level (group level (not last level)))
                  if ADRealLevel = (FRealLevelCount - 2) then
                  begin
// prevlast level (single - single (has subgroup level (group level (not last level))))
// cell of single and after it there is nothing
                    ANeedDrawChild := False;
// draw self on all levels under
                    ADRec.CellProperties := [pca_GSingle, pca_LastLevel];
                  end
                  else
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// expanded cell of single.
// process children
//                  ANeedDrawChild := True;
// draw self on 2 levels
                    ADRec.CellProperties := [pca_GSingle, pca_Expanded];
                    ACellDrawType := 2;
                    ANLevels := 2;
                    ADrawMeasureIndex := -1;
                  end
                  else
                  begin
// collapsed cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// collapsed cell of single.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ADRec.CellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
              else
              begin
// no sublevel of group (group level (not last level))
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                begin
// group value (no sublevel of group (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (group value (no sublevel of group (group level (not last level))))
// expanded cell of collapsed group.
// process children
//                  ANeedDrawChild := True;
// draw self on 1 level
                    ADRec.CellProperties := [pca_GCollapsed, pca_Expanded];
                    ACellDrawType := 2;
                    ADrawMeasureIndex := -1;
                  end
                  else
                  begin
// collapsed cell (group value (no sublevel of group (group level (not last level))))
// collapsed cell of collapsed group.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ADRec.CellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                  end
                end
                else
                begin
// single (no sublevel of group (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (single (no sublevel of group (group level (not last level))))
// expanded cell of single.
// process children
//                  ANeedDrawChild := True;
// draw self on 1 level
                    ADRec.CellProperties := [pca_GSingle, pca_Expanded];
                    ACellDrawType := 2;
                    ADrawMeasureIndex := -1;
                  end
                  else
                  begin
// collapsed cell (single (no sublevel of group (group level (not last level))))
// collapsed cell of single.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ADRec.CellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
            end
            else
            if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) then
            begin
//subgroup level (not last level)
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
              begin
// continuation of collapsed group (subgroup level (not last level))
// continuation of single (subgroup level (not last level))
// process children
//                  ANeedDrawChild := True;
// not draw self, set coordinates after children draw
                ACellDrawType := 0;
                AExtra := 1;
              end
              else
              begin
// cell of detail of expanded group (subgroup level (not last level))
                if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                begin
// expanded cell (cell of detail of expanded group (subgroup level (not last level)))
// expanded cell of detail of expanded group.
// process children
//                  ANeedDrawChild := True;
// draw self on 1 level
                  ADRec.CellProperties := [pca_Simple, pca_Expanded];
                  ACellDrawType := 2;
                  ADrawMeasureIndex := -1;
                end
                else
                begin
// collapsed cell (cell of detail of expanded group (subgroup level (not last level)))
// collapsed cell of detail of expanded group.
                  ANeedDrawChild := False;
// draw self on all levels under
                  ADRec.CellProperties := [pca_Simple, pca_Collapsed, pca_LastLevel];
                end;
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
              end
            end
            else
// simple level (not last level)
            if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
            begin
// expanded cell (simple level (not last level))
// expanded cell.
// process children
//                  ANeedDrawChild := True;
// draw self on 1 level
              ADRec.CellProperties := [pca_Expanded];
              ACellDrawType := 2;
              ADrawMeasureIndex := -1;
            end
            else
            begin
// collapsed cell (simple level (not last level))
// collapsed cell.
              ANeedDrawChild := False;
// draw self on all levels under
              ADRec.CellProperties := [pca_Collapsed, pca_LastLevel];
            end;
          end
          else
          begin
// last level.
// draw self on all levels under
            ANeedDrawChild := False;
// determine type
            if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
// group level (last level)
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
// group value (group level (last level))
                ADRec.CellProperties := [pca_GCollapsed, pca_LastLevel]
              else
              begin
// single (group level (last level))
                ADRec.CellProperties := [pca_GSingle, pca_LastLevel];
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
              end
            else
            begin
// simple cell (not group level (last level))
              ADRec.CellProperties := [pca_Simple, pca_LastLevel];
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Other];
            end
          end;

          if ANeedDrawChild then
          begin
// process children
            DrawAxisCellWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, AChildMeasureIndex);
          end
          else
// set started flag on next level (because not process children)
            for i := ADLevel + 1 to FLevelCount - 1 do
              AArrayStartIndexInLevel[i] := -1;

          if AExtra = 1 then
          begin
            AArrayCellsCount[ADLevel+1].Pos     := AArrayCellsCount[ADLevel + 2].Pos;
          end;
// draw
          case ACellDrawType of
            1: DrawAxisCellWMInternalLevelLong(ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex, ADrawMeasureIndex);
            2: DrawAxisCellWMInternalCellLong(ASTNode.Nodes[AIndexWithOrd].FIndex, ANLevels, ADrawMeasureIndex);
          end;
          if ATerminate then
            Exit;
        end;
      end;
// total after
      if ATotalPositions[AMeasureIndex, ADLevel] = fctp_After then // if total after
// !!! check
      if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
      begin
// visible cell.
// check on start
        if (AArrayStartIndexInLevel[ADLevel] = -2) and
           ((AStartMeasureIndex = -1) or (AStartMeasureIndex = AMeasureIndex)) then
        begin
// if level in insignificant end and mesure is started, then set start flag
          AStartMeasureIndex := -1;
          for i := ADLevel to FLevelCount - 1 do
            AArrayStartIndexInLevel[i] := -1;
        end;
        if AArrayStartIndexInLevel[ADLevel] = -1 then
        begin
// draw total
// determine type
          ADRec.CellProperties := [pca_StartTotal, pca_LastLevel];
          if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) // subgroup level
             and
             ((CellPropertiesByNodeSelf(ASTNode.FIndex, ADLevel - 1, AParentMeasureIndex).State and stGExpanded) = 0) then// group collapsed
// if subgroup level and group is collapsed, not draw cell.
          else
          begin
// draw cell on all levels under
// additional totals
            for i := -1 to CountAdditionalTotalFunctionsInLevel[ASTNode.FLevel] - 1 do
              if AStartAdditionalTotalIndex <= i then
              begin
                DrawAxisCellWMInternalLevelLong(ASTNode.FLevel, ASTNode.FIndex, AMeasureIndex, i);
                if ATerminate then
                  break;
              end;
            AStartAdditionalTotalIndex := -2;
          end;
          if ATerminate then
// need end!
            Exit;
        end;
      end;

    end;
  end;
var
  AMeasureIndex: integer;
  ALevelIndex: TfcxSmallCount;
begin
  if (AStartCell < 0) or (AStartCell > (FVisibleAxisNodes.FCount - 1)) then
    Exit;
  if (AStartLevel < 0) or (AStartLevel > (FRealLevelCount - 1)) then
    Exit;
  if (AStopLevel < 0) or (AStopLevel > (FRealLevelCount - 1)) then
    Exit;
  if AStartLevel > AStopLevel then
    Exit;

  AVisibleLevelCount := VisibleLevelCount;
  AXAxis := FSlice.FXAxisContainer = Self;
  FMeasuresLevel := FAxisSourceTree.FMeasuresLevel;
  if AXAxis then
    ATypeSort := FSlice.FYAxisContainer.FDefaultTypeSort
  else
    ATypeSort := FSlice.FXAxisContainer.FDefaultTypeSort;


  GetMem(AArrayCellsCount, FRealLevelCount * SizeOf(_fcxDinamicPosArray));
  FillChar(AArrayCellsCount^, FRealLevelCount * SizeOf(_fcxDinamicPosArray), 0);


  GetMem(AArrayCellTypeInLevel, FLevelCount * SizeOf(Integer));
  GetMem(AArrayStartIndexInLevel, (FLevelCount + 1) * SizeOf(Integer));

// need get coorinates of start visible cell for each level
  AArrayStartIndexInLevel[FLevelOf[FVisibleAxisNodes.FIndexes[AStartCell]]] := FIndexInLevel[FVisibleAxisNodes.FIndexes[AStartCell]];
  for i := FLevelCount - 1 downto FLevelOf[FVisibleAxisNodes.FIndexes[AStartCell]] + 1 do
    AArrayStartIndexInLevel[i] := -2; // insignificant end
  for i := FLevelOf[FVisibleAxisNodes.FIndexes[AStartCell]] - 1 downto -1 do
    AArrayStartIndexInLevel[i] := FAxisTree[i+1].nodes[AArrayStartIndexInLevel[i+1]].IndParent;

  AArrayStartIndexInLevel[-1] := -1; // grand total always start
  AStartAdditionalTotalIndex := FVisibleAxisNodes.FAdditionalTotalsIndexes[AStartCell];
  ATerminate := False;
  if FMeasuresLevel = -1 then
  begin
    DrawAxisCell(FAxisSortTree.FRootNode, 0, FGrandTotalPosition);
  end
  else
  begin
// started and stopped measure
    AStartMeasureIndex := FVisibleAxisNodes.FMeasureIndexes[AStartCell];
    SetLength(ATotalPositions, FSlice.FMeasuresContainer.Count, FRealLevelCount);
    for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
      if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
      begin
        if FUseGrandTotalPositionFromMeasure then
          ATotalPositions[AMeasureIndex, 0] := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].TotalPositionByLevel[AXAxis, -1] // position total from measure
        else
          ATotalPositions[AMeasureIndex, 0] := FGrandTotalPosition; // grand total position
        for ALevelIndex := 1 to FLevelCount - 1 do
        begin
          if Fields[FAxisTree[ALevelIndex - 1].RegionFieldIndex].FUseTotalPositionFromMeasure then
            ATotalPositions[AMeasureIndex, ALevelIndex] := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].TotalPositionByLevel[AXAxis, ALevelIndex - 1] // position total from measure
          else
            ATotalPositions[AMeasureIndex, ALevelIndex] := Fields[FAxisTree[ALevelIndex - 1].RegionFieldIndex].FTotalPosition; // total position
        end;
      end;

    if FMeasuresLevel = 0 then
      DrawAxisCellWM(FAxisSortTree.FRootNode, -1, 0, -1)
    else
      DrawAxisCellWM(FAxisSortTree.FRootNode, 0, 0, -1);
  end;

  FreeMem(AArrayStartIndexInLevel);
  FreeMem(AArrayCellTypeInLevel);
  FreeMem(AArrayCellsCount);
end;

function TfcxAxisContainer.TraverseAxis_Tree(AStartCell: Integer;
  ADrawProc: TfcxSliceAxisDrawCellProc): TfcxSliceDrawHeaderResult;
var
  i: integer;
  AXAxis: Boolean;
  AVisibleLevelCount: TfcxSmallCount;
  AArrayStartIndexInLevel: PfcxIntegerArrayMinus;
  AStartAdditionalTotalIndex: TfcxSmallCount;
  AStartMeasureIndex: TfcxSmallCount;
  ACellsPos: TfcxPos;
  ATerminate: Boolean;
  ATypeSort: TfcxTypeSortAxis;

  procedure DrawAxisCellStart(ASTNode: PfcxSTNode); 
  var
    ADRec: TfcxSliceDrawHeader;
    ACellProperties: TfcxPropertiesOfCellAxis;

// draw total cell. size 1 cell, on all levels from current to last
// draw collapsed cell, and last level cell
    procedure DrawAxisCellInternalLevelLong(ADLevel, ANodeLevelInternal: TfcxSmallCount; ANodeIndexInternal: integer; AAdditionalTotalIndex: integer = -1);
    var
      ATermination: Boolean;
    begin
      ADRec.TreeRect.Level := ADLevel;
      ADRec.TreeRect.Cell  := ACellsPos.Pos;
      ADRec.TreeRect.SizeLevel := AVisibleLevelCount - ADLevel;
      ADRec.TreeRect.SizeCell  := 1;

      ADRec.Cell      := FAxisTree[ANodeLevelInternal].IndexInLastLevel[ANodeIndexInternal];
      ADRec.Level     := ADLevel;
      ADRec.MeasureIndex := -1;
      ADRec.TotalIndex := AAdditionalTotalIndex;
      ADRec.NodeLevel  := ANodeLevelInternal;
      ADRec.NodeIndex  := ANodeIndexInternal;
      if ANodeLevelInternal >= 0 then
        ADRec.Alignment  := Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].Alignment
      else
        ADRec.Alignment  := Alignment;

      if pca_LastLevel in ADRec.CellProperties then
      begin
        case ATypeSort of
          md_tsa_BySelected:
            begin
              if (ADRec.NodeLevel = FSelectedLevelIndex) and (ADRec.NodeIndex = FSelectedIndexInLevel) and (ADRec.TotalIndex = FSelectedAdditionalTotalIndex) then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end;
          md_tsa_ByTotalValue:
            begin
              if (pca_GrandTotal in ADRec.CellProperties) then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end
        end;
      end;

      if (ADRec.CellProperties * [pca_GrandTotal, pca_StartTotal]) = [] then
        ADRec.ValueIndex := FAxisTree[ANodeLevelInternal].Nodes[ANodeIndexInternal].SSelfNode.Index
      else
        ADRec.ValueIndex := -2;

      ADRec.Text := GetDisplayValue(ADLevel, ADRec.Cell);
      {$ifdef fc_debug}
//      ADRec.Text := ADRec.Text + fcGetNameCellProperties(ADRec.CellProperties);
      {$endif}

      // Additional Total
      if AAdditionalTotalIndex >= 0 then
      begin
        if FLevelOf[ADRec.Cell] = -1 then
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[AdditionalGrandTotalFunction[AAdditionalTotalIndex]])
        else
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].AdditionalTotalFunction[AAdditionalTotalIndex]]);
      end;

      ACellsPos.Pos     := ADRec.TreeRect.Cell + ADRec.TreeRect.SizeCell;
      ATermination := ADrawProc(Self, ADRec);
      ATerminate := ATerminate or ATermination;
    end;

    procedure DrawAxisCell(ASTNode: PfcxSTNode; ADLevel: TfcxSmallCount);
    var
      i: integer;
      ACanDrawCell: Boolean;
      ANodeIndex, ACountChild, AIndexWithOrd: Integer;
      AAscOrder: boolean;
      ADRealLevel: TfcxSmallCount;

      ANeedDrawChild: Boolean; // False - not process children, set start flag; True - process children
      ACellDrawType: integer; // 0 - not draw, 1 - on all levels under
      AMeasureIndex: TfcxSmallCount;

    begin
      ADRealLevel := ADLevel;
// iterate all children, call DrawAxisCell for each visible
// draw with type rules
// iterate and process cell children
      AMeasureIndex := 0;
      AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
      ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
// process children
      for ANodeIndex := 0 to ACountChild - 1 do
      begin
// get index by sort
        if AAscOrder then
          AIndexWithOrd := ANodeIndex
        else
          AIndexWithOrd := ACountChild - ANodeIndex - 1;
        if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible then
// visible cell.
        begin
          if AArrayStartIndexInLevel[ADLevel] <> -1 then
          begin
// no start flag, find start cell
            if AArrayStartIndexInLevel[ADLevel] = ASTNode.Nodes[AIndexWithOrd].FIndex then
            begin
// start index equal, set start flag
              AArrayStartIndexInLevel[ADLevel] := -1;
              if (ADLevel < (FRealLevelCount - 1)) then
//not last level
                if (AArrayStartIndexInLevel[ADLevel+1] = -2) then
// and row of this cell (start index on next level -2)
// set started flag on level under
                  for i := ADLevel + 1 to FLevelCount - 1 do
                    AArrayStartIndexInLevel[i] := -1;
            end
            else
// stat index is not equal, skip child
              continue;
          end;
// determine type cell and need process children
          ACellProperties := [];
// default
// process children
          ANeedDrawChild := True;
// on all levels under
          ACellDrawType := 1;

// call children draw, if need
          if ADRealLevel < (FRealLevelCount - 1) then
          begin
// not last level
            if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
            begin
// group level (not last level)
              if (FAxisTree[ADLevel + 1].LevelType = fcATLT_SubGroup) then
              begin
// has subgroup level (group level (not last level))
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                begin
// group value (has subgroup level (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stGExpanded) = 0) then
                  begin
// group collapsed (group value (has subgroup level (group level (not last level))))
                    if ADRealLevel = (FRealLevelCount - 2) then
                    begin
// prevlast level (group collapsed (group value (has subgroup level (group level (not last level)))))
// collapsed group of last level-1, after it there is nothing
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GCollapsed, pca_LastLevel];
                    end
                    else
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// expanded cell of collapsed group.
// process children
// and draw self on all levels under
                      ACellProperties := [pca_GCollapsed, pca_Expanded, pca_LastLevel];
                      if (AArrayStartIndexInLevel[ADLevel+2] = -2) then
// set started flag on level under
                        for i := ADLevel + 1 to FLevelCount - 1 do
                          AArrayStartIndexInLevel[i] := -1;
                    end
                    else
                    begin
// collapsed cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// collapsed cell of collapsed group.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                    end
                  end
                  else
                  begin
// group expanded (group value (has subgroup level (group level (not last level))))
// expanded group.
// process children
// draw self on all levels under
                    ACellProperties := [pca_GExpanded, pca_LastLevel];
                  end
                end
                else
                begin
// single - single (has subgroup level (group level (not last level)))
                  if ADRealLevel = (FRealLevelCount - 2) then
                  begin
// prevlast level (single - single (has subgroup level (group level (not last level))))
// cell of single and after it there is nothing
                    ANeedDrawChild := False;
// draw self on all levels under
                    ACellProperties := [pca_GSingle, pca_LastLevel];
                  end
                  else
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// expanded cell of single.
// process children
// draw self on all levels under
                    ACellProperties := [pca_GSingle, pca_Expanded, pca_LastLevel];
                    if (AArrayStartIndexInLevel[ADLevel+2] = -2) then
// set started flag on level under
                      for i := ADLevel + 1 to FLevelCount - 1 do
                        AArrayStartIndexInLevel[i] := -1;
                  end
                  else
                  begin
// collapsed cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// collapsed cell of single.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ACellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ACellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
              else
              begin
// no sublevel of group (group level (not last level))
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                begin
// group value (no sublevel of group (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (group value (no sublevel of group (group level (not last level))))
// expanded cell of collapsed group.
// process children
// draw self on all levels under
                    ACellProperties := [pca_GCollapsed, pca_Expanded, pca_LastLevel];
                  end
                  else
                  begin
// collapsed cell (group value (no sublevel of group (group level (not last level))))
// collapsed cell of collapsed group.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ACellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                  end
                end
                else
                begin
// single (no sublevel of group (group level (not last level)))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (single (no sublevel of group (group level (not last level))))
// expanded cell of single.
// process children
// draw self on all levels under
                    ACellProperties := [pca_GSingle, pca_Expanded, pca_LastLevel];
                  end
                  else
                  begin
// collapsed cell (single (no sublevel of group (group level (not last level))))
// collapsed cell of single.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ACellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ACellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
            end
            else
            if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) then
            begin
//subgroup level (not last level)
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
              begin
// continuation of collapsed group (subgroup level (not last level))
// continuation of single (subgroup level (not last level))
// process children
// not draw self
                ACellDrawType := 0;
              end
              else
              begin
// cell of detail of expanded group (subgroup level (not last level))
                if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                begin
// expanded cell (cell of detail of expanded group (subgroup level (not last level)))
// expanded cell of detail of expanded group.
// process children
// draw self on all levels under
                  ACellProperties := [pca_Simple, pca_Expanded, pca_LastLevel];
                end
                else
                begin
// collapsed cell (cell of detail of expanded group (subgroup level (not last level)))
// collapsed cell of detail of expanded group.
                  ANeedDrawChild := False;
// draw self on all levels under
                  ACellProperties := [pca_Simple, pca_Collapsed, pca_LastLevel];
                end;
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ACellProperties := ADRec.CellProperties + [pca_Other];
              end
            end
            else
// simple level (not last level)
            if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
            begin
// expanded cell (simple level (not last level))
// expanded cell.
// process children
// draw self on all levels under
              ACellProperties := [pca_Expanded, pca_LastLevel];
            end
            else
            begin
// collapsed cell (simple level (not last level))
// collapsed cell.
              ANeedDrawChild := False;
// draw self on all levels under
              ACellProperties := [pca_Collapsed, pca_LastLevel];
            end
          end
          else
          begin
// last level.
// draw self on all levels under
            ANeedDrawChild := False;
// determine type
            if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
// group level (last level)
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
// group value (group level (last level))
                ACellProperties := [pca_GCollapsed, pca_LastLevel]
              else
              begin
// single (group level (last level))
                ACellProperties := [pca_GSingle, pca_LastLevel];
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ACellProperties := ADRec.CellProperties + [pca_Other];
              end
            else
            begin
// simple cell (not group level (last level))
              ACellProperties := [pca_Simple, pca_LastLevel];
              if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                ACellProperties := ADRec.CellProperties + [pca_Other];
            end;
          end;
          if not ANeedDrawChild then
// set started flag on next level (because not process children)
            for i := ADLevel + 1 to FLevelCount - 1 do
              AArrayStartIndexInLevel[i] := -1;

          if (ADLevel = (FRealLevelCount - 1)) or (AArrayStartIndexInLevel[ADLevel + 1] < 0) then
// Cell can draw if it in last row or was full start
            ACanDrawCell := True
          else
// else call children draw only
            ACanDrawCell := False;
          if ACanDrawCell then
// draw
            case ACellDrawType of
              1:
                begin
                  ADRec.CellProperties := ACellProperties;
// additional totals
                  for i := -1 to CountAdditionalTotalFunctionsInLevel[ADLevel] - 1 do
                  begin
                    if AStartAdditionalTotalIndex <= i then
                    begin
                      DrawAxisCellInternalLevelLong(ADLevel, ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex, i);
                      if ATerminate then
                        break;
                      if ([pca_Collapsed, pca_GCollapsed] * ACellProperties) <> [] then
                        break;
                    end;
                    if ADLevel = (FLevelCount - 1) then
                      break;
                  end;
                  AStartAdditionalTotalIndex := -2;
                end;
            end;
          if ATerminate then
// if stop flag, then not need draw children
            ANeedDrawChild := False;

          if ANeedDrawChild then
// process children
            DrawAxisCell(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1);

          if ATerminate then
            Exit;
        end;
      end;
    end;
  var
    ADLevel: TfcxSmallCount;
    i: integer;
    AExpanded: Boolean;
  begin
// grand total
// check on start
    ADLevel := -1;
    if (FLevelCount > 0) and (AArrayStartIndexInLevel[ADLevel+1] = -2) then
// if level not last and row of this cell (of grand total) (start index on next level -2)
// set started flag on level under
      for i := 0 to FLevelCount - 1 do
        AArrayStartIndexInLevel[i] := -1;
    AExpanded := (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0;
    if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[0][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
    begin
// visible cell.
      if (FLevelCount = 0) or (AArrayStartIndexInLevel[ADLevel + 1] < 0) then
      begin
// has start
// determine type
        if AExpanded then
          ADRec.CellProperties := [pca_GrandTotal, pca_Expanded, pca_LastLevel]
        else
          ADRec.CellProperties := [pca_GrandTotal, pca_Collapsed, pca_LastLevel];
// draw cell
// additional totals
        for i := -1 to CountAdditionalTotalFunctionsInLevel[-1] - 1 do
          if AStartAdditionalTotalIndex <= i then
          begin
            DrawAxisCellInternalLevelLong(0, ASTNode.FLevel, ASTNode.FIndex, i);
            if ATerminate then
              break;
          end;
        AStartAdditionalTotalIndex := -2;
      end;
    end;
// chaeck on stop
    if ATerminate then
// end if stop
      Exit;
    if AExpanded {and (FGrandTotalPosition = fctp_Before) }then
// process in end of children ig grand total before
      DrawAxisCell(ASTNode, 0);
  end;

  procedure DrawAxisCellWMStart(ASTNode: PfcxSTNode);
  var
    ACellsSize: integer;
    ADRec: TfcxSliceDrawHeader;

    procedure DrawAxisCellWMInternalLevelLong(ADLevel, ADRealLevel, ANodeLevelInternal: TfcxSmallCount; ANodeIndexInternal: integer; AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount);
    var
      ATermination: boolean;
    begin
      ADRec.TreeRect.Level := ADRealLevel;
      ADRec.TreeRect.Cell  := ACellsPos.Pos;
      ADRec.TreeRect.SizeLevel := AVisibleLevelCount - ADLevel;
      ADRec.TreeRect.SizeCell  := 1;

      ADRec.Cell      := FAxisTree[ANodeLevelInternal].IndexInLastLevel[ANodeIndexInternal];
      ADRec.Level     := ADLevel;
      ADRec.MeasureIndex := AMeasureIndex; // ??
      ADRec.TotalIndex := AAdditionalTotalIndex;
      ADRec.NodeLevel  := ANodeLevelInternal;
      ADRec.NodeIndex  := ANodeIndexInternal;
      ADRec.Alignment  := Alignment;
      if not (pca_LastLevel in ADRec.CellProperties) then
        ADRec.CellProperties := ADRec.CellProperties + [pca_TreeCellWithMeasure];

      if pca_LastLevel in ADRec.CellProperties then
      begin
        case ATypeSort of
          md_tsa_BySelected:
            begin
              if (ADRec.MeasureIndex = FSelectedMeasureIndex) and (ADRec.NodeLevel = FSelectedLevelIndex) and (ADRec.NodeIndex = FSelectedIndexInLevel) and (ADRec.TotalIndex = FSelectedAdditionalTotalIndex) then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end;
          md_tsa_ByTotalValue:
            begin
              if FMeasuresLevel = 0 then
              begin
                if (ADRec.MeasureIndex = FSelectedMeasureIndex) and (ADRec.NodeLevel = -1) then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
              end
              else
                if (pca_GrandTotal in ADRec.CellProperties) and (ADRec.MeasureIndex = FSelectedMeasureIndex) then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end
        end;
      end;
{
      if (AMeasureIndex <> -1) and (ADRealLevel <= FMeasuresLevel) then
      begin
        ADRec.Text := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Caption;
        ADRec.ValueIndex := -2;
      end
      else
}
      begin
        if ANodeLevelInternal >= 0 then
          ADRec.Alignment := Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].Alignment
        else
          ADRec.Alignment  := Alignment;
        if (ADRec.CellProperties * [pca_GrandTotal, pca_StartTotal]) = [] then
          ADRec.ValueIndex := FAxisTree[ANodeLevelInternal].Nodes[ANodeIndexInternal].SSelfNode.Index
        else
          ADRec.ValueIndex := -2;
        ADRec.Text := GetDisplayValue(ADLevel, ADRec.Cell);
      end;
      {$ifdef fc_debug}
//      ADRec.Text := ADRec.Text + fcGetNameCellProperties(ADRec.CellProperties);
      {$endif}
      // Additional Total
      if AAdditionalTotalIndex >= 0 then
      begin
        if FLevelOf[ADRec.Cell] = -1 then
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[AdditionalGrandTotalFunction[AAdditionalTotalIndex]])
        else
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].AdditionalTotalFunction[AAdditionalTotalIndex]]);
      end;

//      if (not AWOMeasureCell) and (AMeasureIndex > -1) then
//        ATermination := ADrawProc(Self, ADRec)
//      else
      begin
        ACellsPos.Pos := ADRec.TreeRect.Cell + ADRec.TreeRect.SizeCell;
        ATermination := ADrawProc(Self, ADRec);
      end;
      ATerminate := ATerminate or ATermination;
    end;

    procedure DrawAxisCellWMInternalMeasure(ADLevel, ADRealLevel, ANodeLevelInternal: TfcxSmallCount; ANodeIndexInternal: integer; AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount);
    var
      ATermination: boolean;
    begin
      ADRec.TreeRect.Level := ADRealLevel;
      ADRec.TreeRect.Cell  := ACellsPos.Pos;
      ADRec.TreeRect.SizeLevel := 1;
      ADRec.TreeRect.SizeCell  := 1;

      ADRec.Cell      := FAxisTree[ANodeLevelInternal].IndexInLastLevel[ANodeIndexInternal];
      ADRec.Level     := ADLevel;
      ADRec.MeasureIndex := AMeasureIndex; // ??
      ADRec.TotalIndex := AAdditionalTotalIndex;
      ADRec.NodeLevel  := ANodeLevelInternal;
      ADRec.NodeIndex  := ANodeIndexInternal;
      ADRec.Alignment := FSlice.FMeasuresContainer.Alignment;
      ADRec.CellProperties := ADRec.CellProperties + [pca_TreeMeasureCell];

      if pca_LastLevel in ADRec.CellProperties then
      begin
        case ATypeSort of
          md_tsa_BySelected:
            begin
              if (ADRec.MeasureIndex = FSelectedMeasureIndex) and (ADRec.NodeLevel = FSelectedLevelIndex) and (ADRec.NodeIndex = FSelectedIndexInLevel) and (ADRec.TotalIndex = FSelectedAdditionalTotalIndex) then
                ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end;
          md_tsa_ByTotalValue:
            begin
              if FMeasuresLevel = 0 then
              begin
                if (ADRec.MeasureIndex = FSelectedMeasureIndex) and (ADRec.NodeLevel = -1) then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
              end
              else
                if (pca_GrandTotal in ADRec.CellProperties) and (ADRec.MeasureIndex = FSelectedMeasureIndex) then
                  ADRec.CellProperties := ADRec.CellProperties + [pca_Sort];
            end
        end;
      end;
//            if (AMeasureIndex <> -1) and (ADRealLevel <= FMeasuresLevel) then
      begin
        ADRec.Text := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Caption;
        ADRec.ValueIndex := -2;
{
      end
      else
      begin
        if ANodeLevelInternal >= 0 then
          ADRec.Alignment := FFieldsOfRegion[FAxisTree[ANodeLevelInternal].RegionFieldIndex].Alignment;
        if (ADRec.CellProperties * [pca_GrandTotal, pca_StartTotal]) = [] then
          ADRec.ValueIndex := FAxisTree[ANodeLevelInternal].Nodes[ANodeIndexInternal].SSelfNode.Index
        else
          ADRec.ValueIndex := -2;
        ADRec.Text := GetDisplayValue(ADLevel, ADRec.Cell);
}
      end;
      {$ifdef fc_debug}
//      ADRec.Text := ADRec.Text + fcGetNameCellProperties(ADRec.CellProperties);
      {$endif}
      // Additional Total
      if AAdditionalTotalIndex >= 0 then
      begin
        if FLevelOf[ADRec.Cell] = -1 then
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[AdditionalGrandTotalFunction[AAdditionalTotalIndex]])
        else
          ADRec.Text := ADRec.Text + ' ' + fcxResources.Get(sFuncCaptions[Fields[FAxisTree[ANodeLevelInternal].RegionFieldIndex].AdditionalTotalFunction[AAdditionalTotalIndex]]);
      end;

      ACellsPos.Pos     := ADRec.TreeRect.Cell + ADRec.TreeRect.SizeCell;
      ATermination := ADrawProc(Self, ADRec);
      ATerminate := ATerminate or ATermination;
    end;

// draw simple cell. size is count of children, on need levels
    procedure DrawAxisCellWMInternalCellLong(ADLevel, ADRealLevel, ANodeLevelInternal: TfcxSmallCount; ANodeIndexInternal: integer; AMeasureIndex: TfcxSmallCount = -1);
    var
      ATermination: boolean;
    begin
      ADRec.TreeRect.Level := ADRealLevel;
      ADRec.TreeRect.Cell  := ACellsPos.Pos - ACellsSize;
      ADRec.TreeRect.SizeLevel := AVisibleLevelCount - ADRealLevel - 1;
      ADRec.TreeRect.SizeCell  := ACellsSize;

      ADRec.Cell      := FAxisTree[ANodeLevelInternal].IndexInLastLevel[ANodeIndexInternal];
      ADRec.Level     := ADLevel;
      ADRec.NodeLevel  := ANodeLevelInternal;
      ADRec.NodeIndex  := ANodeIndexInternal;
      ADRec.Alignment  := Alignment;
      if not (pca_LastLevel in ADRec.CellProperties) then
        ADRec.CellProperties := ADRec.CellProperties + [pca_TreeCellWithMeasure];

{
      if ADRealLevel = FMeasuresLevel then
      begin
        ADRec.MeasureIndex := AMeasureIndex; // ??
        ADRec.Text := FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Caption;
        ADRec.ValueIndex := -2;
      end
      else
}
      begin
        if ADLevel >= 0 then
          ADRec.Alignment  := Fields[FAxisTree[ADLevel].RegionFieldIndex].Alignment
        else
          ADRec.Alignment  := Alignment;
        if (ADRec.CellProperties * [pca_GrandTotal, pca_StartTotal]) = [] then
          ADRec.ValueIndex := FAxisTree[ADLevel].Nodes[ANodeIndexInternal].SSelfNode.Index
        else
          ADRec.ValueIndex := -2;
        ADRec.MeasureIndex := AMeasureIndex; // ??
        ADRec.TotalIndex := -1;
        ADRec.Text := GetDisplayValue(ADLevel, ADRec.Cell);
      end;
      {$ifdef fc_debug}
//      ADRec.Text := ADRec.Text + fcGetNameCellProperties(ADRec.CellProperties);
      {$endif}
//            AArrayCellsCount[ADRealLevel].Pos     := ADRec.TreeRect.Cell + ADRec.TreeRect.SizeCell;
      ATermination := ADrawProc(Self, ADRec);
      ATerminate := ATerminate or ATermination;
    end;

    procedure DrawAxisCellWM(ASTNode: PfcxSTNode; ADLevel, ADRealLevel, ADMeasureIndex: TfcxSmallCount);
    var
      ANodeIndex, ACountChild, AIndexWithOrd: Integer;
      AAscOrder: boolean;
      AMeasureIndex: TfcxSmallCount;
      i: integer;
      ANeedExit: Boolean;
      ACanDrawCell: Boolean;
      ANeedDrawChild: Boolean; // False - not process children, set start flag; True - process children
      ACellDrawType: integer; // 0 - not draw, 1 - on all levels under, 2 - draw wit children
      AIncLevelForType2: TfcxSmallCount;
      ASaveStartMeasureIndex: TfcxSmallCount;
      ACellProperties: TfcxPropertiesOfCellAxis;
    begin
      ACellsSize := 0;
      if (FMeasuresLevel = ADRealLevel) and (ADRealLevel <> (FRealLevelCount - 1)) then
      begin
        DrawAxisCellWM(ASTNode, ADLevel, ADRealLevel + 1, ADMeasureIndex);
      end
      else
      if FMeasuresLevel > ADRealLevel then
      begin
// above measures
        AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
        ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
// process children
        for ANodeIndex := 0 to ACountChild - 1 do
        begin
          AMeasureIndex := 0;
// get index by sort
          if AAscOrder then
            AIndexWithOrd := ANodeIndex
          else
            AIndexWithOrd := ACountChild - ANodeIndex - 1;
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible then
// visible cell.
          begin
            if AArrayStartIndexInLevel[ADLevel] <> -1 then
            begin
// no start flag, find start cell
              if (AArrayStartIndexInLevel[ADLevel] = ASTNode.Nodes[AIndexWithOrd].FIndex) then
              begin
// start index equal, set start flag
                AArrayStartIndexInLevel[ADLevel] := -1;
                if (ADLevel < (FLevelCount - 1)) and (AArrayStartIndexInLevel[ADLevel+1] = -2) then
// if level is not last and row of this cell (start index on next level -2)
// set started flag on level under
                  for i := ADLevel + 1 to FLevelCount - 1 do
                    AArrayStartIndexInLevel[i] := -1;
              end
              else
// stat index is not equal, skip child
                continue;
            end;
// determine type cell and need process children
            ACellProperties := [];
// default
// process children
            ANeedDrawChild := True;
// on all levels under
            ACellDrawType := 1;
            AIncLevelForType2 := 0;
            ACellsSize := 0;
            ANeedExit := False;
// call children draw, if need
            if ADRealLevel < (FRealLevelCount - 1) then
            begin
// not last level
              if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
              begin
// group level (not last level)
                if (FAxisTree[ADLevel + 1].LevelType = fcATLT_SubGroup) then
                begin
// has subgroup level (group level (not last level))
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                  begin
// group value (has subgroup level (group level (not last level)))
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stGExpanded) = 0) then
                    begin
// group collapsed (group value (has subgroup level (group level (not last level))))
                      if ADRealLevel = (FRealLevelCount - 2) then
                      begin
// prevlast level (group collapsed (group value (has subgroup level (group level (not last level)))))
// collapsed group of last level-1, after it there is nothing
                        ANeedDrawChild := False;
// draw self on all levels under
                        ACellProperties := [pca_GCollapsed, pca_LastLevel];
                      end
                      else
                      if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                      begin
// expanded cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// expanded cell of collapsed group.
// process children
// and draw self on all levels under
                        ACellProperties := [pca_GCollapsed, pca_Expanded, pca_LastLevel];
                        ACellDrawType := 2;
                        AIncLevelForType2 := 1;
                        if (AArrayStartIndexInLevel[ADLevel+2] = -2) then
// set started flag on level under
                          for i := ADLevel + 1 to FLevelCount - 1 do
                            AArrayStartIndexInLevel[i] := -1;
                      end
                      else
                      begin
// collapsed cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// collapsed cell of collapsed group.
                        ANeedDrawChild := False;
// draw self on all levels under
                        ACellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                      end
                    end
                    else
                    begin
// group expanded (group value (has subgroup level (group level (not last level))))
// expanded group.
// process children
// draw self on all levels under
                      ACellProperties := [pca_GExpanded, pca_LastLevel];
                    end
                  end
                  else
                  begin
// single - single (has subgroup level (group level (not last level)))
                    if ADLevel = (FRealLevelCount - 3) then
                    begin
// prevprevlast level (single - single (has subgroup level (group level (not last level))))
// cell of single and after it there is nothing
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_LastLevel];
                    end
                    else
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// expanded cell of single.
// process children
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_Expanded, pca_LastLevel];
                      ACellDrawType := 2;
                      AIncLevelForType2 := 1;
                      if (AArrayStartIndexInLevel[ADLevel+2] = -2) then
// set started flag on level under
                        for i := ADLevel + 1 to FLevelCount - 1 do
                          AArrayStartIndexInLevel[i] := -1;
                    end
                    else
                    begin
// collapsed cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// collapsed cell of single.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                    end;
                    if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                      ACellProperties := ADRec.CellProperties + [pca_Other];
                  end
                end
                else
                begin
// no sublevel of group (group level (not last level))
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                  begin
// group value (no sublevel of group (group level (not last level)))
                    if ADLevel = (FRealLevelCount - 2) then
                    begin
// prevlast level (group value (no sublevel of group (group level (not last level))))
// cell of group and after it there is nothing
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GCollapsed, pca_LastLevel];
                    end
                    else
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (group value (no sublevel of group (group level (not last level))))
// expanded cell of collapsed group.
// process children
// draw self on all levels under
                      ACellProperties := [pca_GCollapsed, pca_Expanded, pca_LastLevel];
                      ACellDrawType := 2;
                    end
                    else
                    begin
// collapsed cell (group value (no sublevel of group (group level (not last level))))
// collapsed cell of collapsed group.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                    end
                  end
                  else
                  begin
// single (no sublevel of group (group level (not last level)))
                    if ADLevel = (FRealLevelCount - 2) then
                    begin
// prevlast level (single - single (no sublevel of group (group level (not last level))))
// cell of single and after it there is nothing
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_LastLevel];
                    end
                    else
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (single (no sublevel of group (group level (not last level))))
// expanded cell of single.
// process children
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_Expanded, pca_LastLevel];
                      ACellDrawType := 2;
                    end
                    else
                    begin
// collapsed cell (single (no sublevel of group (group level (not last level))))
// collapsed cell of single.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                    end;
                    if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                      ACellProperties := ADRec.CellProperties + [pca_Other];
                  end
                end
              end
              else
              if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) then
              begin
//subgroup level (not last level)
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
                begin
// continuation of collapsed group (subgroup level (not last level))
// continuation of single (subgroup level (not last level))
// process children
// not draw self
                  ACellDrawType := 0;
                end
                else
                begin
// cell of detail of expanded group (subgroup level (not last level))
                  if ADLevel = (FRealLevelCount - 2) then
                  begin
// prevlast level (cell of detail of expanded group (subgroup level (not last level)))
// cell of group and after it there is nothing
                    ANeedDrawChild := False;
// draw self on all levels under
                    ACellProperties := [pca_Simple, pca_LastLevel];
                  end
                  else
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (cell of detail of expanded group (subgroup level (not last level)))
// expanded cell of detail of expanded group.
// process children
// draw self on all levels under
                    ACellProperties := [pca_Simple, pca_Expanded, pca_LastLevel];
                    ACellDrawType := 2;
                  end
                  else
                  begin
// collapsed cell (cell of detail of expanded group (subgroup level (not last level)))
// collapsed cell of detail of expanded group.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ACellProperties := [pca_Simple, pca_Collapsed, pca_LastLevel];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ACellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
              else
// simple level (not last level)
              if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
              begin
// expanded cell (simple level (not last level))
// expanded cell.
// process children
// draw self on all levels under
                if ADLevel <> (FLevelCount - 1) then
                  ACellProperties := [pca_Simple, pca_Expanded, pca_LastLevel]
                else
                  ACellProperties := [pca_Simple, pca_LastLevel];
                ACellDrawType := 2;
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ACellProperties := ADRec.CellProperties + [pca_Other];
              end
              else
              begin
// collapsed cell (simple level (not last level))
// collapsed cell.
                ANeedDrawChild := False;
// draw self on all levels under
                if ADLevel <> (FLevelCount - 1) then
                  ACellProperties := [pca_Simple, pca_Collapsed, pca_LastLevel]
                else
                  ACellProperties := [pca_Simple, pca_LastLevel];
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ACellProperties := ADRec.CellProperties + [pca_Other];
              end;
            end
            else
            begin
// last level.
// draw self on all levels under
             ANeedDrawChild := False;
// determine type
              if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
// group level (last level)
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
// group value (group level (last level))
                  ACellProperties := [pca_GCollapsed, pca_LastLevel]
                else
                begin
// single (group level (last level))
                  ACellProperties := [pca_GSingle, pca_LastLevel];
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ACellProperties := ADRec.CellProperties + [pca_Other];
                end
              else
              begin
// simple cell (not group level (last level))
                ACellProperties := [pca_Simple, pca_LastLevel];
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ACellProperties := ADRec.CellProperties + [pca_Other];
              end;
            end;

            if not ANeedDrawChild then
// set started flag on next level (because not process children)
              for i := ADLevel + 1 to FLevelCount - 1 do
                AArrayStartIndexInLevel[i] := -1;

            if (ADLevel = (FRealLevelCount - 2)) or (AArrayStartIndexInLevel[ADLevel + 1] < 0) then
// Cell can draw if it in prevlast row or was full start
              ACanDrawCell := True
            else
// else call children draw only
              ACanDrawCell := False;
// draw
            if (ACellDrawType = 2) and not ((FMeasuresLevel = (ADLevel+1+AIncLevelForType2)) and (FMeasuresLevel <> FLevelCount)) then
              ACellDrawType := 1;
            case ACellDrawType of
              1:
                if ACanDrawCell then
                begin
                  ANeedExit := False;
                  ACellsSize := 0;
                  if AStartMeasureIndex = -1 then
                    ASaveStartMeasureIndex := 0
                  else
                  begin
                    ASaveStartMeasureIndex := AStartMeasureIndex;
                    AStartMeasureIndex := -1;
                  end;
// need draw all measures
                  for AMeasureIndex := ASaveStartMeasureIndex to FSlice.FMeasuresContainer.Count - 1 do
                  begin
                    if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
                      if (FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][0].State and stDimCollapsed) = 0 then
                      begin
                        ADRec.CellProperties := [pca_Simple, pca_LastLevel];
                        for i := -1 to CountAdditionalTotalFunctionsInLevel[ADLevel] - 1 do
                        begin
                          if AStartAdditionalTotalIndex <= i then
                          begin
                            DrawAxisCellWMInternalMeasure(ADLevel, ADRealLevel, ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex, AMeasureIndex, i);
                            inc(ACellsSize);
                            if ATerminate then
                              break;
                            if ([pca_Collapsed, pca_GCollapsed] * ACellProperties) <> [] then
                              Break;
                          end;
                        end;
                        AStartAdditionalTotalIndex := -2;
                        if ATerminate then
// check on end by stopper
                        begin
                          ANeedExit := True;
                          Break;
                        end;
                      end;
                  end;
                  ADRec.CellProperties := ACellProperties - [pca_LastLevel];
                  DrawAxisCellWMInternalCellLong(ADLevel, ADRealLevel, ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex);
                end;
              2:
                begin
                  ANeedExit := False;
                  if AStartMeasureIndex = -1 then
                    ASaveStartMeasureIndex := 0
                  else
                  begin
                    ASaveStartMeasureIndex := AStartMeasureIndex;
                    AStartMeasureIndex := -1;
                  end;
                  for AMeasureIndex := ASaveStartMeasureIndex to FSlice.FMeasuresContainer.Count - 1 do
                  begin
                    if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
                      if (FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][0].State and stDimCollapsed) = 0 then
                      begin
                        if ACanDrawCell then
                        begin
                          ACellsSize := 0;
                          ADRec.CellProperties := [pca_Simple, pca_LastLevel];//ACellProperties;
                          for i := -1 to CountAdditionalTotalFunctionsInLevel[ADLevel] - 1 do
                          begin
                            if AStartAdditionalTotalIndex <= i then
                            begin
                              DrawAxisCellWMInternalMeasure(ADLevel, ADRealLevel, ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex, AMeasureIndex, i);
                              inc(ACellsSize);
                              if ATerminate then
                                Break;
                            end;
                            if ([pca_Collapsed, pca_GCollapsed] * ACellProperties) <> [] then
                              Break;
                          end;
                          AStartAdditionalTotalIndex := -2;
                          ADRec.CellProperties := ACellProperties - [pca_LastLevel];
                          DrawAxisCellWMInternalCellLong(ADLevel, ADRealLevel, ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex, AMeasureIndex);
                        end;

                        DrawAxisCellWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, AMeasureIndex);
// Cell can draw if last level or next level was processed (-1,-2)
                        if ((ADLevel < (FLevelCount - 1)) and (AArrayStartIndexInLevel[ADLevel + 1] < 0)) then
                          ACanDrawCell := True
                        else
// else call children draw only
                          ACanDrawCell := False;
// check on end by stopper
                        if ATerminate then
                        begin
                          ANeedExit := True;
                          Break;
                        end;
                      end;
                  end;
                end;
            else
              if ANeedDrawChild then
// process children
                DrawAxisCellWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, ADMeasureIndex);
            end;
            if ATerminate then
// if stop flag, then not need draw children
              ANeedDrawChild := False;
            if ANeedDrawChild and not ((FMeasuresLevel = (ADLevel+1+AIncLevelForType2)) and (FMeasuresLevel <> FLevelCount)) then
// process children
              DrawAxisCellWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, -1);

            if ATerminate then
              Exit;
            if ANeedExit then
              Exit;
          end;
        end;

      end
      else
      if FMeasuresLevel < ADRealLevel then // under measures
      begin
        AMeasureIndex := ADMeasureIndex;

// iterate all children, call DrawAxisCell for each visible
// draw with type rules
// iterate and process cell children
        AAscOrder := Fields[FAxisTree[ASTNode.FLevel + 1].RegionFieldIndex].SortDirection = fcsd_Asc; // sort array
        ACountChild := FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
// process children
        for ANodeIndex := 0 to ACountChild - 1 do
        begin
// get index by sort
          if AAscOrder then
            AIndexWithOrd := ANodeIndex
          else
            AIndexWithOrd := ACountChild - ANodeIndex - 1;
          if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel + 1].FPropSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel + 1].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible then
// visible cell.
          begin
            if AArrayStartIndexInLevel[ADLevel] <> -1 then
            begin
// no start flag, find start cell
              if AArrayStartIndexInLevel[ADLevel] = ASTNode.Nodes[AIndexWithOrd].FIndex then
              begin
// start index equal, set start flag
                AArrayStartIndexInLevel[ADLevel] := -1;
                if (ADLevel < (FRealLevelCount - 2)) then
//not last level
                  if (AArrayStartIndexInLevel[ADLevel+1] = -2) then
// and row of this cell (start index on next level -2)
// set started flag on level under
                    for i := ADLevel + 1 to FLevelCount - 1 do
                      AArrayStartIndexInLevel[i] := -1;
              end
              else
// stat index is not equal, skip child
                continue;
            end;
// determine type cell and need process children
            ACellProperties := [];
// default
// process children
            ANeedDrawChild := True;
// on all levels under
            ACellDrawType := 1;

// call children draw, if need
            if ADRealLevel < (FRealLevelCount - 1) then
            begin
// not last level
              if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
              begin
// group level (not last level)
                if (FAxisTree[ADLevel + 1].LevelType = fcATLT_SubGroup) then
                begin
// has subgroup level (group level (not last level))
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                  begin
// group value (has subgroup level (group level (not last level)))
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stGExpanded) = 0) then
                    begin
// group collapsed (group value (has subgroup level (group level (not last level))))
                      if ADRealLevel = (FRealLevelCount - 2) then
                      begin
// prevlast level (group collapsed (group value (has subgroup level (group level (not last level)))))
// collapsed group of last level-1, after it there is nothing
                        ANeedDrawChild := False;
// draw self on all levels under
                        ACellProperties := [pca_GCollapsed, pca_LastLevel];
                      end
                      else
                      if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                      begin
// expanded cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// expanded cell of collapsed group.
// process children
// and draw self on all levels under
                        ACellProperties := [pca_GCollapsed, pca_Expanded, pca_LastLevel];
                        if (AArrayStartIndexInLevel[ADLevel+2] = -2) then
// set started flag on level under
                          for i := ADLevel + 1 to FLevelCount - 1 do
                            AArrayStartIndexInLevel[i] := -1;
                      end
                      else
                      begin
// collapsed cell (not beforelast level (group collapsed (group value (has subgroup level (group level (not last level))))))
// collapsed cell of collapsed group.
                        ANeedDrawChild := False;
// draw self on all levels under
                        ACellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                      end
                    end
                    else
                    begin
// group expanded (group value (has subgroup level (group level (not last level))))
// expanded group.
// process children
// draw self on all levels under
                      ACellProperties := [pca_GExpanded, pca_LastLevel];
                    end
                  end
                  else
                  begin
// single - single (has subgroup level (group level (not last level)))
                    if ADRealLevel = (FRealLevelCount - 2) then
                    begin
// prevlast level (single - single (has subgroup level (group level (not last level))))
// cell of single and after it there is nothing
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_LastLevel];
                    end
                    else
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// expanded cell of single.
// process children
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_Expanded, pca_LastLevel];
                      if (AArrayStartIndexInLevel[ADLevel+2] = -2) then
// set started flag on level under
                        for i := ADLevel + 1 to FLevelCount - 1 do
                          AArrayStartIndexInLevel[i] := -1;
                    end
                    else
                    begin
// collapsed cell (not beforelast level (single - single (has subgroup level (group level (not last level)))))
// collapsed cell of single.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                    end;
                    if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                      ACellProperties := ADRec.CellProperties + [pca_Other];
                  end
                end
                else
                begin
// no sublevel of group (group level (not last level))
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
                  begin
// group value (no sublevel of group (group level (not last level)))
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (group value (no sublevel of group (group level (not last level))))
// expanded cell of collapsed group.
// process children
// draw self on all levels under
                      ACellProperties := [pca_GCollapsed, pca_Expanded, pca_LastLevel];
                    end
                    else
                    begin
// collapsed cell (group value (no sublevel of group (group level (not last level))))
// collapsed cell of collapsed group.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GCollapsed, pca_Collapsed, pca_LastLevel];
                    end
                  end
                  else
                  begin
// single (no sublevel of group (group level (not last level)))
                    if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                    begin
// expanded cell (single (no sublevel of group (group level (not last level))))
// expanded cell of single.
// process children
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_Expanded, pca_LastLevel];
                    end
                    else
                    begin
// collapsed cell (single (no sublevel of group (group level (not last level))))
// collapsed cell of single.
                      ANeedDrawChild := False;
// draw self on all levels under
                      ACellProperties := [pca_GSingle, pca_Collapsed, pca_LastLevel];
                    end;
                    if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                      ACellProperties := ADRec.CellProperties + [pca_Other];
                  end
                end
              end
              else
              if (FAxisTree[ADLevel].LevelType = fcATLT_SubGroup) then
              begin
//subgroup level (not last level)
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_EmptyValue then
                begin
// continuation of collapsed group (subgroup level (not last level))
// continuation of single (subgroup level (not last level))
// process children
// not draw self
                  ACellDrawType := 0;
                end
                else
                begin
// cell of detail of expanded group (subgroup level (not last level))
                  if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
                  begin
// expanded cell (cell of detail of expanded group (subgroup level (not last level)))
// expanded cell of detail of expanded group.
// process children
// draw self on all levels under
                    ACellProperties := [pca_Simple, pca_Expanded, pca_LastLevel];
                  end
                  else
                  begin
// collapsed cell (cell of detail of expanded group (subgroup level (not last level)))
// collapsed cell of detail of expanded group.
                    ANeedDrawChild := False;
// draw self on all levels under
                    ACellProperties := [pca_Simple, pca_Collapsed, pca_LastLevel];
                  end;
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ACellProperties := ADRec.CellProperties + [pca_Other];
                end
              end
              else
// simple level (not last level)
              if ((CellPropertiesByNodeSelf(ASTNode.Nodes[AIndexWithOrd].FIndex, ADLevel, AMeasureIndex).State and stDimCollapsed) = 0) then
              begin
// expanded cell (simple level (not last level))
// expanded cell.
// process children
// draw self on all levels under
                ACellProperties := [pca_Expanded, pca_LastLevel];
              end
              else
              begin
// collapsed cell (simple level (not last level))
// collapsed cell.
                ANeedDrawChild := False;
// draw self on all levels under
                ACellProperties := [pca_Collapsed, pca_LastLevel];
              end
            end
            else
            begin
// last level.
// draw self on all levels under
              ANeedDrawChild := False;
// determine type
              if (FAxisTree[ADLevel].LevelType = fcATLT_HasGroup) then
// group level (last level)
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_GroupValue then
// group value (group level (last level))
                  ACellProperties := [pca_GCollapsed, pca_LastLevel]
                else
                begin
// single (group level (last level))
                  ACellProperties := [pca_GSingle, pca_LastLevel];
                  if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                    ACellProperties := ADRec.CellProperties + [pca_Other];
                end
              else
              begin
// simple cell (not group level (last level))
                ACellProperties := [pca_Simple, pca_LastLevel];
                if FAxisTree[ADLevel].Nodes[ASTNode.Nodes[AIndexWithOrd].FIndex].SSelfNode.NodeType = fcATNT_OtherValue then
                  ACellProperties := ADRec.CellProperties + [pca_Other];
              end;
            end;
            if not ANeedDrawChild then
// set started flag on next level (because not process children)
              for i := ADLevel + 1 to FLevelCount - 1 do
                AArrayStartIndexInLevel[i] := -1;

            if (ADLevel = (FRealLevelCount - 2)) or (AArrayStartIndexInLevel[ADLevel + 1] < 0) then
// Cell can draw if it in last row or was full start
              ACanDrawCell := True
            else
// else call children draw only
              ACanDrawCell := False;
            if ACanDrawCell then
// draw
              case ACellDrawType of
                1:
                  begin
                    ADRec.CellProperties := ACellProperties;
                    ACellsSize := 0;
                    for i := -1 to CountAdditionalTotalFunctionsInLevel[ADLevel] - 1 do
                    begin
                      if AStartAdditionalTotalIndex <= i then
                      begin
                        DrawAxisCellWMInternalLevelLong(ADLevel, ADRealLevel, ADLevel, ASTNode.Nodes[AIndexWithOrd].FIndex, AMeasureIndex, i);
                        inc(ACellsSize);
                        if ATerminate then
                          break;
                      end;
                      if ([pca_Collapsed, pca_GCollapsed] * ACellProperties) <> [] then
                        Break;
                      if ADLevel = (FLevelCount - 1) then
                        Break;
                    end;
                    AStartAdditionalTotalIndex := -2;
                  end;
              end;
            if ATerminate then
// if stop flag, then not need draw children
              ANeedDrawChild := False;
            if ANeedDrawChild then
// process children
              DrawAxisCellWM(ASTNode.Nodes[AIndexWithOrd], ADLevel + 1, ADRealLevel + 1, AMeasureIndex);
            if ATerminate then
              Exit;
          end;
        end;
      end;
    end;

  var
    ADLevel, ADRealLevel, AMeasureIndex: TfcxSmallCount;
    i: integer;
    ANeedExit: Boolean;
    ASaveStartMeasureIndex: TfcxSmallCount;
    AHasNonCollapsedMeasures: Boolean;
  begin
// grand total
// check on start
    ADLevel := -1;
    ADRealLevel := 0;
    AHasNonCollapsedMeasures := False;
    if FLevelCount > 0 then
      for AMeasureIndex := 0 to FSlice.FMeasuresContainer.Count - 1 do
        if FMeasuresLevel = 0 then
        begin
          if (FAxisSourceTree.FSourceTreeLevels[-1].FPropMeasures[AMeasureIndex][0].State and stDimCollapsed) = 0 then
          begin
            AHasNonCollapsedMeasures := True;
            Break;
          end;
        end
        else
        begin
          if (FAxisSourceTree.FSourceTreeLevels[-1].FPropTotalSelf[AMeasureIndex][0].State and stDimCollapsed) = 0 then
          begin
            AHasNonCollapsedMeasures := True;
            Break;
          end;
        end;
    if FMeasuresLevel = 0 then
    begin
// measures on level 0
// iterate measures from started
      ASaveStartMeasureIndex := AStartMeasureIndex;
// check on start
      if (FLevelCount > 0) and (AArrayStartIndexInLevel[ADLevel+1] = -2) then
      begin
// if level not last and row of this cell (of grand total) (start index on next level -2)
// set started flag on level under
        for i := 0 to FLevelCount - 1 do
          AArrayStartIndexInLevel[i] := -1;
        AStartMeasureIndex := -1;
      end;
      for AMeasureIndex := ASaveStartMeasureIndex to FSlice.FMeasuresContainer.Count - 1 do
      begin
        if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
        begin
// draw and process children
          if FLevelCount = 0 then
          begin
// no fields in axis (measures only)
            if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
            begin
// draw grand total
              ADRec.CellProperties := [pca_GrandTotal, pca_LastLevel];
              for i := -1 to -1 do
//              for i := -1 to CountAdditionalTotalFunctionsInLevel[-1] - 1 do
                if AStartAdditionalTotalIndex <= i then
                begin
                  DrawAxisCellWMInternalMeasure(0, ADRealLevel, ASTNode.FLevel, ASTNode.FIndex, AMeasureIndex, i);
                  if ATerminate then
                    break;
                end;
              AStartAdditionalTotalIndex := -2;
            end;
          end
          else
          begin
// exists other fields in axis
            if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
            begin
              if AArrayStartIndexInLevel[ADLevel+1] < 0 then
              begin
// row of this cell (index in next level -2) or next level alredy started (-1)
// draw grand total
// determine type
                if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = stDimCollapsed then
                  ADRec.CellProperties := [pca_Collapsed, pca_LastLevel]
                else
                  ADRec.CellProperties := [pca_Expanded, pca_LastLevel];
                ACellsSize := 0;
                for i := -1 to CountAdditionalTotalFunctionsInLevel[-1] - 1 do
                begin
                  if AStartAdditionalTotalIndex <= i then
                  begin
                    DrawAxisCellWMInternalMeasure(0, ADRealLevel, ASTNode.FLevel, ASTNode.FIndex, AMeasureIndex, i);
                    inc(ACellsSize);
                    if ATerminate then
                      break;
                  end;
                  if pca_Collapsed in ADRec.CellProperties then
                    break;
                end;
                AStartAdditionalTotalIndex := -2;
                ADRec.CellProperties := [pca_GrandTotal];
                DrawAxisCellWMInternalCellLong(0, ADRealLevel, ASTNode.FLevel, ASTNode.FIndex, AMeasureIndex);
              end;
              if ATerminate then
// check on end by stopper
                Exit;
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropMeasures[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = 0 then
// process children
                DrawAxisCellWM(ASTNode, ADLevel + 1, ADRealLevel + 1, AMeasureIndex);
            end;
          end;
          if ATerminate then
// check on end by stopper
            Exit;
        end;
      end;
    end
    else
    begin
// measures under level 0
// iterate measures from started
      ASaveStartMeasureIndex := AStartMeasureIndex;
// check on start
      if (AArrayStartIndexInLevel[ADLevel+1] = -2) then
      begin
// if row of this cell (of grand total) (start index in next level -2)
// set started flag on level under
        for i := 0 to FLevelCount - 1 do
          AArrayStartIndexInLevel[i] := -1;
        AStartMeasureIndex := -1;
// graw grand total
        ACellsSize := 0;
        ANeedExit := False;
        for AMeasureIndex := ASaveStartMeasureIndex to FSlice.FMeasuresContainer.Count - 1 do
        begin
          if FSlice.FMeasuresContainer.FMeasureFields[AMeasureIndex].Visible then
          begin
// draw and process children
            if ((FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stVisible) = stVisible) then
            begin
              if (FAxisSourceTree.FSourceTreeLevels[ASTNode.FLevel].FPropTotalSelf[AMeasureIndex][FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].SSelfNode.IndexInProp].State and stDimCollapsed) = stDimCollapsed then
                ADRec.CellProperties := [pca_GrandTotal, pca_Collapsed, pca_LastLevel]
              else
                ADRec.CellProperties := [pca_GrandTotal, pca_Expanded, pca_LastLevel];
              for i := -1 to CountAdditionalTotalFunctionsInLevel[-1] - 1 do
              begin
                if AStartAdditionalTotalIndex <= i then
                begin
                  DrawAxisCellWMInternalMeasure(0, ADRealLevel, ASTNode.FLevel, ASTNode.FIndex, AMeasureIndex, i);
                  inc(ACellsSize);
                  if ATerminate then
                    break;
                end;
                if pca_Collapsed in ADRec.CellProperties then
                  break;
              end;
              AStartAdditionalTotalIndex := -2;
              if ATerminate then
// check on end by stopper
              begin
                ANeedExit := True;
                Break;
              end;
            end;
            if ATerminate then
// check on end by stopper
            begin
              ANeedExit := True;
              Break;
            end;
          end;
        end;
        if ACellsSize > 0 then
        begin
          ADRec.CellProperties := [pca_GrandTotal];
          DrawAxisCellWMInternalCellLong(0, ADRealLevel, ASTNode.FLevel, ASTNode.FIndex);
        end;
        if ANeedExit then
          Exit;
      end;
      if AHasNonCollapsedMeasures then
        DrawAxisCellWM(ASTNode, ADLevel + 1, ADRealLevel, -1);
    end;
  end;

begin
  if (AStartCell < 0) or (AStartCell > (FVisibleAxisNodes.FCount - 1)) then
    Exit;
  AVisibleLevelCount := VisibleLevelCount;
  AXAxis := FSlice.FXAxisContainer = Self;
  FMeasuresLevel := FAxisSourceTree.FMeasuresLevel;
  if AXAxis then
    ATypeSort := FSlice.FYAxisContainer.FDefaultTypeSort
  else
    ATypeSort := FSlice.FXAxisContainer.FDefaultTypeSort;

  GetMem(AArrayStartIndexInLevel, (FLevelCount + 1) * SizeOf(Integer));
  ACellsPos.Pos := 0;
  ACellsPos.RealPos := 0;

// need get coorinates of start visible cell for each level
  AArrayStartIndexInLevel[FLevelOf[FVisibleAxisNodes.FIndexes[AStartCell]]] := FIndexInLevel[FVisibleAxisNodes.FIndexes[AStartCell]];
  for i := FLevelCount - 1 downto FLevelOf[FVisibleAxisNodes.FIndexes[AStartCell]] + 1 do
    AArrayStartIndexInLevel[i] := -2; // insignificant end
  for i := FLevelOf[FVisibleAxisNodes.FIndexes[AStartCell]] - 1 downto -1 do
    AArrayStartIndexInLevel[i] := FAxisTree[i+1].nodes[AArrayStartIndexInLevel[i+1]].IndParent;
  AArrayStartIndexInLevel[-1] := -1; // grand total always start
  AStartAdditionalTotalIndex := FVisibleAxisNodes.FAdditionalTotalsIndexes[AStartCell];

  ATerminate := False;
  if FMeasuresLevel = -1 then
  begin
    DrawAxisCellStart(FAxisSortTree.FRootNode);
  end
  else
  begin
    AStartMeasureIndex := FVisibleAxisNodes.FMeasureIndexes[AStartCell];
    DrawAxisCellWMStart(FAxisSortTree.FRootNode);
  end;

  FreeMem(AArrayStartIndexInLevel);
end;

procedure TfcxAxisContainer.SetCellsSize(const Value: Smallint);
var
  ALevelIndex, i, AIndexInProp: integer;
begin
  if FSlice.FXAxisContainer = Self then
  begin
    FSlice.StartChange;
    if Value = 0 then
      for ALevelIndex := -1 to FLevelCount - 1 do
      begin
        for i := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FSizeCount - 1 do
          FillChar(FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[i]^, FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), 0);
        for i := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount - 1 do
          FillChar(FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[i]^, FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), 0);
      end
    else
      for ALevelIndex := -1 to FLevelCount - 1 do
      begin
        for i := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FSizeCount - 1 do
          for AIndexInProp := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1  do
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FWidths[i][AIndexInProp] := Value;
        for i := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount - 1 do
          for AIndexInProp := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1  do
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths[i][AIndexInProp] := Value;
      end;
    FSlice.StopChange([chs_ColWidth])
  end
  else
  begin
    FSlice.StartChange;
    if Value = 0 then
      for ALevelIndex := -1 to FLevelCount - 1 do
      begin
        for i := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FSizeCount - 1 do
          FillChar(FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[i]^, FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), 0);
        for i := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount - 1 do
          FillChar(FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[i]^, FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), 0);
      end
    else
      for ALevelIndex := -1 to FLevelCount - 1 do
      begin
        for i := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FSizeCount - 1 do
          for AIndexInProp := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1  do
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FHeights[i][AIndexInProp] := Value;
        for i := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount - 1 do
          for AIndexInProp := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1  do
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights[i][AIndexInProp] := Value;
      end;
    FSlice.StopChange([chs_RowHeight]);
  end;
end;

function TfcxAxisContainer.GetVisibleLevelCollapsed(
  AVisLevelIndex: TfcxSmallCount): Boolean;
begin
  if (FMeasuresLevel = -1) or (AVisLevelIndex < FMeasuresLevel) then
    if FAxisTree[AVisLevelIndex].LevelType <> fcATLT_SubGroup then
      Result := Fields[FAxisTree[AVisLevelIndex].RegionFieldIndex].FSliceField.Collapsed
    else
      Result := Fields[FAxisTree[AVisLevelIndex].RegionFieldIndex].FSliceField.CollapsedSubGroup
  else
  if AVisLevelIndex = FMeasuresLevel then
    Result := FSlice.FMeasuresContainer.Collapsed
  else
    if FAxisTree[AVisLevelIndex - 1].LevelType <> fcATLT_SubGroup then
      Result := Fields[FAxisTree[AVisLevelIndex - 1].RegionFieldIndex].FSliceField.Collapsed
    else
      Result := Fields[FAxisTree[AVisLevelIndex - 1].RegionFieldIndex].FSliceField.CollapsedSubGroup;
end;

procedure TfcxAxisContainer.SetVisibleLevelCollapsed(AVisLevelIndex: TfcxSmallCount;
  const Value: Boolean);
begin
  SetExpandedLevel(AVisLevelIndex, not Value)
end;

function TfcxAxisContainer.GetVisibleLevelCollapsedReal(
  AVisLevelIndex: TfcxSmallCount): Boolean;
begin
  if (FMeasuresLevel = -1) or (AVisLevelIndex < FMeasuresLevel) then
    Result := (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FCanCollapseNodeMultiplier > 0) and (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FCollapsedNodeCount = (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FCanCollapseNodeMultiplier * FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FGoodNodeCount))
  else
  if AVisLevelIndex = FMeasuresLevel then
    Result := False // bung
  else
    Result := (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FCanCollapseNodeMultiplier > 0) and (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FCollapsedNodeCount = (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FCanCollapseNodeMultiplier * FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FGoodNodeCount))
end;

procedure TfcxAxisContainer.CheckVisibleLevelCollapsedReal(
  AVisLevelIndex: TfcxSmallCount);
begin
  if (FMeasuresLevel = -1) or (AVisLevelIndex < FMeasuresLevel) then
    if (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FCanCollapseNodeMultiplier > 0) then
      if FAxisTree[AVisLevelIndex].LevelType <> fcATLT_SubGroup then
        Fields[FAxisTree[AVisLevelIndex].RegionFieldIndex].FSliceField.FCollapsed := (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FCollapsedNodeCount = (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FCanCollapseNodeMultiplier * FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FGoodNodeCount))
      else
        Fields[FAxisTree[AVisLevelIndex].RegionFieldIndex].FSliceField.CollapsedSubGroup := (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FCollapsedNodeCount = (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FCanCollapseNodeMultiplier * FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex].FGoodNodeCount))
    else
    // skip
  else
  if AVisLevelIndex = FMeasuresLevel then
    // bung FSlice.FMeasuresContainer.Collapsed
  else
    if (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FCanCollapseNodeMultiplier > 0) then
      if FAxisTree[AVisLevelIndex - 1].LevelType <> fcATLT_SubGroup then
        Fields[FAxisTree[AVisLevelIndex - 1].RegionFieldIndex].FSliceField.Collapsed := (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FCollapsedNodeCount = (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FCanCollapseNodeMultiplier * FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FGoodNodeCount))
      else
        Fields[FAxisTree[AVisLevelIndex - 1].RegionFieldIndex].FSliceField.CollapsedSubGroup := (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FCollapsedNodeCount = (FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FCanCollapseNodeMultiplier * FAxisSourceTree.FSourceTreeLevels[AVisLevelIndex - 1].FGoodNodeCount))
    else
    // skip
end;

procedure TfcxAxisContainer.SetExpandedLevelInternal(
  ALevelIndex: TfcxSmallCount; AMeasureLevel: Boolean; const Value: Boolean);
var
  AIndex: integer;
  AMeasureIndex: TfcxSmallCount;
begin
  if AMeasureLevel then
  begin
// measures level
    FSlice.StartChange;
    if Value then
      for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasuresCount - 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[AMeasureIndex][AIndex].State :=
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[AMeasureIndex][AIndex].State
            and not stDimCollapsed
    else
      for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasuresCount - 1 do
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[AMeasureIndex][AIndex].State :=
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropMeasures[AMeasureIndex][AIndex].State
            or stDimCollapsed;
    if FSlice.XAxisContainer = Self then
      FSlice.StopChange([chs_ExpandXNode])
    else
      FSlice.StopChange([chs_ExpandYNode]);
// ?? need set flag of collasing level
  end
  else
  begin
    if ALevelIndex = -1 then
    begin
// grand total
      if FLevelCount > 0 then
      begin
        FSlice.StartChange;
        if Value then
          for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
            for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AMeasureIndex][AIndex].State :=
                FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AMeasureIndex][AIndex].State
                and not stDimCollapsed
        else
          for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
            for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AMeasureIndex][AIndex].State :=
                FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AMeasureIndex][AIndex].State
                or stDimCollapsed;
        if FSlice.XAxisContainer = Self then
          FSlice.StopChange([chs_ExpandXNode])
        else
          FSlice.StopChange([chs_ExpandYNode]);
      end
    end
    else
    begin
      FSlice.StartChange;
      if Value then
      begin
        for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
          for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelfCount - 1 do
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AMeasureIndex][AIndex].State :=
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AMeasureIndex][AIndex].State
              and not stDimCollapsed;
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount := 0;
        CheckVisibleLevelCollapsedReal(ALevelIndex);
        if (FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelType = fcATLT_SubGroup) or
           ((FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelType = fcATLT_HasGroup) and
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FGSubLevelVisible
           )
           then
        begin
          if FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelType = fcATLT_SubGroup then
            dec(ALevelIndex)
          else
            inc(ALevelIndex);
          for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
            for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelfCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AMeasureIndex][AIndex].State :=
                FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AMeasureIndex][AIndex].State
                and not stDimCollapsed;
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount := 0;
          CheckVisibleLevelCollapsedReal(ALevelIndex);
        end;
      end
      else
      begin
        for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
          for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelfCount - 1 do
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AMeasureIndex][AIndex].State :=
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AMeasureIndex][AIndex].State
              or stDimCollapsed;
        FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FGoodNodeCount * FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier;
        CheckVisibleLevelCollapsedReal(ALevelIndex);
        if (FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelType = fcATLT_SubGroup) or
           ((FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelType = fcATLT_HasGroup) and
            FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FGSubLevelVisible
           )
           then
        begin
          if FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FLevelType = fcATLT_SubGroup then
            dec(ALevelIndex)
          else
            inc(ALevelIndex);
          for AIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
            for AMeasureIndex := 0 to FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelfCount - 1 do
              FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AMeasureIndex][AIndex].State :=
                FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FPropSelf[AMeasureIndex][AIndex].State
                or stDimCollapsed;
          FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount := FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FGoodNodeCount * FAxisSourceTree.FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier;
          CheckVisibleLevelCollapsedReal(ALevelIndex);
        end;
      end;
      if FSlice.XAxisContainer = Self then
        FSlice.StopChange([chs_ExpandXNode])
      else
        FSlice.StopChange([chs_ExpandYNode]);
    end
  end;
end;

function TfcxAxisContainer.GetLevelOfRegionFieldAbs(
  ARegionFieldIndex: integer): TfcxSmallCount;
var
  ALevelIndex: TfcxSmallCount;
begin
  for ALevelIndex := 0 to FLevelCount - 1 do
    if FAxisTree[ALevelIndex].RegionFieldIndex = ARegionFieldIndex then
    begin
      Result := ALevelIndex;
      exit;
    end;
  Result := -1;
end;

function TfcxAxisContainer.CellIndexWithOffset(
  const AStartLevel, AOffsetLevel: TfcxSmallCount; const AIndexInLevel, AOffset: Integer): Integer;
var
  AWayArray, AWayArrayIndex: Array of Integer;
  AWayArrayType: Array of TfcxAxisTreeNodeType;
  ANodeType: TfcxAxisTreeNodeType;
  i, j, AIndexInLevel2, AUVIndex: integer;
  ANode: PfcxSTNode;
  AExists: Boolean;
begin
  Result := -1;
  if (AStartLevel < AOffsetLevel) or (AStartLevel < 0) or
     (AOffsetLevel < 0) or (AStartLevel > (FLevelCount - 1)) or (AOffsetLevel > (FLevelCount - 1))then
    Exit;
  if AOffset = 0 then
  begin
    Result := AIndexInLevel;
    Exit;
  end;
  SetLength(AWayArrayIndex, AStartLevel+1); // 0..AStartLevel
  SetLength(AWayArray, AStartLevel+1); // 0..AStartLevel
  SetLength(AWayArrayType, AStartLevel+1); // 0..AStartLevel
  AIndexInLevel2 := AIndexInLevel;
// collect way
  for i := AStartLevel downto 0 do
  begin
    AWayArrayType[i] := FAxisTree[i].nodes[AIndexInLevel2].SSelfNode.NodeType;
    if AWayArrayType[i] = fcATNT_OtherValue then
// other
      AWayArray[i] := -1
    else
      AWayArray[i] := FAxisTree[i].nodes[AIndexInLevel2].SSelfNode.Index;
    AWayArrayIndex[i] := AIndexInLevel2;
    AIndexInLevel2 := FAxisTree[i].nodes[AIndexInLevel2].IndParent;
  end;
// use FAxisSortTree !
  ANode := FAxisSortTree.FRootNode;
  for i := 0 to AStartLevel do
  begin
    AExists := False;
    if i <= AOffsetLevel then
      for j := 0 to (FAxisTree[i-1].nodes[ANode.FIndex].IndLast - FAxisTree[i-1].nodes[ANode.FIndex].IndFirst) do
      begin
// Check IndexInLevel
        if ANode.Nodes[j].FIndex = AWayArrayIndex[i] then
        begin
          if i = AOffsetLevel then
          begin
  // Do offset
            if ((AOffset + j) < 0) or ((AOffset + j) > (FAxisTree[i-1].nodes[ANode.FIndex].IndLast - FAxisTree[i-1].nodes[ANode.FIndex].IndFirst)) then
  // out of range
              Exit
            else
            begin
              ANode := ANode.Nodes[AOffset + j];
            end;
          end
          else
          begin
            ANode := ANode.Nodes[j];
          end;
          AExists := True;
          Break;
        end;
      end
    else
      for j := 0 to (FAxisTree[i-1].nodes[ANode.FIndex].IndLast - FAxisTree[i-1].nodes[ANode.FIndex].IndFirst) do
      begin
// Check UVIndex
        ANodeType := FAxisTree[i].nodes[ANode.Nodes[j].FIndex].SSelfNode.NodeType;
        if ANodeType = fcATNT_OtherValue then
// other
          AUVIndex := -1
        else
          AUVIndex := FAxisTree[i].nodes[ANode.Nodes[j].FIndex].SSelfNode.Index;
        if (ANodeType = AWayArrayType[i]) and (AUVIndex = AWayArray[i]) then
        begin
          ANode := ANode.Nodes[j];
          AExists := True;
          Break;
        end;
      end;
    if Not AExists then
      Exit;
  end;
  Result := ANode.FIndex;
end;

function TfcxAxisContainer.GetDisplayValueLI(ALevel: TfcxSmallCount;
  AIndexInLevel: Integer): TfcxString;
begin
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_EmptyValue then
      Result := ''
    else
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_GroupValue then
// group
      Result := Fields[FAxisSourceTree.FFieldIndexOfLevel[ALevel]].FSliceField.GroupCaption[FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.Index]
    else
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_OtherValue then
// other
      Result := fcxResources.GetAnsi('sOther')
    else
      Result := Fields[FAxisSourceTree.FFieldIndexOfLevel[ALevel]].FSliceField.UVCaption[FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.Index];
end;

function TfcxAxisContainer.GetExpressions: TfcxExpressions;
begin
  Result := nil;
  if (FAdditionalGrandTotalExpression.Text <> '') then
  begin
    SetLength(Result, 1);
    Result[0] := FAdditionalGrandTotalExpression;
  end;
end;

function TfcxAxisContainer.GetValueIDLI(ALevel: TfcxSmallCount;
  AIndexInLevel: Integer): integer;
begin
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_EmptyValue then
      Result := -1
    else
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_GroupValue then
// group
      Result := -FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.Index - 2
    else
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_OtherValue then
// other
      Result := -1
    else
      Result := FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.Index;
end;

function TfcxAxisContainer.GetValueLI(ALevel: TfcxSmallCount;
  AIndexInLevel: Integer): Variant;
begin
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_EmptyValue then
      Result := Unassigned
    else
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_GroupValue then
// group
      Result := Fields[FAxisSourceTree.FFieldIndexOfLevel[ALevel]].FSliceField.GroupCaption[FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.Index]
    else
    if FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.NodeType = fcATNT_OtherValue then
// other
      Result := fcxResources.GetAnsi('sOther')
    else
      Result := Fields[FAxisSourceTree.FFieldIndexOfLevel[ALevel]].FSliceField.UVCaption[FAxisTree[ALevel].nodes[AIndexInLevel].SSelfNode.Index];
end;

function TfcxAxisContainer.GetLevelInfoWOMeasures(
  ALevelIndex: TfcxSmallCount): TfcxAxisLevelInfo;
begin
  Result.IsVisible := FAxisTree[ALevelIndex].Visible;
  Result.IsMeasure := False;
  Result.RegionField := Fields[FAxisTree[ALevelIndex].RegionFieldIndex];
  Result.LevelType := FAxisTree[ALevelIndex].LevelType;
  Result.Level := ALevelIndex;
  if Result.LevelType = fcATLT_SubGroup then
    Result.SubLevel := 1
  else
    Result.SubLevel := 0;
end;

function TfcxAxisContainer.CellIndexWithOffsetOnValue(const AStartLevel,
  AOffsetLevel: TfcxSmallCount; const AIndexInLevel: Integer;
  AOffsetValue: Variant): Integer;
var
  AWayArray, AWayArrayIndex: Array of Integer;
  AWayArrayType: Array of TfcxAxisTreeNodeType;
  ANodeType: TfcxAxisTreeNodeType;
  i, j, AIndexInLevel2, AUVIndex: integer;
  AExists: Boolean;
begin
  Result := -1;
  if (AStartLevel < AOffsetLevel) or (AStartLevel < 0) or
     (AOffsetLevel < 0) or (AStartLevel > (FLevelCount - 1)) or (AOffsetLevel > (FLevelCount - 1))then
    Exit;
  if vartype(AOffsetValue) = varEmpty then
  begin
    Result := AIndexInLevel;
    Exit;
  end;
  SetLength(AWayArrayIndex, AStartLevel+1); // 0..AStartLevel
  SetLength(AWayArray, AStartLevel+1); // 0..AStartLevel
  SetLength(AWayArrayType, AStartLevel+1); // 0..AStartLevel
  AIndexInLevel2 := AIndexInLevel;
// collect way
  for i := AStartLevel downto AOffsetLevel do
  begin
    AWayArrayType[i] := FAxisTree[i].nodes[AIndexInLevel2].SSelfNode.NodeType;
    if AWayArrayType[i] = fcATNT_OtherValue then
// other
      AWayArray[i] := -1
    else
      AWayArray[i] := FAxisTree[i].nodes[AIndexInLevel2].SSelfNode.Index;
    AWayArrayIndex[i] := AIndexInLevel2;
    AIndexInLevel2 := FAxisTree[i].nodes[AIndexInLevel2].IndParent;
  end;
// use FAxisTree !

// find index in offset level
  AIndexInLevel2 := -1;
  for i := FAxisTree[AOffsetLevel-1].Nodes[FAxisTree[AOffsetLevel].nodes[AWayArrayIndex[AOffsetLevel]].IndParent].IndFirst to FAxisTree[AOffsetLevel-1].Nodes[FAxisTree[AOffsetLevel].nodes[AWayArrayIndex[AOffsetLevel]].IndParent].IndLast do
  begin
    if  GetValueLI(AOffsetLevel, i) = AOffsetValue then
    begin
      AIndexInLevel2 := i;
      Break;
    end
  end;
  if AIndexInLevel2 = -1 then
    Exit;
  for i := AOffsetLevel + 1 to AStartLevel do
  begin
    AExists := False;
    for j := FAxisTree[i-1].nodes[AIndexInLevel2].IndFirst to FAxisTree[i-1].nodes[AIndexInLevel2].IndLast do
    begin
// Check UVIndex
      ANodeType := FAxisTree[i].nodes[j].SSelfNode.NodeType;
      if ANodeType = fcATNT_OtherValue then
// other
        AUVIndex := -1
      else
        AUVIndex := FAxisTree[i].nodes[j].SSelfNode.Index;
      if (ANodeType = AWayArrayType[i]) and (AUVIndex = AWayArray[i]) then
      begin
        AIndexInLevel2 := j;
        AExists := True;
        Break;
      end;
    end;
    if Not AExists then
      Exit;
  end;
  Result := AIndexInLevel2;
end;

function TfcxAxisContainer.GetAsPlainText(AStartCell, AStopCell: Integer;
  ACloneHeaders: Boolean): TfcxString;
const
  AxisPlainTextBuilderClass: Array[Boolean] of TCustomAxisTextBuilderClass = (TYAxisPlainTextBuilder, TXAxisPlainTextBuilder);
begin
  with AxisPlainTextBuilderClass[Self = FSlice.XAxisContainer].Create(Self, AStartCell, AStopCell, ACloneHeaders) do
  begin
    try
      Result := BuildText;
    finally
      Free;
    end;
  end;
end;

function TfcxAxisContainer.AddDimension(ASliceField: TfcxSliceField; AName,
  ACaption: TfcxString): integer;
begin
  if not Assigned(ASliceField) then
  begin
    Result := -1;
    Exit;
  end;
  Result := Fields.Count;
  if not InsertField(ASliceField, Result, AName, ACaption) then
    Result := -1;
end;

function TfcxAxisContainer.DeleteDimension(AIndex: integer): boolean;
begin
  Result := RemoveFieldByIndex(AIndex);
end;

function TfcxAxisContainer.InsertDimension(ASliceField: TfcxSliceField;
  AIndex: integer; AName, ACaption: TfcxString): integer;
begin
  if InsertField(ASliceField, AIndex, AName, ACaption) then
    Result := AIndex
  else
    Result := -1;
end;

procedure TfcxAxisContainer.SetAdditionalGrandTotalScriptFunction(const Value: String);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangesInSlice =
  (
    [chs_None],
    [chs_CapXAxis, chs_CapMeasures],
    [chs_CapYAxis, chs_CapMeasures],
    [chs_None],
    [chs_None]
  );
begin
  if FAdditionalGrandTotalScriptFunction <> Value then
  begin
    FSlice.StartChange;
    FAdditionalGrandTotalScriptFunction := Value;
    FSlice.StopChange(cChanges[FRegion]);
  end;
end;

function TfcxAxisContainer.GetMeasuresLevelVisibleIndex: TfcxSmallCount;
begin
  Result := FMeasuresLevelVisibleIndex;
end;

function TfcxAxisContainer.GetMeasuresLevelVisibleIndexInternal: TfcxSmallCount;
var
  ALevelIndex: TfcxSmallCount;
begin
  if (FSlice.FMeasuresContainer.FContainer <> Self) then
  begin
    Result := -1;
    Exit;
  end;
  Result := 0;
  for ALevelIndex := 0 to FMeasuresLevel - 1 do
    if FAxisTree[ALevelIndex].Visible then
      inc(Result);
end;

function TfcxAxisContainer.GetIndexOfVisibleLevel(
  AVisLevelIndex: TfcxSmallCount): TfcxSmallCount;
begin
  if (FSlice.FMeasuresContainer.FContainer <> Self) then
  begin
    if FVisibleLevelCount > AVisLevelIndex then
      Result := AVisLevelIndex
    else
      Result := -1;
  end
  else
  begin
    if FMeasuresLevelVisibleIndex = AVisLevelIndex then
      Result := MeasuresLevel
    else
    if FMeasuresLevelVisibleIndex > AVisLevelIndex then
      Result := AVisLevelIndex
    else
      if FVisibleLevelCount > AVisLevelIndex then
        Result := AVisLevelIndex
      else
        Result := -1
  end;
end;

function TfcxAxisContainer.GetVisibleIndexOfLevel(
  AAbsLevelIndex: TfcxSmallCount): TfcxSmallCount;
begin
  if (FSlice.FMeasuresContainer.FContainer <> Self) then
  begin
    if not FAxisTree[AAbsLevelIndex].Visible then
      Result := -1
    else
      Result := AAbsLevelIndex
  end
  else
  begin
    if FMeasuresLevel = AAbsLevelIndex then
      Result := MeasuresLevelVisibleIndex
    else
    if FMeasuresLevel > AAbsLevelIndex then
      if not FAxisTree[AAbsLevelIndex].Visible then
        Result := -1
      else
        Result := AAbsLevelIndex
    else
      if not FAxisTree[AAbsLevelIndex - 1].Visible then
        Result := -1
      else
        Result := AAbsLevelIndex
  end;
end;

function TfcxAxisContainer.GetVisibleLevelInfo(
  AVisLevelIndex: TfcxSmallCount): TfcxAxisLevelInfo;
begin
  Result := LevelInfo[IndexOfVisibleLevel[AVisLevelIndex]]
end;

function TfcxAxisContainer.CellIndexWithValues(
  AWayValues: array of Variant): Integer;
var
  TargetLevel, WayIndex, WayIndex2, LevelIndex: integer;
  i: integer;
  VarStr: String;
begin
  Result := -1;
  TargetLevel := Length(AWayValues) - 1;
  if TargetLevel > (FLevelCount - 1) then
    Exit;
  WayIndex := 0;
  for LevelIndex := 0 to TargetLevel do
  begin
    WayIndex2 := -1;
    if VarType(AWayValues[LevelIndex]) = varOleStr then
      VarStr := TVarData(AWayValues[LevelIndex]).VOleStr;
    for i := FAxisTree[LevelIndex-1].Nodes[WayIndex].IndFirst to FAxisTree[LevelIndex-1].Nodes[WayIndex].IndLast do
    begin
      if VarType(AWayValues[LevelIndex]) = varOleStr then
      begin
        if GetValueLI(LevelIndex, i) = VarStr then
        begin
          WayIndex2 := i;
          Break;
        end
      end
      else
        if GetValueLI(LevelIndex, i) = AWayValues[LevelIndex] then
        begin
          WayIndex2 := i;
          Break;
        end
    end;
    if WayIndex2 = -1 then
      Exit;
    WayIndex := WayIndex2;
  end;
  Result := WayIndex;
end;

function TfcxAxisContainer.CellIndexWithOffsetThrough(
  const ALevel: TfcxSmallCount; const AIndexInLevel,
  AOffset: Integer): Integer;
var
  AWayArraySTNodes: Array of PfcxSTNode;
  AWayArraySTIndex, AOffsetArray: Array of Integer;

  AWayArray, AWayArrayIndex: Array of Integer;
  AWayArrayType: Array of TfcxAxisTreeNodeType;
  i, j, AIndexInLevel2: integer;
  ANode: PfcxSTNode;
  AExists: Boolean;
begin
  Result := -1;
  if (ALevel < 0) or (ALevel > (FLevelCount - 1))then
    Exit;
  if AOffset = 0 then
  begin
    Result := AIndexInLevel;
    Exit;
  end;
  SetLength(AWayArraySTNodes, ALevel+2); // 0..ALevel+1
  SetLength(AWayArraySTIndex, ALevel+2); // 0..ALevel+1
  SetLength(AOffsetArray, ALevel+2); // 0..ALevel+1


  SetLength(AWayArrayIndex, ALevel+1); // 0..ALevel
  SetLength(AWayArray, ALevel+1); // 0..ALevel
  SetLength(AWayArrayType, ALevel+1); // 0..ALevel
  AIndexInLevel2 := AIndexInLevel;
// collect way
  for i := ALevel downto 0 do
  begin
    AWayArrayType[i] := FAxisTree[i].nodes[AIndexInLevel2].SSelfNode.NodeType;
    if AWayArrayType[i] = fcATNT_OtherValue then
// other
      AWayArray[i] := -1
    else
      AWayArray[i] := FAxisTree[i].nodes[AIndexInLevel2].SSelfNode.Index;
    AWayArrayIndex[i] := AIndexInLevel2;
    AIndexInLevel2 := FAxisTree[i].nodes[AIndexInLevel2].IndParent;
    AOffsetArray[i] := 0;
  end;
  AOffsetArray[ALevel + 1] := AOffset;
// use FAxisSortTree !
  ANode := FAxisSortTree.FRootNode;
  AWayArraySTNodes[0] := ANode;
  AWayArraySTIndex[0] := 0;
  for i := 0 to ALevel do
  begin
    AExists := False;
    for j := 0 to (FAxisTree[i-1].nodes[ANode.FIndex].IndLast - FAxisTree[i-1].nodes[ANode.FIndex].IndFirst) do
    begin
// Check IndexInLevel
      if ANode.Nodes[j].FIndex = AWayArrayIndex[i] then
      begin
        ANode := ANode.Nodes[j];
        AWayArraySTIndex[i+1] := j;
        AWayArraySTNodes[i+1] := ANode;
        AExists := True;
        Break;
      end;
    end;
    if Not AExists then
      Exit;
  end;
  i := ALevel;
  while (i <= ALevel) and (i >= 0) do
  begin
    if (AOffsetArray[i+1] <> 0) and ((AOffsetArray[i+1] + AWayArraySTIndex[i+1]) < 0) or ((AOffsetArray[i+1] + AWayArraySTIndex[i+1]) > (FAxisTree[i-1].nodes[AWayArraySTNodes[i].FIndex].IndLast - FAxisTree[i-1].nodes[AWayArraySTNodes[i].FIndex].IndFirst)) then
    begin
// need offset on level ALevel - 1
      if AOffsetArray[i+1] < 0 then
      begin
        AOffsetArray[i] := {AOffsetArray[i]} - 1;
        AOffsetArray[i+1] := AOffsetArray[i+1] + AWayArraySTIndex[i+1] + 1;
      end
      else
      begin
        AOffsetArray[i] := AOffsetArray[i] + 1;
        AOffsetArray[i+1] := AOffsetArray[i+1] + (FAxisTree[i-1].nodes[AWayArraySTNodes[i].FIndex].IndLast - FAxisTree[i-1].nodes[AWayArraySTNodes[i].FIndex].IndFirst) - AWayArraySTIndex[i+1];
      end;
      dec(i);
    end
    else
    begin
      if i = ALevel then
      begin
// found
        Result := AWayArraySTNodes[i].Nodes[AOffsetArray[i+1] + AWayArraySTIndex[i+1]].FIndex;
        Exit;
      end
      else
      begin
// offset and go down
        if AOffsetArray[i+1] <> 0 then
        begin
          AWayArraySTIndex[i+1] := AOffsetArray[i+1] + AWayArraySTIndex[i+1];
          AWayArraySTNodes[i+1] := AWayArraySTNodes[i].Nodes[AWayArraySTIndex[i+1]];
          if AOffsetArray[i+1] < 0 then
            AWayArraySTIndex[i+2] := (FAxisTree[i].nodes[AWayArraySTNodes[i+1].FIndex].IndLast - FAxisTree[i].nodes[AWayArraySTNodes[i+1].FIndex].IndFirst)
          else
            AWayArraySTIndex[i+2] := 0;
          AWayArraySTNodes[i+2] := AWayArraySTNodes[i+1].Nodes[AWayArraySTIndex[i+2]];         // AWayArraySTNodes[i].Nodes[AWayArraySTIndex[i+2]];
        end;
        inc(i);
      end
    end
  end;
end;

{ TfcxAxisField }

function TfcxAxisField.ByEventCompare(ANode1,
  ANode2: PfcxSTNode): Integer;
begin
{ TODO -cNeed : Make}
  Result := InternalCompare(ANode1, ANode2);
end;

function TfcxAxisField.ByScriptCompare(ANode1,
  ANode2: PfcxSTNode): Integer;
begin
{ TODO -cNeed : Make}
  Result := InternalCompare(ANode1, ANode2);
end;

constructor TfcxAxisField.Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion; ASliceField: TfcxSliceField; AName, ACaption: TfcxString);
begin
  inherited Create(AFieldsOfRegion, AName, ACaption);
  FSliceField := ASliceField;
  // TODO: assign DisplayFormat
  FSTNodeCompare := InternalCompare;
  FSortDirection := FSliceField.FSlice.Settings.AxisFieldSettings.SortDirection;
  FTotalPosition := FSliceField.FSlice.Settings.AxisFieldSettings.TotalPosition;
  FUseTotalPositionFromMeasure := False;
  FGroupMemberField := False;
  FAdditionalTotalFunctions := [];
  FCountAdditionalTotalFunctions := 0;
  FAdditionalTotalScriptFunction := '';
  FAdditionalTotalExpression.UseFields := False;
  if (Owner.FContainer = Owner.FContainer.FSlice.YAxisContainer) and Owner.FContainer.FSlice.SyncronizeYFieldsWidth then
    CaptionWidth := Width;
end;

constructor TfcxAxisField.Create(AOwner: TfcxCommonFieldsOfRegion);
begin
  inherited;
  FTopNProcessor := TfcxTopNProcessor.Create(Self);
  FAdditionalTotalExpression.UseFields := False;
end;

destructor TfcxAxisField.Destroy;
begin
  FreeAndNil(FTopNProcessor);
  inherited;
end;

function TfcxAxisField.GetAdditionalTotalFunction(
  AAdditionalTotalIndex: TfcxSmallCount): TfcxAgrFunc;
var
  AAgrFunc: TfcxAgrFunc;
begin
  for AAgrFunc := Low(TfcxAgrFunc) to High(TfcxAgrFunc) do
  begin
    if AAgrFunc in FAdditionalTotalFunctions then
      dec(AAdditionalTotalIndex);
    if AAdditionalTotalIndex < 0 then
    begin
      Result := AAgrFunc;
      Exit;
    end;
  end;
  Result := af_None;
end;

function TfcxAxisField.GetCanGroup: boolean;
begin
  Result := FSliceField.CanGroup;
end;

function TfcxAxisField.GetDisplayFormat: TfcxFormat;
begin
//  if FFormat = nil then
    Result := FSliceField.DisplayFormat
//  else
//    Result := FFormat;
end;

function TfcxAxisField.GetAdditionalTotalExpression: String;
begin
  Result := FAdditionalTotalExpression.Text;
end;

procedure TfcxAxisField.SetAdditionalTotalExpression(const AValue: String);
begin
  if FAdditionalTotalExpression.Text <> AValue then
  begin
    FAdditionalTotalExpression.Text := AValue;
    ExpressionChanged;
  end;
end;

function TfcxAxisField.GetGroupCaption(AGroupIndex: integer): TfcxString;
begin
  Result := FSliceField.GroupCaption[AGroupIndex];
end;

function TfcxAxisField.GetGroupCount: Integer;
begin
  Result := FSliceField.GroupCount;
end;

function TfcxAxisField.GetHasGroups: boolean;
begin
  Result := FSliceField.HasGroups;
end;

function TfcxAxisField.HasDisplayFormat: Boolean;
begin
  Result := FSliceField.HasDisplayFormat;
end;

function TfcxAxisField.GetExpressions: TfcxExpressions;
begin
  Result := nil;
  if (FAdditionalTotalExpression.Text <> '') then
  begin
    SetLength(Result, 1);
    Result[0] := FAdditionalTotalExpression;
  end;
end;

function TfcxAxisField.GetWidth: SmallInt;
begin
  Result := FSliceField.Width;
end;

procedure TfcxAxisField.SetWidth(const AValue: SmallInt);
begin
  FSliceField.Width := AValue;
end;

function TfcxAxisField.GetWidthSubGroup: SmallInt;
begin
  Result := FSliceField.WidthSubGroup;
end;

procedure TfcxAxisField.SetWidthSubGroup(const AValue: SmallInt);
begin
  FSliceField.WidthSubGroup := AValue;
end;

function TfcxAxisField.GetHeight: SmallInt;
begin
  Result := FSliceField.Height;
end;

procedure TfcxAxisField.SetHeight(const AValue: SmallInt);
begin
  FSliceField.Height := AValue;
end;

function TfcxAxisField.GetHeightSubGroup: SmallInt;
begin
  Result := FSliceField.HeightSubGroup;
end;

procedure TfcxAxisField.SetHeightSubGroup(const AValue: SmallInt);
begin
  FSliceField.HeightSubGroup := AValue;
end;

class function TfcxAxisField.InternalCompare(ANode1,
  ANode2: PfcxSTNode): Integer;
begin
  if ANode1.FIndex = ANode2.FIndex then
    Result := 0
  else
    Result := fcxCompareHelper[ANode1.FIndex > ANode2.FIndex];
end;

function TfcxAxisField.LoadFromXML(AItem: TfcxXMLItem): boolean;
var
  i, j: integer;
  AAdditionalTotalFunctions: TfcxSetAgrFunc;
begin
  Result := False;
  FSliceField := FOwner.Container.Slice.SliceFields.ItemByName[AItem.Prop['field_name']];
  if FSliceField = nil then
    exit;
  Result := inherited LoadFromXML(AItem);
  FSortDirection := TfcxSortDirection(GetEnumValue(TypeInfo(TfcxSortDirection), AItem.Prop['SortDirection']));
  FTotalPosition := TfcxTotalPosition(GetEnumValue(TypeInfo(TfcxTotalPosition), AItem.Prop['TotalPosition']));
  FUseTotalPositionFromMeasure := AItem.BoolProp['UseTotalPositionFromMeasure'];
  AdditionalTotalScriptFunction := AItem.Prop['AdditionalTotalScriptFunction'];
  AdditionalTotalExpression := AItem.Prop['AdditionalExpressionTotal'];
  for i := 0 to AItem.Count - 1 do
    if AItem[i].Name = 'AdditionalTotals' then
    begin
      AAdditionalTotalFunctions := [];
      for j := 0 to AItem[i].Count - 1 do
        AAdditionalTotalFunctions := AAdditionalTotalFunctions + [TfcxAgrFunc(GetEnumValue(TypeInfo(TfcxAgrFunc), AItem[i][j].Prop['AgrFunc']))];
      AdditionalTotalFunctions := AAdditionalTotalFunctions;
      Break;
    end
end;

function TfcxAxisField.MeasureValueCompare(ANode1,
  ANode2: PfcxSTNode): Integer;
begin
{ TODO -cNeed : Make}
  Result := InternalCompare(ANode1, ANode2);
end;

procedure TfcxAxisField.SaveToXML(AItem: TfcxXMLItem);
var
  AItem1, AItem2: TfcxXMLItem;
  i: integer;
begin
  inherited SaveToXML(AItem);
  AItem.Prop['field_name'] := SliceField.FieldName;
  AItem.Prop['SortDirection'] := GetEnumName(TypeInfo(TfcxSortDirection), Ord(SortDirection));
  AItem.Prop['TotalPosition'] := GetEnumName(TypeInfo(TfcxTotalPosition), Ord(TotalPosition));
  AItem.BoolProp['UseTotalPositionFromMeasure'] := UseTotalPositionFromMeasure;
// AdditionalTotals
  AItem1 := AItem.Add;
  AItem1.Name := 'AdditionalTotals';
  AItem.Prop['AdditionalExpressionTotal'] := AdditionalTotalExpression;
  AItem1.Prop['AdditionalTotalScriptFunction'] := AdditionalTotalScriptFunction;
  for i := 0 to FCountAdditionalTotalFunctions - 1 do
  begin
    AItem2 := AItem1.Add;
    AItem2.Name := 'Total';
    AItem2.Prop['AgrFunc'] := GetEnumName(TypeInfo(TfcxAgrFunc), Ord(AdditionalTotalFunction[i]));
  end;
end;

procedure TfcxAxisField.SetAdditionalTotalFunctions(
  const Value: TfcxSetAgrFunc);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_None,
    chs_CapXAxis,
    chs_CapYAxis,
    chs_None,
    chs_None
  );
var
  AAgrFunc: TfcxAgrFunc;
begin
  if FAdditionalTotalFunctions <> Value then
  begin
    Owner.FContainer.FSlice.StartChange;
    FAdditionalTotalFunctions := Value;
    FCountAdditionalTotalFunctions := 0;
    for AAgrFunc := Low(TfcxAgrFunc) to High(TfcxAgrFunc) do
      if AAgrFunc in FAdditionalTotalFunctions then
        inc(FCountAdditionalTotalFunctions);
    Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion], chs_CapMeasures]);
  end;
end;

procedure TfcxAxisField.SetAdditionalTotalScriptFunction(const Value: String);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_None,
    chs_CapXAxis,
    chs_CapYAxis,
    chs_None,
    chs_None
  );
begin
  if FAdditionalTotalScriptFunction <> Value then
  begin
    Owner.FContainer.FSlice.StartChange;
    FAdditionalTotalScriptFunction := Value;
    Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion], chs_CapMeasures]);
  end;
end;

procedure TfcxAxisField.SetDisplayFormat(const Value: TfcxFormat);
begin
  if Assigned(FSliceField) then
  begin
    FSliceField.DisplayFormat := Value;
    Changed;
  end
(*
  if DisplayFormat.Equal(Value) then
    exit;
  if FFormat = nil then
  begin
    FFormat := TfcxFormat.Create;
    FFormat.Assign(Value);
    FFormat.OnChange := DoFormatChange;
    Changed;
  end
  else
  begin
    if FSliceField.DisplayFormat.Equal(Value) then
    begin
      FFormat.Free;
      FFormat := nil;
      Changed;
    end
    else
    begin
      FFormat.Assign(Value);
    end
  end
*)
end;

procedure TfcxAxisField.SetSortDirection(const Value: TfcxSortDirection);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_None,
    chs_XSortDirection,
    chs_YSortDirection,
    chs_None,
    chs_None
  );
begin
  if FSortDirection <> Value then
  begin
    Owner.FContainer.FSlice.StartChange;
    FSortDirection := Value;
    Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion]]);
  end;
end;

procedure TfcxAxisField.SetTotalPosition(const Value: TfcxTotalPosition);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_None,
    chs_XTotalPosition,
    chs_YTotalPosition,
    chs_None,
    chs_None
  );
begin
  if FTotalPosition <> Value then
  begin
    Owner.FContainer.FSlice.StartChange;
    FTotalPosition := Value;
    Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion]]);
  end;
end;

procedure TfcxAxisField.SetUseTotalPositionFromMeasure(
  const Value: Boolean);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_None,
    chs_XTotalPosition,
    chs_YTotalPosition,
    chs_None,
    chs_None
  );
begin
  if FUseTotalPositionFromMeasure <> Value then
  begin
    Owner.FContainer.FSlice.StartChange;
    FUseTotalPositionFromMeasure := Value;
    Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion]]);
  end;
end;

{ TfcxMeasuresContainer }

procedure TfcxMeasuresContainer.Clear;
var
  i, j: Integer;
  AMeasureIndex, ABaseLevel, ASecondLevel: TfcxSmallCount;
begin
  if FIsCleared then
    Exit;
  for AMeasureIndex := FSaveCountMeasures - 1 downto 0 do
    ClearMeasure(AMeasureIndex);
  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
    for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
      for i := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
      begin
        if not FLevelsProp[ABaseLevel][ASecondLevel][i].Simple then
          FreeMem(FLevelsProp[ABaseLevel][ASecondLevel][i].IndexList);
        FreeMem(FLevelsProp[ABaseLevel][ASecondLevel][i].CellDetailInfo);
      end;
  if FMeasureCellDetailRecordsTemp <> nil then
  begin
    for i := 0 to FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count - 1 do
    begin
      for j := 0 to FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels - 1][i].Count - 1 do
      begin
        FreeMem(FMeasureCellDetailRecordsTemp[i][j].Records);
      end;
      FreeMem(FMeasureCellDetailRecordsTemp[i]);
    end;
    FreeMem(FMeasureCellDetailRecordsTemp);
    FMeasureCellDetailRecordsTemp := nil;
  end;

  if FBaseDetailRecords <> nil then
  begin
    for i := 0 to FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count - 1 do
      FreeMem(FBaseDetailRecords[i].Records);
    FreeMem(FBaseDetailRecords);
  end;
  if FSecondDetailRecords <> nil then
  begin
    for i := 0 to FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].Count - 1 do
      FreeMem(FSecondDetailRecords[i].Records);
    FreeMem(FSecondDetailRecords);
  end;

  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
  begin
    for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
      FreeMem(FLevelsProp[ABaseLevel][ASecondLevel]);
    FreeMem(FLevelsProp[ABaseLevel]);
  end;
  FreeMem(FLevelsProp);
  FLevelsProp := nil;

  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
    FreeMem(FSaveAdditionalTotalsBaseLevels[ABaseLevel]);
  for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
    FreeMem(FSaveAdditionalTotalsSecondLevels[ASecondLevel]);
  FreeMem(FSaveAdditionalTotalsBaseLevels);
  FreeMem(FSaveAdditionalTotalsSecondLevels);
  FSaveAdditionalTotalsBaseLevels := nil;
  FSaveAdditionalTotalsSecondLevels := nil;

  FreeMem(FSaveCountAdditionalTotalsBaseLevels);
  FreeMem(FSaveCountAdditionalTotalsSecondLevels);
  FSaveCountAdditionalTotalsBaseLevels := nil;
  FSaveCountAdditionalTotalsSecondLevels := nil;

  FCount := 0;
  FSecondCount := 0;
  FSaveCountMeasures := 0;
  FSaveCountBaseLevels := 0;
  FSaveCountSecondLevels := 0;
  FIsCleared := True;
end;

constructor TfcxMeasuresContainer.Create(ASlice: TfcxSlice; ARegion: TfcxRegionOfField);
begin
  inherited;
  FIsCleared := True;
  FFinalized := True;
//  FSlice := ASlice;
//  FRegion := rf_CapFacts;
  FBaseAxisIsX := True;
  FLevelsProp := nil;
  FMeasureCellDetailRecordsTemp := nil;
  FBaseDetailRecords := nil;
  FSecondDetailRecords := nil;
  FCount := 0;
  FSecondCount := 0;
  FContainer := FSlice.FPageContainer;
  FPosition := 0;
  FMeasureFields := TfcxMeasureFields.Create(Self);
  FSaveCountMeasures := 0;
  FSaveCountBaseLevels := 0;
  FSaveCountSecondLevels := 0;
  FSaveCountAdditionalTotalsBaseLevels := nil;
  FSaveCountAdditionalTotalsSecondLevels := nil;
  FSaveAdditionalTotalsBaseLevels := nil;
  FSaveAdditionalTotalsSecondLevels := nil;
  FCaption := fcxResources.GetAnsi('sMeasures');
end;

destructor TfcxMeasuresContainer.Destroy;
begin
  Clear;
  FreeAndNil(FMeasureFields);
  inherited;
end;

function TfcxMeasuresContainer.FindByIndex(const ABaseLevel, ASecondLevel: TfcxSmallCount;
  const AIndexInBaseLevel, AIndexInSecondLevel: Integer; var AResIndex: Integer): Boolean;
var
  ALeft, ARight, AMiddle: Integer;
  ALevelInCellList: _fcxLevelInCellList;
begin
  ALevelInCellList := FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel];
  Result := False;
  if ALevelInCellList.Count = 0 then
  begin
    AResIndex := -1;
    exit;
  end;
  ALeft := 0;
  ARight := ALevelInCellList.Count - 1;
  if ALevelInCellList.IndexList[ARight] < AIndexInSecondLevel then
  begin
    AResIndex := -1;//ARight + 1;
    exit;
  end
  else
  if ALevelInCellList.IndexList[ARight] = AIndexInSecondLevel then
  begin
    AResIndex := ARight;
    Result := True;
    exit;
  end;
  if ALevelInCellList.IndexList[ALeft] > AIndexInSecondLevel then
  begin
    AResIndex := -1;//ALeft;
    exit;
  end
  else
  if ALevelInCellList.IndexList[ALeft] = AIndexInSecondLevel then
  begin
    AResIndex := ALeft;
    Result := True;
    exit;
  end;
  while ALeft <= ARight do
  begin
    AMiddle := (ALeft + ARight) shr 1; // middle
    if ALevelInCellList.IndexList[AMiddle] = AIndexInSecondLevel then
    begin
      // Found
      Result := True;
      AResIndex := AMiddle;
      Exit;
    end
    else
    if ALevelInCellList.IndexList[AMiddle] > AIndexInSecondLevel then
      ARight := AMiddle - 1
    else
      ALeft := AMiddle + 1;
  end;
  AResIndex := -1//ALeft;
end;

function TfcxMeasuresContainer.GetCount: TfcxSmallCount;
begin
  Result := FMeasureFields.Count;
end;

function TfcxMeasuresContainer.AddMeasure(ASliceField: TfcxSliceField; AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc): Integer;
begin
  Result := AddMeasureInternal(TfcxMeasureField.Create(FMeasureFields, AAgrFunc, ASliceField, AName, ACaption), True);
end;

function TfcxMeasuresContainer.AddMeasure(AField: TfcxMeasureField): Integer;
begin
  Result := AddMeasureInternal(AField, True);
end;

function TfcxMeasuresContainer.AddMeasure(ASliceField, ASliceFieldDistinct,
  ASliceFieldExtra: TfcxSliceField; AName, ACaption: TfcxString;
  AAgrFunc: TfcxAgrFunc): Integer;
begin
  Result := AddMeasureInternal(TfcxMeasureField.Create(FMeasureFields, AAgrFunc, ASliceField, ASliceFieldDistinct, ASliceFieldExtra, AName, ACaption), True);
end;

procedure TfcxMeasuresContainer.CreateMatrix;
var
  AExistsCreateAllCells: boolean;

  procedure CreateCellsStep1;
    procedure ProcessTotals(ARight: Boolean; ABaseLevel1, ASecondLevel1: TfcxSmallCount; AIndexInBaseLevel1, AIndexInSecondLevel1: Integer);
    begin
// ARight: False - from down, True - from right
//go from cell to left and to up
      with FLevelsProp[ABaseLevel1][ASecondLevel1][AIndexInBaseLevel1] do
        if (PfcxByteArray(IndexList)[AIndexInSecondLevel1 shr 3] and BitmapSet[AIndexInSecondLevel1 mod 8]) = 0 then
        begin
          PfcxByteArray(IndexList)[AIndexInSecondLevel1 shr 3] :=
            PfcxByteArray(IndexList)[AIndexInSecondLevel1 shr 3] or BitmapSet[AIndexInSecondLevel1 mod 8];
          inc(Count);
          if ARight then
          begin
  //if from Right then go to left and to up
  // up
            if not FBaseContainer.FNoFields then
              ProcessTotals(False, ABaseLevel1 - 1, ASecondLevel1, FBaseContainer.FAxisTree[ABaseLevel1].Nodes[AIndexInBaseLevel1].IndParent, AIndexInSecondLevel1);
  // left
            if ASecondLevel1 = -1 then
              Exit;
            if not FSecondContainer.FNoFields then
              ProcessTotals(True, ABaseLevel1, ASecondLevel1 - 1, AIndexInBaseLevel1, FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel1].IndParent);
          end
          else
          begin
  //if from down then go to up
  // up
            if ABaseLevel1 = -1 then
              Exit;
            if not FBaseContainer.FNoFields then
              ProcessTotals(False, ABaseLevel1 - 1, ASecondLevel1, FBaseContainer.FAxisTree[ABaseLevel1].Nodes[AIndexInBaseLevel1].IndParent, AIndexInSecondLevel1);
          end;
        end;
    end;

  var
    ABaseLevel, ASecondLevel: TfcxSmallCount;
    AGoodRecIndex: Integer;
    AIndexInBaseLevel, AIndexInSecondLevel: Integer;
    ASimple, AIsLastLevels: Boolean;
    AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount;
  begin
// first step: calc counts and create primary arrays (allocate memory)
// create temp array
    for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
      for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
        if FSecondContainer.FAxisTree[ASecondLevel].Count > 0 then
          for AIndexInBaseLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
            with FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel] do
            begin
              GetMem(IndexList, SizeOf(_fcxByteArray) * ((FSecondContainer.FAxisTree[ASecondLevel].Count - 1) shr 3 + 1));
              FillChar(IndexList^, SizeOf(_fcxByteArray) * ((FSecondContainer.FAxisTree[ASecondLevel].Count - 1) shr 3 + 1), 0);
            end;
// calc count of cells
    ABaseLevel := FSaveCountBaseLevels - 1;
    ASecondLevel := FSaveCountSecondLevels - 1;
    for AGoodRecIndex := 0 to FSaveGoodRecCount - 1 do
    begin
      AIndexInBaseLevel := FBaseContainer.FCoordinate[FBaseContainer.FAxisSourceTree.FSourceOrderIndexes[AGoodRecIndex]];
      AIndexInSecondLevel := FSecondContainer.FCoordinate[FSecondContainer.FAxisSourceTree.FSourceOrderIndexes[AGoodRecIndex]];
      with FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel] do
      begin
        if (PfcxByteArray(IndexList)[AIndexInSecondLevel shr 3] and BitmapSet[AIndexInSecondLevel mod 8]) = 0 then
        begin
          PfcxByteArray(IndexList)[AIndexInSecondLevel shr 3] :=
            PfcxByteArray(IndexList)[AIndexInSecondLevel shr 3] or BitmapSet[AIndexInSecondLevel mod 8];
          inc(Count);
// [rus] calc counts in total
// ARight: False - from down, True - from right
// to up
          if not FBaseContainer.FNoFields then
            ProcessTotals(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, AIndexInSecondLevel);
// to left
          if not FSecondContainer.FNoFields then
            ProcessTotals(True, ABaseLevel, ASecondLevel - 1, AIndexInBaseLevel, FSecondContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent);
        end;

      end;
    end;
// create primary arrays
    for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
      for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
      begin
        AIsLastLevels := (ASecondLevel = (FSaveCountSecondLevels - 1)) and (ABaseLevel = (FSaveCountBaseLevels - 1));
        for AIndexInBaseLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
          with FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel] do
          begin
  // free temp array
            FreeMem(IndexList);
            IndexList := nil;
            ASimple := ((ABaseLevel = -1) and (ASecondLevel = -1)) or
              ((Count + 1) > (FSecondContainer.FAxisTree[ASecondLevel].Count shr 1));
            Simple := ASimple;
            if ASimple then
            begin
              Count := FSecondContainer.FAxisTree[ASecondLevel].Count;
            end
            else
            begin
              GetMem(IndexList, Count * SizeOf(_fcxIntegerArray));
              FillChar(IndexList^, Count * SizeOf(_fcxIntegerArray), $FF);
            end;
            Capacity := Count;
            FreeIndex := 0;
            if AIsLastLevels then
            begin
              GetMem(FMeasureCellDetailRecordsTemp[AIndexInBaseLevel], Count * SizeOf(TfcxMeasureCellDetailRecords));
              FillChar(FMeasureCellDetailRecordsTemp[AIndexInBaseLevel][0], Count * SizeOf(TfcxMeasureCellDetailRecords), 0);
            end;

            for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
              with FMeasureFields[AMeasureIndex] do
                if FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                begin
                  GetMem(PfcxArrPointerArray(FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel], FSecondContainer.FAxisTree[ASecondLevel].Count * SizeOf(_fcxPointerArray));
                  FillChar(PfcxArrPointerArray(FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel]^, FSecondContainer.FAxisTree[ASecondLevel].Count * SizeOf(_fcxPointerArray), 0);
                end
                else
                begin
                  GetMem(PfcxArrPointerArray(FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel], Count * SizeOf(_fcxPointerArray));
                  FillChar(PfcxArrPointerArray(FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel]^, Count * SizeOf(_fcxPointerArray), 0);
                end;

            if ABaseLevel <> (FSaveCountBaseLevels - 1) then
              for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
                for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
                  with FMeasureFields[AMeasureIndex] do
                    if FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                    begin
                      GetMem(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel], FSecondContainer.FAxisTree[ASecondLevel].Count * SizeOf(_fcxPointerArray));
                      FillChar(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel]^, FSecondContainer.FAxisTree[ASecondLevel].Count * SizeOf(_fcxPointerArray), 0);
                    end
                    else
                    begin
                      GetMem(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel], Count * SizeOf(_fcxPointerArray));
                      FillChar(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel]^, Count * SizeOf(_fcxPointerArray), 0);
                    end;

            if ASecondLevel <> (FSaveCountSecondLevels - 1) then
              for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
                for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
                  with FMeasureFields[AMeasureIndex] do
                    if FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                    begin
                      GetMem(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel], FSecondContainer.FAxisTree[ASecondLevel].Count * SizeOf(_fcxPointerArray));
                      FillChar(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel]^, FSecondContainer.FAxisTree[ASecondLevel].Count * SizeOf(_fcxPointerArray), 0);
                    end
                    else
                    begin
                      GetMem(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel], Count * SizeOf(_fcxPointerArray));
                      FillChar(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel]^, Count * SizeOf(_fcxPointerArray), 0);
                    end;

          end;
      end;
  end;

  procedure CreateCellsStep2;
    function AddCellsStep2(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer;
      var AResSecondIndex: Integer): boolean;

      function FindByIndexStep2(const ABaseLevel, ASecondLevel: TfcxSmallCount;
        const AIndexInBaseLevel, AIndexInSecondLevel: Integer; var AResIndex: Integer): Boolean;
      var
        ALeft, ARight, AMiddle: Integer;
      begin
        Result := False;
        with FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel] do
        begin
          if Count = 0 then
          begin
            AResIndex := -1;
            exit;
          end;
          if FreeIndex = 0 then
          begin
            AResIndex := 0;
            exit;
          end;
          ALeft := 0;
          ARight := FreeIndex - 1;
          if IndexList[ARight] < AIndexInSecondLevel then
          begin
            AResIndex := ARight + 1;
            exit;
          end;
          if IndexList[ARight] = AIndexInSecondLevel then
          begin
            AResIndex := ARight;
            Result := True;
            exit;
          end;
          if IndexList[ALeft] > AIndexInSecondLevel then
          begin
            AResIndex := ALeft;
            exit;
          end;
          if IndexList[ALeft] = AIndexInSecondLevel then
          begin
            AResIndex := ALeft;
            Result := True;
            exit;
          end;

          while ALeft <= ARight do
          begin
            AMiddle := (ALeft + ARight) shr 1; // middle
            if (IndexList[AMiddle] > AIndexInSecondLevel) then
              ARight := AMiddle - 1
            else
            if (IndexList[AMiddle] < AIndexInSecondLevel) then
              ALeft := AMiddle + 1
            else
            begin
              // Found
              Result := True;
              AResIndex := AMiddle;
              Exit;
            end
          end;
        end;
        AResIndex := ALeft;
      end;

      procedure CreateCellStep2(const ABaseLevel, ASecondLevel: TfcxSmallCount;
        const AIndexInBaseLevel, AResSecondIndex, AIndexInSecondLevel: Integer);
      var
        AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount;
        ANeedMove: Boolean;
        AMoveCount: integer;
      begin
        with FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel] do
        begin
          ANeedMove := AResSecondIndex <> FreeIndex;
          AMoveCount := (FreeIndex - AResSecondIndex);
          for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
            with FMeasureFields[AMeasureIndex] do
              if not FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
              begin
                if ANeedMove then
                  System.Move(PfcxArrPointerArray(FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AResSecondIndex], PfcxArrPointerArray(FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AResSecondIndex + 1],
                     AMoveCount * SizeOf(_fcxPointerArray));
                PfcxArrPointerArray(FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AResSecondIndex] :=
                  FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.NewMeasureValue(FMeasureFields[AMeasureIndex]);
              end;
          if ABaseLevel <> (FSaveCountBaseLevels - 1) then
            for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
              for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
                with FMeasureFields[AMeasureIndex] do
                  if not FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                  begin
                    if ANeedMove then
                      System.Move(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AResSecondIndex], PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AResSecondIndex + 1],
                        AMoveCount * SizeOf(_fcxPointerArray));
                    PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AResSecondIndex] :=
                      cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex]].NewMeasureValue(FMeasureFields[AMeasureIndex]);
                  end;
          if ASecondLevel <> (FSaveCountSecondLevels - 1) then
            for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
              for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
                with FMeasureFields[AMeasureIndex] do
                  if not FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                  begin
                    if ANeedMove then
                      System.Move(PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AResSecondIndex], PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AResSecondIndex + 1],
                        AMoveCount * SizeOf(_fcxPointerArray));
                    PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AResSecondIndex] :=
                      cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex]].NewMeasureValue(FMeasureFields[AMeasureIndex]);
                  end;
          if ANeedMove then
            System.Move(IndexList[AResSecondIndex], IndexList[AResSecondIndex + 1],
              AMoveCount * SizeOf(_fcxIntegerArray));
          IndexList[AResSecondIndex] := AIndexInSecondLevel;
          if (ASecondLevel = (FSaveCountSecondLevels - 1)) and (ABaseLevel = (FSaveCountBaseLevels - 1)) then
          begin
            if ANeedMove then
              System.Move(FMeasureCellDetailRecordsTemp[AIndexInBaseLevel][AResSecondIndex], FMeasureCellDetailRecordsTemp[AIndexInBaseLevel][AResSecondIndex + 1],
                AMoveCount * SizeOf(TfcxMeasureCellDetailRecords));
            with FMeasureCellDetailRecordsTemp[AIndexInBaseLevel][AResSecondIndex] do
            begin
              Count := 0;
              Records := nil;
            end;
          end;
          FreeIndex := FreeIndex + 1;
        end;
      end;


    var
      AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount;
      ANotCreateAllCellsMeasureIndex: TfcxSmallCount;
    begin
      Result := False;
      AResSecondIndex := AIndexInSecondLevel;
      if not FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple then
      begin
        if not FindByIndexStep2(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AResSecondIndex) then
        begin
          if AResSecondIndex = -1 then
          begin
            Result := False;
          end
          else
          begin
            CreateCellStep2(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AResSecondIndex, AIndexInSecondLevel);
            Result := True;
          end;
        end;
      end
      else
      begin
        if FSaveCountMeasures = 0 then
          Result := True
        else
        begin
          ANotCreateAllCellsMeasureIndex := -1;
          for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
            if not FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
            begin
              ANotCreateAllCellsMeasureIndex := AMeasureIndex;
              Break;
            end;

          if (ANotCreateAllCellsMeasureIndex <> -1) and (PfcxArrPointerArray(FMeasureFields[ANotCreateAllCellsMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AResSecondIndex] = nil) then
          begin
// create Cell if need
            for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
              with FMeasureFields[AMeasureIndex] do
                if not FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                  PfcxArrPointerArray(FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AResSecondIndex] :=
                    FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.NewMeasureValue(FMeasureFields[AMeasureIndex]);
            if ABaseLevel <> (FSaveCountBaseLevels - 1) then
              for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
                for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
                  with FMeasureFields[AMeasureIndex] do
                    if not FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                      PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AResSecondIndex] :=
                        cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex]].NewMeasureValue(FMeasureFields[AMeasureIndex]);
            if ASecondLevel <> (FSaveCountSecondLevels - 1) then
              for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
                for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
                  with FMeasureFields[AMeasureIndex] do
                    if not FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                      PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AResSecondIndex] :=
                        cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex]].NewMeasureValue(FMeasureFields[AMeasureIndex]);
            if (ASecondLevel = (FSaveCountSecondLevels - 1)) and (ABaseLevel = (FSaveCountBaseLevels - 1)) then
              with FMeasureCellDetailRecordsTemp[AIndexInBaseLevel][AResSecondIndex] do
              begin
                Count := 0;
                Records := nil;
              end;
            Result := True;
          end;
        end
      end;
    end;

    procedure ProcessAdding(ARight: Boolean; ABaseLevel1, ASecondLevel1: TfcxSmallCount; AIndexInBaseLevel1, AIndexInSecondLevel1: Integer);
    var
      AResSecondIndex: integer;
    begin
// ARight: False - from down, True - from right
//go from cell to left and to up
      if not AddCellsStep2(ABaseLevel1, ASecondLevel1, AIndexInBaseLevel1, AIndexInSecondLevel1, AResSecondIndex) then
// stop if Cell not created
        exit;
      if ARight then
      begin
//if from Right then go to left and to up
// up
        if not FBaseContainer.FNoFields then
          ProcessAdding(False, ABaseLevel1 - 1, ASecondLevel1, FBaseContainer.FAxisTree[ABaseLevel1].Nodes[AIndexInBaseLevel1].IndParent, AIndexInSecondLevel1);
// left
        if ASecondLevel1 = -1 then
          Exit;
        if not FSecondContainer.FNoFields then
          ProcessAdding(True, ABaseLevel1, ASecondLevel1 - 1, AIndexInBaseLevel1, FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel1].IndParent);
      end
      else
      begin
//if from down then go to up
// up
        if ABaseLevel1 = -1 then
          Exit;
        if not FBaseContainer.FNoFields then
          ProcessAdding(False, ABaseLevel1 - 1, ASecondLevel1, FBaseContainer.FAxisTree[ABaseLevel1].Nodes[AIndexInBaseLevel1].IndParent, AIndexInSecondLevel1);
      end;
    end;

  var
    ABaseLevel, ASecondLevel, AMeasureIndex: TfcxSmallCount;
    ABaseLevel2, ASecondLevel2: TfcxSmallCount;
    AAdditionalTotalIndex: TfcxSmallCount;
    AGoodRecIndex: Integer;
    AIndexInBaseLevel, AIndexInSecondLevel: Integer;
    AResSecondIndex: integer;
  begin
// step 2: fill primary arrays, create cells
    if AExistsCreateAllCells then
    begin
      for ABaseLevel2 := -1 to FSaveCountBaseLevels - 1 do
        for ASecondLevel2 := -1 to FSaveCountSecondLevels - 1 do
        begin
          for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
            with FMeasureFields[AMeasureIndex] do
              if FMeasurePropInLevelsListArray[ABaseLevel2][ASecondLevel2].CreateAllCells then
              begin
                for AIndexInBaseLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel2].Count - 1 do
                begin
                  for AIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel2].Count - 1 do
                    PfcxArrPointerArray(FMeasureCells[ABaseLevel2][ASecondLevel2])[AIndexInBaseLevel][AIndexInSecondLevel] :=
                      FMeasurePropInLevelsListArray[ABaseLevel2][ASecondLevel2].MeasureProcessorClass.NewMeasureValue(FMeasureFields[AMeasureIndex]);
                  if ABaseLevel2 <> (FSaveCountBaseLevels - 1) then
                    for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel2] - 1 do
                    begin
                      for AIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel2].Count - 1 do
                        PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel2][ASecondLevel2])[AAdditionalTotalIndex])[AIndexInBaseLevel][AIndexInSecondLevel] :=
                          cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevel2, AAdditionalTotalIndex]].NewMeasureValue(FMeasureFields[AMeasureIndex]);
                    end;
                  if ASecondLevel2 <> (FSaveCountSecondLevels - 1) then
                    for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel2] - 1 do
                    begin
                      for AIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel2].Count - 1 do
                        PfcxArrPointerArray(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel2][ASecondLevel2])[AAdditionalTotalIndex])[AIndexInBaseLevel][AIndexInSecondLevel] :=
                          cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevel2, AAdditionalTotalIndex]].NewMeasureValue(FMeasureFields[AMeasureIndex]);
                    end;
                end
              end
        end;
    end;

    ABaseLevel := FSaveCountBaseLevels - 1;
    ASecondLevel := FSaveCountSecondLevels - 1;
    for AGoodRecIndex := 0 to FSaveGoodRecCount - 1 do
    begin
      AIndexInBaseLevel := FBaseContainer.FCoordinate[FBaseContainer.FAxisSourceTree.FSourceOrderIndexes[AGoodRecIndex]];
      AIndexInSecondLevel := FSecondContainer.FCoordinate[FSecondContainer.FAxisSourceTree.FSourceOrderIndexes[AGoodRecIndex]];
// create cell if not exists
      if AddCellsStep2(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AResSecondIndex) then
      begin
        inc(FMeasureCellDetailRecordsTemp[AIndexInBaseLevel][AResSecondIndex].Count);
// process total cells
// ARight: False - from down, True - from right
// to up
        if not FBaseContainer.FNoFields then
          ProcessAdding(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, AIndexInSecondLevel);
// to left
        if not FSecondContainer.FNoFields then
          ProcessAdding(True, ABaseLevel, ASecondLevel - 1, AIndexInBaseLevel, FSecondContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent);
      end
      else
      begin
        if AResSecondIndex = -1 then
        begin
        end
        else
          inc(FMeasureCellDetailRecordsTemp[AIndexInBaseLevel][AResSecondIndex].Count);
      end
    end;
  end;

  procedure CreateCellsStep3;
  var
    ABaseLevel, ASecondLevel: TfcxSmallCount;
    AGoodRecIndex: Integer;
    AIndexInBaseLevel, AIndexInSecondLevel, AResSecondIndex: Integer;
  begin
// step 3: create and fill FMeasureCellDetailRecordsTemp

    ABaseLevel := FSaveCountBaseLevels - 1;
    ASecondLevel := FSaveCountSecondLevels - 1;
    for AGoodRecIndex := 0 to FSaveGoodRecCount - 1 do
    begin
      AIndexInBaseLevel := FBaseContainer.FCoordinate[FBaseContainer.FAxisSourceTree.FSourceOrderIndexes[AGoodRecIndex]];
      AIndexInSecondLevel := FSecondContainer.FCoordinate[FSecondContainer.FAxisSourceTree.FSourceOrderIndexes[AGoodRecIndex]];
      AResSecondIndex := AIndexInSecondLevel;
      if not FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple then
        if not FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AResSecondIndex) then
          AResSecondIndex := -1;
      if AResSecondIndex <> -1 then
      begin
        with FMeasureCellDetailRecordsTemp[AIndexInBaseLevel][AResSecondIndex] do
        begin
          if Records = nil then
          begin
            GetMem(Records, (Count + 1) * SizeOf(integer));
            Records[Count] := 0;
          end;
          Records[Records[Count]] := FSlice.FFilterManager.AbsIndex[FSlice.FTopNs.GoodIndex[AGoodRecIndex]];
          Records[Count] := Records[Count] + 1;
        end;
      end;
    end;
  end;
var
  AStep: Integer;
  AFuncsOfLevels: PfcxArrMinusPointerArrayMinus;
  APass: Integer;

  function Calculate_FormulaDetail(ABaseLevel1, ASecondLevel1: TfcxSmallCount;
    AIndexInBaseLevel1, AIndexInSecondLevel1, AGoodRecIndex: integer; AMeasureField: TfcxMeasureField; AValue: Variant; AFinal: Boolean; ADetailValue: Variant): Variant;
  var
    AMeasurePropInLevels: ^_fcxMeasurePropInLevels;
    ARealIndex1: integer;
  begin
    AMeasurePropInLevels := @(AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1]);
    if not AMeasurePropInLevels.ScriptError then
    begin
      if FLevelsProp[ABaseLevel1][ASecondLevel1][AIndexInBaseLevel1].Simple or (AMeasurePropInLevels.CreateAllCells) then
        ARealIndex1 := AIndexInSecondLevel1
      else
        FindByIndex(ABaseLevel1, ASecondLevel1, AIndexInBaseLevel1, AIndexInSecondLevel1, ARealIndex1);
      TfcxCustomObjectHack(FSlice.FCustomObject).FValue := PfcxMeasureValueWithPointer(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[AIndexInBaseLevel1][ARealIndex1]).PValue;
      if FBaseAxisIsX then
      begin
        TCommonScriptItems(FSlice.FMeasures).FXLevel := ABaseLevel1;
        TCommonScriptItems(FSlice.FMeasures).FYLevel := ASecondLevel1;
        TCommonScriptItems(FSlice.FMeasures).FIndexInXLevel := AIndexInBaseLevel1;
        TCommonScriptItems(FSlice.FMeasures).FIndexInYLevel := AIndexInSecondLevel1;
        TCommonScriptItems(FSlice.FMeasures).FTotalOnX := (ABaseLevel1 < (FBaseContainer.FLevelCount - 1));
        TCommonScriptItems(FSlice.FMeasures).FTotalOnY := (ASecondLevel1 < (FSecondContainer.FLevelCount - 1));
        TCommonScriptItems(FSlice.FMeasures).FCol := 0;//FBaseContainer.FVisibleAxisNodes.VisibleIndex[FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[AIndexInBaseLevel1], AMeasureField.Index, -1];
        TCommonScriptItems(FSlice.FMeasures).FRow := 0;//FSecondContainer.FVisibleAxisNodes.VisibleIndex[FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel1], AMeasureField.Index, -1];
        TCommonScriptItems(FSlice.FDimensions).FXLevel := ABaseLevel1;
        TCommonScriptItems(FSlice.FDimensions).FYLevel := ASecondLevel1;
        TCommonScriptItems(FSlice.FDimensions).FIndexInXLevel := AIndexInBaseLevel1;
        TCommonScriptItems(FSlice.FDimensions).FIndexInYLevel := AIndexInSecondLevel1;
        TCommonScriptItems(FSlice.FDimensions).FTotalOnX := (ABaseLevel1 < (FBaseContainer.FLevelCount - 1));
        TCommonScriptItems(FSlice.FDimensions).FTotalOnY := (ASecondLevel1 < (FSecondContainer.FLevelCount - 1));
        TCommonScriptItems(FSlice.FDimensions).FCol := 0;//FBaseContainer.FVisibleAxisNodes.VisibleIndex[FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[AIndexInBaseLevel1], AMeasureField.Index, -1];
        TCommonScriptItems(FSlice.FDimensions).FRow := 0;//FSecondContainer.FVisibleAxisNodes.VisibleIndex[FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel1], AMeasureField.Index, -1];
      end
      else
      begin
        TCommonScriptItems(FSlice.FMeasures).FYLevel := ABaseLevel1;
        TCommonScriptItems(FSlice.FMeasures).FXLevel := ASecondLevel1;
        TCommonScriptItems(FSlice.FMeasures).FIndexInYLevel := AIndexInBaseLevel1;
        TCommonScriptItems(FSlice.FMeasures).FIndexInXLevel := AIndexInSecondLevel1;
        TCommonScriptItems(FSlice.FMeasures).FTotalOnY := (ABaseLevel1 < (FBaseContainer.FLevelCount - 1));
        TCommonScriptItems(FSlice.FMeasures).FTotalOnX := (ASecondLevel1 < (FSecondContainer.FLevelCount - 1));
        TCommonScriptItems(FSlice.FMeasures).FRow := 0;//FBaseContainer.FVisibleAxisNodes.VisibleIndex[FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[AIndexInBaseLevel1], AMeasureField.Index, -1];
        TCommonScriptItems(FSlice.FMeasures).FCol := 0;//FSecondContainer.FVisibleAxisNodes.VisibleIndex[FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel1], AMeasureField.Index, -1];
        TCommonScriptItems(FSlice.FDimensions).FYLevel := ABaseLevel1;
        TCommonScriptItems(FSlice.FDimensions).FXLevel := ASecondLevel1;
        TCommonScriptItems(FSlice.FDimensions).FIndexInYLevel := AIndexInBaseLevel1;
        TCommonScriptItems(FSlice.FDimensions).FIndexInXLevel := AIndexInSecondLevel1;
        TCommonScriptItems(FSlice.FDimensions).FTotalOnY := (ABaseLevel1 < (FBaseContainer.FLevelCount - 1));
        TCommonScriptItems(FSlice.FDimensions).FTotalOnX := (ASecondLevel1 < (FSecondContainer.FLevelCount - 1));
        TCommonScriptItems(FSlice.FDimensions).FRow := 0;//FBaseContainer.FVisibleAxisNodes.VisibleIndex[FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[AIndexInBaseLevel1], AMeasureField.Index, -1];
        TCommonScriptItems(FSlice.FDimensions).FCol := 0;//FSecondContainer.FVisibleAxisNodes.VisibleIndex[FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel1], AMeasureField.Index, -1];
      end;
      TCommonScriptItems(FSlice.FMeasures).FBaseLevel := ABaseLevel1;
      TCommonScriptItems(FSlice.FMeasures).FSecondLevel := ASecondLevel1;
      TCommonScriptItems(FSlice.FMeasures).FIndexInBaseLevel := AIndexInBaseLevel1;
      TCommonScriptItems(FSlice.FMeasures).FIndexInSecondLevel := AIndexInSecondLevel1;
      TCommonScriptItems(FSlice.FMeasures).FBaseAdditionalTotalIndex := -1;
      TCommonScriptItems(FSlice.FMeasures).FSecondAdditionalTotalIndex := -1;

      TCommonScriptItems(FSlice.FDimensions).FBaseLevel := ABaseLevel1;
      TCommonScriptItems(FSlice.FDimensions).FSecondLevel := ASecondLevel1;
      TCommonScriptItems(FSlice.FDimensions).FIndexInBaseLevel := AIndexInBaseLevel1;
      TCommonScriptItems(FSlice.FDimensions).FIndexInSecondLevel := AIndexInSecondLevel1;
      TCommonScriptItems(FSlice.FDimensions).FBaseAdditionalTotalIndex := -1;
      TCommonScriptItems(FSlice.FDimensions).FSecondAdditionalTotalIndex := -1;
      TCommonScriptItems(FSlice.FDimensions).FDetailValue := ADetailValue;
      FSlice.FSliceFields.FRow := FSlice.FFilterManager.AbsIndex[FSlice.FTopNs.GoodIndex[AGoodRecIndex]];
      try
        if AMeasurePropInLevels.CachedFunction = nil then
        begin
          if AMeasurePropInLevels.Expression.Text <> '' then
            AMeasurePropInLevels.CachedFunction := FSlice.Interpreter.GetExpressionPointer(AMeasurePropInLevels.Expression)
          else
            AMeasurePropInLevels.CachedFunction := FSlice.Interpreter.GetFunctionPointer(AMeasurePropInLevels.ScriptFunction);
        end;
        Result := FSlice.Interpreter.CallFunction(AMeasurePropInLevels.CachedFunction, VarArrayOf([AValue, AFinal]));
        if not AFinal then
          PfcxMeasureValueWithPointer(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[AIndexInBaseLevel1][ARealIndex1]).PValue := TfcxCustomObjectHack(FSlice.FCustomObject).FValue;
      except
        on E: Exception do
          begin
            AMeasurePropInLevels.ScriptError := True;
            Result := Unassigned;
            if Assigned(FSlice.FOnScriptError) then
              if FSlice.Interpreter.ErrorMsg <> '' then
                FSlice.FOnScriptError(cst_MeasureFormulaDetail, FSlice.Interpreter.ErrorMsg)
              else
                FSlice.FOnScriptError(cst_MeasureFormulaDetail, E.Message)
          end;
      end;
    end
    else
    begin
      Result := Unassigned;
    end;
  end;

  procedure MainProc; // calc measures (based on source records)
  var
    AVariant1: Variant;
    AVariantArray, AVariantArray2: Array of Variant;
    ATempValueArray: Array of integer;
    ACalcRowArray: Array of boolean;
    AGoodRecIndex: Integer;

    procedure TESTProcessCalcFunc(ARight: Boolean; ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel: Integer);
    var
      ARealIndexInSecondLevel, AFunctionIndex: integer;
      AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount;
      AMeasureField: TfcxMeasureField;
    begin
// calc facts
// iterate measures, calculated for this cell (found on axis levels)
      for AFunctionIndex := 1 to PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] do
      begin
        AMeasureIndex := PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[AFunctionIndex];
        AMeasureField := FMeasureFields[AMeasureIndex];
        if ACalcRowArray[AMeasureIndex] then
        begin
// index of cell in row of matrix
          if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
            ARealIndexInSecondLevel := AIndexInSecondLevel
          else
            FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, ARealIndexInSecondLevel);
          if ARealIndexInSecondLevel <> -1 then
          begin
            if AMeasureField.FSliceField <> nil then
            begin
// calc
              if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc = af_FormulaDetail then
                AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], Calculate_FormulaDetail(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AGoodRecIndex, AMeasureField, PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel]).Value, False, null),
                  False, AStep, -1, AMeasureField)
              else
              if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc in ExtraFuncs then
                AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues2(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex], AVariantArray2[AMeasureIndex],
                  False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
              else
                AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex],
                  False, AStep, ATempValueArray[AMeasureIndex], AMeasureField);
// Addon Totals
              if ABaseLevel <> (FSaveCountBaseLevels - 1) then
                for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
                  if FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex] <> af_Formula then
                    if FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex] = af_FormulaDetail then
// to-do
                    else
                    if FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex] in ExtraFuncs then
                      cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex]].FuncValues2(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex], AVariantArray2[AMeasureIndex],
                        False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
                    else
                      cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex],
                        False, AStep, ATempValueArray[AMeasureIndex], AMeasureField);
              if ASecondLevel <> (FSaveCountSecondLevels - 1) then
                for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
                  if FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex] <> af_Formula then
                    if FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex] = af_FormulaDetail then
// to-do
                    else
                    if FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex] in ExtraFuncs then
                      cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex]].FuncValues2(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex], AVariantArray2[AMeasureIndex],
                        False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
                    else
                      cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex],
                        False, AStep, ATempValueArray[AMeasureIndex], AMeasureField);
            end
            else
// need calc?? (no based field)
              if AMeasureField.FAgrFunc <> af_Formula then   // ??
              begin
//          if AMeasureField2.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].AgrFunc <> af_Formula then
                if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc = af_FormulaDetail then
                  AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], Calculate_FormulaDetail(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AGoodRecIndex, AMeasureField, PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel]).Value, False, null),
                    False, AStep, -1, AMeasureField)
                else
                  AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], 1,
                    False, AStep, ATempValueArray[AMeasureIndex], AMeasureField);
// Addon Totals
                if ABaseLevel <> (FSaveCountBaseLevels - 1) then
                  for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
                    if FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex] <> af_Formula then
                      if FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex] = af_FormulaDetail then
// to-do
                      else
                        cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevel, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], 1,
                          False, AStep, ATempValueArray[AMeasureIndex], AMeasureField);
                if ASecondLevel <> (FSaveCountSecondLevels - 1) then
                  for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
                    if FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex] <> af_Formula then
                      if FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex] = af_FormulaDetail then
// to-do
                      else
                        cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevel, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], 1,
                          False, AStep, ATempValueArray[AMeasureIndex], AMeasureField);
              end
          end;
        end;
      end;

// calc totals
// ARight: False - from down, True - from right
//go from cell to left and to up
      if ARight then
      begin
//if from Right then go to left and to up
// up
        if not FBaseContainer.FNoFields then
          TESTProcessCalcFunc(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, AIndexInSecondLevel);
// left
        if ASecondLevel = -1 then
          Exit;
        if not FSecondContainer.FNoFields then
          TESTProcessCalcFunc(True, ABaseLevel, ASecondLevel - 1, AIndexInBaseLevel, FSecondContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent);
      end
      else
      begin
//if from down then go to up
// up
        if ABaseLevel = -1 then
          Exit;
        if not FBaseContainer.FNoFields then
          TESTProcessCalcFunc(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, AIndexInSecondLevel);
      end;
    end;

    function Calculate_Filter(AMeasureField: TfcxMeasureField): Boolean;
    begin
      if not AMeasureField.FFilterScriptError then
      begin
        try
          FSlice.FSliceFields.FRow := FSlice.FFilterManager.AbsIndex[FSlice.FTopNs.GoodIndex[AGoodRecIndex]];
          if AMeasureField.CachedFilterFunction = nil then
          begin
            if AMeasureField.FFilterExpression.Text <> '' then
              AMeasureField.CachedFilterFunction := FSlice.Interpreter.GetExpressionPointer(AMeasureField.FFilterExpression)
            else
              AMeasureField.CachedFilterFunction := FSlice.Interpreter.GetFunctionPointer(AMeasureField.FFilterScriptFunction);
          end;
          Result := FSlice.Interpreter.CallFunction(AMeasureField.CachedFilterFunction, Null);
        except
          on E: Exception do
            begin
              AMeasureField.FFilterScriptError := True;
              Result := False;
              if Assigned(FSlice.FOnScriptError) then
                if FSlice.Interpreter.ErrorMsg <> '' then
                  FSlice.FOnScriptError(cst_MeasureFilter, FSlice.Interpreter.ErrorMsg)
                else
                  FSlice.FOnScriptError(cst_MeasureFilter, E.Message)
            end;
        end;
      end
      else
      begin
        Result := False;
      end;
    end;
  var
    ABaseLevel, ASecondLevel: TfcxSmallCount;
    AFunctionIndex: integer;
    AMeasureIndex: TfcxSmallCount;
    AMeasureField: TfcxMeasureField;
    AIndexInBaseLevel, AIndexInSecondLevel, ARealIndexInSecondLevel: Integer;
  begin
// start from cells, go to totals
    ABaseLevel := FBaseContainer.LevelCount - 1;
    ASecondLevel := FSecondContainer.LevelCount - 1;
    if FSaveCountMeasures = 0 then
      exit;

    SetLength(AVariantArray, FSaveCountMeasures);
    SetLength(ATempValueArray, FSaveCountMeasures);
    SetLength(AVariantArray2, FSaveCountMeasures);
    SetLength(ACalcRowArray, FSaveCountMeasures);
// iterate source records
    for AGoodRecIndex := 0 to FSaveGoodRecCount - 1 do
    begin
// find coordinates of record in axises
      AIndexInBaseLevel := FBaseContainer.FCoordinate[FBaseContainer.FAxisSourceTree.FSourceOrderIndexes[AGoodRecIndex]];
      AIndexInSecondLevel := FSecondContainer.FCoordinate[FSecondContainer.FAxisSourceTree.FSourceOrderIndexes[AGoodRecIndex]];
// calc facts
// iterate measures, calculated for this cell (found on axis levels)
      for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
      begin
        ACalcRowArray[AMeasureIndex] := True;
        AMeasureField := FMeasureFields[AMeasureIndex];
        if AMeasureField.FSliceField <> nil then
        begin
// index of cell in matrix row
          if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
            ARealIndexInSecondLevel := AIndexInSecondLevel
          else
            FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, ARealIndexInSecondLevel);
          if ARealIndexInSecondLevel <> -1 then
          begin
            if AMeasureField.FSliceField = AMeasureField.FSliceFieldDistinct then
            begin
              ATempValueArray[AMeasureIndex] := AMeasureField.SliceField.GetUVIndexAndVariantFromRec(FSlice.FFilterManager.AbsIndex[FSlice.FTopNs.GoodIndex[AGoodRecIndex]], AVariant1);
              AVariantArray[AMeasureIndex] := AVariant1;
            end
            else
            begin
              ATempValueArray[AMeasureIndex] := AMeasureField.SliceFieldDistinct.GetUVIndexFromRec(FSlice.FFilterManager.AbsIndex[FSlice.FTopNs.GoodIndex[AGoodRecIndex]]);
              AVariantArray[AMeasureIndex] := AMeasureField.SliceField.GetUVValueFromRec(FSlice.FFilterManager.AbsIndex[FSlice.FTopNs.GoodIndex[AGoodRecIndex]]);
            end;
            if AMeasureField.FSliceField = AMeasureField.FSliceFieldExtra then
            begin
              AVariantArray2[AMeasureIndex] := AVariantArray[AMeasureIndex];
            end
            else
            begin
              AVariantArray2[AMeasureIndex] := AMeasureField.SliceFieldExtra.GetUVValueFromRec(FSlice.FFilterManager.AbsIndex[FSlice.FTopNs.GoodIndex[AGoodRecIndex]]);
            end
          end;
        end
        else
        begin
          if AMeasureField.FSliceFieldDistinct <> nil then
            ATempValueArray[AMeasureIndex] := AMeasureField.SliceFieldDistinct.GetUVIndexFromRec(FSlice.FFilterManager.AbsIndex[FSlice.FTopNs.GoodIndex[AGoodRecIndex]])
          else
            ATempValueArray[AMeasureIndex] := -1;
          AVariantArray[AMeasureIndex] := 1;
          AVariantArray2[AMeasureIndex] := 0;
        end;
      end;
      for AFunctionIndex := 1 to PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] do
      begin
        AMeasureIndex := PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[AFunctionIndex];
        AMeasureField := FMeasureFields[AMeasureIndex];
// Check filter
        if (AMeasureField.FFilterExpression.Text <> '') or (AMeasureField.FFilterScriptFunction <> '') then
// Calc filter
          ACalcRowArray[AMeasureIndex] := Calculate_Filter(AMeasureField);
        if ACalcRowArray[AMeasureIndex] then
        begin
          if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
            ARealIndexInSecondLevel := AIndexInSecondLevel
          else
            FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, ARealIndexInSecondLevel);
          if ARealIndexInSecondLevel <> -1 then
            if AMeasureField.FSliceField <> nil then
            begin
// calc
              if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc = af_FormulaDetail then
                AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], Calculate_FormulaDetail(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AGoodRecIndex, AMeasureField, PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel]).Value, False, null),
                  False, AStep, -1, AMeasureField)
              else
              if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc in ExtraFuncs then
                AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues2(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex], AVariantArray2[AMeasureIndex],
                  False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
              else
                AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex],
                  False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
            end
            else
            begin
// need calc?? (no based field)
              if AMeasureField.FAgrFunc <> af_Formula then
                if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc = af_FormulaDetail then
                  AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], Calculate_FormulaDetail(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AGoodRecIndex, AMeasureField, PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel]).Value, False, null),
                    False, AStep, -1, AMeasureField)
                else
                  AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], 1,
                    False, AStep, ATempValueArray[AMeasureIndex], AMeasureField);
            end;
        end
      end;
// run total calc
// to up
      if not FBaseContainer.FNoFields then
        TESTProcessCalcFunc(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, AIndexInSecondLevel);
// to left
      if not FSecondContainer.FNoFields then
        TESTProcessCalcFunc(True, ABaseLevel, ASecondLevel - 1, AIndexInBaseLevel, FSecondContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent);
    end;
  end;

  procedure PrepareVariance;
  var
    AFunctionIndex: Integer;
    AVarTemp: Variant;
    ABaseLevel, ASecondLevel: TfcxSmallCount;
    AIndexInBaseLevel, ARealIndexInSecondLevel: integer;
    AMeasureIndex: TfcxSmallCount;
    AMeasureValueWithCountAndVar: PfcxMeasureValueWithCountAndVar;
    ACountInSecondLevel: integer;
  begin
// iterate all levels intersections
    for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
      for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
        if PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] > 0 then
          for AIndexInBaseLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
          begin
            for AFunctionIndex := 1 to PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] do
            begin
              AMeasureIndex := PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[AFunctionIndex];
              if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                ACountInSecondLevel := FSecondContainer.FAxisTree[ASecondLevel].Count
              else
                ACountInSecondLevel := FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Count;
              for ARealIndexInSecondLevel := 0 to ACountInSecondLevel - 1 do
              begin
                AMeasureValueWithCountAndVar := PfcxMeasureValueWithCountAndVar(PfcxArrPointerArray(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel]);
                if AMeasureValueWithCountAndVar <> nil then
                begin
                  AVarTemp := AMeasureValueWithCountAndVar.VarTemp;
                  if not (TVarData(AVarTemp).VType <= 1) then
                    AMeasureValueWithCountAndVar.VarTemp := AVarTemp / AMeasureValueWithCountAndVar.MeasureValueWithCount.Count;
                end;
              end
            end
          end;
  end;

  procedure Final;
  var
    AMeasureIndex1: TfcxSmallCount;
    AMeasureField1: TfcxMeasureField;
    i, i1, j1, ACountInSecondLevel: integer;
    ABaseLevel1, ASecondLevel1, AAdditionalTotalIndex: TfcxSmallCount;
    AMeasureValueOld, AMeasureValueNew: PfcxMeasureValue;
  begin
    for AMeasureIndex1 := 0 to Count - 1 do
    begin
      AMeasureField1 := FMeasureFields[AMeasureIndex1];
      if AMeasureField1.FFinalNeed then
      begin
        for i := 0 to AMeasureField1.FUCArrayCount - 1 do
          FreeMem(AMeasureField1.FUCArray[i].PointerArray);
        FreeMem(AMeasureField1.FUCArray);
        AMeasureField1.FUCArray := nil;
        AMeasureField1.FUCArrayCount := 0;
        for i := 0 to AMeasureField1.FVLArrayCount - 1 do
          FreeMem(PfcxMeasureValueWithValuesList(AMeasureField1.FVLArray[i]).VLArr);
        FreeMem(AMeasureField1.FVLArray);
        AMeasureField1.FVLArray := nil;
        AMeasureField1.FVLArrayCount := 0;

      end;
      if AMeasureField1.FRestrucNeed or AMeasureField1.FRestrucAdditionalNeed then
      begin
// recreate FMeasureCells
        for ABaseLevel1 := -1 to FSaveCountBaseLevels - 1 do
          for ASecondLevel1 := -1 to FSaveCountSecondLevels - 1 do
          begin
            for i1 := 0 to FBaseContainer.FAxisTree[ABaseLevel1].Count - 1 do
            begin
              if AMeasureField1.FRestrucNeed and (AMeasureField1.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].AgrFunc in RestrucFuncs) then
              begin
                if AMeasureField1.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
                  ACountInSecondLevel := FSecondContainer.FAxisTree[ASecondLevel1].Count
                else
                  ACountInSecondLevel := FLevelsProp[ABaseLevel1][ASecondLevel1][i1].Count;
                for j1 := 0 to ACountInSecondLevel - 1 do
                begin
                  AMeasureValueOld := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField1.FMeasureCells[ABaseLevel1][ASecondLevel1])[i1][j1]);
                  if AMeasureValueOld <> nil then // not empty cell
                  begin
                    AMeasureValueNew := TfcxMeasureProcessor.NewMeasureValue(nil);
                    AMeasureValueNew.Value := AMeasureField1.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].MeasureProcessorClass.GetItem(AMeasureValueOld);
                    AMeasureField1.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].MeasureProcessorClass.DestroyMeasure(AMeasureValueOld);
                    PfcxArrPointerArray(AMeasureField1.FMeasureCells[ABaseLevel1][ASecondLevel1])[i1][j1] := AMeasureValueNew;
                  end;
                end;
              end;
              if AMeasureField1.FRestrucAdditionalNeed then
              begin
                if ABaseLevel1 <> (FSaveCountBaseLevels - 1) then
                  for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel1] - 1 do
                    if FSaveAdditionalTotalsBaseLevels[ABaseLevel1, AAdditionalTotalIndex] in RestrucFuncs then
                    begin
                      if AMeasureField1.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
                        ACountInSecondLevel := FSecondContainer.FAxisTree[ASecondLevel1].Count
                      else
                        ACountInSecondLevel := FLevelsProp[ABaseLevel1][ASecondLevel1][i1].Count;
                      for j1 := 0 to ACountInSecondLevel - 1 do
                      begin
                        AMeasureValueOld := PfcxArrPointerArray(PfcxPointerArray(AMeasureField1.FAdditionalTotalsMeasureCellsBase[ABaseLevel1][ASecondLevel1])[AAdditionalTotalIndex])[i1][j1];
                        if AMeasureValueOld <> nil then
                        begin
                          AMeasureValueNew := TfcxMeasureProcessor.NewMeasureValue(nil);
                          AMeasureValueNew.Value := cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevel1, AAdditionalTotalIndex]].GetItem(AMeasureValueOld);
                          cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevel1, AAdditionalTotalIndex]].DestroyMeasure(AMeasureValueOld);
                          PfcxArrPointerArray(PfcxPointerArray(AMeasureField1.FAdditionalTotalsMeasureCellsBase[ABaseLevel1][ASecondLevel1])[AAdditionalTotalIndex])[i1][j1] := AMeasureValueNew;
                        end;
                      end;
                    end;
                if ASecondLevel1 <> (FSaveCountSecondLevels - 1) then
                  for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel1] - 1 do
                    if FSaveAdditionalTotalsSecondLevels[ASecondLevel1, AAdditionalTotalIndex] in RestrucFuncs then
                    begin
                      if AMeasureField1.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
                        ACountInSecondLevel := FSecondContainer.FAxisTree[ASecondLevel1].Count
                      else
                        ACountInSecondLevel := FLevelsProp[ABaseLevel1][ASecondLevel1][i1].Count;
                      for j1 := 0 to ACountInSecondLevel - 1 do
                      begin
                        AMeasureValueOld := PfcxArrPointerArray(PfcxPointerArray(AMeasureField1.FAdditionalTotalsMeasureCellsSecond[ABaseLevel1][ASecondLevel1])[AAdditionalTotalIndex])[i1][j1];
                        if AMeasureValueOld <> nil then
                        begin
                          AMeasureValueNew := TfcxMeasureProcessor.NewMeasureValue(nil);
                          AMeasureValueNew.Value := cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevel1, AAdditionalTotalIndex]].GetItem(AMeasureValueOld);
                          cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevel1, AAdditionalTotalIndex]].DestroyMeasure(AMeasureValueOld);
                          PfcxArrPointerArray(PfcxPointerArray(AMeasureField1.FAdditionalTotalsMeasureCellsSecond[ABaseLevel1][ASecondLevel1])[AAdditionalTotalIndex])[i1][j1] := AMeasureValueNew;
                        end;
                      end;
                    end;
              end
            end;
          end
      end;
    end
  end;

  procedure MainProcFormulasDetailFinal;
  var
    AFunctionIndex: Integer;
    ABaseLevel, ASecondLevel: TfcxSmallCount;
    AMeasureIndex: TfcxSmallCount;
    AMeasureField: TfcxMeasureField;
    AMeasureValue: PfcxMeasureValue;
    AIndexInBaseLevel, ARealIndexInSecondLevel: integer;
  begin
    for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
      for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
        if PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] > 0 then
        begin
          for AFunctionIndex := 1 to PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] do
          begin
            AMeasureIndex := PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[AFunctionIndex];
            AMeasureField := FMeasureFields[AMeasureIndex];
            if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc <> af_FormulaDetail then
              Continue;

            for AIndexInBaseLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
            begin
              if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
                for ARealIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel].Count - 1 do
                begin
                  AMeasureValue := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel]);
                  if AMeasureValue <> nil then // not empty cell
                    AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(AMeasureValue, Calculate_FormulaDetail(ABaseLevel, ASecondLevel, AIndexInBaseLevel, ARealIndexInSecondLevel, -1, AMeasureField, AMeasureValue.Value, True, null), False, AStep, -1, AMeasureField)
                end
              else
                for ARealIndexInSecondLevel := 0 to FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Count - 1 do
                begin
                  AMeasureValue := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel]);
                  AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(AMeasureValue, Calculate_FormulaDetail(ABaseLevel, ASecondLevel, AIndexInBaseLevel, FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].IndexList[ARealIndexInSecondLevel], -1, AMeasureField, AMeasureValue.Value, True, null), False, AStep, -1, AMeasureField)
                end;
            end;
          end;
        end;
  end;

  procedure MainProcFormulas;
  var
    AVariant1: Variant;
    ATempValue: integer;

    function Calculate_Measure(AMeasureField: TfcxMeasureField; const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const ABaseAdditionalTotalIndex: TfcxSmallCount = -1; const ASecondAdditionalTotalIndex: TfcxSmallCount = -1): Variant;
    var
      AMeasurePropInLevels: ^_fcxMeasurePropInLevels;
    begin
      AMeasurePropInLevels := @(AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel]);
      if not AMeasurePropInLevels.ScriptError then
      begin
        try
//          TfcxCustomObjectHack(FSlice.FCustomObject).FValue := nil;
          if FBaseAxisIsX then
          begin
            TCommonScriptItems(FSlice.FMeasures).FXLevel := ABaseLevel;
            TCommonScriptItems(FSlice.FMeasures).FYLevel := ASecondLevel;
            TCommonScriptItems(FSlice.FMeasures).FIndexInXLevel := AIndexInBaseLevel;
            TCommonScriptItems(FSlice.FMeasures).FIndexInYLevel := AIndexInSecondLevel;
            TCommonScriptItems(FSlice.FMeasures).FTotalOnX := (ABaseLevel < (FBaseContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FMeasures).FTotalOnY := (ASecondLevel < (FSecondContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FMeasures).FCol := 0;//FBaseContainer.FVisibleAxisNodes.VisibleIndex[FBaseContainer.FAxisTree[ABaseLevel].IndexInLastLevel[AIndexInBaseLevel], AMeasureField.Index, ABaseAdditionalTotalIndex];
            TCommonScriptItems(FSlice.FMeasures).FRow := 0;//FSecondContainer.FVisibleAxisNodes.VisibleIndex[FSecondContainer.FAxisTree[ASecondLevel].IndexInLastLevel[AIndexInSecondLevel], AMeasureField.Index, ASecondAdditionalTotalIndex];
            TCommonScriptItems(FSlice.FDimensions).FXLevel := ABaseLevel;
            TCommonScriptItems(FSlice.FDimensions).FYLevel := ASecondLevel;
            TCommonScriptItems(FSlice.FDimensions).FIndexInXLevel := AIndexInBaseLevel;
            TCommonScriptItems(FSlice.FDimensions).FIndexInYLevel := AIndexInSecondLevel;
            TCommonScriptItems(FSlice.FDimensions).FTotalOnX := (ABaseLevel < (FBaseContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FDimensions).FTotalOnY := (ASecondLevel < (FSecondContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FDimensions).FCol := 0;//FBaseContainer.FVisibleAxisNodes.VisibleIndex[FBaseContainer.FAxisTree[ABaseLevel].IndexInLastLevel[AIndexInBaseLevel], AMeasureField.Index, ABaseAdditionalTotalIndex];
            TCommonScriptItems(FSlice.FDimensions).FRow := 0;//FSecondContainer.FVisibleAxisNodes.VisibleIndex[FSecondContainer.FAxisTree[ASecondLevel].IndexInLastLevel[AIndexInSecondLevel], AMeasureField.Index, ASecondAdditionalTotalIndex];
          end
          else
          begin
            TCommonScriptItems(FSlice.FMeasures).FYLevel := ABaseLevel;
            TCommonScriptItems(FSlice.FMeasures).FXLevel := ASecondLevel;
            TCommonScriptItems(FSlice.FMeasures).FIndexInYLevel := AIndexInBaseLevel;
            TCommonScriptItems(FSlice.FMeasures).FIndexInXLevel := AIndexInSecondLevel;
            TCommonScriptItems(FSlice.FMeasures).FTotalOnY := (ABaseLevel < (FBaseContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FMeasures).FTotalOnX := (ASecondLevel < (FSecondContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FMeasures).FRow := 0;//FBaseContainer.FVisibleAxisNodes.VisibleIndex[FBaseContainer.FAxisTree[ABaseLevel].IndexInLastLevel[AIndexInBaseLevel], AMeasureField.Index, ABaseAdditionalTotalIndex];
            TCommonScriptItems(FSlice.FMeasures).FCol := 0;//FSecondContainer.FVisibleAxisNodes.VisibleIndex[FSecondContainer.FAxisTree[ASecondLevel].IndexInLastLevel[AIndexInSecondLevel], AMeasureField.Index, ASecondAdditionalTotalIndex];
            TCommonScriptItems(FSlice.FDimensions).FYLevel := ABaseLevel;
            TCommonScriptItems(FSlice.FDimensions).FXLevel := ASecondLevel;
            TCommonScriptItems(FSlice.FDimensions).FIndexInYLevel := AIndexInBaseLevel;
            TCommonScriptItems(FSlice.FDimensions).FIndexInXLevel := AIndexInSecondLevel;
            TCommonScriptItems(FSlice.FDimensions).FTotalOnY := (ABaseLevel < (FBaseContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FDimensions).FTotalOnX := (ASecondLevel < (FSecondContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FDimensions).FRow := 0;//FBaseContainer.FVisibleAxisNodes.VisibleIndex[FBaseContainer.FAxisTree[ABaseLevel].IndexInLastLevel[AIndexInBaseLevel], AMeasureField.Index, ABaseAdditionalTotalIndex];
            TCommonScriptItems(FSlice.FDimensions).FCol := 0;//FSecondContainer.FVisibleAxisNodes.VisibleIndex[FSecondContainer.FAxisTree[ASecondLevel].IndexInLastLevel[AIndexInSecondLevel], AMeasureField.Index, ASecondAdditionalTotalIndex];
          end;
          TCommonScriptItems(FSlice.FMeasures).FBaseLevel := ABaseLevel;
          TCommonScriptItems(FSlice.FMeasures).FSecondLevel := ASecondLevel;
          TCommonScriptItems(FSlice.FMeasures).FIndexInBaseLevel := AIndexInBaseLevel;
          TCommonScriptItems(FSlice.FMeasures).FIndexInSecondLevel := AIndexInSecondLevel;
          TCommonScriptItems(FSlice.FMeasures).FBaseAdditionalTotalIndex := ABaseAdditionalTotalIndex;
          TCommonScriptItems(FSlice.FMeasures).FSecondAdditionalTotalIndex := ASecondAdditionalTotalIndex;
          TCommonScriptItems(FSlice.FMeasures).FCurrentMeasureIndex := AMeasureField.Index;

          TCommonScriptItems(FSlice.FDimensions).FBaseLevel := ABaseLevel;
          TCommonScriptItems(FSlice.FDimensions).FSecondLevel := ASecondLevel;
          TCommonScriptItems(FSlice.FDimensions).FIndexInBaseLevel := AIndexInBaseLevel;
          TCommonScriptItems(FSlice.FDimensions).FIndexInSecondLevel := AIndexInSecondLevel;
          TCommonScriptItems(FSlice.FDimensions).FBaseAdditionalTotalIndex := ABaseAdditionalTotalIndex;
          TCommonScriptItems(FSlice.FDimensions).FSecondAdditionalTotalIndex := ASecondAdditionalTotalIndex;
          TCommonScriptItems(FSlice.FDimensions).FCurrentMeasureIndex := AMeasureField.Index;

//          FSlice.SliceFields.FRow := -1;
          if (ABaseAdditionalTotalIndex = -1) and (ASecondAdditionalTotalIndex = -1) then
          begin
            if AMeasurePropInLevels.CachedFunction = nil then
            begin
              if AMeasurePropInLevels.Expression.Text <> '' then
                AMeasurePropInLevels.CachedFunction := FSlice.Interpreter.GetExpressionPointer(AMeasurePropInLevels.Expression)
              else
                AMeasurePropInLevels.CachedFunction := FSlice.Interpreter.GetFunctionPointer(AMeasurePropInLevels.ScriptFunction);
            end;
            Result := FSlice.Interpreter.CallFunction(AMeasurePropInLevels.CachedFunction, Null);
          end
          else
          begin
{ TODO -cNeed : Calc AdditionalTotal.}
            Result := Unassigned;
          end
        except
          on E: Exception do
            begin
              AMeasurePropInLevels.ScriptError := True;
              Result := Unassigned;
              if Assigned(FSlice.FOnScriptError) then
                if FSlice.Interpreter.ErrorMsg <> '' then
                  FSlice.FOnScriptError(cst_MeasureFormula, FSlice.Interpreter.ErrorMsg)
                else
                  FSlice.FOnScriptError(cst_MeasureFormula, E.Message)
            end;
        end;
      end
      else
      begin
        Result := Unassigned;
      end;
      //LogString('Result = ' + VarToStr(Result));
    end;

    procedure TESTProcessCalcFunc(ARight: Boolean; ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel: Integer; AMeasureIndex: TfcxSmallCount);
    var
      ARealIndexInSecondLevel, AFunctionIndex: integer;
      AMeasureField: TfcxMeasureField;
    begin
// calc facts
// iterate measures, calculated for this cell (found on axis levels)
      for AFunctionIndex := 1 to PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] do
      begin
        if AMeasureIndex = PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[AFunctionIndex] then
        begin
          AMeasureField := FMeasureFields[AMeasureIndex];
          if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc <> af_Formula then
          begin
// index of cell in row of matrix
            if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
              ARealIndexInSecondLevel := AIndexInSecondLevel
            else
              FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, ARealIndexInSecondLevel);
            if ARealIndexInSecondLevel <> -1 then
              AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariant1, False, AStep, ATempValue, AMeasureField);
          end;
          Break;
        end;
      end;
// run total calc
// ARight: False - From down, True - From right
//go from cell to left and to up
      if ARight then
      begin
//if from Right then go to left and to up
// up
        if not FBaseContainer.FNoFields and (ABaseLevel > -1) then
          TESTProcessCalcFunc(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, AIndexInSecondLevel, AMeasureIndex);
// left
        if ASecondLevel > -1 then
          Exit;
        if not FSecondContainer.FNoFields and (ASecondLevel > -1) then
          TESTProcessCalcFunc(True, ABaseLevel, ASecondLevel - 1, AIndexInBaseLevel, FSecondContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent, AMeasureIndex);
      end
      else
      begin
//if from down then go to up
// up
        if ABaseLevel > -1 then
          Exit;
        if not FBaseContainer.FNoFields and (ABaseLevel > -1) then
          TESTProcessCalcFunc(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, AIndexInSecondLevel, AMeasureIndex);
      end;
    end;

  var
    AIndexInBaseLevel, AIndexInSecondLevel, ARealIndexInSecondLevel, AFunctionIndex: Integer;
    ABaseLevel, ASecondLevel: TfcxSmallCount;
    AMeasureIndex: TfcxSmallCount;
    AMeasureField: TfcxMeasureField;
    AMeasureValueTotal: PfcxMeasureValue;
  begin
    for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
      for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
        if PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] > 0 then
        begin
          for AFunctionIndex := 1 to PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[0] do
          begin
            AMeasureIndex := PfcxSmallCountArray(AFuncsOfLevels[ABaseLevel][ASecondLevel])[AFunctionIndex];
            AMeasureField := FMeasureFields[AMeasureIndex];
            if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].AgrFunc <> af_Formula then
              Continue;

            for AIndexInBaseLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
            begin
              if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
              begin
                for AIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel].Count - 1 do
                begin
                  AMeasureValueTotal := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AIndexInSecondLevel]);
                  if AMeasureValueTotal <> nil then // not empty cell
                  begin
                    AVariant1 := Calculate_Measure(AMeasureField, ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel);
                    AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(AMeasureValueTotal, AVariant1, False, AStep, -1, AMeasureField);
// Addon Totals
{
                    if ABaseLevel1 <> (FSaveCountBaseLevels - 1) then
                      for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel1] - 1 do
                        if FSaveAdditionalTotalsBaseLevels[ABaseLevel1, AAdditionalTotalIndex] = af_Formula then
                        begin
                          AVariant2 := Calculate_Measure(AMeasureField1, ABaseLevel1, ASecondLevel1, i1, j1);
                          cfcMeasureProcessorMap[FSaveAdditionalTotalsBaseLevels[ABaseLevel1, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField1.FAdditionalTotalsMeasureCellsBase[ABaseLevel1][ASecondLevel1])[AAdditionalTotalIndex])[i1][j1], AVariant2,
                            False, AStep);
                        end;
                    if ASecondLevel1 <> (FSaveCountSecondLevels - 1) then
                      for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel1] - 1 do
                        if FSaveAdditionalTotalsSecondLevels[ASecondLevel1, AAdditionalTotalIndex] = af_Formula then
                        begin
                          AVariant2 := Calculate_Measure(AMeasureField1, ABaseLevel1, ASecondLevel1, i1, j1);
                          cfcMeasureProcessorMap[FSaveAdditionalTotalsSecondLevels[ASecondLevel1, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField1.FAdditionalTotalsMeasureCellsSecond[ABaseLevel1][ASecondLevel1])[AAdditionalTotalIndex])[i1][j1], AVariant2,
                            False, AStep);
                        end;
}
                    if APass = 0 then
                    begin
                      AVariant1 := AMeasureValueTotal.Value;
                      ATempValue := -1;
// run total calc
// to up
                      if not FBaseContainer.FNoFields and (ABaseLevel > -1) then
                        TESTProcessCalcFunc(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, AIndexInSecondLevel, AMeasureIndex);
// to left
                      if not FSecondContainer.FNoFields and (ASecondLevel > -1) then
                        TESTProcessCalcFunc(True, ABaseLevel, ASecondLevel - 1, AIndexInBaseLevel, FSecondContainer.FAxisTree[ASecondLevel].Nodes[AIndexInSecondLevel].IndParent, AMeasureIndex);
                    end;
                  end
                  else
                  begin
// empty cell
                  end;
                end;
              end
              else
              begin
                for ARealIndexInSecondLevel := 0 to FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Count - 1 do
                begin
                  AMeasureValueTotal := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][ARealIndexInSecondLevel]);
                  AVariant1 := Calculate_Measure(AMeasureField, ABaseLevel, ASecondLevel, AIndexInBaseLevel, FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].IndexList[ARealIndexInSecondLevel]);
                  AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].MeasureProcessorClass.FuncValues(AMeasureValueTotal, AVariant1, False, AStep, -1, AMeasureField);
                  if APass = 0 then
                  begin
                    AVariant1 := AMeasureValueTotal.Value;
                    ATempValue := -1;
// run total calc
// to up
                    if not FBaseContainer.FNoFields and (ABaseLevel > -1) then
                      TESTProcessCalcFunc(False, ABaseLevel - 1, ASecondLevel, FBaseContainer.FAxisTree[ABaseLevel].Nodes[AIndexInBaseLevel].IndParent, FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].IndexList[ARealIndexInSecondLevel], AMeasureIndex);
// to left
                    if not FSecondContainer.FNoFields and (ASecondLevel > -1) then
                      TESTProcessCalcFunc(True, ABaseLevel, ASecondLevel - 1, AIndexInBaseLevel, FSecondContainer.FAxisTree[ASecondLevel].Nodes[FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].IndexList[ARealIndexInSecondLevel]].IndParent, AMeasureIndex);
                  end
                end
              end
            end
          end;
        end;
  end;

  procedure MainProcAdditionalFormulas;
  var
    AVariant1: Variant;

    function Calculate_Measure(AMeasureField: TfcxMeasureField; AIsBaseAxis, AIsGrandTotal: Boolean; const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer): Variant;
    var
      ScriptError: Boolean;
      AxisContainer: TfcxAxisContainer;
      AxisField: TfcxAxisField;
    begin
      Result := Unassigned;
      if AIsBaseAxis then
        AxisContainer := FBaseContainer
      else
        AxisContainer := FSecondContainer;
      if AIsGrandTotal then
      begin
        if (AxisContainer.FAdditionalGrandTotalExpression.Text = '') and (AxisContainer.FAdditionalGrandTotalScriptFunction = '') then
          Exit;
        ScriptError := AxisContainer.FAdditionalGrandTotalScriptError;
        AxisField := nil;
      end
      else
      begin
        if AIsBaseAxis then
          AxisField := AxisContainer.Fields[AxisContainer.FAxisTree[ABaseLevel].RegionFieldIndex]
        else
          AxisField := AxisContainer.Fields[AxisContainer.FAxisTree[ASecondLevel].RegionFieldIndex];
        if (AxisField.FAdditionalTotalExpression.Text = '') and (AxisField.FAdditionalTotalScriptFunction = '') then
          Exit;
        ScriptError := AxisField.FAdditionalTotalScriptError;
      end;
      if not ScriptError then
      begin
        try
          if FBaseAxisIsX then
          begin
            TCommonScriptItems(FSlice.FMeasures).FXLevel := ABaseLevel;
            TCommonScriptItems(FSlice.FMeasures).FYLevel := ASecondLevel;
            TCommonScriptItems(FSlice.FMeasures).FIndexInXLevel := AIndexInBaseLevel;
            TCommonScriptItems(FSlice.FMeasures).FIndexInYLevel := AIndexInSecondLevel;
            TCommonScriptItems(FSlice.FMeasures).FTotalOnX := (ABaseLevel < (FBaseContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FMeasures).FTotalOnY := (ASecondLevel < (FSecondContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FMeasures).FCol := 0;
            TCommonScriptItems(FSlice.FMeasures).FRow := 0;
            TCommonScriptItems(FSlice.FDimensions).FXLevel := ABaseLevel;
            TCommonScriptItems(FSlice.FDimensions).FYLevel := ASecondLevel;
            TCommonScriptItems(FSlice.FDimensions).FIndexInXLevel := AIndexInBaseLevel;
            TCommonScriptItems(FSlice.FDimensions).FIndexInYLevel := AIndexInSecondLevel;
            TCommonScriptItems(FSlice.FDimensions).FTotalOnX := (ABaseLevel < (FBaseContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FDimensions).FTotalOnY := (ASecondLevel < (FSecondContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FDimensions).FCol := 0;
            TCommonScriptItems(FSlice.FDimensions).FRow := 0;
          end
          else
          begin
            TCommonScriptItems(FSlice.FMeasures).FYLevel := ABaseLevel;
            TCommonScriptItems(FSlice.FMeasures).FXLevel := ASecondLevel;
            TCommonScriptItems(FSlice.FMeasures).FIndexInYLevel := AIndexInBaseLevel;
            TCommonScriptItems(FSlice.FMeasures).FIndexInXLevel := AIndexInSecondLevel;
            TCommonScriptItems(FSlice.FMeasures).FTotalOnY := (ABaseLevel < (FBaseContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FMeasures).FTotalOnX := (ASecondLevel < (FSecondContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FMeasures).FRow := 0;
            TCommonScriptItems(FSlice.FMeasures).FCol := 0;
            TCommonScriptItems(FSlice.FDimensions).FYLevel := ABaseLevel;
            TCommonScriptItems(FSlice.FDimensions).FXLevel := ASecondLevel;
            TCommonScriptItems(FSlice.FDimensions).FIndexInYLevel := AIndexInBaseLevel;
            TCommonScriptItems(FSlice.FDimensions).FIndexInXLevel := AIndexInSecondLevel;
            TCommonScriptItems(FSlice.FDimensions).FTotalOnY := (ABaseLevel < (FBaseContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FDimensions).FTotalOnX := (ASecondLevel < (FSecondContainer.FLevelCount - 1));
            TCommonScriptItems(FSlice.FDimensions).FRow := 0;
            TCommonScriptItems(FSlice.FDimensions).FCol := 0;
          end;
          TCommonScriptItems(FSlice.FMeasures).FBaseLevel := ABaseLevel;
          TCommonScriptItems(FSlice.FMeasures).FSecondLevel := ASecondLevel;
          TCommonScriptItems(FSlice.FMeasures).FIndexInBaseLevel := AIndexInBaseLevel;
          TCommonScriptItems(FSlice.FMeasures).FIndexInSecondLevel := AIndexInSecondLevel;
          TCommonScriptItems(FSlice.FMeasures).FBaseAdditionalTotalIndex := -1;
          TCommonScriptItems(FSlice.FMeasures).FSecondAdditionalTotalIndex := -1;
          TCommonScriptItems(FSlice.FMeasures).FCurrentMeasureIndex := AMeasureField.Index;

          TCommonScriptItems(FSlice.FDimensions).FBaseLevel := ABaseLevel;
          TCommonScriptItems(FSlice.FDimensions).FSecondLevel := ASecondLevel;
          TCommonScriptItems(FSlice.FDimensions).FIndexInBaseLevel := AIndexInBaseLevel;
          TCommonScriptItems(FSlice.FDimensions).FIndexInSecondLevel := AIndexInSecondLevel;
          TCommonScriptItems(FSlice.FDimensions).FBaseAdditionalTotalIndex := -1;
          TCommonScriptItems(FSlice.FDimensions).FSecondAdditionalTotalIndex := -1;
          TCommonScriptItems(FSlice.FDimensions).FCurrentMeasureIndex := AMeasureField.Index;
          if AIsGrandTotal then
          begin
            if AxisContainer.CachedAdditionalGrandTotalScriptFunction = nil then
            begin
              if AxisContainer.FAdditionalGrandTotalExpression.Text <> '' then
                AxisContainer.CachedAdditionalGrandTotalScriptFunction := FSlice.Interpreter.GetExpressionPointer(AxisContainer.FAdditionalGrandTotalExpression)
              else
                AxisContainer.CachedAdditionalGrandTotalScriptFunction := FSlice.Interpreter.GetFunctionPointer(AxisContainer.AdditionalGrandTotalScriptFunction);
            end;
            Result := FSlice.Interpreter.CallFunction(AxisContainer.CachedAdditionalGrandTotalScriptFunction, Null);
          end
          else
          begin
            if AxisField.CachedAdditionalTotalScriptFunction = nil then
            begin
              if AxisField.FAdditionalTotalExpression.Text <> '' then
                AxisField.CachedAdditionalTotalScriptFunction := FSlice.Interpreter.GetExpressionPointer(AxisField.FAdditionalTotalExpression)
              else
                AxisField.CachedAdditionalTotalScriptFunction := FSlice.Interpreter.GetFunctionPointer(AxisField.AdditionalTotalScriptFunction);
            end;
            Result := FSlice.Interpreter.CallFunction(AxisField.CachedAdditionalTotalScriptFunction, Null);
          end;
        except
          on E: Exception do
            begin
              if AIsGrandTotal then
                AxisContainer.FAdditionalGrandTotalScriptError := True
              else
                AxisField.FAdditionalTotalScriptError := True;
              Result := Unassigned;
              if Assigned(FSlice.FOnScriptError) then
                if FSlice.Interpreter.ErrorMsg <> '' then
                  FSlice.FOnScriptError(cst_MeasureFormula, FSlice.Interpreter.ErrorMsg)
                else
                  FSlice.FOnScriptError(cst_MeasureFormula, E.Message)
            end;
        end;
      end;
      //LogString('Result = ' + VarToStr(Result));
    end;

  var
    AAdditionalTotal, AAdditionalBaseTotalIndex, AAdditionalSecondTotalIndex: Integer;
    AIndexInBaseLevel, AIndexInSecondLevel, ARealIndexInSecondLevel: integer;
    ABaseLevel, ASecondLevel: TfcxSmallCount;
    AMeasureIndex: TfcxSmallCount;
    AMeasureField: TfcxMeasureField;
    AMeasureValueTotal: PfcxMeasureValue;
    ANeedCalcBase, ANeedCalcSecond: Boolean;
  begin
    AAdditionalSecondTotalIndex := -1;
    AAdditionalBaseTotalIndex := -1;
    for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
    begin
      ANeedCalcBase := False;
      if ABaseLevel <> (FSaveCountBaseLevels - 1) then
        for AAdditionalTotal := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
          if FSaveAdditionalTotalsBaseLevels[ABaseLevel][AAdditionalTotal] = af_Formula then
          begin
            ANeedCalcBase := True;
            AAdditionalBaseTotalIndex := AAdditionalTotal;
            Break;
          end;
      for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
      begin
        ANeedCalcSecond := False;
        if ASecondLevel <> (FSaveCountSecondLevels - 1) then
          for AAdditionalTotal := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
            if FSaveAdditionalTotalsSecondLevels[ASecondLevel][AAdditionalTotal] = af_Formula then
            begin
              ANeedCalcSecond := True;
              AAdditionalSecondTotalIndex := AAdditionalTotal;
              Break;
            end;
        for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
        begin
          AMeasureField := FMeasureFields[AMeasureIndex];
          for AIndexInBaseLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
          begin
            if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
            begin
              for AIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel].Count - 1 do
              begin
                if ANeedCalcBase then
                begin
                  AMeasureValueTotal := PfcxMeasureValue(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalBaseTotalIndex])[AIndexInBaseLevel][AIndexInSecondLevel]);
                  if AMeasureValueTotal <> nil then // not empty cell
                  begin
                    AVariant1 := Calculate_Measure(AMeasureField, True, ABaseLevel = -1, ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel);
                    cfcMeasureProcessorMap[False, af_Formula].FuncValues(AMeasureValueTotal, AVariant1, False, AStep, -1, AMeasureField);
                  end
                  else
                  begin
  // empty cell
                  end;
                end;
                if ANeedCalcSecond then
                begin
                  AMeasureValueTotal := PfcxMeasureValue(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalSecondTotalIndex])[AIndexInBaseLevel][AIndexInSecondLevel]);
                  if AMeasureValueTotal <> nil then // not empty cell
                  begin
                    AVariant1 := Calculate_Measure(AMeasureField, False, ASecondLevel = -1, ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel);
                    cfcMeasureProcessorMap[False, af_Formula].FuncValues(AMeasureValueTotal, AVariant1, False, AStep, -1, AMeasureField);
                  end
                  else
                  begin
  // empty cell
                  end;
                end;
              end;
            end
            else
            begin
              for ARealIndexInSecondLevel := 0 to FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Count - 1 do
              begin
                if ANeedCalcBase then
                begin
                  AMeasureValueTotal := PfcxMeasureValue(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalBaseTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel]);
                  AVariant1 := Calculate_Measure(AMeasureField, True, ABaseLevel = -1, ABaseLevel, ASecondLevel, AIndexInBaseLevel, FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].IndexList[ARealIndexInSecondLevel]);
                  cfcMeasureProcessorMap[False, af_Formula].FuncValues(AMeasureValueTotal, AVariant1, False, AStep, -1, AMeasureField);
                end;
                if ANeedCalcSecond then
                begin
                  AMeasureValueTotal := PfcxMeasureValue(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalSecondTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel]);
                  AVariant1 := Calculate_Measure(AMeasureField, False, ASecondLevel = -1, ABaseLevel, ASecondLevel, AIndexInBaseLevel, FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].IndexList[ARealIndexInSecondLevel]);
                  cfcMeasureProcessorMap[False, af_Formula].FuncValues(AMeasureValueTotal, AVariant1, False, AStep, -1, AMeasureField);
                end;
              end
            end
          end

        end;
      end
    end
  end;

  procedure MainProcTotals;
  var
    AFunctionIndex: Integer;
    ABaseLevelTotal, ASecondLevelTotal: TfcxSmallCount;
    ABaseLevelSource, ASecondLevelSource: TfcxSmallCount;
    AMeasureIndex: TfcxSmallCount;
    AMeasureValueSource, AMeasureValueTotal: PfcxMeasureValue;
    AMeasureField: TfcxMeasureField;
    ABaseIndexTotal, ASecondIndexTotal: integer;
    AAdditionalTotalIndex: integer;
    ABaseIndexSource, ASecondIndexSource: integer;
    AVarValue: Variant;
  begin
    for ABaseLevelTotal := -1 to FSaveCountBaseLevels - 1 do
      for ASecondLevelTotal := -1 to FSaveCountSecondLevels - 1 do
        for AFunctionIndex := 1 to PfcxSmallCountArray(AFuncsOfLevels[ABaseLevelTotal][ASecondLevelTotal])[0] do
        begin
          AMeasureIndex := PfcxSmallCountArray(AFuncsOfLevels[ABaseLevelTotal][ASecondLevelTotal])[AFunctionIndex];
          AMeasureField := FMeasureFields[AMeasureIndex];
          if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].AgrFunc = af_Formula then
            Continue;
          ABaseLevelSource := ABaseLevelTotal;
          ASecondLevelSource := ASecondLevelTotal;
          if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].UseTotalOfBaseAxis then
            inc(ABaseLevelSource)
          else
            inc(ASecondLevelSource);

          for ABaseIndexSource := 0 to FBaseContainer.FAxisTree[ABaseLevelSource].Count - 1 do
          begin
            if ABaseLevelSource <> ABaseLevelTotal then // offset in base axis
              ABaseIndexTotal := FBaseContainer.FAxisTree[ABaseLevelSource].Nodes[ABaseIndexSource].IndParent
            else
              ABaseIndexTotal := ABaseIndexSource;
            if FLevelsProp[ABaseLevelSource][ASecondLevelSource][ABaseIndexSource].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelSource][ASecondLevelSource].CreateAllCells then
            begin
              for ASecondIndexSource := 0 to FSecondContainer.FAxisTree[ASecondLevelSource].Count - 1 do
              begin
                AMeasureValueSource := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevelSource][ASecondLevelSource])[ABaseIndexSource][ASecondIndexSource]);
                if AMeasureValueSource <> nil then
                begin
                  if ASecondLevelSource <> ASecondLevelTotal then
                    if FLevelsProp[ABaseLevelTotal][ASecondLevelTotal][ABaseIndexTotal].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].CreateAllCells then
                      ASecondIndexTotal := FSecondContainer.FAxisTree[ASecondLevelSource].Nodes[ASecondIndexSource].IndParent
                    else
                      FindByIndex(ABaseLevelTotal, ASecondLevelTotal, ABaseIndexTotal, FSecondContainer.FAxisTree[ASecondLevelSource].Nodes[ASecondIndexSource].IndParent, ASecondIndexTotal)
                  else
                    if FLevelsProp[ABaseLevelTotal][ASecondLevelTotal][ABaseIndexTotal].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].CreateAllCells then
                      ASecondIndexTotal := ASecondIndexSource
                    else
                      FindByIndex(ABaseLevelTotal, ASecondLevelTotal, ABaseIndexTotal, ASecondIndexSource, ASecondIndexTotal);
                  if ASecondIndexTotal <> -1 then
                  begin
                    AMeasureValueTotal := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevelTotal][ASecondLevelTotal])[ABaseIndexTotal][ASecondIndexTotal]);
                    if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].AgrFunc = af_FormulaDetail then
                      AVarValue := Calculate_FormulaDetail(ABaseLevelTotal, ASecondLevelTotal, ABaseIndexTotal, ASecondIndexTotal, -1{AGoodRecIndex}, AMeasureField, PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevelTotal][ASecondLevelTotal])[ABaseIndexTotal][ASecondIndexTotal]).Value, False, AMeasureValueSource.Value)
                    else
                      AVarValue := AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelSource][ASecondLevelSource].MeasureProcessorClass.GetItem(AMeasureValueSource);
                    AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].MeasureProcessorClass.FuncValues(AMeasureValueTotal, AVarValue,
                      False, AStep, -1, AMeasureField);
// Addon Totals
                    if ABaseLevelTotal <> (FSaveCountBaseLevels - 1) then
                      for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevelTotal] - 1 do
                        if FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex] <> af_Formula then
                          if FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex] = af_FormulaDetail then
// to-do
                          else
                          if FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex] in ExtraFuncs then
// to-do
//                            cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex]].FuncValues2(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex], AVariantArray2[AMeasureIndex],
//                              False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
                          else
                            cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevelTotal][ASecondLevelTotal])[AAdditionalTotalIndex])[ABaseIndexTotal][ASecondIndexTotal], AVarValue,
                              False, AStep, -1, AMeasureField);
                    if ASecondLevelTotal <> (FSaveCountSecondLevels - 1) then
                      for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevelTotal] - 1 do
                        if FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex] <> af_Formula then
                          if FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex] = af_FormulaDetail then
// to-do
                          else
                          if FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex] in ExtraFuncs then
// to-do
//                            cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex]].FuncValues2(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex], AVariantArray2[AMeasureIndex],
//                              False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
                          else
                            cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevelTotal][ASecondLevelTotal])[AAdditionalTotalIndex])[ABaseIndexTotal][ASecondIndexTotal], AVarValue,
                              False, AStep, -1, AMeasureField);
                  end;
                end;
              end;
            end
            else
            begin
              for ASecondIndexSource := 0 to FLevelsProp[ABaseLevelSource][ASecondLevelSource][ABaseIndexSource].Count - 1 do
              begin
                AMeasureValueSource := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevelSource][ASecondLevelSource])[ABaseIndexSource][ASecondIndexSource]);
                if ASecondLevelSource <> ASecondLevelTotal then
                  if FLevelsProp[ABaseLevelTotal][ASecondLevelTotal][ABaseIndexTotal].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].CreateAllCells then
                    ASecondIndexTotal := FSecondContainer.FAxisTree[ASecondLevelSource].Nodes[FLevelsProp[ABaseLevelSource][ASecondLevelSource][ABaseIndexSource].IndexList[ASecondIndexSource]].IndParent
                  else
                    FindByIndex(ABaseLevelTotal, ASecondLevelTotal, ABaseIndexTotal, FSecondContainer.FAxisTree[ASecondLevelSource].Nodes[FLevelsProp[ABaseLevelSource][ASecondLevelSource][ABaseIndexSource].IndexList[ASecondIndexSource]].IndParent, ASecondIndexTotal)
                else
                  if FLevelsProp[ABaseLevelTotal][ASecondLevelTotal][ABaseIndexTotal].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].CreateAllCells then
                    ASecondIndexTotal := FLevelsProp[ABaseLevelSource][ASecondLevelSource][ABaseIndexSource].IndexList[ASecondIndexSource]
                  else
                    FindByIndex(ABaseLevelTotal, ASecondLevelTotal, ABaseIndexTotal, FLevelsProp[ABaseLevelSource][ASecondLevelSource][ABaseIndexSource].IndexList[ASecondIndexSource], ASecondIndexTotal);
                if ASecondIndexTotal <> -1 then
                begin
                  AMeasureValueTotal := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevelTotal][ASecondLevelTotal])[ABaseIndexTotal][ASecondIndexTotal]);
                    if AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].AgrFunc = af_FormulaDetail then
                      AVarValue := Calculate_FormulaDetail(ABaseLevelTotal, ASecondLevelTotal, ABaseIndexTotal, ASecondIndexTotal, -1{AGoodRecIndex}, AMeasureField, PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevelTotal][ASecondLevelTotal])[ABaseIndexTotal][ASecondIndexTotal]).Value, False, AMeasureValueSource.Value)
                    else
                      AVarValue := AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelSource][ASecondLevelSource].MeasureProcessorClass.GetItem(AMeasureValueSource);
                  AMeasureField.FMeasurePropInLevelsListArray[ABaseLevelTotal][ASecondLevelTotal].MeasureProcessorClass.FuncValues(AMeasureValueTotal, AVarValue,
                    False, AStep, -1, AMeasureField);
// Addon Totals
                  if ABaseLevelTotal <> (FSaveCountBaseLevels - 1) then
                    for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevelTotal] - 1 do
                      if FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex] <> af_Formula then
                        if FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex] = af_FormulaDetail then
// to-do
                        else
                        if FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex] in ExtraFuncs then
// to-do
//                            cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex]].FuncValues2(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex], AVariantArray2[AMeasureIndex],
//                              False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
                        else
                          cfcMeasureProcessorMap[False, FSaveAdditionalTotalsBaseLevels[ABaseLevelTotal, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsBase[ABaseLevelTotal][ASecondLevelTotal])[AAdditionalTotalIndex])[ABaseIndexTotal][ASecondIndexTotal], AVarValue,
                            False, AStep, -1, AMeasureField);
                  if ASecondLevelTotal <> (FSaveCountSecondLevels - 1) then
                    for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevelTotal] - 1 do
                      if FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex] <> af_Formula then
                        if FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex] = af_FormulaDetail then
// to-do
                        else
                        if FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex] in ExtraFuncs then
// to-do
//                            cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex]].FuncValues2(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][ARealIndexInSecondLevel], AVariantArray[AMeasureIndex], AVariantArray2[AMeasureIndex],
//                              False, AStep, ATempValueArray[AMeasureIndex], AMeasureField)
                        else
                          cfcMeasureProcessorMap[False, FSaveAdditionalTotalsSecondLevels[ASecondLevelTotal, AAdditionalTotalIndex]].FuncValues(PfcxArrPointerArray(PfcxPointerArray(AMeasureField.FAdditionalTotalsMeasureCellsSecond[ABaseLevelTotal][ASecondLevelTotal])[AAdditionalTotalIndex])[ABaseIndexTotal][ASecondIndexTotal], AVarValue,
                            False, AStep, -1, AMeasureField);
                end;
              end;
            end;
          end;
        end;
  end;

  procedure PrepareInterpreter_ForCalculating;
  begin
    TCommonScriptItems(FSlice.FMeasures).ClearItems;
    TCommonScriptItems(FSlice.FDimensions).ClearItems;
    TfcxCustomObjectHack(FSlice.FCustomObject).FValue := nil;
    FSlice.Interpreter.Compile;
    if FSlice.Interpreter.ErrorMsg <> '' then
    begin
//      AMeasurePropInLevels.ScriptError := True;
      if Assigned(FSlice.FOnScriptError) then
        FSlice.FOnScriptError(cst_MeasureFormulaDetail, FSlice.Interpreter.ErrorMsg);
    end;
  end;
var
  ABaseIndex0, ASecondIndex0, AFindIndex, AIndex2, AIndex3: integer;
  ABaseIndex, ASecondIndex: Integer;
  ABaseLevel, ASecondLevel, AAdditionalTotal: TfcxSmallCount;
  ASecondDetailRecordsCount: PfcxIntegerArray;
  AOne: Integer;
  AAdditionalFormulaNeed, AAdditionalFormulaDetailNeed: Boolean;
  APassCount: Integer;
  AVarianceExistsArray: PfcxBooleanArray;
  AExistsFormula, AFormulaNeed, AFormulaDetailNeed, ARestrucNeed, AFinalNeed: Boolean;
  i, j, k: integer;
  AMeasureIndex: TfcxSmallCount;
  AMeasureField: TfcxMeasureField;
  AFuncsOfPassAndStep: PfcxArrPointerArray;
begin
  Clear;
  PrepareInterpreter_ForCalculating;
  FIsCleared := False;
  FFinalized := False;
  FSaveCountMeasures := GetCount;

  if (FSlice.FTopNs.FStep = 1) and (FSlice.FTopNs.ActiveCount > 0) and FSlice.FTopNs.FAsFilter then
    FSaveGoodRecCount := FSlice.FTopNs.FGoodRecCount
  else
    FSaveGoodRecCount := FSlice.FFilterManager.GoodRecCount;

  AOne := 1;
  FBaseAxisIsX := FSlice.FXAxisContainer.FCellsCount < FSlice.FYAxisContainer.FCellsCount;
  if FBaseAxisIsX then
  begin
    FBaseContainer := FSlice.FXAxisContainer;
    FSecondContainer := FSlice.FYAxisContainer;
  end
  else
  begin
    FBaseContainer := FSlice.FYAxisContainer;
    FSecondContainer := FSlice.FXAxisContainer;
  end;
  FSaveCountBaseLevels := FBaseContainer.FLevelCount;
  FSaveCountSecondLevels := FSecondContainer.FLevelCount;

  GetMem(FSaveCountAdditionalTotalsBaseLevels, (FSaveCountBaseLevels + 1) * SizeOf(TfcxSmallCount));
  GetMem(FSaveCountAdditionalTotalsSecondLevels, (FSaveCountSecondLevels + 1) * SizeOf(TfcxSmallCount));
  GetMem(FSaveAdditionalTotalsBaseLevels, (FSaveCountBaseLevels + 1) * SizeOf(Pointer));
  GetMem(FSaveAdditionalTotalsSecondLevels, (FSaveCountSecondLevels + 1) * SizeOf(Pointer));
  AAdditionalFormulaNeed := False;
  AAdditionalFormulaDetailNeed := False;
  FBaseContainer.FAdditionalGrandTotalScriptError := False;
  FBaseContainer.CachedAdditionalGrandTotalScriptFunction := nil;
  for ABaseLevel := 0 to FBaseContainer.Fields.Count - 1 do
    with FBaseContainer.Fields[ABaseLevel] do
    begin
      FAdditionalTotalScriptError := False;
      CachedAdditionalTotalScriptFunction := nil;
    end;
  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
  begin
    FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] := FBaseContainer.CountAdditionalTotalFunctionsInLevel[ABaseLevel];
    GetMem(FSaveAdditionalTotalsBaseLevels[ABaseLevel], SizeOf(TfcxAgrFunc) * FSaveCountAdditionalTotalsBaseLevels[ABaseLevel]);
    for AAdditionalTotal := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
    begin
      FSaveAdditionalTotalsBaseLevels[ABaseLevel][AAdditionalTotal] := FBaseContainer.AdditionalTotalFunctionInLevel[ABaseLevel, AAdditionalTotal];
      AAdditionalFormulaNeed := AAdditionalFormulaNeed or (FSaveAdditionalTotalsBaseLevels[ABaseLevel][AAdditionalTotal] = af_Formula);
      AAdditionalFormulaDetailNeed := AAdditionalFormulaDetailNeed or (FSaveAdditionalTotalsBaseLevels[ABaseLevel][AAdditionalTotal] = af_FormulaDetail);
    end;
  end;
  FSecondContainer.FAdditionalGrandTotalScriptError := False;
  FSecondContainer.CachedAdditionalGrandTotalScriptFunction := nil;
  for ASecondLevel := 0 to FSecondContainer.Fields.Count - 1 do
    with FSecondContainer.Fields[ASecondLevel] do
    begin
      FAdditionalTotalScriptError := False;
      CachedAdditionalTotalScriptFunction := nil;
    end;
  for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
  begin
    FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] := FSecondContainer.CountAdditionalTotalFunctionsInLevel[ASecondLevel];
    GetMem(FSaveAdditionalTotalsSecondLevels[ASecondLevel], SizeOf(TfcxAgrFunc) * FSaveCountAdditionalTotalsSecondLevels[ASecondLevel]);
    for AAdditionalTotal := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
    begin
      FSaveAdditionalTotalsSecondLevels[ASecondLevel][AAdditionalTotal] := FSecondContainer.AdditionalTotalFunctionInLevel[ASecondLevel, AAdditionalTotal];
      AAdditionalFormulaNeed := AAdditionalFormulaNeed or (FSaveAdditionalTotalsSecondLevels[ASecondLevel][AAdditionalTotal] = af_Formula);
      AAdditionalFormulaDetailNeed := AAdditionalFormulaDetailNeed or (FSaveAdditionalTotalsSecondLevels[ASecondLevel][AAdditionalTotal] = af_FormulaDetail);
    end;
  end;

  FCount := FBaseContainer.FCellsCount;
  FSecondCount := FSecondContainer.FCellsCount;
  if FCount = 0 then
    exit;
  APassCount := 1;
  GetMem(AVarianceExistsArray, SizeOf(Boolean));
  AVarianceExistsArray[0] := False;
  AFinalNeed := False;
  ARestrucNeed := False;
  for AMeasureIndex := 0 to Count - 1 do
    FMeasureFields[AMeasureIndex].CreateMeasurePropArrayS1;
  AFormulaNeed := False;
  AExistsCreateAllCells := False;
  AFormulaDetailNeed := False;
  for i := FSaveCountBaseLevels - 1 downto -1 do
  begin
    for j := FSaveCountSecondLevels - 1 downto -1 do
    begin
      AExistsFormula := False;
      APass := 0;
      for AMeasureIndex := 0 to Count - 1 do
      begin
        AMeasureField := FMeasureFields[AMeasureIndex];
        with AMeasureField.FMeasurePropInLevelsListArray[i][j] do
        begin
          AExistsFormula := AExistsFormula or (AgrFunc = af_Formula);
          AExistsCreateAllCells := AExistsCreateAllCells or CreateAllCells;
          AFormulaDetailNeed := AFormulaDetailNeed or (AgrFunc = af_FormulaDetail);
          if UseTotalToCalc then
          begin
            if UseTotalOfBaseAxis then
              Pass := AMeasureField.FMeasurePropInLevelsListArray[i + 1][j].Pass + 1
            else
              Pass := AMeasureField.FMeasurePropInLevelsListArray[i][j + 1].Pass + 1;
            if APass < Pass then
              APass := Pass;
            if AMeasureField.FPassCount < Pass then
            begin
              AMeasureField.FPassCount := Pass;
            end
          end;
        end;
      end;
      if AExistsFormula and (APass > 0) then
      begin
        for AMeasureIndex := 0 to Count - 1 do
        begin
          AMeasureField := FMeasureFields[AMeasureIndex];
          with AMeasureField.FMeasurePropInLevelsListArray[i][j] do
            if AgrFunc = af_Formula then
            begin
              if Pass < APass then
                Pass := APass;
              if AMeasureField.FPassCount < Pass then
              begin
                AMeasureField.FPassCount := Pass;
              end
            end;
        end;
      end;
      AFormulaNeed := AFormulaNeed or AExistsFormula;
    end;
  end;

  for AMeasureIndex := 0 to Count - 1 do
    with FMeasureFields[AMeasureIndex] do
    begin
      FFilterScriptError := False;
      CachedFilterFunction := nil;
  //    AMeasureField := FMeasureFields[AMeasureIndex];
      CreateMeasurePropArrayS2;
      CreateTotalPositionArray;
      if APassCount < FPassCount then
      begin
        ReallocMem(AVarianceExistsArray, SizeOf(Boolean) * FPassCount);
        FillChar(AVarianceExistsArray[APassCount], SizeOf(Boolean) * (FPassCount - APassCount), 0);
        APassCount := FPassCount;
      end;
      for j := 0 to FPassCount - 1 do
      begin
        AVarianceExistsArray[j] := AVarianceExistsArray[j] or
                                   FVarianceExistsArray[j];
      end;
      AFinalNeed := AFinalNeed or FFinalNeed;
      ARestrucNeed := ARestrucNeed or FRestrucNeed or FRestrucAdditionalNeed;
    end;

// Create FLevelsProp
  GetMem(FLevelsProp, (FSaveCountBaseLevels + 1) * SizeOf(_fcxArr2LevelInCellListArray));
  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
  begin
    GetMem(FLevelsProp[ABaseLevel], (FSaveCountSecondLevels + 1) * SizeOf(_fcxArrLevelInCellListArray));
    for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
    begin
      GetMem(FLevelsProp[ABaseLevel][ASecondLevel], FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxLevelInCellListArray));
      FillChar(FLevelsProp[ABaseLevel][ASecondLevel]^, FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxLevelInCellListArray), 0);
    end;
  end;


// Create FMeasureCells
  for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
    with FMeasureFields[AMeasureIndex] do
    begin
      GetMem(FMeasureCells, (FSaveCountBaseLevels + 1) * SizeOf(_fcxPointerArray));
      for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
      begin
        GetMem(FMeasureCells[ABaseLevel], (FSaveCountSecondLevels + 1) * SizeOf(_fcxPointerArray));
        for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
        begin
          GetMem(FMeasureCells[ABaseLevel][ASecondLevel], FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxPointerArray));
          FillChar(FMeasureCells[ABaseLevel][ASecondLevel]^, FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxPointerArray), 0);
        end;
      end;
    end;

// Create  FAdditionalTotalsMeasureCellsBase and  FAdditionalTotalsMeasureCellsSecond
  for AMeasureIndex := 0 to FSaveCountMeasures - 1 do
    with FMeasureFields[AMeasureIndex] do
    begin
      GetMem(FAdditionalTotalsMeasureCellsBase, (FSaveCountBaseLevels + 1) * SizeOf(_fcxPointerArray));
      GetMem(FAdditionalTotalsMeasureCellsSecond, (FSaveCountBaseLevels + 1) * SizeOf(_fcxPointerArray));
      for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
      begin
        if ABaseLevel <> (FSaveCountBaseLevels - 1) then
          GetMem(FAdditionalTotalsMeasureCellsBase[ABaseLevel], (FSaveCountSecondLevels + 1) * SizeOf(_fcxPointerArray))
        else
          FAdditionalTotalsMeasureCellsBase[ABaseLevel] := nil;
        GetMem(FAdditionalTotalsMeasureCellsSecond[ABaseLevel], (FSaveCountSecondLevels + 1) * SizeOf(_fcxPointerArray));
        for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
        begin
          if ABaseLevel <> (FSaveCountBaseLevels - 1) then
          begin
            GetMem(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel], FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] * SizeOf(_fcxPointerArray));
            for i := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
            begin
              GetMem(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[i], FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxPointerArray));
              FillChar(PfcxPointerArray(FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[i]^, FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxPointerArray), 0);
            end;
          end;
          if ASecondLevel = (FSaveCountSecondLevels - 1) then
          begin
            FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel] := nil
          end
          else
          begin
            GetMem(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel], FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] * SizeOf(_fcxPointerArray));
            for i := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
            begin
              GetMem(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[i], FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxPointerArray));
              FillChar(PfcxPointerArray(FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[i]^, FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxPointerArray), 0);
            end;
          end;
        end;
      end;
    end;

  GetMem(FMeasureCellDetailRecordsTemp, (FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count) * SizeOf(_fcxArrPointerArray));
  FillChar(FMeasureCellDetailRecordsTemp^, (FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count) * SizeOf(_fcxArrPointerArray), 0);

  CreateCellsStep1;
  CreateCellsStep2;
  CreateCellsStep3;
  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
  begin
    for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
    begin
      for i := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
      begin
        GetMem(FLevelsProp[ABaseLevel][ASecondLevel][i].CellDetailInfo, (FLevelsProp[ABaseLevel][ASecondLevel][i].Count) * SizeOf(TfcxCellDetailInfo));
        FillChar(FLevelsProp[ABaseLevel][ASecondLevel][i].CellDetailInfo^, (FLevelsProp[ABaseLevel][ASecondLevel][i].Count) * SizeOf(TfcxCellDetailInfo), 0);
      end;
    end;
  end;
  GetMem(FBaseDetailRecords, FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count * SizeOf(TfcxMeasureCellDetailRecords));
  FillChar(FBaseDetailRecords^, FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count * SizeOf(TfcxMeasureCellDetailRecords), 0);
  GetMem(FSecondDetailRecords, FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].Count * SizeOf(TfcxMeasureCellDetailRecords));
  FillChar(FSecondDetailRecords^, FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].Count * SizeOf(TfcxMeasureCellDetailRecords), 0);

  GetMem(ASecondDetailRecordsCount, FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].Count * SizeOf(Integer));
  FillChar(ASecondDetailRecordsCount^, FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].Count * SizeOf(Integer), 0);
  for i := 0 to FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count - 1 do
  begin
    for j := 0 to FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Count - 1 do
    begin
      ASecondIndex0 := j;
      if not FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Simple then
        ASecondIndex0 := FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].IndexList[j];
      ASecondDetailRecordsCount[ASecondIndex0] := ASecondDetailRecordsCount[ASecondIndex0] + FMeasureCellDetailRecordsTemp[i][j].Count;
    end;
  end;
  for j := 0 to FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].Count - 1 do
    GetMem(FSecondDetailRecords[j].Records, ASecondDetailRecordsCount[j] * SizeOf(Integer));

  FillChar(ASecondDetailRecordsCount^, FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].Count * SizeOf(Integer), 0);
  for i := 0 to FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count - 1 do
  begin
    for j := 0 to FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Count - 1 do
    begin
      ASecondIndex0 := j;
      if not FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Simple then
        ASecondIndex0 := FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].IndexList[j];
      Move(FMeasureCellDetailRecordsTemp[i][j].Records[0], FSecondDetailRecords[ASecondIndex0].Records[ASecondDetailRecordsCount[ASecondIndex0]], FMeasureCellDetailRecordsTemp[i][j].Count * SizeOf(Integer));
      ASecondDetailRecordsCount[ASecondIndex0] := ASecondDetailRecordsCount[ASecondIndex0] + FMeasureCellDetailRecordsTemp[i][j].Count;
    end;
  end;

  FillChar(ASecondDetailRecordsCount^, FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].Count * SizeOf(Integer), 0);
  for i := 0 to FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count - 1 do
  begin
    ABaseIndex0 := FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].IndexInLastLevel[i];

    for j := 0 to FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Count - 1 do
      FBaseDetailRecords[i].Count := FBaseDetailRecords[i].Count + FMeasureCellDetailRecordsTemp[i][j].Count;
    GetMem(FBaseDetailRecords[i].Records, FBaseDetailRecords[i].Count * SizeOf(Integer));
    k := 0;
    for j := 0 to FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Count - 1 do
    begin
      Move(FMeasureCellDetailRecordsTemp[i][j].Records[0], FBaseDetailRecords[i].Records[k], FMeasureCellDetailRecordsTemp[i][j].Count * SizeOf(Integer));
      k := k + FMeasureCellDetailRecordsTemp[i][j].Count;
    end;
    k := 0;

    for j := 0 to FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Count - 1 do
    begin
      if FMeasureCellDetailRecordsTemp[i][j].Count > 0 then
      begin
        k := k + FMeasureCellDetailRecordsTemp[i][j].Count;
        ASecondIndex0 := j;
        if not FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Simple then
          ASecondIndex0 := FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].IndexList[j];
        ASecondDetailRecordsCount[ASecondIndex0] := ASecondDetailRecordsCount[ASecondIndex0] + FMeasureCellDetailRecordsTemp[i][j].Count;
// fill DetailInfo
        FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].CellDetailInfo[j].Count := FMeasureCellDetailRecordsTemp[i][j].Count;
        FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].CellDetailInfo[j].Start := k - FMeasureCellDetailRecordsTemp[i][j].Count;
// totals.
// In Base Axis
        AIndex2 := ASecondIndex0;
        for ASecondLevel := FSaveCountSecondLevels-2 downto -1 do
        begin
          AIndex2 := FSecondContainer.FIndexInLevel[FSecondContainer.FIndexOfParent[FSecondContainer.FAxisTree[ASecondLevel+1].IndexInLastLevel[AIndex2]]];
          AFindIndex := AIndex2;
          if not FLevelsProp[FBaseContainer.FLevelOf[ABaseIndex0]][ASecondLevel][FBaseContainer.FIndexInLevel[ABaseIndex0]].Simple then
            FindByIndex(FBaseContainer.FLevelOf[ABaseIndex0], ASecondLevel, FBaseContainer.FIndexInLevel[ABaseIndex0], AFindIndex, AFindIndex);
          if FLevelsProp[FSaveCountBaseLevels - 1][ASecondLevel][i].CellDetailInfo[AFindIndex].Count = 0 then
            FLevelsProp[FSaveCountBaseLevels - 1][ASecondLevel][i].CellDetailInfo[AFindIndex].Start := k - FMeasureCellDetailRecordsTemp[i][j].Count;
          FLevelsProp[FSaveCountBaseLevels - 1][ASecondLevel][i].CellDetailInfo[AFindIndex].Count := FLevelsProp[FSaveCountBaseLevels - 1][ASecondLevel][i].CellDetailInfo[AFindIndex].Count + FMeasureCellDetailRecordsTemp[i][j].Count;
// and set count on total intersactions
          AIndex3 := ABaseIndex0;
          for ABaseLevel := FSaveCountBaseLevels-2 downto -1 do
          begin
            AIndex3 := FBaseContainer.FIndexOfParent[AIndex3];
            AFindIndex := AIndex2;
            if not FLevelsProp[FBaseContainer.FLevelOf[AIndex3]][ASecondLevel][FBaseContainer.FIndexInLevel[AIndex3]].Simple then
              FindByIndex(FBaseContainer.FLevelOf[AIndex3], ASecondLevel, FBaseContainer.FIndexInLevel[AIndex3], AFindIndex, AFindIndex);
            if FLevelsProp[FBaseContainer.FLevelOf[AIndex3]][ASecondLevel][FBaseContainer.FIndexInLevel[AIndex3]].CellDetailInfo[AFindIndex].Count = 0 then
              FLevelsProp[FBaseContainer.FLevelOf[AIndex3]][ASecondLevel][FBaseContainer.FIndexInLevel[AIndex3]].CellDetailInfo[AFindIndex].Start := -1;
            FLevelsProp[FBaseContainer.FLevelOf[AIndex3]][ASecondLevel][FBaseContainer.FIndexInLevel[AIndex3]].CellDetailInfo[AFindIndex].Count := FLevelsProp[FBaseContainer.FLevelOf[AIndex3]][ASecondLevel][FBaseContainer.FIndexInLevel[AIndex3]].CellDetailInfo[AFindIndex].Count + FMeasureCellDetailRecordsTemp[i][j].Count;
          end;
        end;
// In Second Axis
        AIndex2 := ABaseIndex0;
        for ABaseLevel := FSaveCountBaseLevels-2 downto -1 do
        begin
          AIndex2 := FBaseContainer.FIndexOfParent[AIndex2];
          AFindIndex := ASecondIndex0;
          if not FLevelsProp[FBaseContainer.FLevelOf[AIndex2]][FSaveCountSecondLevels-1][FBaseContainer.FIndexInLevel[AIndex2]].Simple then
            FindByIndex(FBaseContainer.FLevelOf[AIndex2], FSaveCountSecondLevels-1, FBaseContainer.FIndexInLevel[AIndex2], AFindIndex, AFindIndex);
          if FLevelsProp[FBaseContainer.FLevelOf[AIndex2]][FSaveCountSecondLevels-1][FBaseContainer.FIndexInLevel[AIndex2]].CellDetailInfo[AFindIndex].Count = 0 then
            FLevelsProp[FBaseContainer.FLevelOf[AIndex2]][FSaveCountSecondLevels-1][FBaseContainer.FIndexInLevel[AIndex2]].CellDetailInfo[AFindIndex].Start := ASecondDetailRecordsCount[ASecondIndex0] - FMeasureCellDetailRecordsTemp[i][j].Count;
          FLevelsProp[FBaseContainer.FLevelOf[AIndex2]][FSaveCountSecondLevels-1][FBaseContainer.FIndexInLevel[AIndex2]].CellDetailInfo[AFindIndex].Count := FLevelsProp[FBaseContainer.FLevelOf[AIndex2]][FSaveCountSecondLevels-1][FBaseContainer.FIndexInLevel[AIndex2]].CellDetailInfo[AFindIndex].Count + FMeasureCellDetailRecordsTemp[i][j].Count;
        end;
      end
    end;
  end;
  FreeMem(ASecondDetailRecordsCount);

  if FMeasureCellDetailRecordsTemp <> nil then
  begin
    for i := 0 to FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count - 1 do
    begin
      ABaseIndex := FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].IndexInLastLevel[i];
      for j := 0 to FLevelsProp[FSaveCountBaseLevels - 1][FSaveCountSecondLevels-1][i].Count - 1 do
      begin
        FreeMem(FMeasureCellDetailRecordsTemp[i][j].Records);
      end;
      FreeMem(FMeasureCellDetailRecordsTemp[i]);
    end;
    FreeMem(FMeasureCellDetailRecordsTemp);
    FMeasureCellDetailRecordsTemp := nil;
  end;

  GetMem(AFuncsOfPassAndStep, APassCount * SizeOf(_fcxArrPointerArray));
  for i := 0 to APassCount - 1 do
  begin
    GetMem(AFuncsOfPassAndStep[i], 2 * SizeOf(_fcxPointerArray));
    GetMem(AFuncsOfLevels, (FSaveCountBaseLevels + 1) * SizeOf(_fcxArrMinusPointerArrayMinus));
    for j := -1 to FSaveCountBaseLevels - 1 do
    begin
      GetMem(AFuncsOfLevels[j], (FSaveCountSecondLevels + 1) * SizeOf(_fcxPointerArrayMinus));
      for k := -1 to FSaveCountSecondLevels - 1 do
      begin
        GetMem(AFuncsOfLevels[j][k], SizeOf(_fcxSmallCountArray));
        PfcxSmallCountArray(AFuncsOfLevels[j][k])[0] := 0;
      end;
    end;
    AFuncsOfPassAndStep[i][0] := AFuncsOfLevels;
    GetMem(AFuncsOfLevels, (FSaveCountBaseLevels + 1) * SizeOf(_fcxArrMinusPointerArrayMinus));
    for j := -1 to FSaveCountBaseLevels - 1 do
    begin
      GetMem(AFuncsOfLevels[j], (FSaveCountSecondLevels + 1) * SizeOf(_fcxPointerArrayMinus));
      for k := -1 to FSaveCountSecondLevels - 1 do
      begin
        GetMem(AFuncsOfLevels[j][k], SizeOf(_fcxSmallCountArray));
        PfcxSmallCountArray(AFuncsOfLevels[j][k])[0] := 0;
      end;
    end;
    AFuncsOfPassAndStep[i][AOne] := AFuncsOfLevels;
  end;

  for AMeasureIndex := 0 to Count - 1 do
  begin
    AMeasureField := FMeasureFields[AMeasureIndex];
    for i := FBaseContainer.FLevelCount - 1 downto -1 do
    begin
      for j := FSecondContainer.FLevelCount - 1 downto -1 do
      begin
        if AMeasureField.FMeasurePropInLevelsListArray[i][j].Variance then
        begin
          PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][0])[i][j])[0] := PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][0])[i][j])[0] + 1;
          ReallocMem(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][0])[i][j], (PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][0])[i][j])[0] + 1) * SizeOf(_fcxSmallCountArray));
          PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][0])[i][j])[PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][0])[i][j])[0]] := AMeasureIndex;
        end;
        PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][AOne])[i][j])[0] := PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][AOne])[i][j])[0] + 1;
        ReallocMem(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][AOne])[i][j], (PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][AOne])[i][j])[0] + 1) * SizeOf(_fcxSmallCountArray));
        PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][AOne])[i][j])[PfcxSmallCountArray(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[AMeasureField.FMeasurePropInLevelsListArray[i][j].Pass][AOne])[i][j])[0]] := AMeasureIndex;
      end;
    end;
  end;

// function based on data from sources

  APass := 0;
  If AVarianceExistsArray[0] then
  begin
    AStep := 1;
    AFuncsOfLevels := AFuncsOfPassAndStep[APass][0];
    MainProc;
    PrepareVariance;
  end;
  AStep := 2;
  AFuncsOfLevels := AFuncsOfPassAndStep[APass][AOne];
  MainProc;
  if AFormulaDetailNeed or AAdditionalFormulaDetailNeed then
    MainProcFormulasDetailFinal;
  if AFormulaNeed then
    MainProcFormulas;
  if AAdditionalFormulaNeed and (APassCount = 1) then
    MainProcAdditionalFormulas;

// function based on data from totals
  for i := 1 to APassCount - 1 do
  begin
    APass := i;
    If AVarianceExistsArray[i] then
    begin
      AStep := 1;
      AFuncsOfLevels := AFuncsOfPassAndStep[APass][0];
      MainProcTotals;
      PrepareVariance;
    end;
    AStep := 2;
    AFuncsOfLevels := AFuncsOfPassAndStep[APass][AOne];
    MainProcTotals;
    if AFormulaNeed or AAdditionalFormulaNeed  then
      MainProcFormulas;
    if AAdditionalFormulaNeed  and (i = (APassCount - 1)) then
      MainProcAdditionalFormulas;
  end;

  if AFinalNeed or ARestrucNeed then
    Final;
  FFinalized := True;
  for i := 0 to APassCount - 1 do
  begin
    for j := -1 to FSaveCountBaseLevels - 1 do
    begin
      for k := -1 to FSaveCountSecondLevels - 1 do
      begin
        FreeMem(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[i][0])[j][k]);
        FreeMem(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[i][AOne])[j][k]);
      end;
      FreeMem(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[i][0])[j]);
      FreeMem(PfcxArrMinusPointerArrayMinus(AFuncsOfPassAndStep[i][AOne])[j]);
    end;
    FreeMem(AFuncsOfPassAndStep[i][0]);
    FreeMem(AFuncsOfPassAndStep[i][AOne]);
    FreeMem(AFuncsOfPassAndStep[i]);
  end;
  FreeMem(AFuncsOfPassAndStep);

  FreeMem(AVarianceExistsArray);

  for AMeasureIndex := 0 to Count - 1 do
    if (FMeasureFields[AMeasureIndex].DisplayAs in da_Ranks) then
      CalcRank(AMeasureIndex);
end;

procedure TfcxMeasuresContainer.DeleteMeasure(AMeasureIndex: TfcxSmallCount);
begin
  DeleteMeasureInternal(AMeasureIndex, True);
end;

procedure TfcxMeasuresContainer.SetPosition(APosition: TfcxSmallCount);
var
  AOldPosition: TfcxSmallCount;
  AChanges: TfcxChangesInSlice;
begin
  if APosition <> FPosition then
  begin
    FSlice.StartChange;
    AOldPosition := FPosition;
    FPosition := APosition;
    AChanges := [chs_MeasuresFieldPosition, chs_Fact];
    if FContainer is TfcxAxisContainer then
    begin
      if FSlice.MeasuresContainer.FPosition > FContainer.Fields.Count then
        FPosition := FContainer.Fields.Count;
      TfcxAxisContainer(FContainer).MeasuresMoved(AOldPosition);
//      TfcxAxisContainer(FContainer).FillVisibleArray;
      if FContainer = FSlice.FXAxisContainer then
        AChanges := AChanges + [chs_NeedFillVisibleX, chs_XAxis]
      else
        AChanges := AChanges + [chs_NeedFillVisibleY, chs_YAxis];
    end
    else
      AChanges := AChanges + [chs_Page];
    FSlice.StopChange(AChanges);
  end;
end;

function TfcxMeasuresContainer.GetCells(const ABaseLevel, ASecondLevel: TfcxSmallCount;
      const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex: TfcxSmallCount): PfcxMeasureValue;
var
  AFindIndex: Integer;
begin
  if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or FMeasureFields[AMeasureIndex].CreateAllCells then
  begin
    Result := PfcxArrPointerArray(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AIndexInSecondLevel];
  end
  else
  if FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AFindIndex) then
    Result := PfcxArrPointerArray(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AFindIndex]
  else
    Result := nil;
end;

function TfcxMeasuresContainer.GetBaseAdditionalTotalCells(const ABaseLevel, ASecondLevel: TfcxSmallCount;
  const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): PfcxMeasureValue;
var
  AFindIndex: Integer;
begin
  Result := nil;
  if (ABaseLevel = (FSaveCountBaseLevels - 1)) or (FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] <= AAdditionalTotalIndex) then
    exit;
  if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or FMeasureFields[AMeasureIndex].CreateAllCells then
    Result := PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AIndexInSecondLevel]
  else
  if FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AFindIndex) then
    Result := PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AFindIndex];
end;

function TfcxMeasuresContainer.GetSecondAdditionalTotalCells(const ABaseLevel, ASecondLevel: TfcxSmallCount;
  const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): PfcxMeasureValue;
var
  AFindIndex: Integer;
begin
  Result := nil;
  if (ASecondLevel = (FSaveCountSecondLevels - 1)) or (FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] <= AAdditionalTotalIndex) then
    exit;
  if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or FMeasureFields[AMeasureIndex].CreateAllCells then
    Result := PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AIndexInSecondLevel]
  else
  if FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AFindIndex) then
    Result := PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[AIndexInBaseLevel][AFindIndex];
end;

procedure TfcxMeasuresContainer.ReCreateTotalPositions;
var
  AMeasureIndex: TfcxSmallCount;
begin
  for AMeasureIndex := 0 to Count - 1 do
  begin
    FMeasureFields[AMeasureIndex].ClearTotalPositionArray;
    FMeasureFields[AMeasureIndex].CreateTotalPositionArray;
  end;
end;

function TfcxMeasuresContainer.GetCaption: TfcxString;
begin
  if (FCaption = '') then
    Result := fcxResources.GetAnsi('sMeasures')
  else
    Result := FCaption;
end;

function TfcxMeasuresContainer.GetCaptionWidth: SmallInt;
begin
  if FCaptionWidth = 0 then
    Result := FSlice.DefaultColWidth
  else
    Result := FCaptionWidth;
end;

procedure TfcxMeasuresContainer.SetCaption(const Value: TfcxString);
begin
  if (Value = fcxResources.GetAnsi('sMeasures')) then
    FCaption := ''
  else
    FCaption := Value;
end;

procedure TfcxMeasuresContainer.SetCaptionWidth(const Value: SmallInt);
var
  AValue: SmallInt;
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_CapPageWidth,
    chs_CapXWidth,
    chs_CapYWidth,
    chs_None,
    chs_None
  );
begin
  if Value = FSlice.DefaultColWidth then
    AValue := 0
  else
    AValue := Value;
  if FCaptionWidth <> AValue then
  begin
    FSlice.StartChange;
    FCaptionWidth := AValue;
    if (FContainer = FSlice.YAxisContainer) and FSlice.SyncronizeYFieldsWidth then
    begin
      if FSlice.FMeasuresContainer.Width <> AValue then
        FSlice.FMeasuresContainer.Width := AValue;
    end;
    FSlice.StopChange([cChanges[FRegion]]);
  end

end;

function TfcxMeasuresContainer.GetMeasure(AIndex: TfcxSmallCount): TfcxMeasureField;
begin
  Result := FMeasureFields[AIndex];
end;

function TfcxMeasuresContainer.MoveMeasure(AFromIndex,
  AToIndex: TfcxSmallCount): boolean;
var
  AChanges: TfcxChangesInSlice;
begin
  if (AFromIndex > -1) and (AFromIndex < Count) and
     (AToIndex > -1) and (AToIndex < Count) and (AFromIndex <> AToIndex) then
  begin
    Result := True;
    FSlice.StartChange;
    AChanges := [{chs_MeasuresFieldPosition, }chs_Fact];
    FMeasureFields.FFields.Move(AFromIndex, AToIndex);
    if FSaveCountMeasures > 0 then
      MeasureMoved(AFromIndex, AToIndex);
    if FContainer is TfcxAxisContainer then
    begin
      TfcxAxisContainer(FContainer).MeasureMoved(AFromIndex, AToIndex);
      if FContainer = FSlice.FXAxisContainer then
        AChanges := AChanges + [chs_NeedFillVisibleX, chs_XAxis]
      else
        AChanges := AChanges + [chs_NeedFillVisibleY, chs_YAxis];
    end;
    FSlice.FXAxisContainer.SelectedMeasureMoved(AFromIndex, AToIndex);
    FSlice.FYAxisContainer.SelectedMeasureMoved(AFromIndex, AToIndex);
    FSlice.StopChange(AChanges);
  end
  else
    Result := False;
end;

procedure TfcxMeasuresContainer.InverseVisible;
var
  AMeasureIndex: TfcxSmallCount;
begin
  FSlice.StartChange;
  for AMeasureIndex := 0 to Count - 1  do
    Measures[AMeasureIndex].Visible := not Measures[AMeasureIndex].Visible;
  FSlice.StopChange([]);
end;

procedure TfcxMeasuresContainer.SetAllVisible;
var
  AMeasureIndex: TfcxSmallCount;
begin
  FSlice.StartChange;
  for AMeasureIndex := 0 to Count - 1  do
    Measures[AMeasureIndex].Visible := True;
  FSlice.StopChange([]);
end;

procedure TfcxMeasuresContainer.SetNoneVisible;
var
  AMeasureIndex: TfcxSmallCount;
begin
  FSlice.StartChange;
  for AMeasureIndex := 0 to Count - 1  do
    Measures[AMeasureIndex].Visible := False;
  FSlice.StopChange([]);
end;

function TfcxMeasuresContainer.GetVisibleCount: TfcxSmallCount;
var
  AMeasureIndex: TfcxSmallCount;
begin
  Result := 0;
  for AMeasureIndex := 0 to FMeasureFields.Count - 1 do
    if FMeasureFields[AMeasureIndex].Visible then
      inc(Result);
end;

function TfcxMeasuresContainer.GetCellRanks(const ABaseLevel, ASecondLevel: TfcxSmallCount;
  const AIndexInBaseLevel, AIndexInSecondLevel: Integer; const AMeasureIndex: TfcxSmallCount; AXAxis: Boolean): Integer;
var
  AFindIndex: Integer;
begin
  if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple or FMeasureFields[AMeasureIndex].CreateAllCells then
  begin
    if AXAxis xor FBaseAxisIsX then
      Result := PfcxArrIntegerArray(FMeasureFields[AMeasureIndex].FSecondRanks[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AIndexInSecondLevel]
    else
      Result := PfcxArrIntegerArray(FMeasureFields[AMeasureIndex].FBaseRanks[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AIndexInSecondLevel]
  end
  else
  if FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AFindIndex) then
    if AXAxis xor FBaseAxisIsX then
      Result := PfcxArrIntegerArray(FMeasureFields[AMeasureIndex].FSecondRanks[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AFindIndex]
    else
      Result := PfcxArrIntegerArray(FMeasureFields[AMeasureIndex].FBaseRanks[ABaseLevel][ASecondLevel])[AIndexInBaseLevel][AFindIndex]
  else
    Result := 0;
end;

procedure TfcxMeasuresContainer.CalcRank(AMeasureIndex: TfcxSmallCount);
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  i: integer;
  AMeasureField: TfcxMeasureField;

  procedure CalcRankInternal(AMeasureIndex1, ABaseLevel1, ASecondLevel1: TfcxSmallCount);
  var
    i1, j1, k1, ABaseIndex1, AIndex, AFindIndex: integer;
    AParentIndex: integer;
    ASortArray: PfcxIntegerArray;
    ABaseIndexArray: PfcxIntegerArray;
    ACount, ACapacity: integer;
    AMeasureValue: PfcxMeasureValue;

    function Find(AValue: Variant; var AIndex: Integer): Boolean;
    var
      ALeft, ARight, AMiddle: Integer;
      AValue2: Variant;
    begin
      Result := False;
      ALeft := 0;
      ARight := ACount - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        AValue2 := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[i1][ASortArray[AMiddle]]).Value;
        if AValue2 = AValue then
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end
        else
        if AValue2 > AValue then
          ARight := AMiddle - 1
        else
          ALeft := AMiddle + 1;
      end;
      AIndex := ALeft;
    end;

    function Find2(AValue: Variant; var AIndex: Integer): Boolean;
    var
      ALeft, ARight, AMiddle: Integer;
      AValue2: Variant;
    begin
      Result := False;
      ALeft := 0;
      ARight := ACount - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        AValue2 := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[FBaseContainer.FLevelOf[ABaseIndexArray[AMiddle]]][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[AMiddle]]][ASortArray[AMiddle]]).Value;
        if AValue2 = AValue then
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end
        else
        if AValue2 > AValue then
          ARight := AMiddle - 1
        else
          ALeft := AMiddle + 1;
      end;
      AIndex := ALeft;
    end;

    procedure SetRank1;
    var
      ARank, j1: integer;
    begin
      ARank := 0;
      for j1 := ACount - 1 downto 0 do
      begin
        if j1 = (ACount - 1) then
          inc(ARank)
        else
          if PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[FBaseContainer.FLevelOf[ABaseIndex1]][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndex1]][ASortArray[j1]]).Value <>
             PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[FBaseContainer.FLevelOf[ABaseIndex1]][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndex1]][ASortArray[j1 + 1]]).Value then
            inc(ARank);
        PfcxArrIntegerArray(AMeasureField.FSecondRanks[FBaseContainer.FLevelOf[ABaseIndex1]][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndex1]][ASortArray[j1]] := ARank;
      end;
    end;

    procedure SetRank2;
    var
      ARank, j1: integer;
    begin
      ARank := 0;
      for j1 := ACount - 1 downto 0 do
      begin
        if j1 = (ACount - 1) then
          inc(ARank)
        else
          if PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[FBaseContainer.FLevelOf[ABaseIndexArray[j1]]][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[j1]]][ASortArray[j1]]).Value <>
             PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[FBaseContainer.FLevelOf[ABaseIndexArray[j1 + 1]]][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[j1 + 1]]][ASortArray[j1 + 1]]).Value then
            inc(ARank);
        PfcxArrIntegerArray(AMeasureField.FBaseRanks[FBaseContainer.FLevelOf[ABaseIndexArray[j1]]][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[j1]]][ASortArray[j1]] := ARank;
      end;
    end;
  var
    ACountInSecondLevel: Integer;
  begin
    ASortArray := nil;
    ABaseIndexArray := nil;
    ACapacity := 0;

    for i1 := 0 to FBaseContainer.FAxisTree[ABaseLevel1].Count - 1 do
    begin
      ABaseIndex1 := FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[i1];
      if FLevelsProp[ABaseLevel1][ASecondLevel1][i1].Simple or FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
        ACountInSecondLevel := FSecondContainer.FAxisTree[ASecondLevel1].Count
      else
        ACountInSecondLevel := FLevelsProp[ABaseLevel1][ASecondLevel1][i1].Count;
      if ACapacity < ACountInSecondLevel then
      begin
        ACapacity :=  ACountInSecondLevel;
        ReallocMem(ASortArray, ACapacity * SizeOf(Integer));
      end;
      ACount := 0;
      AParentIndex := -2;
      if FLevelsProp[ABaseLevel1][ASecondLevel1][i1].Simple or FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
      begin
        for j1 := 0 to ACountInSecondLevel - 1 do
        begin
          if PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[i1][j1] <> nil then
          begin
            if (AParentIndex = -2) then
              AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[j1].IndParent
            else
            if (FSecondContainer.FAxisTree[ASecondLevel1].Nodes[j1].IndParent <> AParentIndex) then
            begin
              SetRank1;
              AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[j1].IndParent;
              ACount := 0;
            end;
            Find(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[i1][j1]).Value, AIndex);
            Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
            ASortArray[AIndex] := j1;
            ACount := ACount + 1;
          end
        end

      end
      else
      begin
        for j1 := 0 to ACountInSecondLevel - 1 do
        begin
          if (AParentIndex = -2) then
            AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[FLevelsProp[ABaseLevel1][ASecondLevel1][i1].IndexList[j1]].IndParent
          else
          if  (FSecondContainer.FAxisTree[ASecondLevel1].Nodes[FLevelsProp[ABaseLevel1][ASecondLevel1][i1].IndexList[j1]].IndParent <> AParentIndex) then
          begin
            SetRank1;
            AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[FLevelsProp[ABaseLevel1][ASecondLevel1][i1].IndexList[j1]].IndParent;
            ACount := 0;
          end;

          Find(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[i1][j1]).Value, AIndex);
          Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
          ASortArray[AIndex] := j1;
          ACount := ACount + 1;
        end
      end;
      if ACount > 0 then
        SetRank1;
    end;

    if ACapacity < FBaseContainer.FAxisTree[ABaseLevel1].Count then
    begin
      ACapacity := FBaseContainer.FAxisTree[ABaseLevel1].Count;
      ReallocMem(ASortArray, ACapacity * SizeOf(Integer));
    end;
    GetMem(ABaseIndexArray, ACapacity * SizeOf(Integer));
    for k1 := 0 to FSecondContainer.FAxisTree[ASecondLevel1].Count - 1 do
    begin
      ACount := 0;
      AParentIndex := -2;
      for i1 := 0 to FBaseContainer.FAxisTree[ABaseLevel1].Count - 1 do
      begin
        ABaseIndex1 := FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[i1];
        AFindIndex := k1;
        if FLevelsProp[ABaseLevel1][ASecondLevel1][i1].Simple or FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
          AMeasureValue := PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[i1][AFindIndex]
        else
          if FindByIndex(ABaseLevel1, ASecondLevel1, i1, AFindIndex, AFindIndex) then
            AMeasureValue := PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[i1][AFindIndex]
          else
            AMeasureValue := nil;

        if AMeasureValue <> nil then
        begin
          if (AParentIndex = -2) then
            AParentIndex := FBaseContainer.FAxisTree[ABaseLevel1].Nodes[i1].IndParent
          else
          if  (FBaseContainer.FAxisTree[ABaseLevel1].Nodes[i1].IndParent <> AParentIndex) then
          begin
            SetRank2;
            AParentIndex := FBaseContainer.FAxisTree[ABaseLevel1].Nodes[i1].IndParent;
            ACount := 0;
          end;

          Find2(AMeasureValue.Value, AIndex);
          Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
          Move(ABaseIndexArray[AIndex], ABaseIndexArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
          ASortArray[AIndex] := AFindIndex;
          ABaseIndexArray[AIndex] := ABaseIndex1;
          ACount := ACount + 1;
        end;
      end;
      if ACount > 0 then
        SetRank2;
    end;

    FreeMem(ASortArray);
    FreeMem(ABaseIndexArray);
  end;
var
  ACountInSecondLevel: Integer;
begin
  AMeasureField := FMeasureFields[AMeasureIndex];
  if AMeasureField.FBaseRanks <> nil then
    exit;
  GetMem(AMeasureField.FBaseRanks, (FSaveCountBaseLevels + 1) * SizeOf(_fcxPointerArray));
  GetMem(AMeasureField.FSecondRanks, (FSaveCountBaseLevels + 1) * SizeOf(_fcxPointerArray));
  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
  begin
    GetMem(AMeasureField.FBaseRanks[ABaseLevel], (FSaveCountSecondLevels + 1) * SizeOf(_fcxPointerArray));
    GetMem(AMeasureField.FSecondRanks[ABaseLevel], (FSaveCountSecondLevels + 1) * SizeOf(_fcxPointerArray));
    for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
    begin
      GetMem(AMeasureField.FBaseRanks[ABaseLevel][ASecondLevel], FCount * SizeOf(_fcxPointerArray));
      GetMem(AMeasureField.FSecondRanks[ABaseLevel][ASecondLevel], FCount * SizeOf(_fcxPointerArray));
      for i := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
      begin
        if FLevelsProp[ABaseLevel][ASecondLevel][i].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
          ACountInSecondLevel := FSecondContainer.FAxisTree[ASecondLevel].Count
        else
          ACountInSecondLevel := FLevelsProp[ABaseLevel][ASecondLevel][i].Count;
        GetMem(PfcxArrIntegerArray(AMeasureField.FBaseRanks[ABaseLevel][ASecondLevel])[i], ACountInSecondLevel * SizeOf(Integer));
        GetMem(PfcxArrIntegerArray(AMeasureField.FSecondRanks[ABaseLevel][ASecondLevel])[i], ACountInSecondLevel * SizeOf(Integer));
        FillChar(PfcxArrIntegerArray(AMeasureField.FBaseRanks[ABaseLevel][ASecondLevel])[i]^, ACountInSecondLevel * SizeOf(Integer), 0);
        FillChar(PfcxArrIntegerArray(AMeasureField.FSecondRanks[ABaseLevel][ASecondLevel])[i]^, ACountInSecondLevel * SizeOf(Integer), 0);
      end;
    end;
  end;
  for ABaseLevel := FSaveCountBaseLevels - 1 downto -1 do
  begin
    for ASecondLevel := FSaveCountSecondLevels - 1 downto -1 do
    begin
      CalcRankInternal(AMeasureIndex, ABaseLevel, ASecondLevel);
    end;
  end;
end;

procedure TfcxMeasuresContainer.CalcMaxMins(
  AMeasureIndex: TfcxSmallCount);
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  i: integer;
  AMeasureField: TfcxMeasureField;

  procedure CalcMaxMinsInternal(ABaseLevel1, ASecondLevel1: TfcxSmallCount);
  var
    ABaseIndexInLevel, ANewBaseIndexInLevel, j1, AIndexInSecondLevel: integer;
    ABaseIndex, ANewBaseIndex, ASecondIndex, AFindIndex: integer;
    AParentIndex: integer;
    ACount: integer;
    AMeasureValue: PfcxMeasureValue;
    AMin, AMax, AValue: Double;

    procedure SetHighlights1;
    var
      ASecondIndexTotal: integer;
      AFindIndex1, AIndexInSecondLevel1: integer;
    begin
      ASecondIndexTotal := FSecondContainer.FIndexOfParent[ASecondIndex];
      AIndexInSecondLevel1 := FSecondContainer.FIndexInLevel[ASecondIndexTotal];
      AFindIndex1 := AIndexInSecondLevel1;
      if not (FLevelsProp[ABaseLevel1][ASecondLevel1-1][ABaseIndexInLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1-1].CreateAllCells) then
        FindByIndex(ABaseLevel1, ASecondLevel1-1, ABaseIndexInLevel, AFindIndex1, AFindIndex1);
      PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel1][ASecondLevel1-1])[ABaseIndexInLevel][AFindIndex1].MaxSecond := AMax;
      PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel1][ASecondLevel1-1])[ABaseIndexInLevel][AFindIndex1].MinSecond := AMin;
    end;

    procedure SetHighlights2;
    var
      ABaseIndexInLevel2, AFindIndex1: integer;
    begin
      ABaseIndexInLevel2 := FBaseContainer.FIndexInLevel[FBaseContainer.FIndexOfParent[ABaseIndex]];
      AFindIndex1 := AIndexInSecondLevel;
      if not (FLevelsProp[ABaseLevel1-1][ASecondLevel1][ABaseIndexInLevel2].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1-1][ASecondLevel1].CreateAllCells) then
        FindByIndex(ABaseLevel1-1, ASecondLevel1, ABaseIndexInLevel2, AFindIndex1, AFindIndex1);
      PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel1-1][ASecondLevel1])[ABaseIndexInLevel2][AFindIndex1].MaxBase := AMax;
      PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel1-1][ASecondLevel1])[ABaseIndexInLevel2][AFindIndex1].MinBase := AMin;
    end;

  begin

    if ASecondLevel1 > -1 then
      for ABaseIndexInLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel1].Count - 1 do
      begin
        ABaseIndex := FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[ABaseIndexInLevel];
        ACount := 0;
        AParentIndex := -2;
        if FLevelsProp[ABaseLevel1][ASecondLevel1][ABaseIndexInLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
        begin
          for AIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel1].Count - 1 do
          begin
            if PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][AIndexInSecondLevel] <> nil then
            begin
              AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][AIndexInSecondLevel]).Value);
              if (AParentIndex = -2) then
              begin
                AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent;
                AMin := AValue;
                AMax := AValue;
              end
              else
              if  (FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent <> AParentIndex) then
              begin
                SetHighlights1;
                AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent;
                ACount := 0;
                AMin := AValue;
                AMax := AValue;
              end
              else
              begin
                if AMin > AValue then
                  AMin := AValue;
                if AMax < AValue then
                  AMax := AValue;
              end;
              ASecondIndex := FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel];
              inc(ACount);
            end
          end
        end
        else
        begin
          for j1 := 0 to FLevelsProp[ABaseLevel1][ASecondLevel1][ABaseIndexInLevel].Count - 1 do
          begin
            AIndexInSecondLevel := FLevelsProp[ABaseLevel1][ASecondLevel1][ABaseIndexInLevel].IndexList[j1];
            AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][j1]).Value);
            if (AParentIndex = -2) then
            begin
              AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent;
              AMin := AValue;
              AMax := AValue;
            end
            else
            if (FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent <> AParentIndex) then
            begin
              SetHighlights1;
              AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent;
              ACount := 0;
              AMin := AValue;
              AMax := AValue;
            end
            else
            begin
              if AMin > AValue then
                AMin := AValue;
              if AMax < AValue then
                AMax := AValue;
            end;
            inc(ACount);
            ASecondIndex := FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel];
          end
        end;
        if ACount > 0 then
          SetHighlights1;
      end;
    if ABaseLevel1 > -1 then
      for AIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel1].Count - 1 do
      begin
        ASecondIndex := FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel];
        ACount := 0;
        AParentIndex := -2;
        for ANewBaseIndexInLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel1].Count - 1 do
        begin
          ANewBaseIndex := FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[ANewBaseIndexInLevel];
          AFindIndex := AIndexInSecondLevel;
          if FLevelsProp[ABaseLevel1][ASecondLevel1][ANewBaseIndexInLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
            AMeasureValue := PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ANewBaseIndexInLevel][AFindIndex]
          else
            if FindByIndex(ABaseLevel1, ASecondLevel1, ANewBaseIndexInLevel, AFindIndex, AFindIndex) then
              AMeasureValue := PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ANewBaseIndexInLevel][AFindIndex]
            else
              AMeasureValue := nil;

          if AMeasureValue <> nil then
          begin
            AValue := VarToDouble(AMeasureValue.Value);
            if (AParentIndex = -2) then
            begin
              AParentIndex := FBaseContainer.FAxisTree[ABaseLevel1].Nodes[ANewBaseIndexInLevel].IndParent;
              AMin := AValue;
              AMax := AValue;
            end
            else
            if  (FBaseContainer.FAxisTree[ABaseLevel1].Nodes[ANewBaseIndexInLevel].IndParent <> AParentIndex) then
            begin
              SetHighlights2;
              AParentIndex := FBaseContainer.FAxisTree[ABaseLevel1].Nodes[ANewBaseIndexInLevel].IndParent;
              AMin := AValue;
              AMax := AValue;
              ACount := 0;
            end
            else
            begin
              if AMin > AValue then
                AMin := AValue;
              if AMax < AValue then
                AMax := AValue;
            end;
            inc(ACount);
            ABaseIndexInLevel := ANewBaseIndexInLevel;
            ABaseIndex := ANewBaseIndex;
          end;
        end;
        if ACount > 0 then
          SetHighlights2;
      end;

  end;
var
  ACountInSecondLevel: Integer;
begin
  AMeasureField := FMeasureFields[AMeasureIndex];
  if AMeasureField.FCellHighlights <> nil then
    exit;
  GetMem(AMeasureField.FCellHighlights, (FSaveCountBaseLevels + 1) * SizeOf(_fcxPointerArray));
  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
  begin
    GetMem(AMeasureField.FCellHighlights[ABaseLevel], (FSaveCountSecondLevels + 1) * SizeOf(_fcxPointerArray));
    for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
    begin
      GetMem(AMeasureField.FCellHighlights[ABaseLevel][ASecondLevel], FBaseContainer.FAxisTree[ABaseLevel].Count * SizeOf(_fcxPointerArray));
      for i := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
      begin
        if FLevelsProp[ABaseLevel][ASecondLevel][i].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
          ACountInSecondLevel := FSecondContainer.FAxisTree[ASecondLevel].Count
        else
          ACountInSecondLevel := FLevelsProp[ABaseLevel][ASecondLevel][i].Count;
        GetMem(PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel][ASecondLevel])[i], ACountInSecondLevel * SizeOf(TfcxMeasureCellHighlights));
        FillChar(PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel][ASecondLevel])[i]^, ACountInSecondLevel * SizeOf(TfcxMeasureCellHighlights), 0);
      end;
    end;
  end;

  for ABaseLevel := FSaveCountBaseLevels - 1 downto -1 do
    for ASecondLevel := FSaveCountSecondLevels - 1 downto -1 do
      if (ABaseLevel <> -1) or (ASecondLevel <> -1) then
        CalcMaxMinsInternal(ABaseLevel, ASecondLevel);
end;

procedure TfcxMeasuresContainer.CalcPercentile(
  AMeasureIndex: TfcxSmallCount; APercentile: Double);
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  AKoeff: Double;
  AMeasureField: TfcxMeasureField;

  procedure CalcPercentileInternal(ABaseLevel1, ASecondLevel1: TfcxSmallCount);
  var
    ABaseIndexInLevel, ANewBaseIndexInLevel, j1, AIndexInSecondLevel: integer;
    ABaseIndex, ANewBaseIndex, ASecondIndex, AFindIndex: integer;
    AParentIndex: integer;
    ACount: integer;
    AMeasureValue: PfcxMeasureValue;
    AValue: Double;
    ASortArray: PfcxIntegerArray;
    ABaseIndexArray: PfcxIntegerArray;
    ACapacity, AIndex: integer;

    function Find(AValue: Variant; var AIndex: Integer): Boolean;
    var
      ALeft, ARight, AMiddle: Integer;
      AValue2: Variant;
    begin
      Result := False;
      ALeft := 0;
      ARight := ACount - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        AValue2 := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][ASortArray[AMiddle]]).Value;
        if AValue2 = AValue then
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end
        else
        if AValue2 > AValue then
          ARight := AMiddle - 1
        else
          ALeft := AMiddle + 1;
      end;
      AIndex := ALeft;
    end;

    function Find2(AValue: Variant; var AIndex: Integer): Boolean;
    var
      ALeft, ARight, AMiddle: Integer;
      AValue2: Variant;
    begin
      Result := False;
      ALeft := 0;
      ARight := ACount - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        AValue2 := PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[FBaseContainer.FLevelOf[ABaseIndexArray[AMiddle]]][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[AMiddle]]][ASortArray[AMiddle]]).Value;
        if AValue2 = AValue then
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end
        else
        if AValue2 > AValue then
          ARight := AMiddle - 1
        else
          ALeft := AMiddle + 1;
      end;
      AIndex := ALeft;
    end;

    procedure SetPercentile1;
    var
      ASecondIndexTotal: integer;
      AFindIndex1, AIndexInSecondLevel1: integer;
      AStopPoint, AValue: Double;
    begin
      if ACount > 1 then
      begin
        AStopPoint := ACount * AKoeff;
        if (AStopPoint = 0) or (AStopPoint <> trunc(AStopPoint)) then
          AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][ASortArray[trunc(AStopPoint)]]).Value)
        else
        if (AStopPoint = ACount) then
          AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][ASortArray[trunc(AStopPoint) - 1]]).Value)
        else
          AValue := VarToDouble((PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][ASortArray[trunc(AStopPoint)]]).Value + PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][ASortArray[trunc(AStopPoint) - 1]]).Value) / 2);
      end
      else
        AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][ASortArray[0]]).Value);
      ASecondIndexTotal := FSecondContainer.FIndexOfParent[ASecondIndex];
      AIndexInSecondLevel1 := FSecondContainer.FIndexInLevel[ASecondIndexTotal];
      AFindIndex1 := AIndexInSecondLevel1;
      if not (FLevelsProp[ABaseLevel1][ASecondLevel1-1][ABaseIndexInLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1-1].CreateAllCells) then
        FindByIndex(ABaseLevel1, ASecondLevel1-1, ABaseIndexInLevel, AFindIndex1, AFindIndex1);
      ReallocMem(PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel1][ASecondLevel1-1])[ABaseIndexInLevel][AFindIndex1].PercentilesSecond, AMeasureField.FPercentilesCount * SizeOf(Double));
      PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel1][ASecondLevel1-1])[ABaseIndexInLevel][AFindIndex1].PercentilesSecond[AMeasureField.FPercentilesCount - 1] := AValue;
    end;

    procedure SetPercentile2;
    var
      ABaseIndexInLevel2: integer;
      AFindIndex1: integer;
      AStopPoint, AValue: Double;
    begin
      if ACount > 1 then
      begin
        AStopPoint := ACount * AKoeff;
        if (AStopPoint = 0) or (AStopPoint <> trunc(AStopPoint)) then
          AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[trunc(AStopPoint)]]][ASortArray[trunc(AStopPoint)]]).Value)
        else
        if (AStopPoint = ACount) then
          AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[trunc(AStopPoint) - 1]]][ASortArray[trunc(AStopPoint) - 1]]).Value)
        else
          AValue := VarToDouble((PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[trunc(AStopPoint)]]][ASortArray[trunc(AStopPoint)]]).Value + PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[trunc(AStopPoint)-1]]][ASortArray[trunc(AStopPoint)-1]]).Value) / 2);
      end
      else
        AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[FBaseContainer.FIndexInLevel[ABaseIndexArray[0]]][ASortArray[0]]).Value);
      ABaseIndexInLevel2 := FBaseContainer.FIndexInLevel[FBaseContainer.FIndexOfParent[ABaseIndex]];
      AFindIndex1 := AIndexInSecondLevel;
      if not (FLevelsProp[ABaseLevel1-1][ASecondLevel1][ABaseIndexInLevel2].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1-1][ASecondLevel1].CreateAllCells) then
        FindByIndex(ABaseLevel1-1, ASecondLevel1, ABaseIndexInLevel2, AFindIndex1, AFindIndex1);
      ReallocMem(PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel1-1][ASecondLevel1])[ABaseIndexInLevel2][AFindIndex1].PercentilesBase, AMeasureField.FPercentilesCount * SizeOf(Double));
      PfcxArrayMeasureCellHighlightsArray(AMeasureField.FCellHighlights[ABaseLevel1-1][ASecondLevel1])[ABaseIndexInLevel2][AFindIndex1].PercentilesBase[AMeasureField.FPercentilesCount - 1] := AValue;
    end;
  var
    ACountInSecondLevel: Integer;
  begin
    ASortArray := nil;
    ABaseIndexArray := nil;
    ACapacity := 0;

    if ASecondLevel1 > -1 then
      for ABaseIndexInLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel1].Count - 1 do
      begin
        ABaseIndex := FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[ABaseIndexInLevel];
        if FLevelsProp[ABaseLevel1][ASecondLevel1][ABaseIndexInLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
          ACountInSecondLevel := FSecondContainer.FAxisTree[ASecondLevel1].Count
        else
          ACountInSecondLevel := FLevelsProp[ABaseLevel1][ASecondLevel1][ABaseIndexInLevel].Count;
        if ACapacity < ACountInSecondLevel then
        begin
          ACapacity :=  ACountInSecondLevel;
          ReallocMem(ASortArray, ACapacity * SizeOf(Integer));
        end;
        ACount := 0;
        AParentIndex := -2;
        if FLevelsProp[ABaseLevel1][ASecondLevel1][ABaseIndexInLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
        begin
          for AIndexInSecondLevel := 0 to ACountInSecondLevel - 1 do
          begin
            if PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][AIndexInSecondLevel] <> nil then
            begin
              AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][AIndexInSecondLevel]).Value);
              if (AParentIndex = -2) then
              begin
                AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent;
              end
              else
              if  (FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent <> AParentIndex) then
              begin
                SetPercentile1;
                AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent;
                ACount := 0;
              end;
              Find(AValue, AIndex);
              Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
              ASortArray[AIndex] := AIndexInSecondLevel;
              ASecondIndex := FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel];
              inc(ACount);
            end
          end
        end
        else
        begin
          for j1 := 0 to ACountInSecondLevel - 1 do
          begin
            AIndexInSecondLevel := FLevelsProp[ABaseLevel1][ASecondLevel1][ABaseIndexInLevel].IndexList[j1];
            AValue := VarToDouble(PfcxMeasureValue(PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ABaseIndexInLevel][j1]).Value);
            if (AParentIndex = -2) then
            begin
              AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent;
            end
            else
            if (FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent <> AParentIndex) then
            begin
              SetPercentile1;
              AParentIndex := FSecondContainer.FAxisTree[ASecondLevel1].Nodes[AIndexInSecondLevel].IndParent;
              ACount := 0;
            end;
            Find(AValue, AIndex);
            Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
            ASortArray[AIndex] := j1;
            inc(ACount);
            ASecondIndex := FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel];
          end
        end;
        if ACount > 0 then
          SetPercentile1;
      end;
    if ABaseLevel1 > -1 then
    begin
      if ACapacity < FBaseContainer.FAxisTree[ABaseLevel1].Count then
      begin
        ACapacity := FBaseContainer.FAxisTree[ABaseLevel1].Count;
        ReallocMem(ASortArray, ACapacity * SizeOf(Integer));
      end;
      GetMem(ABaseIndexArray, ACapacity * SizeOf(Integer));
      for AIndexInSecondLevel := 0 to FSecondContainer.FAxisTree[ASecondLevel1].Count - 1 do
      begin
        ASecondIndex := FSecondContainer.FAxisTree[ASecondLevel1].IndexInLastLevel[AIndexInSecondLevel];
        ACount := 0;
        AParentIndex := -2;
        for ANewBaseIndexInLevel := 0 to FBaseContainer.FAxisTree[ABaseLevel1].Count - 1 do
        begin
          ANewBaseIndex := FBaseContainer.FAxisTree[ABaseLevel1].IndexInLastLevel[ANewBaseIndexInLevel];
          AFindIndex := AIndexInSecondLevel;
          if FLevelsProp[ABaseLevel1][ASecondLevel1][ANewBaseIndexInLevel].Simple or AMeasureField.FMeasurePropInLevelsListArray[ABaseLevel1][ASecondLevel1].CreateAllCells then
            AMeasureValue := PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ANewBaseIndexInLevel][AFindIndex]
          else
            if FindByIndex(ABaseLevel1, ASecondLevel1, ANewBaseIndexInLevel, AFindIndex, AFindIndex) then
              AMeasureValue := PfcxArrPointerArray(AMeasureField.FMeasureCells[ABaseLevel1][ASecondLevel1])[ANewBaseIndexInLevel][AFindIndex]
            else
              AMeasureValue := nil;

          if AMeasureValue <> nil then
          begin
            AValue := VarToDouble(AMeasureValue.Value);
            if (AParentIndex = -2) then
            begin
              AParentIndex := FBaseContainer.FAxisTree[ABaseLevel1].Nodes[ANewBaseIndexInLevel].IndParent;
            end
            else
            if  (FBaseContainer.FAxisTree[ABaseLevel1].Nodes[ANewBaseIndexInLevel].IndParent <> AParentIndex) then
            begin
              SetPercentile2;
              AParentIndex := FBaseContainer.FAxisTree[ABaseLevel1].Nodes[ANewBaseIndexInLevel].IndParent;
              ACount := 0;
            end;
            Find2(AValue, AIndex);
            Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
            Move(ABaseIndexArray[AIndex], ABaseIndexArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
            ASortArray[AIndex] := AFindIndex;
            ABaseIndexArray[AIndex] := ANewBaseIndex;
            inc(ACount);
            ABaseIndexInLevel := ANewBaseIndexInLevel;
            ABaseIndex := ANewBaseIndex;
          end;
        end;
        if ACount > 0 then
          SetPercentile2;
      end;
    end;
    FreeMem(ASortArray);
    FreeMem(ABaseIndexArray);

  end;

begin
  AMeasureField := FMeasureFields[AMeasureIndex];
  AKoeff := APercentile / 100;
  inc(AMeasureField.FPercentilesCount);
  ReallocMem(AMeasureField.FPercentiles, AMeasureField.FPercentilesCount * SizeOf(_fcxDoubleArray));
  AMeasureField.FPercentiles[AMeasureField.FPercentilesCount - 1] := APercentile;

  for ABaseLevel := FSaveCountBaseLevels - 1 downto -1 do
    for ASecondLevel := FSaveCountSecondLevels - 1 downto -1 do
      if (ABaseLevel <> -1) or (ASecondLevel <> -1) then
        CalcPercentileInternal(ABaseLevel, ASecondLevel);

end;

procedure TfcxMeasuresContainer.ClearMeasure(AMeasureIndex: Integer);
var
  i, j: Integer;
  ABaseLevel, ASecondLevel, AAdditionalTotalIndex: TfcxSmallCount;
begin
  for ABaseLevel := -1 to FSaveCountBaseLevels - 1 do
  begin
    for ASecondLevel := -1 to FSaveCountSecondLevels - 1 do
    begin
      for i := 0 to FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
      begin
        if FLevelsProp[ABaseLevel][ASecondLevel][i].Simple or FMeasureFields[AMeasureIndex].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
        begin
          for j := 0 to FSecondContainer.FAxisTree[ASecondLevel].Count - 1 do
            if PfcxArrPointerArray(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[i][j] <> nil then
              TfcxMeasureProcessor.DestroyMeasure(PfcxArrPointerArray(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[i][j]);
          if ABaseLevel <> (FSaveCountBaseLevels - 1) then
            for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
              for j := 0 to FSecondContainer.FAxisTree[ASecondLevel].Count - 1 do
                if PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[i][j] <> nil then
                  TfcxMeasureProcessor.DestroyMeasure(PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[i][j]);
          if ASecondLevel <> (FSaveCountSecondLevels - 1) then
            for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
              for j := 0 to FSecondContainer.FAxisTree[ASecondLevel].Count - 1 do
                if PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[i][j] <> nil then
                  TfcxMeasureProcessor.DestroyMeasure(PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[i][j]);
        end
        else
        begin
          for j := 0 to FLevelsProp[ABaseLevel][ASecondLevel][i].Count - 1 do
            TfcxMeasureProcessor.DestroyMeasure(PfcxArrPointerArray(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[i][j]);
          if ABaseLevel <> (FSaveCountBaseLevels - 1) then
            for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
              for j := 0 to FLevelsProp[ABaseLevel][ASecondLevel][i].Count - 1 do
                TfcxMeasureProcessor.DestroyMeasure(PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[i][j]);
          if ASecondLevel <> (FSaveCountSecondLevels - 1) then
            for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
              for j := 0 to FLevelsProp[ABaseLevel][ASecondLevel][i].Count - 1 do
                TfcxMeasureProcessor.DestroyMeasure(PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[i][j]);
        end;
        FreeMem(PfcxArrPointerArray(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel])[i]);
        if ABaseLevel <> (FSaveCountBaseLevels - 1) then
          for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
            FreeMem(PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[i]);
        if ASecondLevel <> (FSaveCountSecondLevels - 1) then
          for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
            FreeMem(PfcxArrPointerArray(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex])[i]);
      end;
      FreeMem(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel][ASecondLevel]);
      if ABaseLevel <> (FSaveCountBaseLevels - 1) then
      begin
        for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsBaseLevels[ABaseLevel] - 1 do
          FreeMem(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex]);
        FreeMem(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel][ASecondLevel]);
      end;
      if ASecondLevel <> (FSaveCountSecondLevels - 1) then
        for AAdditionalTotalIndex := 0 to FSaveCountAdditionalTotalsSecondLevels[ASecondLevel] - 1 do
          FreeMem(PfcxPointerArray(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel])[AAdditionalTotalIndex]);
        FreeMem(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel][ASecondLevel]);
    end;
    FreeMem(FMeasureFields[AMeasureIndex].FMeasureCells[ABaseLevel]);
    if ABaseLevel <> (FSaveCountBaseLevels - 1) then
      FreeMem(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase[ABaseLevel]);
    FreeMem(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond[ABaseLevel]);
  end;
  FreeMem(FMeasureFields[AMeasureIndex].FMeasureCells);
  FMeasureFields[AMeasureIndex].FMeasureCells := nil;
  FreeMem(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase);
  FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsBase := nil;
  FreeMem(FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond);
  FMeasureFields[AMeasureIndex].FAdditionalTotalsMeasureCellsSecond := nil;

  FMeasureFields[AMeasureIndex].ClearRanks;
  FMeasureFields[AMeasureIndex].ClearCellHighlights;
  FMeasureFields[AMeasureIndex].ClearMeasurePropArray;
  FMeasureFields[AMeasureIndex].ClearTotalPositionArray;
  dec(FSaveCountMeasures);
end;

procedure TfcxMeasuresContainer.SaveToXML(AItem: TfcxXMLItem);
var
  I: Integer;
  AItem1: TfcxXMLItem;
begin
  inherited SaveToXML(AItem);
  AItem.Prop['caption'] := Caption;
  AItem.IntProp['CaptionWidth'] := CaptionWidth;
  if FContainer = FSlice.FXAxisContainer then
    AItem.Prop['Region'] := 'X'
  else
  if FContainer = FSlice.FYAxisContainer then
    AItem.Prop['Region'] := 'Y'
  else
    AItem.Prop['Region'] := '';
  AItem.IntProp['Position'] := FPosition;
  AItem1 := AItem.Add;
  AItem1.Name := 'fields';
    for I := 0 to MeasureFields.Count - 1 do
      MeasureFields[I].SaveToXML(AItem1.Add);
end;

procedure TfcxMeasuresContainer.LoadFromXML(AItem: TfcxXMLItem);
var
  i, j: Integer;
  Field: TfcxMeasureField;
begin
  inherited;
  Caption := AItem.Prop['caption'];
  CaptionWidth := AItem.IntProp['CaptionWidth'];
  for i := 0 to AItem.Count - 1 do
  begin
    if AItem[i].Name = 'fields' then
    begin
      for j := 0 to AItem[i].Count - 1 do
      begin
        Field := TfcxMeasureField.Create(MeasureFields);
        if not Field.LoadFromXML(AItem[i][j]) then
          Field.Free
        else
          MeasureFields.Add(Field);
      end;
      break;
    end;
  end;
  if AItem.Prop['Region'] = 'X' then
    SetContainer(FSlice.FXAxisContainer)
  else
  if AItem.Prop['Region'] = 'Y' then
    SetContainer(FSlice.FYAxisContainer);
  SetPosition(AItem.IntProp['Position']);
end;

function TfcxMeasuresContainer.GetDetailRecordsCount(const ABaseLevel, ASecondLevel: TfcxSmallCount;
  const AIndexInBaseLevel, AIndexInSecondLevel: Integer): integer;
var
  AFindIndex: Integer;
begin
  if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple then
    Result := FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].CellDetailInfo[AIndexInSecondLevel].Count
  else
  if FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AFindIndex) then
    Result := FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].CellDetailInfo[AFindIndex].Count
  else
    Result := 0;
end;

function TfcxMeasuresContainer.GetDetailRecords(const ABaseLevel, ASecondLevel: TfcxSmallCount;
  const AIndexInBaseLevel, AIndexInSecondLevel, ARecIndex: Integer): integer;
var
  ACount, AFindIndex, i: Integer;
  ABaseIndex, ASecondIndex: integer;
begin
  ABaseIndex := FBaseContainer.FAxisTree[ABaseLevel].IndexInLastLevel[AIndexInBaseLevel];
  ASecondIndex := FSecondContainer.FAxisTree[ASecondLevel].IndexInLastLevel[AIndexInSecondLevel];

  Result := -1;
  if ABaseLevel = (FSaveCountBaseLevels - 1) then
  begin
    if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple then
      Result := FBaseDetailRecords[AIndexInBaseLevel].Records[FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].CellDetailInfo[AIndexInSecondLevel].Start + ARecIndex]
    else
    if FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AFindIndex) then
      Result := FBaseDetailRecords[AIndexInBaseLevel].Records[FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].CellDetailInfo[AFindIndex].Start + ARecIndex]
    else
      Result := -1;
  end
  else
  if ASecondLevel = (FSaveCountSecondLevels - 1) then
  begin
    if FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].Simple then
      Result := FSecondDetailRecords[AIndexInSecondLevel].Records[FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].CellDetailInfo[AIndexInSecondLevel].Start + ARecIndex]
    else
    if FindByIndex(ABaseLevel, ASecondLevel, AIndexInBaseLevel, AIndexInSecondLevel, AFindIndex) then
      Result := FSecondDetailRecords[AIndexInSecondLevel].Records[FLevelsProp[ABaseLevel][ASecondLevel][AIndexInBaseLevel].CellDetailInfo[AFindIndex].Start + ARecIndex]
    else
      Result := -1;
  end
  else
  begin
    if (FSaveCountSecondLevels - ASecondLevel) < (FSaveCountBaseLevels - ABaseLevel) then
    begin
      ACount := 0;
      for i := 0 to FLevelsProp[ABaseLevel][FSaveCountSecondLevels - 1][AIndexInBaseLevel].Count - 1 do
      begin
        if FLevelsProp[ABaseLevel][FSaveCountSecondLevels - 1][AIndexInBaseLevel].Simple then
          AFindIndex := FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].IndexInLastLevel[i]
        else
          AFindIndex := FSecondContainer.FAxisTree[FSaveCountSecondLevels - 1].IndexInLastLevel[FLevelsProp[ABaseLevel][FSaveCountSecondLevels - 1][AIndexInBaseLevel].IndexList[i]];
        if AFindIndex <> -1 then
        begin
          if FSecondContainer.IndexOfTotalInLevel[AFindIndex, ASecondLevel] = ASecondIndex then
          begin
            if (ACount + FLevelsProp[ABaseLevel][FSaveCountSecondLevels - 1][AIndexInBaseLevel].CellDetailInfo[i].Count) > ARecIndex then
            begin
              Result := FSecondDetailRecords[i].Records[FLevelsProp[ABaseLevel][FSaveCountSecondLevels - 1][AIndexInBaseLevel].CellDetailInfo[i].Start + ARecIndex - ACount];
              Break;
            end;
            ACount := ACount + FLevelsProp[ABaseLevel][FSaveCountSecondLevels - 1][AIndexInBaseLevel].CellDetailInfo[i].Count;
          end
        end;
      end;
    end
    else
    begin
      ACount := 0;
      for i := 0 to FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].Count - 1 do
      begin
        if FBaseContainer.IndexOfTotalInLevel[FBaseContainer.FAxisTree[FSaveCountBaseLevels - 1].IndexInLastLevel[i], ABaseLevel] = ABaseIndex then
        begin
          AFindIndex := AIndexInSecondLevel;
          if not FLevelsProp[FSaveCountBaseLevels - 1][ASecondLevel][i].Simple then
            FindByIndex(FSaveCountBaseLevels - 1, ASecondLevel, i, AFindIndex, AFindIndex);
          if AFindIndex <> -1 then
          begin
            if (ACount + FLevelsProp[FSaveCountBaseLevels - 1][ASecondLevel][i].CellDetailInfo[AFindIndex].Count) > ARecIndex then
            begin
              Result := FBaseDetailRecords[i].Records[FLevelsProp[FSaveCountBaseLevels - 1][ASecondLevel][i].CellDetailInfo[AFindIndex].Start + ARecIndex - ACount];
              Break;
            end;
            ACount := ACount + FLevelsProp[FSaveCountBaseLevels - 1][ASecondLevel][i].CellDetailInfo[AFindIndex].Count;
          end;
        end
      end;
    end;
  end
end;

procedure TfcxMeasuresContainer.MeasureDeleted(
  AMeasureIndex: TfcxSmallCount);
begin
end;

procedure TfcxMeasuresContainer.MeasureMoved(AFromIndex,
  AToIndex: TfcxSmallCount);
begin
end;

procedure TfcxMeasuresContainer.SetContainer(const AContainer: TfcxFieldsContainer);
var
  AOldContainer: TfcxFieldsContainer;
  AChanges: TfcxChangesInSlice;
begin
  if (FSlice.Cube = nil) or not FSlice.Cube.Active then
    Exit;
  if AContainer <> FContainer then
  begin
    FSlice.StartChange;
    AOldContainer := FContainer;
    FContainer := AContainer;
    AChanges := [chs_MeasuresFieldPosition, chs_Fact];
    if AOldContainer is TfcxAxisContainer then
    begin
      TfcxAxisContainer(AOldContainer).FRealLevelCount := AOldContainer.LevelCount;
      TfcxAxisContainer(AOldContainer).MeasuresDeleted;
      if AOldContainer = FSlice.FXAxisContainer then
        AChanges := AChanges + [chs_NeedFillVisibleX, chs_XAxis]
      else
        AChanges := AChanges + [chs_NeedFillVisibleY, chs_YAxis];
    end
    else
      AChanges := AChanges + [chs_Page];
    if FContainer is TfcxAxisContainer then
    begin
      TfcxAxisContainer(FContainer).FRealLevelCount := FContainer.LevelCount + 1;
      TfcxAxisContainer(FContainer).MeasuresInserted;
      if FContainer = FSlice.FXAxisContainer then
        AChanges := AChanges + [chs_NeedFillVisibleX, chs_XAxis]
      else
        AChanges := AChanges + [chs_NeedFillVisibleY, chs_YAxis];
    end
    else
      AChanges := AChanges + [chs_Page];
    FSlice.StopChange(AChanges);
  end;
end;

procedure TfcxMeasuresContainer.SetHeight(const Value: SmallInt);
begin
  if Value = FSlice.DefaultRowHeight then
  begin
    if FHeight <> 0 then
    begin
      FHeight := 0;
      FSlice.StartChange;
      FSlice.StopChange([chs_XHeight]);
    end
  end
  else
  begin
    if FHeight <> Value then
    begin
      FHeight := Value;
      FSlice.StartChange;
      FSlice.StopChange([chs_XHeight]);
    end
  end
end;

procedure TfcxMeasuresContainer.SetWidth(const Value: SmallInt);
begin
  if Value = FSlice.DefaultColWidth then
  begin
    if FWidth <> 0 then
    begin
      FWidth := 0;
      FSlice.StartChange;
      FSlice.StopChange([chs_YWidth]);
    end
  end
  else
  begin
    if FWidth <> Value then
    begin
      FWidth := Value;
      FSlice.StartChange;
      FSlice.StopChange([chs_YWidth]);
    end
  end
end;

procedure TfcxMeasuresContainer.SetCollapsed(const Value: Boolean);
begin
  if FCollapsed <> Value then
  begin
    FCollapsed := Value;
  end
end;

function TfcxMeasuresContainer.GetVisibleMeasures(
  AVisIndex: Integer): TfcxMeasureField;
var
  AMeasureIndex: TfcxSmallCount;
begin
  Result := nil;
  for AMeasureIndex := 0 to FMeasureFields.Count - 1 do
    if FMeasureFields[AMeasureIndex].Visible then
    begin
      if AVisIndex = 0 then
      begin
        Result := FMeasureFields[AMeasureIndex];
        Exit;
      end;
      dec(AVisIndex);
    end;
end;

function TfcxMeasuresContainer.InAxis: Boolean;
begin
  Result := FContainer is TfcxAxisContainer;
end;

procedure TfcxMeasuresContainer.Recalculate;
begin
  Slice.StartChange;
  Slice.StopChange([chs_CapMeasures]);
end;

function TfcxMeasuresContainer.GetVisibleIndex(AIndex: Integer): integer;
var
  AMeasureIndex: TfcxSmallCount;
begin
  Result := -1;
  if not FMeasureFields[AIndex].Visible then
    Exit;
  for AMeasureIndex := 0 to AIndex do
    if FMeasureFields[AMeasureIndex].Visible then
      inc(Result);
end;

function TfcxMeasuresContainer.GetAsPlainText(AStartRow, AStopRow,
  AStartCol, AStopCol: Integer): TfcxString;
var
  I, J: Integer;
  L: TStringList;
  S: TfcxString;
begin
  L := TStringList.Create;
  try
    for I := AStartRow to AStopRow do
    begin
      S:= '';
      for J := AStartCol to AStopCol do
        S := S + FSlice.GetMeasureValueCaption(J, I) + #9;
      Delete(S, Length(S), 1);
      L.Add(S);
    end;
    Result := L.DelimitedText;
  finally
    L.Free;
  end;
end;

procedure TfcxMeasuresContainer.InsertMeasure(AField: TfcxMeasureField;
  AIndex: TfcxSmallCount);
begin
  InsertMeasureInternal(AField, AIndex, True);
end;

procedure TfcxMeasuresContainer.InsertMeasure(ASliceField: TfcxSliceField;
  AName, ACaption: TfcxString; AAgrFunc: TfcxAgrFunc;
  AIndex: TfcxSmallCount);
begin
  InsertMeasure(TfcxMeasureField.Create(FMeasureFields, AAgrFunc, ASliceField, AName, ACaption), AIndex);
end;

function TfcxMeasuresContainer.AddCalcMeasure(AName, ACaption: TfcxString;
  AAgrFunc: TfcxAgrFunc; AScriptFunctionName: String; AScriptFunctionCode: TfcxString): Integer;
begin
  Result := AddCalcMeasure(AName, ACaption, AAgrFunc, AScriptFunctionName, '', AScriptFunctionCode);
end;

procedure TfcxMeasuresContainer.InsertCalcMeasure(AName, ACaption: TfcxString;
  AAgrFunc: TfcxAgrFunc; AScriptFunctionName: String;
  AScriptFunctionCode: TfcxString; AIndex: TfcxSmallCount);
begin
  InsertCalcMeasure(AName, ACaption, AAgrFunc, AScriptFunctionName, '', AScriptFunctionCode, AIndex)
end;

procedure TfcxMeasuresContainer.InsertCalcMeasure(AName,
  ACaption: TfcxString; AAgrFunc: TfcxAgrFunc; AScriptFunctionName: String;
  AScriptFunctionVarSection, AScriptFunctionCode: TfcxString;
  AIndex: TfcxSmallCount);
var
  MeasureField: TfcxMeasureField;
begin
  if not (AAgrFunc in ScriptFuncs) then
    AAgrFunc := af_Formula;
  MeasureField := TfcxMeasureField.Create(FMeasureFields, AAgrFunc, nil, AName, ACaption);
  if AAgrFunc = af_Formula then
    AddCalculationScript(cst_MeasureFormula, AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode)
  else
    AddCalculationScript(cst_MeasureFormulaDetail, AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode);
  MeasureField.ScriptFunction := AScriptFunctionName;
  InsertMeasure(MeasureField, AIndex);
end;

function TfcxMeasuresContainer.ChangeCalcMeasureScript(AName: TfcxString; AScriptFunctionName: String; AScriptFunctionCode: TfcxString): Integer;
begin
  Result := ChangeCalcMeasureScript(AName, AScriptFunctionName, '', AScriptFunctionCode);
end;

function TfcxMeasuresContainer.ChangeCalcMeasureScript(AName: TfcxString; AScriptFunctionName: String; AScriptFunctionVarSection, AScriptFunctionCode: TfcxString): Integer;
begin
  Result := MeasureFields.GetIndexByName(AName);
  if Result <> -1 then
  begin
    with Measures[Result] do
    begin
      if AgrFunc = af_Formula then
        AddCalculationScript(cst_MeasureFormula, AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode)
      else
        AddCalculationScript(cst_MeasureFormulaDetail, AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode);
      ScriptFunction := AScriptFunctionName;
    end;
  end
end;

function TfcxMeasuresContainer.AddCalcMeasure(AName, ACaption: TfcxString;
  AAgrFunc: TfcxAgrFunc; AScriptFunctionName: String;
  AScriptFunctionVarSection, AScriptFunctionCode: TfcxString): Integer;
begin
  Result := FMeasureFields.Count;
  InsertCalcMeasure(AName, ACaption, AAgrFunc, AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode, Result);
  if Result = FMeasureFields.Count then
    Result := -1;
end;

procedure TfcxMeasuresContainer.DeleteMeasures;
var
  i: integer;
begin
  if Count > 0 then
  begin
    Slice.StartChange;
    for i := Count - 1 downto 0 do
      DeleteMeasureInternal(i, False);
    if FSlice.FYAxisContainer.FDefaultTypeSort in cfc_SortByValue  then
      FSlice.FYAxisContainer.DefaultTypeSort := md_tsa_ByAxisValue;
    if FSlice.FXAxisContainer.FDefaultTypeSort in cfc_SortByValue  then
      FSlice.FXAxisContainer.DefaultTypeSort := md_tsa_ByAxisValue;
    if FContainer is TfcxAxisContainer then
    begin
      TfcxAxisContainer(FContainer).FAxisSourceTree.ResetLevelsType;
      TfcxAxisContainer(FContainer).ResetLevelsType;
    end;
    Slice.StopChange([chs_CapMeasures]);
  end
end;

function TfcxMeasuresContainer.AddMeasureInternal(AField: TfcxMeasureField;
  ADoStopChange: Boolean): Integer;
begin
  Result := FMeasureFields.Count;
  InsertMeasureInternal(AField, Result, ADoStopChange);
end;

procedure TfcxMeasuresContainer.InsertMeasureInternal(
  AField: TfcxMeasureField; AIndex: TfcxSmallCount;
  ADoStopChange: Boolean);
var
  AAllow: boolean;
begin
  if not FSlice.Loading and Assigned(FSlice.FOnBeforeAddSliceFieldToRegion) then
  begin
    AAllow := True;
    FSlice.OnBeforeAddSliceFieldToRegion(AField.Name, FRegion, AAllow);
    if not AAllow then
    begin
//      Result := -1;
      Exit;
    end
  end;
  if ADoStopChange then
    FSlice.StartChange;
  FMeasureFields.Insert(AField, AIndex);
  Inc(FMeasureFields.FVisibleCount);
  if not FSlice.Loading and Assigned(FSlice.FOnAfterAddSliceFieldToRegion) then
    FSlice.OnAfterAddSliceFieldToRegion(AIndex, FRegion);
  if ADoStopChange then
  begin
    if FContainer is TfcxAxisContainer then
      TfcxAxisContainer(FContainer).MeasureInserted(AIndex);
    FSlice.StopChange([chs_CapMeasures]);
  end
end;

procedure TfcxMeasuresContainer.DeleteMeasureInternal(
  AMeasureIndex: TfcxSmallCount; ADoStopChange: Boolean);
var
  AAllow: boolean;
begin
  if (AMeasureIndex < 0) or (AMeasureIndex > (FMeasureFields.Count - 1)) then
    exit;
  AAllow := True;
  if not FSlice.Loading and Assigned(FSlice.FOnBeforeRemoveSliceField) then
    FSlice.OnBeforeRemoveSliceFieldFromRegion(FMeasureFields[AMeasureIndex].Name, FRegion, AAllow);
  if not AAllow then
    Exit;
  if ADoStopChange then
    FSlice.StartChange;
  FSlice.FTopNs.MeasureDeleting(FMeasureFields[AMeasureIndex]);
  ClearMeasure(AMeasureIndex);
  FMeasureFields.Delete(AMeasureIndex);
  Dec(FMeasureFields.FVisibleCount);
  if FSaveCountMeasures <> 0 then
    MeasureDeleted(AMeasureIndex);
  if ADoStopChange then
  begin
    if FContainer is TfcxAxisContainer then
      TfcxAxisContainer(FContainer).MeasureDeleted(AMeasureIndex);
    FSlice.FXAxisContainer.SelectedMeasureDeleted(AMeasureIndex);
    FSlice.FYAxisContainer.SelectedMeasureDeleted(AMeasureIndex);
    FSlice.StopChange([chs_CapMeasures]);
  end;
end;

procedure TfcxMeasuresContainer.AddCalculationScript(ACalcScriptType: TfcxCalcScriptType;
  AScriptFunctionName: String; AScriptFunctionVarSection,
  AScriptFunctionCode: TfcxString);
var
  LinePos: Integer;
begin
  if ACalcScriptType = cst_MeasureFormulaDetail then
    LinePos := fcxLocateEventHandler(FSlice.Script, FSlice.ScriptLanguage, AScriptFunctionName, TypeInfo(TfcxGetValue2))
  else
    LinePos := fcxLocateEventHandler(FSlice.Script, FSlice.ScriptLanguage, AScriptFunctionName, TypeInfo(TfcxGetValue));
  if LinePos = -1 then
  begin
    if ACalcScriptType = cst_MeasureFormulaDetail then
      fcxAddEventWCode(FSlice.Script, FSlice.ScriptLanguage, TypeInfo(TfcxGetValue2), AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode)
    else
      fcxAddEventWCode(FSlice.Script, FSlice.ScriptLanguage, TypeInfo(TfcxGetValue), AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode);
  end
  else
  begin
    if AScriptFunctionCode <> '' then
    begin
    if ACalcScriptType = cst_MeasureFormulaDetail then
      fcxReplaceEvent(FSlice.Script, FSlice.ScriptLanguage, TypeInfo(TfcxGetValue2), AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode)
    else
      fcxReplaceEvent(FSlice.Script, FSlice.ScriptLanguage, TypeInfo(TfcxGetValue), AScriptFunctionName, AScriptFunctionVarSection, AScriptFunctionCode);
    end
  end;
end;

{ TfcxMeasureProcessor }

class procedure TfcxMeasureProcessor.Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep: Integer; AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
end;

class procedure TfcxMeasureProcessor.Func2(
  const AMeasureValue: PfcxMeasureValue; AValue, AValue2: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
end;

class procedure TfcxMeasureProcessor.DestroyMeasure(AMeasureValue: PfcxMeasureValue);
begin
  Finalize(AMeasureValue.Value);
  FreeMem(AMeasureValue);
end;

class procedure TfcxMeasureProcessor.FuncValues(AMeasureValue: PfcxMeasureValue; AValue: Variant; ZeroNull: Boolean; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if (TVarData(AValue).VType <= 1) and ZeroNull then AValue := 0;
  Func(AMeasureValue, AValue, AStep, AIndexValue, AMeasureField);
end;

class procedure TfcxMeasureProcessor.FuncValues2(
  AMeasureValue: PfcxMeasureValue; AValue, AValue2: Variant; ZeroNull: Boolean;
  AStep, AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if ZeroNull then
  begin
    if (TVarData(AValue).VType <= 1) then AValue := 0;
    if (TVarData(AValue2).VType <= 1) then AValue2 := 0;
  end;
  Func2(AMeasureValue, AValue, AValue2, AStep, AIndexValue, AMeasureField);
end;

class function TfcxMeasureProcessor.GetItem(AMeasureValue: PfcxMeasureValue): Variant;
begin
  Result := AMeasureValue.Value;
end;

class function TfcxMeasureProcessor.GetSize: Integer;
begin
  Result := SizeOf(_fcxMeasureValue);
end;

class function TfcxMeasureProcessor.NewMeasureValue(AMeasureField: TfcxMeasureField): PfcxMeasureValue;
var
  ASize: Integer;
begin
  ASize := GetSize;
  GetMem(Result, ASize);
  FillChar(Result^, ASize, 0);
end;

class procedure TfcxMeasureProcessor.SetItem(AMeasureValue: PfcxMeasureValue;
  const Value: Variant);
begin
  AMeasureValue.Value := Value;
end;

{ TfcxMeasureField }

constructor TfcxMeasureField.Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion;
  AAgrFunc: TfcxAgrFunc; AName, ACaption: TfcxString);
begin
  Create(AFieldsOfRegion, AAgrFunc, nil, AName, ACaption);
end;

procedure TfcxMeasureField.ClearMeasurePropArray;
var
  i, j: TfcxSmallCount;
begin
  for i := 0 to FUCArrayCount - 1 do
    FreeMem(FUCArray[i].PointerArray);
  FreeMem(FUCArray);
  FUCArray := nil;
  FUCArrayCount := 0;
  for i := 0 to FVLArrayCount - 1 do
    FreeMem(PfcxMeasureValueWithValuesList(FVLArray[i]).VLArr);
  FreeMem(FVLArray);
  FVLArray := nil;
  FVLArrayCount := 0;
  FreeMem(FVarianceExistsArray);
  FVarianceExistsArray := nil;
  if FMeasurePropInLevelsListArray <> nil then
    for i := -1 to FMeasuresContainer.FSaveCountBaseLevels - 1 do
    begin
      for j := -1 to FMeasuresContainer.FSaveCountSecondLevels - 1 do
        Finalize(FMeasurePropInLevelsListArray[i][j].ScriptFunction);
      FreeMem(FMeasurePropInLevelsListArray[i]);
    end;
  FreeMem(FMeasurePropInLevelsListArray);
  FMeasurePropInLevelsListArray := nil;
end;

constructor TfcxMeasureField.Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion;
  AAgrFunc: TfcxAgrFunc; ASliceField: TfcxSliceField; AName, ACaption: TfcxString;
  ADistinct: Boolean);
begin
  if (ASliceField = nil) and (AAgrFunc = af_Count) and (ACaption = '') then
    ACaption := fcxResources.GetAnsi(sFuncLabels[af_Count]) + fcxResources.GetAnsi('SCounter');
  inherited Create(AFieldsOfRegion, AName, ACaption);
  FDistinct := ADistinct;
  FTotalPosition := fctp_After;
  FAgrFunc := AAgrFunc;
  FSliceField := ASliceField;
  FSliceFieldDistinct := ASliceField;
  FSliceFieldExtra := ASliceField;
  if (FSliceField <> nil) and (FSliceField is TfcxCommonSliceCubeField) then
    FFormat.Assign(FSliceField.DisplayFormat);
  // TODO: assign DisplayFormat
  FAgrFuncForTotals := AAgrFunc;
  FUseXAxisTotalsAsBase := True;
  FConflictResolve := tcrEmptyValue;
  FAlignment := taRightJustify;
  FVisible := True;
  FDisplayAs := da_Value;
  FScriptOrder := -1;
  FCalcAllCells := False;
{ !!! in TfcxMeasureField.Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion)
  FMeasuresContainer := TfcxMeasuresContainer(AFieldsOfRegion.FContainer);
  FHighlights := TfcxMeasureHighlights.Create(Self);
  FHighlights.OnChange := DoHighlightsChange;
}
end;

constructor TfcxMeasureField.Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion;
  AAgrFunc: TfcxAgrFunc; ASliceField, ASliceFieldDistinct: TfcxSliceField;
  AName, ACaption: TfcxString; ADistinct: Boolean);
begin
  Create(AFieldsOfRegion, AAgrFunc, ASliceField, AName, ACaption, ADistinct);
  if Assigned(ASliceFieldDistinct) then
    FSliceFieldDistinct := ASliceFieldDistinct;
end;

constructor TfcxMeasureField.Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion;
  AAgrFunc: TfcxAgrFunc; ASliceField, ASliceFieldDistinct,
  ASliceFieldExtra: TfcxSliceField; AName, ACaption: TfcxString;
  ADistinct: Boolean);
begin
  Create(AFieldsOfRegion, AAgrFunc, ASliceField, ASliceFieldDistinct, AName, ACaption, ADistinct);
  if Assigned(ASliceFieldExtra) then
    FSliceFieldExtra := ASliceFieldExtra;
end;

procedure TfcxMeasureField.CreateMeasurePropArrayS1;
var
{ for future. do not delete
  AIndexCrossCF, AIndexForBaseCF, AIndexForSecondCF: TfcxSmallCount;
  AIndexCrossCT, AIndexForBaseCT, AIndexForSecondCT: TfcxSmallCount;
  AChangedFunction, AChangedTotalToCalc: Boolean;
  ASilceFieldBase, ASilceFieldSecond: TfcxSliceField;
  i1: TfcxSmallCount
}
  i, j: TfcxSmallCount;
begin
  FPassCount := 0;
  GetMem(FMeasurePropInLevelsListArray, SizeOf(PfcxMeasurePropInLevelsList) * (FMeasuresContainer.FSaveCountBaseLevels + 1));
  for i := -1 to FMeasuresContainer.FSaveCountBaseLevels - 1 do
  begin
    GetMem(FMeasurePropInLevelsListArray[i], SizeOf(_fcxMeasurePropInLevels) *  (FMeasuresContainer.FSecondContainer.FLevelCount + 1));
    FillChar(FMeasurePropInLevelsListArray[i]^, SizeOf(_fcxMeasurePropInLevels) * (FMeasuresContainer.FSecondContainer.FLevelCount + 1), 0);
  end;
  for i := FMeasuresContainer.FSaveCountBaseLevels - 1 downto -1 do
  begin
{ for future. do not delete
    if i = -1 then
      ASilceFieldBase := nil
    else
      ASilceFieldBase := FMeasuresContainer.FBaseContainer.Fields[FMeasuresContainer.FBaseContainer.FAxisSourceTree.FFieldIndexOfLevel[i]].FSliceField;
}
    for j := FMeasuresContainer.FSaveCountSecondLevels - 1 downto -1 do
    begin
{ for future. do not delete
      if j = -1 then
        ASilceFieldSecond := nil
      else
        ASilceFieldSecond := FMeasuresContainer.FSecondContainer.Fields[FMeasuresContainer.FSecondContainer.FAxisSourceTree.FFieldIndexOfLevel[j]].FSliceField;
      AChangedFunction := False;
      AIndexCrossCF := -1;
      AIndexForBaseCF := -1;
      AIndexForSecondCF := -1;
      AIndexCrossCT := -1;
      AIndexForBaseCT := -1;
      AIndexForSecondCT := -1;
      for i1 := 0 to FAgrFuncForDimCount - 1 do
      begin
        if FAgrFuncForDimensionArray[i1].CrossDim then
        begin
          if (((ASilceFieldBase = FAgrFuncForDimensionArray[i1].SliceField) and
              (ASilceFieldSecond = FAgrFuncForDimensionArray[i1].SliceField2)) or
             ((ASilceFieldBase = FAgrFuncForDimensionArray[i1].SliceField2) and
              (ASilceFieldSecond = FAgrFuncForDimensionArray[i1].SliceField))) then
          begin
            if FAgrFuncForDimensionArray[i1].UseDifferentAgg then
              AIndexCrossCF := i1;
            if FAgrFuncForDimensionArray[i1].ChangeTotalToCalc then
              AIndexCrossCT := i1;
          end
        end
        else
          if (ASilceFieldBase = FAgrFuncForDimensionArray[i1].SliceField) then
          begin
            if FAgrFuncForDimensionArray[i1].UseDifferentAgg then
              AIndexForBaseCF := i1;
            if FAgrFuncForDimensionArray[i1].ChangeTotalToCalc then
              AIndexForBaseCT := i1;
          end
          else
          if (ASilceFieldSecond = FAgrFuncForDimensionArray[i1].SliceField) then
          begin
            if FAgrFuncForDimensionArray[i1].UseDifferentAgg then
              AIndexForSecondCF := i1;
            if FAgrFuncForDimensionArray[i1].ChangeTotalToCalc then
              AIndexForSecondCT := i1;
          end
      end;
      if AIndexCrossCF <> -1 then
// has properties changing on intersections
      begin
        FMeasurePropInLevelsListArray[i][j].AgrFunc := FAgrFuncForDimensionArray[AIndexCrossCF].AgrFunc;
        AChangedFunction := True;
      end;
      if not AChangedFunction then
        if (AIndexForBaseCF = -1) and (AIndexForSecondCF = -1) then
        begin
}
          if FUseDifferentAggForTotals and
            ((i <> FMeasuresContainer.FSaveCountBaseLevels - 1) or (j <> FMeasuresContainer.FSaveCountSecondLevels - 1)) then
          begin
            FMeasurePropInLevelsListArray[i][j].AgrFunc := FAgrFuncForTotals;
            FMeasurePropInLevelsListArray[i][j].Distinct := FDistinctForTotals and (FAgrFuncForTotals in CanDistinctFuncs);
            FMeasurePropInLevelsListArray[i][j].MeasureProcessorClass := cfcMeasureProcessorMap[FMeasurePropInLevelsListArray[i][j].Distinct, FAgrFuncForTotals];
            FMeasurePropInLevelsListArray[i][j].CreateAllCells := CreateAllCells or CreateAllCellsForTotals;
            FMeasurePropInLevelsListArray[i][j].Expression := FTotalsExpression;
            FMeasurePropInLevelsListArray[i][j].ScriptFunction := FScriptFunctionForTotals;
            FMeasurePropInLevelsListArray[i][j].ScriptOrder := FScriptOrderForTotals;
          end
          else
          begin
            FMeasurePropInLevelsListArray[i][j].AgrFunc := FAgrFunc;
            FMeasurePropInLevelsListArray[i][j].Distinct := FDistinct and (FAgrFunc in CanDistinctFuncs);
            FMeasurePropInLevelsListArray[i][j].MeasureProcessorClass := cfcMeasureProcessorMap[FMeasurePropInLevelsListArray[i][j].Distinct, FAgrFunc];
            FMeasurePropInLevelsListArray[i][j].CreateAllCells := CreateAllCells;
            FMeasurePropInLevelsListArray[i][j].Expression := FExpression;
            FMeasurePropInLevelsListArray[i][j].ScriptFunction := FScriptFunction;
            FMeasurePropInLevelsListArray[i][j].ScriptOrder := FScriptOrder;
          end;
          if not (FMeasurePropInLevelsListArray[i][j].AgrFunc in ScriptFuncs) then
          begin
            FMeasurePropInLevelsListArray[i][j].Expression.Text := '';
            FMeasurePropInLevelsListArray[i][j].ScriptFunction := '';
            FMeasurePropInLevelsListArray[i][j].ScriptOrder := 0;
          end;
{
          if not (FMeasurePropInLevelsListArray[i][j].AgrFunc in CanCreateAllCellsFuncs) and not FCalcTotalsOnTotals and not CreateAllCells then
            FMeasurePropInLevelsListArray[i][j].CreateAllCells := False;
}
{ for future. do not delete
        end
        else
        begin
// has properties changing
          if (AIndexForBaseCF <> -1) and (AIndexForSecondCF <> -1) then
          begin
            if FConflictResolve = tcrEmptyValue then
            begin
              FMeasurePropInLevelsListArray[i][j].AgrFunc := af_None;
              AChangedFunction := True;
            end
            else
            begin
              if (FConflictResolve = tcrUseXAxis) xor FMeasuresContainer.FBaseAxisIsX then
                AIndexForBaseCF := -1
              else
                AIndexForSecondCF := -1
            end;
          end;
          if not AChangedFunction then
            if AIndexForBaseCF <> -1 then
              FMeasurePropInLevelsListArray[i][j].AgrFunc := FAgrFuncForDimensionArray[AIndexForBaseCF].AgrFunc
            else
              FMeasurePropInLevelsListArray[i][j].AgrFunc := FAgrFuncForDimensionArray[AIndexForSecondCF].AgrFunc;
        end;
}
      FMeasurePropInLevelsListArray[i][j].Variance := FMeasurePropInLevelsListArray[i][j].AgrFunc in VarianceFuncs;
      FMeasurePropInLevelsListArray[i][j].FinalNeed := (FMeasurePropInLevelsListArray[i][j].AgrFunc in FinalFuncs)
        or (FMeasurePropInLevelsListArray[i][j].Distinct);
      FMeasurePropInLevelsListArray[i][j].RestrucNeed := (FMeasurePropInLevelsListArray[i][j].AgrFunc in RestrucFuncs)
        or (FMeasurePropInLevelsListArray[i][j].Distinct);

{ for future. do not delete
      AChangedTotalToCalc := False;
      if AIndexCrossCT <> -1 then
      begin
        if FMeasurePropInLevelsListArray[i][j].AgrFunc = af_Formula then
          FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
        else
          FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := FAgrFuncForDimensionArray[AIndexCrossCT].CalcOnTotals;
        if FMeasurePropInLevelsListArray[i][j].UseTotalToCalc then
        begin
          if j = (FMeasuresContainer.FSaveCountSecondLevels - 1) then
            if i = (FMeasuresContainer.FSaveCountBaseLevels - 1) then
              FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
            else
              FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := True
          else
          if i = (FMeasuresContainer.FSaveCountBaseLevels - 1) then
            FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := False
          else
          begin
            if FMeasurePropInLevelsListArray[i + 1][j].AgrFunc = af_None then
              if FMeasurePropInLevelsListArray[i][j + 1].AgrFunc = af_None then
                FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
              else
                FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := False
            else
            if FMeasurePropInLevelsListArray[i][j + 1].AgrFunc = af_None then
              FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := True
            else
            begin
              if ASilceFieldBase = FAgrFuncForDimensionArray[AIndexCrossCT].SliceField then
                FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := FAgrFuncForDimensionArray[AIndexCrossCT].UseFieldAxisTotalsAsBase
              else
                FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := not FAgrFuncForDimensionArray[AIndexCrossCT].UseFieldAxisTotalsAsBase;
            end
          end
        end;
        AChangedTotalToCalc := True;
      end;
      if not AChangedTotalToCalc then
        if (AIndexForBaseCT = -1) and (AIndexForSecondCT = -1) then
        begin
}
          if FMeasurePropInLevelsListArray[i][j].AgrFunc = af_Formula then
            FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
          else
            FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := FCalcTotalsOnTotals;
          if FMeasurePropInLevelsListArray[i][j].UseTotalToCalc then
          begin
            if j = (FMeasuresContainer.FSaveCountSecondLevels - 1) then
              if i = (FMeasuresContainer.FSaveCountBaseLevels - 1) then
                FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
              else
                FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := True
            else
            if i = (FMeasuresContainer.FSaveCountBaseLevels - 1) then
              FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := False
            else
            begin
              if FMeasurePropInLevelsListArray[i + 1][j].AgrFunc = af_None then
                if FMeasurePropInLevelsListArray[i][j + 1].AgrFunc = af_None then
                  FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
                else
                  FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := False
              else
              if FMeasurePropInLevelsListArray[i][j + 1].AgrFunc = af_None then
                FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := True
              else
              begin
                FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := not (FUseXAxisTotalsAsBase xor FMeasuresContainer.FBaseAxisIsX);
              end
            end
          end;
{ for future. do not delete
        end
        else
        begin
// has properties changing
          if (AIndexForBaseCT <> -1) and (AIndexForSecondCT <> -1) then
          begin
            if FConflictResolve = tcrEmptyValue then
            begin
              AChangedTotalToCalc := True;
            end
            else
            begin
              if (FConflictResolve = tcrUseXAxis) xor FMeasuresContainer.FBaseAxisIsX then
                AIndexForBaseCT := -1
              else
                AIndexForSecondCT := -1
            end;
          end;
          if not AChangedTotalToCalc then
            if AIndexForBaseCT <> -1 then
            begin
              if FMeasurePropInLevelsListArray[i][j].AgrFunc = af_Formula then
                FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
              else
                FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := FAgrFuncForDimensionArray[AIndexForBaseCT].CalcOnTotals;
              if FMeasurePropInLevelsListArray[i][j].UseTotalToCalc then
              begin
                if j = (FMeasuresContainer.FSaveCountSecondLevels - 1) then
                  if i = (FMeasuresContainer.FSaveCountBaseLevels - 1) then
                    FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
                  else
                    FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := True
                else
                if i = (FMeasuresContainer.FSaveCountBaseLevels - 1) then
                  FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := False
                else
                begin
                  if FMeasurePropInLevelsListArray[i + 1][j].AgrFunc = af_None then
                    if FMeasurePropInLevelsListArray[i][j + 1].AgrFunc = af_None then
                      FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
                    else
                      FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := False
                  else
                  if FMeasurePropInLevelsListArray[i][j + 1].AgrFunc = af_None then
                    FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := True
                  else
                  begin
                    FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := FAgrFuncForDimensionArray[AIndexForBaseCT].UseFieldAxisTotalsAsBase
                  end
                end;
              end;
            end
            else
            begin
              if FMeasurePropInLevelsListArray[i][j].AgrFunc = af_Formula then
                FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
              else
                FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := FAgrFuncForDimensionArray[AIndexForSecondCT].CalcOnTotals;
              if FMeasurePropInLevelsListArray[i][j].UseTotalToCalc then
              begin
                if j = (FMeasuresContainer.FSaveCountSecondLevels - 1) then
                  if i = (FMeasuresContainer.FSaveCountBaseLevels - 1) then
                    FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
                  else
                    FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := True
                else
                if i = (FMeasuresContainer.FSaveCountBaseLevels - 1) then
                  FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := False
                else
                begin
                  if FMeasurePropInLevelsListArray[i + 1][j].AgrFunc = af_None then
                    if FMeasurePropInLevelsListArray[i][j + 1].AgrFunc = af_None then
                      FMeasurePropInLevelsListArray[i][j].UseTotalToCalc := False
                    else
                      FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := False
                  else
                  if FMeasurePropInLevelsListArray[i][j + 1].AgrFunc = af_None then
                    FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := True
                  else
                  begin
                    FMeasurePropInLevelsListArray[i][j].UseTotalOfBaseAxis := FAgrFuncForDimensionArray[AIndexForSecondCT].UseFieldAxisTotalsAsBase
                  end
                end;
              end;
            end
        end;
}
    end
  end;
end;

procedure TfcxMeasureField.CreateMeasurePropArrayS2;
var
  i, j: TfcxSmallCount;
begin
  inc(FPassCount);
  GetMem(FVarianceExistsArray, FPassCount * SizeOf(Boolean));
  FillChar(FVarianceExistsArray^, SizeOf(Boolean) * FPassCount, 0);
  FVarianceExists := False;
  FFinalNeed := False;
  FRestrucNeed := False;
  FRestrucAdditionalNeed := False;
  for i := FMeasuresContainer.FSaveCountBaseLevels - 1 downto -1 do
  begin
    for j := FMeasuresContainer.FSaveCountSecondLevels - 1 downto -1 do
    begin
      FVarianceExistsArray[FMeasurePropInLevelsListArray[i][j].Pass] :=
         FVarianceExistsArray[FMeasurePropInLevelsListArray[i][j].Pass] or
         FMeasurePropInLevelsListArray[i][j].Variance;
      FVarianceExists := FVarianceExists or FVarianceExistsArray[FMeasurePropInLevelsListArray[i][j].Pass];
      FFinalNeed := FFinalNeed or FMeasurePropInLevelsListArray[i][j].FinalNeed;
      FRestrucNeed := FRestrucNeed or FMeasurePropInLevelsListArray[i][j].RestrucNeed;
    end;
  end;
  for i := -1 to FMeasuresContainer.FSaveCountBaseLevels - 1 do
  begin
    for j := 0 to FMeasuresContainer.FSaveCountAdditionalTotalsBaseLevels[i] - 1 do
    begin
      FFinalNeed := FFinalNeed or (FMeasuresContainer.FSaveAdditionalTotalsBaseLevels[i, j] in FinalFuncs);
      FRestrucAdditionalNeed := FRestrucAdditionalNeed or (FMeasuresContainer.FSaveAdditionalTotalsBaseLevels[i, j] in RestrucFuncs);
//      FVarianceExistsArray[0] := FVarianceExistsArray[0] or (FMeasuresContainer.FSaveAdditionalTotalsBaseLevels[i, j] in VarianceFuncs)
//      FVarianceExists := FVarianceExists or FVarianceExistsArray[0];
    end;
  end;
  for i := -1 to FMeasuresContainer.FSaveCountSecondLevels - 1 do
  begin
    for j := 0 to FMeasuresContainer.FSaveCountAdditionalTotalsSecondLevels[i] - 1 do
    begin
      FFinalNeed := FFinalNeed or (FMeasuresContainer.FSaveAdditionalTotalsSecondLevels[i, j] in FinalFuncs);
      FRestrucAdditionalNeed := FRestrucAdditionalNeed or (FMeasuresContainer.FSaveAdditionalTotalsSecondLevels[i, j] in RestrucFuncs);
//      FVarianceExistsArray[0] := FVarianceExistsArray[0] or (FMeasuresContainer.FSaveAdditionalTotalsSecondLevels[i, j] in VarianceFuncs)
//      FVarianceExists := FVarianceExists or FVarianceExistsArray[0];
    end;
  end;
  FRestrucNeed := FRestrucNeed or FRestrucAdditionalNeed;
  if FFinalNeed then
  begin
    if FSliceFieldDistinct is TfcxCommonSliceCubeField then
    begin
      FUCArrayCount := TfcxCommonUVField(TfcxCommonSliceCubeField(FSliceFieldDistinct).FCubeField).UniqueValues.Count;
//      FBitMaskHeap := TfcxHeap.Create(TfcxCommonUVField(TfcxCommonSliceCubeField(FSliceField).FCubeField).UniqueValues.Count shr 3 + 1)
    end
    else
    if FSliceFieldDistinct is TfcxSliceDateSplitCubeField then
    begin
      FUCArrayCount := TfcxCommonDatePathField(TfcxSliceDateSplitCubeField(FSliceFieldDistinct).FCubeField).DatePathProcessor.CountUV;
//      FBitMaskHeap := TfcxHeap.Create(TfcxCommonDatePathField(TfcxSliceDateSplitCubeField(FSliceField).FCubeField).DatePathProcessor.CountUV shr 3 + 1)
    end
    else
    if FSliceFieldDistinct is TfcxSliceTimeSplitCubeField then
    begin
      FUCArrayCount := TfcxCommonTimePathField(TfcxSliceTimeSplitCubeField(FSliceFieldDistinct).FCubeField).TimePathProcessor.CountUV;
//      FBitMaskHeap := TfcxHeap.Create(TfcxCommonTimePathField(TfcxSliceTimeSplitCubeField(FSliceField).FCubeField).TimePathProcessor.CountUV shr 3 + 1)
    end;
    GetMem(FUCArray, FUCArrayCount * SizeOf(TfcxRecPointerArray));
    FillChar(FUCArray^, FUCArrayCount * SizeOf(TfcxRecPointerArray), 0);
    FVLArray := nil;
    FVLArrayCount := 0;
  end;
end;

destructor TfcxMeasureField.Destroy;
var
  i: TfcxSmallCount;
begin
  FreeMem(FVarianceExistsArray);
{ for future. do not delete
  FreeMem(FAgrFuncForDimensionArray);
}
  ClearCellHighlights;
  ClearRanks;
  if FMeasurePropInLevelsListArray <> nil then
    for i := -1 to FMeasuresContainer.FSaveCountBaseLevels - 1 do
      FreeMem(FMeasurePropInLevelsListArray[i]);
  FreeMem(FMeasurePropInLevelsListArray);
  FreeMem(FTotalPositionsInLevelsListBase);
  FreeMem(FTotalPositionsInLevelsListSecond);
  FreeAndNil(FHighlights);
  inherited;
end;

procedure TfcxMeasureField.SetTotalPosition(const Value: TfcxTotalPosition);
begin
  if FTotalPosition <> Value then
  begin
    FMeasuresContainer.FSlice.StartChange;
    FTotalPosition := Value;
    if FMeasuresContainer.FContainer = FMeasuresContainer.FSlice.FXAxisContainer then
      FMeasuresContainer.FSlice.StopChange([chs_XTotalPosition])
    else
    if FMeasuresContainer.FContainer = FMeasuresContainer.FSlice.FYAxisContainer then
      FMeasuresContainer.FSlice.StopChange([chs_YTotalPosition])
    else
      FMeasuresContainer.FSlice.StopChange([])
  end;
end;

procedure TfcxMeasureField.SetCaptionWidth(const Value: SmallInt);
var
  AValue: SmallInt;
begin
  if Value = FMeasuresContainer.FSlice.DefaultColWidth then
    AValue := 0
  else
    AValue := Value;
  if FCaptionWidth <> AValue then
  begin
    FMeasuresContainer.FSlice.StartChange;
    FCaptionWidth := AValue;
    if FMeasuresContainer.FContainer = FMeasuresContainer.FSlice.FXAxisContainer then
      FMeasuresContainer.FSlice.StopChange([chs_CapXWidth])
    else
    if FMeasuresContainer.FContainer = FMeasuresContainer.FSlice.FYAxisContainer then
      FMeasuresContainer.FSlice.StopChange([chs_CapYWidth])
    else
      FMeasuresContainer.FSlice.StopChange([chs_CapPageWidth]);
  end
end;

procedure TfcxMeasureField.SetChangeNullToZero(const Value: Boolean);
begin
  if FChangeNullToZero <> Value then
  begin
    FChangeNullToZero := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.ClearTotalPositionArray;
begin
  FreeMem(FTotalPositionsInLevelsListBase);
  FTotalPositionsInLevelsListBase := nil;
  FreeMem(FTotalPositionsInLevelsListSecond);
  FTotalPositionsInLevelsListSecond := nil;
end;

function TfcxMeasureField.GetExpression: String;
begin
  Result := FExpression.Text;
end;

function TfcxMeasureField.GetFilterExpression: String;
begin
  Result := FFilterExpression.Text;
end;

function TfcxMeasureField.GetTotalsExpression: String;
begin
  Result := FTotalsExpression.Text;
end;

procedure TfcxMeasureField.SetExpression(const AValue: String);
begin
  if FExpression.Text <> AValue then
  begin
    FExpression.Text := AValue;
    ExpressionChanged;
  end;
end;

procedure TfcxMeasureField.SetFilterExpression(const AValue: String);
begin
  if FFilterExpression.Text <> AValue then
  begin
    FFilterExpression.Text := AValue;
    ExpressionChanged;
  end;
end;

procedure TfcxMeasureField.CreateTotalPositionArray;
var
{ for future. do not delete
  i1: TfcxSmallCount;
}
  i: TfcxSmallCount;
begin
  GetMem(FTotalPositionsInLevelsListBase, SizeOf(TfcxTotalPosition) * (FMeasuresContainer.FSaveCountBaseLevels + 1));
  FillChar(FTotalPositionsInLevelsListBase^, SizeOf(TfcxTotalPosition) * (FMeasuresContainer.FSaveCountBaseLevels + 1), 0);
  GetMem(FTotalPositionsInLevelsListSecond, SizeOf(TfcxTotalPosition) * (FMeasuresContainer.FSaveCountSecondLevels + 1));
  FillChar(FTotalPositionsInLevelsListSecond^, SizeOf(TfcxTotalPosition) * (FMeasuresContainer.FSaveCountSecondLevels + 1), 0);

  if FMeasuresContainer.FBaseContainer.AxisType = at_Standard then
  begin
    if FMeasuresContainer.FBaseContainer.UseGrandTotalPositionFromMeasure then
      FTotalPositionsInLevelsListBase[-1] := DefaultTotalPosition
    else
      FTotalPositionsInLevelsListBase[-1] := FMeasuresContainer.FBaseContainer.GrandTotalPosition;

    for i := FMeasuresContainer.FSaveCountBaseLevels - 1 downto 0 do
    begin
      if FMeasuresContainer.FBaseContainer.Fields[FMeasuresContainer.FBaseContainer.FAxisSourceTree.FFieldIndexOfLevel[i]].UseTotalPositionFromMeasure then
        FTotalPositionsInLevelsListBase[i] := FTotalPosition
      else
        FTotalPositionsInLevelsListBase[i] := FMeasuresContainer.FBaseContainer.Fields[FMeasuresContainer.FBaseContainer.FAxisSourceTree.FFieldIndexOfLevel[i]].FTotalPosition;
{ for future. do not delete
      for i1 := 0 to FAgrFuncForDimCount - 1 do
        if (FMeasuresContainer.FBaseContainer.Fields[FMeasuresContainer.FBaseContainer.FAxisSourceTree.FFieldIndexOfLevel[i]].FSliceField = FAgrFuncForDimensionArray[i1].SliceField) and
            (FAgrFuncForDimensionArray[i1].SliceField2 = nil) then
        begin
          if FAgrFuncForDimensionArray[i1].ChangeTotalPosition then
            FTotalPositionsInLevelsListBase[i] := FAgrFuncForDimensionArray[i1].TotalPosition
          else
            FTotalPositionsInLevelsListBase[i] := FTotalPosition;
          Break;
        end;
}
    end;
  end;

  if FMeasuresContainer.FSecondContainer.AxisType = at_Standard then
  begin
    if FMeasuresContainer.FSecondContainer.UseGrandTotalPositionFromMeasure then
      FTotalPositionsInLevelsListSecond[-1] := DefaultTotalPosition
    else
      FTotalPositionsInLevelsListSecond[-1] := FMeasuresContainer.FSecondContainer.GrandTotalPosition;

    for i := FMeasuresContainer.FSaveCountSecondLevels - 1 downto 0 do
    begin
      if FMeasuresContainer.FSecondContainer.Fields[FMeasuresContainer.FSecondContainer.FAxisSourceTree.FFieldIndexOfLevel[i]].UseTotalPositionFromMeasure then
        FTotalPositionsInLevelsListSecond[i] := FTotalPosition
      else
        FTotalPositionsInLevelsListSecond[i] := FMeasuresContainer.FSecondContainer.Fields[FMeasuresContainer.FSecondContainer.FAxisSourceTree.FFieldIndexOfLevel[i]].FTotalPosition;
{ for future. do not delete
      for i1 := 0 to FAgrFuncForDimCount - 1 do
        if (FMeasuresContainer.FSecondContainer.Fields[FMeasuresContainer.FSecondContainer.FAxisSourceTree.FFieldIndexOfLevel[i]].FSliceField = FAgrFuncForDimensionArray[i1].SliceField) and
            (FAgrFuncForDimensionArray[i1].SliceField2 = nil) then
        begin
          if FAgrFuncForDimensionArray[i1].ChangeTotalPosition then
            FTotalPositionsInLevelsListSecond[i] := FAgrFuncForDimensionArray[i1].TotalPosition
          else
            FTotalPositionsInLevelsListSecond[i] := FTotalPosition;
          Break;
        end;
}
    end;
  end;
end;

function TfcxMeasureField.GetTotalPositionByLevel(AXAxis: Boolean; ALevelIndex: TfcxSmallCount): TfcxTotalPosition;
begin
  Result := FTotalPosition;
  if FMeasuresContainer.FIsCleared then
    Exit;
  if AXAxis xor FMeasuresContainer.FBaseAxisIsX then
  begin
    if FTotalPositionsInLevelsListSecond <> nil then
      Result := FTotalPositionsInLevelsListSecond[ALevelIndex];
  end
  else
  begin
    if FTotalPositionsInLevelsListBase <> nil then
      Result := FTotalPositionsInLevelsListBase[ALevelIndex];
  end;
end;

procedure TfcxMeasureField.SetTotalsExpression(const AValue: String);
begin
  if FTotalsExpression.Text <> AValue then
  begin
    FTotalsExpression.Text := AValue;
    ExpressionChanged;
  end;
end;

procedure TfcxMeasureField.SetVisible(const Value: Boolean);
var
  AChanges: TfcxChangesInSlice;
begin
  if FVisible <> Value then
  begin
    AChanges := [{chs_MeasuresFieldPosition, }chs_Fact];
    FMeasuresContainer.FSlice.StartChange;
    FVisible := Value;
    if FVisible then
      inc(TfcxMeasureFields(Owner).FVisibleCount)
    else
      dec(TfcxMeasureFields(Owner).FVisibleCount);
    if FMeasuresContainer.FContainer is TfcxAxisContainer then
    begin
      if FMeasuresContainer.FContainer = FMeasuresContainer.FSlice.FXAxisContainer then
        AChanges := AChanges + [chs_NeedFillVisibleX, chs_XAxis]
      else
        AChanges := AChanges + [chs_NeedFillVisibleY, chs_YAxis];
      if FMeasuresContainer.FSlice.HideColZeros then
        AChanges := AChanges + [chs_NeedFillVisibleX];
      if FMeasuresContainer.FSlice.HideRowZeros then
        AChanges := AChanges + [chs_NeedFillVisibleY];
    end;
    FMeasuresContainer.FSlice.StopChange(AChanges);
  end
end;

procedure TfcxMeasureField.SetDisplayAs(const Value: TfcxDisplayAs);
var
  AIndex: TfcxSmallCount;
begin
  if FDisplayAs <> Value then
  begin
    FMeasuresContainer.FSlice.StartChange;
    FDisplayAs := Value;
    AIndex := FMeasuresContainer.FMeasureFields.IndexOf[Self];
    if (FMeasuresContainer.FSlice.FChangeCount = 1) and (Value in da_Ranks) and (AIndex >= 0) and not Assigned(FBaseRanks) then
    begin
      FMeasuresContainer.CalcRank(AIndex);
      FMeasuresContainer.FSlice.StopChange([chs_CapMeasures]);
    end
    else
      FMeasuresContainer.FSlice.StopChange([chs_Fact]);
  end;
end;

procedure TfcxMeasureField.SetAgrFunc(const Value: TfcxAgrFunc);
begin
  if FAgrFunc <> Value then
  begin
    FAgrFunc := Value;
    GeneralChanged;
  end
end;

procedure TfcxMeasureField.SetSliceField(const Value: TfcxSliceField);
begin
  if FSliceField <> Value then
  begin
    if FSliceField = FSliceFieldDistinct then
      FSliceFieldDistinct := Value;
    FSliceField := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetSliceFieldDistinct(const Value: TfcxSliceField);
begin
  if FSliceFieldDistinct <> Value then
  begin
    FSliceFieldDistinct := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetSliceFieldExtra(const Value: TfcxSliceField);
begin
  if FSliceFieldExtra <> Value then
  begin
    FSliceFieldExtra := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetHighlights(const Value: TfcxCustomHighlights);
begin
  FHighlights.Assign(Value);
  Changed;
end;

procedure TfcxMeasureField.DoHighlightsChange(Sender: TObject);
begin
  Changed;
end;

function TfcxMeasureField.GetExpressions: TfcxExpressions;
var
  I: Integer;
begin
  // set to max
  SetLength(Result, 3);
  I := 0;
  if FExpression.Text <> '' then
  begin
    Result[I] := FExpression;
    inc(I);
  end;
  if FTotalsExpression.Text <> '' then
  begin
    Result[I] := FTotalsExpression;
    inc(I);
  end;
  if FFilterExpression.Text <> '' then
  begin
    Result[I] := FFilterExpression;
    inc(I);
  end;
  SetLength(Result, I);
end;

function TfcxMeasureField.GetWidth: SmallInt;
begin
  Result := FWidth;
end;

procedure TfcxMeasureField.SaveToXML(AItem: TfcxXMLItem);
begin
  inherited;
  if Assigned(SliceField) then
    AItem.Prop['field_name'] := SliceField.FieldName
  else
    AItem.Prop['field_name'] := '';
  if Assigned(SliceFieldDistinct) then
    AItem.Prop['field2_name'] := SliceFieldDistinct.FieldName
  else
    AItem.Prop['field2_name'] := '';
  if Assigned(SliceFieldExtra) then
    AItem.Prop['fieldadv_name'] := SliceFieldExtra.FieldName
  else
    AItem.Prop['fieldavd_name'] := '';
  AItem.Prop['AgrFunc'] := GetEnumName(TypeInfo(TfcxAgrFunc), Ord(AgrFunc));
  AItem.BoolProp['Visible'] := Visible;
  AItem.Prop['DisplayAs'] := GetEnumName(TypeInfo(TfcxDisplayAs), Ord(DisplayAs));

  AItem.Prop['Expression'] := FExpression.Text;
  AItem.Prop['ExpressionFilter'] := FFilterExpression.Text;
  AItem.Prop['ExpressionTotals'] := FTotalsExpression.Text;

  AItem.Prop['ScriptFunction'] := ScriptFunction;
  AItem.IntProp['ScriptOrder'] := ScriptOrder;
  AItem.Prop['FilterScriptFunction'] := FilterScriptFunction;
  AItem.Prop['ScriptFunctionForTotals'] := ScriptFunctionForTotals;
  AItem.IntProp['ScriptOrderForTotals'] := ScriptOrderForTotals;
  AItem.BoolProp['CalcAllCells'] := CalcAllCells;
  AItem.BoolProp['CalcAllCellsForTotals'] := CalcAllCellsForTotals;
  AItem.BoolProp['Distinct'] := Distinct;
  AItem.BoolProp['ChangeNullToZero'] := ChangeNullToZero;

  // totals
  AItem.Prop['DefaultTotalPosition'] := GetEnumName(TypeInfo(TfcxTotalPosition), Ord(DefaultTotalPosition));
  AItem.BoolProp['UseDifferentAggForTotals'] := UseDifferentAggForTotals;
  AItem.Prop['AgrFuncForTotals'] := GetEnumName(TypeInfo(TfcxAgrFunc), Ord(AgrFuncForTotals));
  AItem.BoolProp['CalcTotalsOnTotals'] := CalcTotalsOnTotals;
  AItem.BoolProp['UseXAxisTotalsAsBase'] := UseXAxisTotalsAsBase;
  AItem.Prop['ConflictResolve'] := GetEnumName(TypeInfo(TfcxTotalsConflictResolve), Ord(ConflictResolve));

  Highlights.SaveToXML(AItem.Add);
end;

constructor TfcxMeasureField.Create(AFieldsOfRegion: TfcxCommonFieldsOfRegion);
begin
  inherited Create(AFieldsOfRegion);
  FFormat := TfcxFormat.Create;
  FFormat.OnChange := DoFormatChange;
  FMeasuresContainer := TfcxMeasuresContainer(AFieldsOfRegion.FContainer);
  FHighlights := TfcxCustomHighlights.Create(Self);
  FHighlights.OnChange := DoHighlightsChange;
  FMeasureCells := nil;
  FCellHighlights := nil;
  FPercentilesCount := 0;
  FPercentiles := nil;
  FBaseRanks := nil;
  FSecondRanks := nil;
  FAdditionalTotalsMeasureCellsBase := nil;
  FAdditionalTotalsMeasureCellsSecond := nil;
  FCalcAllCells := False;
  FSliceField := nil;
  FSliceFieldDistinct := nil;
  FSliceFieldExtra := nil;
  FDistinct := False;
  FChangeNullToZero := False;
  FExpression.Text := '';
  FExpression.UseFields := False;
  FFilterExpression.Text := '';
  FFilterExpression.UseFields := True;
  FTotalsExpression.Text := '';
  FTotalsExpression.UseFields := False;
end;

function TfcxMeasureField.LoadFromXML(AItem: TfcxXMLItem): boolean;
var
  I: Integer;
begin
  Result := inherited LoadFromXML(AItem);
  if not Result then
    Exit;
  FSliceField := Owner.Container.Slice.SliceFields.ItemByName[AItem.Prop['field_name']];
  if FSliceField = nil then
    if AItem.Prop['field_name'] <> '' then
    begin
      Result := False;
      Exit;
    end;
  if AItem.PropExists('field2_name') then
    FSliceFieldDistinct := Owner.Container.Slice.SliceFields.ItemByName[AItem.Prop['field2_name']]
  else
    FSliceFieldDistinct := FSliceField;
  if AItem.PropExists('fieldadv_name') then
    FSliceFieldExtra := Owner.Container.Slice.SliceFields.ItemByName[AItem.Prop['fieldadv_name']]
  else
    FSliceFieldExtra := FSliceField;
  AgrFunc := TfcxAgrFunc(GetEnumValue(TypeInfo(TfcxAgrFunc), AItem.Prop['AgrFunc']));
  CalcAllCells := AItem.BoolProp['CalcAllCells'];
  Distinct := AItem.BoolProp['Distinct'];
  Visible := AItem.BoolProp['Visible'];
  if AItem.PropExists('ChangeNullToZero') then
    FChangeNullToZero := AItem.BoolProp['ChangeNullToZero']
  else
    FChangeNullToZero := False;

  DisplayAs := TfcxDisplayAs(GetEnumValue(TypeInfo(TfcxDisplayAs), AItem.Prop['DisplayAs']));

  FExpression.Text := AItem.Prop['Expression'];
  FFilterExpression.Text := AItem.Prop['ExpressionFilter'];
  FTotalsExpression.Text := AItem.Prop['ExpressionTotals'];

  ScriptFunction := AItem.Prop['ScriptFunction'];
  ScriptOrder := AItem.IntProp['ScriptOrder'];
  FilterScriptFunction := AItem.Prop['FilterScriptFunction'];

  // totals
  DefaultTotalPosition := TfcxTotalPosition(GetEnumValue(TypeInfo(TfcxTotalPosition), AItem.Prop['DefaultTotalPosition']));
  UseDifferentAggForTotals := AItem.BoolProp['UseDifferentAggForTotals'];
  AgrFuncForTotals := TfcxAgrFunc(GetEnumValue(TypeInfo(TfcxAgrFunc), AItem.Prop['AgrFuncForTotals']));
  CalcAllCellsForTotals := AItem.BoolProp['CalcAllCellsForTotals'];
  ScriptFunctionForTotals := AItem.Prop['ScriptFunctionForTotals'];
  ScriptOrderForTotals := AItem.IntProp['ScriptOrderForTotals'];
  CalcTotalsOnTotals := AItem.BoolProp['CalcTotalsOnTotals'];
  UseXAxisTotalsAsBase := AItem.BoolProp['UseXAxisTotalsAsBase'];
  ConflictResolve := TfcxTotalsConflictResolve(GetEnumValue(TypeInfo(TfcxTotalsConflictResolve), AItem.Prop['ConflictResolve']));

  for I := 0 to AItem.Count - 1 do
    if AItem[I].Name = 'HIGHLIGHTS' then
      Highlights.LoadFromXML(AItem[I]);
end;

procedure TfcxMeasureField.SetFilterScriptFunction(const Value: String);
begin
  FFilterScriptFunction := Value;
  ExpressionChanged;
end;

procedure TfcxMeasureField.SetScriptFunction(const Value: String);
begin
  FScriptFunction := Value;
  ExpressionChanged;
end;

procedure TfcxMeasureField.SetScriptOrder(const Value: Integer);
begin
  FScriptOrder := Value;
  ExpressionChanged;
end;

procedure TfcxMeasureField.SetAgrFuncForTotals(const Value: TfcxAgrFunc);
begin
  if FAgrFuncForTotals <> Value then
  begin
    FAgrFuncForTotals := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetUseDifferentAggForTotals(const Value: Boolean);
begin
  if FUseDifferentAggForTotals <> Value then
  begin
    FUseDifferentAggForTotals := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetCalcTotalsOnTotals(const Value: Boolean);
begin
  if FCalcTotalsOnTotals <> Value then
  begin
    FCalcTotalsOnTotals := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetUseXAxisTotalsAsBase(const Value: Boolean);
begin
  if FUseXAxisTotalsAsBase <> Value then
  begin
    FUseXAxisTotalsAsBase := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetConflictResolve(const Value: TfcxTotalsConflictResolve);
begin
  if FConflictResolve <> Value then
  begin
    FConflictResolve := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.AddVLArr(AMeasureValue: PfcxMeasureValueWithValuesList);
begin
  inc(FVLArrayCount);
  ReallocMem(FVLArray, FVLArrayCount * SizeOf(Pointer));
  FVLArray[FVLArrayCount-1] := AMeasureValue;
  AMeasureValue.VLArr[0] := 0;
end;

procedure TfcxMeasureField.ClearCellHighlights;
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  i, j: integer;
  ACountInSecondLevel: Integer;
begin
  if FCellHighlights <> nil then
  begin
    for ABaseLevel := -1 to FMeasuresContainer.FSaveCountBaseLevels - 1 do
    begin
      for ASecondLevel := -1 to FMeasuresContainer.FSaveCountSecondLevels - 1 do
      begin
        for i := 0 to FMeasuresContainer.FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
        begin
          if FMeasuresContainer.FLevelsProp[ABaseLevel][ASecondLevel][i].Simple or FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel].CreateAllCells then
            ACountInSecondLevel := FMeasuresContainer.FSecondContainer.FAxisTree[ASecondLevel].Count
          else
            ACountInSecondLevel := FMeasuresContainer.FLevelsProp[ABaseLevel][ASecondLevel][i].Count;
          for j := 0 to ACountInSecondLevel - 1 do
          begin
            FreeMem(PfcxArrayMeasureCellHighlightsArray(FCellHighlights[ABaseLevel][ASecondLevel])[i][j].PercentilesBase);
            FreeMem(PfcxArrayMeasureCellHighlightsArray(FCellHighlights[ABaseLevel][ASecondLevel])[i][j].PercentilesSecond);
          end;
          FreeMem(PfcxArrPointerArray(FCellHighlights[ABaseLevel][ASecondLevel])[i]);
        end;
        FreeMem(FCellHighlights[ABaseLevel][ASecondLevel]);
      end;
      FreeMem(FCellHighlights[ABaseLevel]);
      FreeMem(FPercentiles);
      FPercentiles := nil;
      FPercentilesCount := 0;
    end;
    FreeMem(FCellHighlights);
    FCellHighlights := nil;
  end;
end;

procedure TfcxMeasureField.ClearRanks;
var
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  i: integer;
begin
  if FBaseRanks <> nil then
  begin
    for ABaseLevel := -1 to FMeasuresContainer.FSaveCountBaseLevels - 1 do
    begin
      for ASecondLevel := -1 to FMeasuresContainer.FSaveCountSecondLevels - 1 do
      begin
        for i := 0 to FMeasuresContainer.FBaseContainer.FAxisTree[ABaseLevel].Count - 1 do
        begin
          FreeMem(PfcxArrPointerArray(FBaseRanks[ABaseLevel][ASecondLevel])[i]);
          FreeMem(PfcxArrPointerArray(FSecondRanks[ABaseLevel][ASecondLevel])[i]);
        end;
        FreeMem(FBaseRanks[ABaseLevel][ASecondLevel]);
        FreeMem(FSecondRanks[ABaseLevel][ASecondLevel]);
      end;
      FreeMem(FBaseRanks[ABaseLevel]);
      FreeMem(FSecondRanks[ABaseLevel]);
    end;
    FreeMem(FBaseRanks);
    FreeMem(FSecondRanks);
    FBaseRanks := nil;
    FSecondRanks := nil;
  end;
end;

procedure TfcxMeasureField.SetHeight(const Value: SmallInt);
begin
  if Value = FMeasuresContainer.FSlice.DefaultRowHeight then
  begin
    if FHeight <> 0 then
    begin
      FHeight := 0;
      FMeasuresContainer.FSlice.StartChange;
      FMeasuresContainer.FSlice.StopChange([chs_XHeight]);
    end
  end
  else
  begin
    if FHeight <> Value then
    begin
      FHeight := Value;
      FMeasuresContainer.FSlice.StartChange;
      FMeasuresContainer.FSlice.StopChange([chs_XHeight]);
    end
  end
end;

procedure TfcxMeasureField.SetWidth(const Value: SmallInt);
begin
  if Value = FMeasuresContainer.FSlice.DefaultColWidth then
  begin
    if FWidth <> 0 then
    begin
      FWidth := 0;
      FMeasuresContainer.FSlice.StartChange;
      FMeasuresContainer.FSlice.StopChange([chs_YWidth]);
    end
  end
  else
  begin
    if FWidth <> Value then
    begin
      FWidth := Value;
      FMeasuresContainer.FSlice.StartChange;
      FMeasuresContainer.FSlice.StopChange([chs_YWidth]);
    end
  end
end;

function TfcxMeasureField.GetHeight: SmallInt;
begin
  Result := FHeight;
end;

function TfcxMeasureField.GetDisplayFormat: TfcxFormat;
begin
  Result := FFormat;
end;

procedure TfcxMeasureField.SetDisplayFormat(const Value: TfcxFormat);
begin
  if not FFormat.Equal(Value) then
    FFormat.Assign(Value);
end;

procedure TfcxMeasureField.SetCalcAllCells(const Value: Boolean);
begin
  if FCalcAllCells <> Value then
  begin
    FCalcAllCells := Value;
    GeneralChanged;
  end
end;

function TfcxMeasureField.GetCalculated: Boolean;
begin
  Result := FAgrFunc in ScriptFuncs
end;

function TfcxMeasureField.GetCalculatedTotals: Boolean;
begin
  Result := FAgrFuncForTotals in ScriptFuncs
end;

function TfcxMeasureField.CreateAllCells: Boolean;
begin
  Result := FCalcAllCells and (FAgrFunc in CanCreateAllCellsFuncs);
end;

procedure TfcxMeasureField.SetCalcAllCellsForTotals(const Value: Boolean);
begin
  if FCalcAllCellsForTotals <> Value then
  begin
    FCalcAllCellsForTotals := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetScriptFunctionForTotals(const Value: String);
begin
  FScriptFunctionForTotals := Value;
  ExpressionChanged;
end;

procedure TfcxMeasureField.SetScriptOrderForTotals(const Value: Integer);
begin
  FScriptOrderForTotals := Value;
  ExpressionChanged;
end;

function TfcxMeasureField.CreateAllCellsForTotals: Boolean;
begin
  Result := (FCalcAllCellsForTotals and (FAgrFuncForTotals in CanCreateAllCellsFuncs)) or FCalcTotalsOnTotals or CreateAllCells;
end;

procedure TfcxMeasureField.SetDistinct(const Value: Boolean);
begin
  if FDistinct <> Value then
  begin
    FDistinct := Value;
    GeneralChanged;
  end;
end;

procedure TfcxMeasureField.SetDistinctForTotals(const Value: Boolean);
begin
  if FDistinctForTotals <> Value then
  begin
    FDistinctForTotals := Value;
    GeneralChanged;
  end;
end;

function TfcxMeasureField.HasDisplayFormat: Boolean;
begin
  Result := True;
end;

{ TfcxMeasureFields }

function TfcxMeasureFields.GetItem(Index: Integer): TfcxMeasureField;
begin
  Result := FFields.List[Index];
end;

{ TfcxSumMeasureProcessor }

class procedure TfcxSumMeasureProcessor.Func(const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    AMeasureValue.Value := AValue;
  end else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
{$IFDEF SQL_TYPES_EXTRA1}
        if (TVarData(AValue).VType = VarSQLTimeStamp) then
          AMeasureValue.Value := ''
        else
{$ENDIF}
          AMeasureValue.Value := AMeasureValue.Value + AValue;
      except
      end;
    end;
  end;
end;

{ TfcxAvgMeasureProcessor }

class procedure TfcxAvgMeasureProcessor.Func(const AMeasureValue: PfcxMeasureValue;
  AValue: Variant; AStep, AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      AMeasureValue.Value := AValue;
      PfcxMeasureValueWithCount(AMeasureValue).Count := 1;
    end;
  end
  else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
{$IFDEF SQL_TYPES_EXTRA1}
        if (TVarData(AValue).VType = VarSQLTimeStamp) then
          AMeasureValue.Value := 0
        else
{$ENDIF}
          AMeasureValue.Value := AMeasureValue.Value + AValue;
        PfcxMeasureValueWithCount(AMeasureValue).Count := PfcxMeasureValueWithCount(AMeasureValue).Count + 1;
      except
      end;
    end;
  end;
end;

class function TfcxAvgMeasureProcessor.GetItem(AMeasureValue: PfcxMeasureValue): Variant;
begin
  If PfcxMeasureValueWithCount(AMeasureValue).Count <= 1 then
    Result := AMeasureValue.Value
  else
    if VarIsStr(AMeasureValue.Value) then
      Result := LeftStr(VarToStr(AMeasureValue.Value), Length(AMeasureValue.Value) div PfcxMeasureValueWithCount(AMeasureValue).Count)
    else
      try
        Result := AMeasureValue.Value / PfcxMeasureValueWithCount(AMeasureValue).Count;
      except
        Result := UnAssigned;
      end;
end;

class function TfcxAvgMeasureProcessor.GetSize: Integer;
begin
  Result := SizeOf(_fcxMeasureValueWithCount);
end;

class function TfcxAvgMeasureProcessor.NewMeasureValue(AMeasureField: TfcxMeasureField): PfcxMeasureValue;
begin
  Result := inherited NewMeasureValue(AMeasureField);
  PfcxMeasureValueWithCount(Result).Count := -1;
end;

{ TfcxVarianceMeasureProcessor }

class procedure TfcxVarianceMeasureProcessor.DestroyMeasure(AMeasureValue: PfcxMeasureValue);
begin
  Finalize(PfcxMeasureValueWithCountAndVar(AMeasureValue).VarTemp);
  inherited;
end;

class procedure TfcxVarianceMeasureProcessor.Func(const AMeasureValue: PfcxMeasureValue;
  AValue: Variant; AStep, AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  If AStep = 1 then
  begin
    if TVarData(PfcxMeasureValueWithCountAndVar(AMeasureValue).VarTemp).VType <= 1 then
    begin
      if not (TVarData(AValue).VType <= 1) then
      begin
        PfcxMeasureValueWithCountAndVar(AMeasureValue).VarTemp  := AValue;
        PfcxMeasureValueWithCount(AMeasureValue).Count := 1;
      end;
    end
    else
    begin
      if not (TVarData(AValue).VType <= 1) then
      begin
        try
{$IFDEF SQL_TYPES_EXTRA1}
          if (TVarData(AValue).VType = VarSQLTimeStamp) then
            PfcxMeasureValueWithCountAndVar(AMeasureValue).VarTemp  := 0
          else
{$ENDIF}
            PfcxMeasureValueWithCountAndVar(AMeasureValue).VarTemp  := PfcxMeasureValueWithCountAndVar(AMeasureValue).VarTemp + AValue;
          PfcxMeasureValueWithCount(AMeasureValue).Count := PfcxMeasureValueWithCount(AMeasureValue).Count + 1;
        except
        end;
      end;
    end;
  end else
  begin
    if TVarData(AMeasureValue.Value).VType <= 1 then
    begin
      if not (TVarData(AValue).VType <= 1) then
      begin
        AMeasureValue.Value := SQR(AValue - PfcxMeasureValueWithCountAndVar(AMeasureValue).VarTemp);
      end;
    end else
    begin
      if not (TVarData(AValue).VType <= 1) then
      begin
        try
{$IFDEF SQL_TYPES_EXTRA1}
          if (TVarData(AValue).VType = VarSQLTimeStamp) then
            AMeasureValue.Value := 0
          else
{$ENDIF}
            AMeasureValue.Value := AMeasureValue.Value + SQR(AValue - PfcxMeasureValueWithCountAndVar(AMeasureValue).VarTemp);
        except
        end;
      end;
    end;
  end;
end;

class function TfcxVarianceMeasureProcessor.GetItem(
  AMeasureValue: PfcxMeasureValue): Variant;
begin
  If PfcxMeasureValueWithCount(AMeasureValue).Count = -1 then
    Result := AMeasureValue.Value
  else
    If PfcxMeasureValueWithCount(AMeasureValue).Count < 2 then
      Result := 0
    else
      Result := AMeasureValue.Value / (PfcxMeasureValueWithCount(AMeasureValue).Count - 1);
end;

class function TfcxVarianceMeasureProcessor.GetSize: Integer;
begin
  Result := SizeOf(_fcxMeasureValueWithCountAndVar);
end;

{ TfcxVarianceSMeasureProcessor }

class function TfcxVarianceSMeasureProcessor.GetItem(
  AMeasureValue: PfcxMeasureValue): Variant;
begin
  If PfcxMeasureValueWithCount(AMeasureValue).Count = -1 then
    Result := AMeasureValue.Value
  else
    If PfcxMeasureValueWithCount(AMeasureValue).Count < 2 then
      Result := 0
    else
      Result := AMeasureValue.Value / PfcxMeasureValueWithCount(AMeasureValue).Count;
end;

{ TfcxStdDevMeasureProcessor }

class function TfcxStdDevMeasureProcessor.GetItem(AMeasureValue: PfcxMeasureValue): Variant;
begin
  If PfcxMeasureValueWithCount(AMeasureValue).Count = -1 then
    Result := AMeasureValue.Value
  else
    If PfcxMeasureValueWithCount(AMeasureValue).Count < 2 then
      Result := 0
    else
      Result := SQRT(AMeasureValue.Value / (PfcxMeasureValueWithCount(AMeasureValue).Count - 1));
end;

{ TfcxStdDevSMeasureProcessor }

class function TfcxStdDevSMeasureProcessor.GetItem(
  AMeasureValue: PfcxMeasureValue): Variant;
begin
  If PfcxMeasureValueWithCount(AMeasureValue).Count = -1 then
    Result := AMeasureValue.Value
  else
    If PfcxMeasureValueWithCount(AMeasureValue).Count < 2 then
      Result := 0
    else
      Result := SQRT(AMeasureValue.Value / PfcxMeasureValueWithCount(AMeasureValue).Count);
end;

{ TfcxBitMaskMeasureProcessor }

class function TfcxBitMaskMeasureProcessor.GetSize: Integer;
begin
  Result := SizeOf(_fcxMeasureValueWithBitMask);
end;

class function TfcxBitMaskMeasureProcessor.NewMeasureValue(AMeasureField: TfcxMeasureField): PfcxMeasureValue;
begin
  Result := inherited NewMeasureValue(AMeasureField);
  PfcxMeasureValueWithBitMask(Result).UCArr := AMeasureField.FUCArray;
end;

class function TfcxBitMaskMeasureProcessor.Check(const AMeasureValue: PfcxMeasureValueWithBitMask; AIndexValue: Integer): Boolean;
var
  ALeft, ARight, AMiddle: Integer;
  AResIndex: Integer;
begin
  Result := False;
  AResIndex := 0;
// Find PtrUInt(AMeasureValue) in AMeasureValue.UCArr[AIndexValue].PointerArray
  if AMeasureValue.UCArr[AIndexValue].Count > 0 then
  begin
    ALeft := 0;
    ARight := AMeasureValue.UCArr[AIndexValue].Count - 1;
    while ALeft <= ARight do
    begin
      AMiddle := (ALeft + ARight) shr 1; // middle
      if PtrUInt(AMeasureValue.UCArr[AIndexValue].PointerArray[AMiddle]) = PtrUInt(AMeasureValue) then
      begin
        // Found
        Result := True;
        Exit;
      end
      else
      if PtrUInt(AMeasureValue.UCArr[AIndexValue].PointerArray[AMiddle]) > PtrUInt(AMeasureValue) then
        ARight := AMiddle - 1
      else
        ALeft := AMiddle + 1;
    end;
    AResIndex := ALeft;
  end;
// insert new
  AMeasureValue.UCArr[AIndexValue].Count := AMeasureValue.UCArr[AIndexValue].Count + 1;
  ReallocMem(AMeasureValue.UCArr[AIndexValue].PointerArray, AMeasureValue.UCArr[AIndexValue].Count * SizeOf(TfcxRecPointerArray));
  if AMeasureValue.UCArr[AIndexValue].Count > 1 then
    Move(AMeasureValue.UCArr[AIndexValue].PointerArray[AResIndex], AMeasureValue.UCArr[AIndexValue].PointerArray[AResIndex + 1], (AMeasureValue.UCArr[AIndexValue].Count - AResIndex - 1) * SizeOf(TfcxRecPointerArray));
  AMeasureValue.UCArr[AIndexValue].PointerArray[AResIndex] := AMeasureValue;
end;

{ TfcxCntUniMeasureProcessor }

class procedure TfcxCntUniMeasureProcessor.Func(const AMeasureValue: PfcxMeasureValue;
  AValue: Variant; AStep, AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if (AIndexValue >= 0) then
  begin
    if Check(PfcxMeasureValueWithBitMask(AMeasureValue), AIndexValue) then
      Exit;
  end;
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    if not (TVarData(AValue).VType <= 1) then
      AMeasureValue.Value := 1;
  end
  else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
        AMeasureValue.Value := AMeasureValue.Value + 1;
      except
      end;
    end;
  end;
end;

{ TfcxMinMeasureProcessor }

class procedure TfcxMinMeasureProcessor.Func(const AMeasureValue: PfcxMeasureValue;
  AValue: Variant; AStep, AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    if not (TVarData(AValue).VType <= 1) then
      AMeasureValue.Value := AValue;
  end
  else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
        if VariantCompareNotNull(AMeasureValue.Value, AValue) > 0 then AMeasureValue.Value := AValue;
      except
      end;
    end;
  end;
end;

{ TfcxMaxMeasureProcessor }

class procedure TfcxMaxMeasureProcessor.Func(const AMeasureValue: PfcxMeasureValue;
  AValue: Variant; AStep, AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    if not (TVarData(AValue).VType <= 1) then
      AMeasureValue.Value := AValue;
  end
  else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
        if VariantCompareNotNull(AMeasureValue.Value, AValue) < 0 then AMeasureValue.Value := AValue;
      except
      end;
    end;
  end;
end;

{ TfcxMulMeasureProcessor }

class procedure TfcxMulMeasureProcessor.Func(const AMeasureValue: PfcxMeasureValue;
  AValue: Variant; AStep, AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    AMeasureValue.Value := AValue;
  end else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
{$IFDEF SQL_TYPES_EXTRA1}
        if (TVarData(AValue).VType = VarSQLTimeStamp) then
          AMeasureValue.Value := ''
        else
{$ENDIF}
          AMeasureValue.Value := AMeasureValue.Value * AValue;
      except
      end;
    end;
  end;
end;

{ TfcxFirstValueMeasureProcessor }

class procedure TfcxFirstValueMeasureProcessor.Func(
  const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    if not (TVarData(AValue).VType <= 1) then
      AMeasureValue.Value := AValue;
  end;
end;

{ TfcxLastValueMeasureProcessor }

class procedure TfcxLastValueMeasureProcessor.Func(
  const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if not (TVarData(AValue).VType <= 1) then
    AMeasureValue.Value := AValue;
end;

{ TfcxListOfUniqueProcessor }

class procedure TfcxListOfUniqueProcessor.Func(
  const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if (AIndexValue >= 0) then
  begin
    if Check(PfcxMeasureValueWithBitMask(AMeasureValue), AIndexValue) then
      Exit;
  end;
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    if not (TVarData(AValue).VType <= 1) then
      AMeasureValue.Value := VarToStr(AValue);
  end
  else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
        AMeasureValue.Value := AMeasureValue.Value + ', ' + VarToStr(AValue);
      except
      end;
    end;
  end;
end;

{ TfcxDistinctSumMeasureProcessor }

class procedure TfcxDistinctSumMeasureProcessor.Func(
  const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if (AIndexValue >= 0) then
  begin
    if Check(PfcxMeasureValueWithBitMask(AMeasureValue), AIndexValue) then
      Exit;
  end;
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    AMeasureValue.Value := AValue;
  end else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
        AMeasureValue.Value := AMeasureValue.Value + AValue;
      except
      end;
    end;
  end;
end;

{ TfcxValuesListMeasureProcessor }

class procedure TfcxValuesListMeasureProcessor.Add(
  const AMeasureValue: PfcxMeasureValueWithValuesList; AIndexValue: Integer);
var
  ALeft, ARight, AMiddle: Integer;
  AResIndex: Integer;
begin
  AResIndex := 0;
// Find AIndexValue position
  if AMeasureValue.VLArr[0] > 0 then
  begin
    ALeft := 1;
    ARight := AMeasureValue.VLArr[0];
    while ALeft <= ARight do
    begin
      AMiddle := (ALeft + ARight) shr 1; // middle
      if AMeasureValue.VLArr[AMiddle] = AIndexValue then
      begin
        // Found
        AResIndex := AMiddle;
        Break;
      end
      else
      if AMeasureValue.VLArr[AMiddle] > AIndexValue then
        ARight := AMiddle - 1
      else
        ALeft := AMiddle + 1;
    end;
    if AResIndex = 0 then
      AResIndex := ALeft;
  end
  else
    AResIndex := 1;
// insert new

  AMeasureValue.VLArr[0] := AMeasureValue.VLArr[0] + 1;
  ReallocMem(AMeasureValue.VLArr, (AMeasureValue.VLArr[0] + 1) * SizeOf(integer));
  if AMeasureValue.VLArr[0] > 1 then
    Move(AMeasureValue.VLArr[AResIndex], AMeasureValue.VLArr[AResIndex + 1], (AMeasureValue.VLArr[0] - AResIndex) * SizeOf(integer));
  AMeasureValue.VLArr[AResIndex] := AIndexValue;

end;

class function TfcxValuesListMeasureProcessor.GetSize: Integer;
begin
  Result := SizeOf(_fcxMeasureValueWithValuesList);
end;

class function TfcxValuesListMeasureProcessor.NewMeasureValue(
  AMeasureField: TfcxMeasureField): PfcxMeasureValue;
begin
  Result := inherited NewMeasureValue(AMeasureField);
  GetMem(PfcxMeasureValueWithValuesList(Result).VLArr, SizeOf(Integer));
  AMeasureField.AddVLArr(PfcxMeasureValueWithValuesList(Result));
end;

{ TfcxMedianMeasureProcessor }

class procedure TfcxMedianMeasureProcessor.Func(
  const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  inherited;
  if (AIndexValue >= 0) then
    Add(PfcxMeasureValueWithValuesList(AMeasureValue), AIndexValue);
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    AMeasureValue.Value := AValue;
  end else
  begin
    if not (TVarData(AValue).VType <= 1) then
    begin
      try
        if (PfcxMeasureValueWithValuesList(AMeasureValue).VLArr[0] mod 2) <> 0 then
          AMeasureValue.Value := AMeasureField.FSliceField.GetUVValue(PfcxMeasureValueWithValuesList(AMeasureValue).VLArr[(PfcxMeasureValueWithValuesList(AMeasureValue).VLArr[0] div 2) + 1])
        else
          AMeasureValue.Value := (AMeasureField.FSliceField.GetUVValue(PfcxMeasureValueWithValuesList(AMeasureValue).VLArr[(PfcxMeasureValueWithValuesList(AMeasureValue).VLArr[0] div 2)]) + AMeasureField.FSliceField.GetUVValue(PfcxMeasureValueWithValuesList(AMeasureValue).VLArr[(PfcxMeasureValueWithValuesList(AMeasureValue).VLArr[0] div 2) + 1])) / 2
      except
      end;
    end;
  end;
end;

{ TfcxWeightedMeanMeasureProcessor }

class procedure TfcxWeightedMeanMeasureProcessor.DestroyMeasure(
  AMeasureValue: PfcxMeasureValue);
begin
  Finalize(PfcxMeasureValueWithVariant(AMeasureValue).Value2);
  inherited;
end;

class procedure TfcxWeightedMeanMeasureProcessor.Func2(
  const AMeasureValue: PfcxMeasureValue; AValue, AValue2: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if TVarData(AMeasureValue.Value).VType <= 1 then
  begin
    if not (TVarData(AValue).VType <= 1) and not (TVarData(AValue2).VType <= 1) then
    begin
      try
        AMeasureValue.Value := AValue * AValue2;
        if TVarData(PfcxMeasureValueWithVariant(AMeasureValue).Value2).VType <= 1 then
          PfcxMeasureValueWithVariant(AMeasureValue).Value2 := AValue2
        else
          PfcxMeasureValueWithVariant(AMeasureValue).Value2 := PfcxMeasureValueWithVariant(AMeasureValue).Value2 + AValue2
      except
      end;
    end;
  end
  else
  begin
    if not (TVarData(AValue).VType <= 1) and not (TVarData(AValue2).VType <= 1) then
    begin
      try
        AMeasureValue.Value := AMeasureValue.Value + AValue * AValue2;
        if TVarData(PfcxMeasureValueWithVariant(AMeasureValue).Value2).VType <= 1 then
          PfcxMeasureValueWithVariant(AMeasureValue).Value2 := AValue2
        else
          PfcxMeasureValueWithVariant(AMeasureValue).Value2 := PfcxMeasureValueWithVariant(AMeasureValue).Value2 + AValue2;
      except
      end;
    end;
  end;

end;

class function TfcxWeightedMeanMeasureProcessor.GetItem(
  AMeasureValue: PfcxMeasureValue): Variant;
begin
  if not (VarIsStr(AMeasureValue.Value) or VarIsStr(PfcxMeasureValueWithVariant(AMeasureValue).Value2)) then
  begin
    if PfcxMeasureValueWithVariant(AMeasureValue).Value2 = 0 then
      Result := AMeasureValue.Value
    else
      try
        Result := AMeasureValue.Value / PfcxMeasureValueWithVariant(AMeasureValue).Value2;
      except
        Result := UnAssigned;
      end;
  end
  else
    Result := UnAssigned;
end;

class function TfcxWeightedMeanMeasureProcessor.GetSize: Integer;
begin
  Result := SizeOf(_fcxMeasureValueWithVariant);
end;

{ TfcxVisibleAxisNodes }

function TfcxVisibleAxisNodes.AddWM(AIndex: Integer;
  AMeasureIndex: TfcxSmallCount; AAdditionalTotalIndex: Integer = -1): Integer;
begin
  if FCount = FCapacity then
  begin
    inc(FCapacity, 1000);
    ReallocMem(FIndexes, FCapacity * SizeOf(Integer));
    ReallocMem(FMeasureIndexes, FCapacity * SizeOf(TfcxSmallCount));
//    if FAxisContainer.CountAdditionalTotalFunctions > 0 then
      ReallocMem(FAdditionalTotalsIndexes, FCapacity * SizeOf(TfcxSmallCount));
  end;
  Result := FCount;
  FIndexes[Result] := AIndex;
  FMeasureIndexes[Result] := AMeasureIndex;
//  if FAxisContainer.CountAdditionalTotalFunctions > 0 then
    FAdditionalTotalsIndexes[Result] := AAdditionalTotalIndex;
  inc(FCount);
end;

function TfcxVisibleAxisNodes.AddWOM(AIndex: Integer; AAdditionalTotalIndex: Integer = -1): Integer;
begin
  if FCount = FCapacity then
  begin
    inc(FCapacity, 1000);
    ReallocMem(FIndexes, FCapacity * SizeOf(Integer));
//    if FAxisContainer.CountAdditionalTotalFunctions > 0 then
      ReallocMem(FAdditionalTotalsIndexes, FCapacity * SizeOf(TfcxSmallCount));
  end;
  Result := FCount;
  FIndexes[Result] := AIndex;
//  if FAxisContainer.CountAdditionalTotalFunctions > 0 then
    FAdditionalTotalsIndexes[Result] := AAdditionalTotalIndex;
  inc(FCount);
end;

procedure TfcxVisibleAxisNodes.Clear;
var
  i: integer;
begin
  if FVisIndexes <> nil then
    for i := 0 to FAxisContainer.FCellsCount - 1 do
      FreeMem(FVisIndexes[i]);
  FreeMem(FVisIndexes);
  FVisIndexes := nil;
  FreeMem(FIndexes);
  FreeMem(FMeasureIndexes);
  FreeMem(FAdditionalTotalsIndexes);
  FCapacity := 0;
  FCount    := 0;
  FIndexes  := nil;
  FMeasureIndexes := nil;
  FAdditionalTotalsIndexes := nil;
end;

constructor TfcxVisibleAxisNodes.Create(AAxisContainer: TfcxAxisContainer);
begin
  FAxisContainer := AAxisContainer;
  FCount    := 0;
  FCapacity := 0;
  FIndexes  := nil;
  FMeasureIndexes := nil;
  FAdditionalTotalsIndexes := nil;
  FVisIndexes := nil;
end;

destructor TfcxVisibleAxisNodes.Destroy;
begin
  Clear;
  inherited;
end;

function TfcxVisibleAxisNodes.GetMeasureIndex(AIndex: Integer): TfcxSmallCount;
begin
  if (AIndex >= 0) and (AIndex < FCount) then
    if (FAxisContainer.FSlice.FMeasuresContainer.FContainer = FAxisContainer) and (FAxisContainer.FSlice.FMeasuresContainer.Count > 1) then
      Result := FMeasureIndexes[AIndex]
    else
      Result := 0
  else
    Result := -1
end;

function TfcxVisibleAxisNodes.GetIndex(AIndex: Integer): Integer;
begin
  if (AIndex >= 0) and (AIndex < FCount) then
    Result := FIndexes[AIndex]
  else
    Result := -1;
end;

procedure TfcxVisibleAxisNodes.SetCapacity(ACapacity: Integer);
begin
  if FCapacity < ACapacity then
  begin
    FCapacity := ACapacity;
    ReallocMem(FIndexes, FCapacity * SizeOf(Integer));
  end;
  if (FAxisContainer.FSlice.FMeasuresContainer.FContainer = FAxisContainer) and
     (FAxisContainer.FSlice.FMeasuresContainer.Count > 1) and
     (FMeasureIndexes = nil) then
  begin
    ReallocMem(FMeasureIndexes, FCapacity * SizeOf(TfcxSmallCount));
  end;
//  if FAxisContainer.CountAdditionalTotalFunctions > 0 then
    ReallocMem(FAdditionalTotalsIndexes, FCapacity * SizeOf(TfcxSmallCount));
end;

function TfcxVisibleAxisNodes.GetAdditionalTotalIndex(
  AIndex: Integer): TfcxSmallCount;
begin
//  if FAxisContainer.CountAdditionalTotalFunctions > 0 then
    if (AIndex >= 0) and (AIndex < FCount) then
      Result := FAdditionalTotalsIndexes[AIndex]
    else
      Result := -1
//  else
//    Result := -1
end;

procedure TfcxVisibleAxisNodes.FillVisIndexes;
var
  i: integer;
begin
  GetMem(FVisIndexes, FAxisContainer.FCellsCount * SizeOf(Pointer));
  if (FAxisContainer.FSlice.FMeasuresContainer.FContainer = FAxisContainer) and (FAxisContainer.FSlice.FMeasuresContainer.Count > 1) then
    for i := 0 to FAxisContainer.FCellsCount - 1 do
    begin
      if FAxisContainer.FLevelOf[i] = (FAxisContainer.FLevelCount - 1) then
      begin
        GetMem(FVisIndexes[i], FAxisContainer.FSlice.FMeasuresContainer.Count * SizeOf(Integer));
        FillChar(FVisIndexes[i]^, FAxisContainer.FSlice.FMeasuresContainer.Count * SizeOf(Integer), $FF);
      end
      else
      begin
        GetMem(FVisIndexes[i], (FAxisContainer.CountAdditionalTotalFunctionsInLevel[FAxisContainer.FLevelOf[i]] + 1) * FAxisContainer.FSlice.FMeasuresContainer.Count  * SizeOf(Integer));
        FillChar(FVisIndexes[i]^, (FAxisContainer.CountAdditionalTotalFunctionsInLevel[FAxisContainer.FLevelOf[i]] + 1) * FAxisContainer.FSlice.FMeasuresContainer.Count  * SizeOf(Integer), $FF);
      end
    end
  else
    for i := 0 to FAxisContainer.FCellsCount - 1 do
    begin
      if FAxisContainer.FLevelOf[i] = (FAxisContainer.FLevelCount - 1) then
      begin
        GetMem(FVisIndexes[i], SizeOf(Integer));
        FillChar(FVisIndexes[i]^, SizeOf(Integer), $FF);
      end
      else
      begin
        GetMem(FVisIndexes[i], (FAxisContainer.CountAdditionalTotalFunctionsInLevel[FAxisContainer.FLevelOf[i]] + 1) * SizeOf(Integer));
        FillChar(FVisIndexes[i]^, (FAxisContainer.CountAdditionalTotalFunctionsInLevel[FAxisContainer.FLevelOf[i]] + 1) * SizeOf(Integer), $FF);
      end
    end;

  if (FAxisContainer.FSlice.FMeasuresContainer.FContainer = FAxisContainer) and (FAxisContainer.FSlice.FMeasuresContainer.Count > 1) then
    for i := 0 to Count - 1 do
      if FAxisContainer.FLevelOf[FIndexes[i]] = (FAxisContainer.FLevelCount - 1) then
        FVisIndexes[FIndexes[i]][FMeasureIndexes[i]] := i
      else
        FVisIndexes[FIndexes[i]][FMeasureIndexes[i] * (FAxisContainer.CountAdditionalTotalFunctionsInLevel[FAxisContainer.FLevelOf[FIndexes[i]]] + 1) + FAdditionalTotalsIndexes[i] + 1] := i
  else
    for i := 0 to Count - 1 do
      if FAxisContainer.FLevelOf[FIndexes[i]] = (FAxisContainer.FLevelCount - 1) then
        FVisIndexes[FIndexes[i]][0] := i
      else
        FVisIndexes[FIndexes[i]][FAdditionalTotalsIndexes[i] + 1] := i
end;

function TfcxVisibleAxisNodes.GetVisibleIndex(AAbsIndex: Integer;
  AMeasureIndex, AAdditionalTotalIndex: TfcxSmallCount): Integer;
begin
  if AAbsIndex < 0 then
    AAbsIndex := 0;
  if AMeasureIndex = -1 then
    AMeasureIndex := 0;
  if (AMeasureIndex < FAxisContainer.Slice.MeasuresContainer.Count) and (FCount > AAbsIndex) and (FAxisContainer.FCellsCount > AAbsIndex) then
  begin
    Result := FVisIndexes[AAbsIndex][AMeasureIndex * (FAxisContainer.CountAdditionalTotalFunctionsInLevel[FAxisContainer.FLevelOf[AAbsIndex]] + 1) + AAdditionalTotalIndex + 1]
  end
  else
    Result := 0;
end;

procedure TfcxVisibleAxisNodes.DeleteWOM(AVisIndex: Integer);
begin
  if AVisIndex < (FCount - 1) then
  begin
    Move(FIndexes[AVisIndex + 1], FIndexes[AVisIndex], (FCount - AVisIndex - 1) * SizeOf(Integer));
    Move(FAdditionalTotalsIndexes[AVisIndex + 1], FAdditionalTotalsIndexes[AVisIndex], (FCount - AVisIndex - 1) * SizeOf(TfcxSmallCount));
    Dec(FCount);
  end;
end;

procedure TfcxVisibleAxisNodes.DeleteWM(AVisIndex: Integer);
begin
  if AVisIndex < (FCount - 1) then
  begin
    Move(FIndexes[AVisIndex + 1], FIndexes[AVisIndex], (FCount - AVisIndex - 1) * SizeOf(Integer));
    Move(FMeasureIndexes[AVisIndex + 1], FMeasureIndexes[AVisIndex], (FCount - AVisIndex - 1) * SizeOf(TfcxSmallCount));
    Move(FAdditionalTotalsIndexes[AVisIndex + 1], FAdditionalTotalsIndexes[AVisIndex], (FCount - AVisIndex - 1) * SizeOf(TfcxSmallCount));
    Dec(FCount);
  end;
end;

function TfcxVisibleAxisNodes.GetLevelOf(AIndex: Integer): TfcxSmallCount;
begin
  Result := FAxisContainer.FLevelOf[FIndexes[AIndex]]
end;

{ TfcxAxisSortTree }

procedure TfcxAxisSortTree.Clear;
begin
  FMainTreeHeap.Free;
  FMainTreeHeap := TfcxHeap.Create(SizeOf(_fcxSTNode));
  FRootNode     := CreateStNode(-1, 0);
end;

function TfcxAxisSortTree.CompareBySelected(ANode1,
  ANode2: PfcxSTNode): Integer;
var
  AValue1, AValue2: PfcxMeasureValue;
  AMeasureIndex: TfcxSmallCount;
begin
  if FAxisContainer = FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer then
  begin
    AMeasureIndex := FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedMeasureIndex;
    if AMeasureIndex < 0 then
      AMeasureIndex := 0;
    if FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedAdditionalTotalIndex = -1 then
    begin
      AValue1 := FAxisContainer.FSlice.FMeasuresContainer.Cells[ANode1.FLevel, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedLevelIndex, ANode1.FIndex, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedIndexInLevel, AMeasureIndex];
      AValue2 := FAxisContainer.FSlice.FMeasuresContainer.Cells[ANode2.FLevel, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedLevelIndex, ANode2.FIndex, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedIndexInLevel, AMeasureIndex];
    end
    else
    begin
      AValue1 := FAxisContainer.FSlice.FMeasuresContainer.SecondAdditionalTotalCells[ANode1.FLevel, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedLevelIndex, ANode1.FIndex, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedIndexInLevel, AMeasureIndex, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedAdditionalTotalIndex];
      AValue2 := FAxisContainer.FSlice.FMeasuresContainer.SecondAdditionalTotalCells[ANode2.FLevel, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedLevelIndex, ANode2.FIndex, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedIndexInLevel, AMeasureIndex, FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedAdditionalTotalIndex];
    end;
  end
  else
  begin
    AMeasureIndex := FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedMeasureIndex;
    if AMeasureIndex < 0 then
      AMeasureIndex := 0;
    if FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedAdditionalTotalIndex = -1 then
    begin
      AValue1 := FAxisContainer.FSlice.FMeasuresContainer.Cells[FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedLevelIndex, ANode1.FLevel, FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedIndexInLevel, ANode1.FIndex, AMeasureIndex];
      AValue2 := FAxisContainer.FSlice.FMeasuresContainer.Cells[FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedLevelIndex, ANode2.FLevel, FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedIndexInLevel, ANode2.FIndex, AMeasureIndex];
    end
    else
    begin
      AValue1 := FAxisContainer.FSlice.FMeasuresContainer.BaseAdditionalTotalCells[FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedLevelIndex, ANode1.FLevel, FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedIndexInLevel, ANode1.FIndex, AMeasureIndex, FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedAdditionalTotalIndex];
      AValue2 := FAxisContainer.FSlice.FMeasuresContainer.BaseAdditionalTotalCells[FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedLevelIndex, ANode2.FLevel, FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedIndexInLevel, ANode2.FIndex, AMeasureIndex, FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedAdditionalTotalIndex];
    end
  end;
  if AValue1 = nil then
    if AValue2 = nil then
      Result := 0
    else
      Result := -1
  else
    if AValue2 = nil then
      Result := 1
    else
      if AValue1.Value = AValue2.Value then
        Result := 0
      else
        Result := fcxCompareHelper[AValue1.Value > AValue2.Value];
  if Result = 0 then
    if ANode1.FIndex = ANode2.FIndex then
      Result := 0
    else
      Result := fcxCompareHelper[ANode1.FIndex > ANode2.FIndex];
end;

function TfcxAxisSortTree.CompareByTotal(ANode1,
  ANode2: PfcxSTNode): Integer;
var
  AValue1, AValue2: PfcxMeasureValue;
  AMeasureIndex: TfcxSmallCount;
begin
  if FAxisContainer = FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer then
  begin
    AMeasureIndex := FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.FSelectedMeasureIndex;
    if AMeasureIndex < 0 then
      AMeasureIndex := 0;
    AValue1 := FAxisContainer.FSlice.FMeasuresContainer.Cells[ANode1.FLevel, -1, ANode1.FIndex, 0, AMeasureIndex];
    AValue2 := FAxisContainer.FSlice.FMeasuresContainer.Cells[ANode2.FLevel, -1, ANode2.FIndex, 0, AMeasureIndex];
  end
  else
  begin
    AMeasureIndex := FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.FSelectedMeasureIndex;
    if AMeasureIndex < 0 then
      AMeasureIndex := 0;
    AValue1 := FAxisContainer.FSlice.FMeasuresContainer.Cells[-1, ANode2.FLevel, 0, ANode1.FIndex, AMeasureIndex];
    AValue2 := FAxisContainer.FSlice.FMeasuresContainer.Cells[-1, ANode2.FLevel, 0, ANode2.FIndex, AMeasureIndex]
  end;
  if AValue1 = nil then
    if AValue2 = nil then
      Result := 0
    else
      Result := -1
  else
    if AValue2 = nil then
      Result := 1
    else
      if AValue1.Value = AValue2.Value then
        Result := 0
      else
        Result := fcxCompareHelper[AValue1.Value > AValue2.Value];
  if Result = 0 then
    if ANode1.FIndex = ANode2.FIndex then
      Result := 0
    else
      Result := fcxCompareHelper[ANode1.FIndex > ANode2.FIndex];
end;

constructor TfcxAxisSortTree.Create(AAxisContainer: TfcxAxisContainer);
begin
  FAxisContainer := AAxisContainer;
  FMainTreeHeap  := TfcxHeap.Create(SizeOf(_fcxSTNode));
  FRootNode      := CreateStNode(-1, 0);
{
  FMainTreeHeap.GetMem(FRootNode.Nodes, FAxisContainer.FAxisTree[0].Count * SizeOf(_fcxSTNodeArray));
  FRootNode.Nodes[0] := FMainTreeHeap.AllocMem(SizeOf(TMDNode2));
}
end;

function TfcxAxisSortTree.CreateSTNode(ALevel: TfcxSmallCount;
  AIndex: Integer): PfcxSTNode;
begin
  Result := FMainTreeHeap.AllocMem(SizeOf(_fcxSTNode));
  Result^.FLevel := ALevel;
  Result^.FIndex := AIndex;
end;

destructor TfcxAxisSortTree.Destroy;
begin
  FreeAndNil(FMainTreeHeap);
  inherited;
end;

procedure TfcxAxisSortTree.FillSortTree;
  procedure FillSTNode(ASTNode: PfcxSTNode);
  var
    ACount, AIndex: Integer;
  begin
    if FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst = -1 then
      Exit;
    ACount := FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
    FMainTreeHeap.GetMem(Pointer(ASTNode.Nodes), ACount * SizeOf(_fcxSTNodeArray));
//    for AIndex := FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst to FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast do
    for AIndex := 0 to ACount - 1 do
    begin
      ASTNode.Nodes[AIndex] := FMainTreeHeap.AllocMem(SizeOf(_fcxSTNode));
      ASTNode.Nodes[AIndex].FIndex := FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + AIndex;
      ASTNode.Nodes[AIndex].FLevel := ASTNode.FLevel + 1;
      if ASTNode.FLevel < (FAxisContainer.FLevelCount - 2) then
        FillSTNode(ASTNode.Nodes[AIndex]);
    end;
  end;
begin
  FillSTNode(FRootNode);
end;

function TfcxAxisSortTree.InternalCompare(ANode1, ANode2: PfcxSTNode): Integer;
begin
  if ANode1.FIndex = ANode2.FIndex then
    Result := 0
  else
    Result := fcxCompareHelper[ANode1.FIndex > ANode2.FIndex];
end;

procedure TfcxAxisSortTree.SortTree;
var
  AQSStack: TfcxQSStack;
  AInternalCompare: TfcxSTNodeCompare;
  procedure SortSTNode(ASTNode: PfcxSTNode);
  var
    ACount, AIndex: Integer;
  begin
    if FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast = -1 then
      Exit;
    ACount := FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndLast - FAxisContainer.FAxisTree[ASTNode.FLevel].Nodes[ASTNode.FIndex].IndFirst + 1;
    if ACount > 1 then 
    begin
      if ASTNode.Nodes[0].FLevel <= (FAxisContainer.Fields.Count - 1) then
        SortNoRecurseGlobalStack(PfcxPointerArray(ASTNode.Nodes), 0, ACount - 1, TfcxSortPointerCompare(AInternalCompare){TfcxSortPointerCompare(FAxisContainer.Fields[FAxisContainer.FAxisSourceTree.FFieldIndexOfLevel[ASTNode.Nodes[0].FLevel]].FSTNodeCompare)}, AQSStack)
      else 
        SortNoRecurseGlobalStack(PfcxPointerArray(ASTNode.Nodes), 0, ACount - 1, TfcxSortPointerCompare(AInternalCompare), AQSStack);
    end;
    for AIndex := 0 to ACount - 1 do
    begin
      if ASTNode.FLevel < FAxisContainer.FLevelCount - 2 then
        SortSTNode(ASTNode.Nodes[AIndex]);
    end;
  end;
begin
  GetMem(AQSStack, SizeOf(_fcxQSStack));
  AQSStack.Size := 2048;
  GetMem(AQSStack.Left, AQSStack.Size * SizeOf(integer));
  GetMem(AQSStack.Right, AQSStack.Size * SizeOf(integer));
  if FAxisContainer.FDefaultTypeSort in cfc_SortByValue then
    if (FAxisContainer.FSlice.FMeasuresContainer <> nil) and
       (FAxisContainer.FSlice.FMeasuresContainer.Count > 0) and
       (not FAxisContainer.FSlice.FMeasuresContainer.FIsCleared) then
    begin
      if FAxisContainer.FDefaultTypeSort = md_tsa_ByTotalValue then
        AInternalCompare := CompareByTotal
      else
        AInternalCompare := CompareBySelected;
      if FAxisContainer = FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer then
      begin
        if not FAxisContainer.FSlice.FMeasuresContainer.FSecondContainer.CheckSelected(FAxisContainer.FDefaultTypeSort) then
          AInternalCompare := InternalCompare
      end
      else
      begin
        if not FAxisContainer.FSlice.FMeasuresContainer.FBaseContainer.CheckSelected(FAxisContainer.FDefaultTypeSort) then
          AInternalCompare := InternalCompare
      end
    end
    else
      AInternalCompare := InternalCompare
  else
    AInternalCompare := InternalCompare;
  SortSTNode(FRootNode);
  FreeMem(AQSStack.Left);
  FreeMem(AQSStack.Right);
  FreeMem(AQSStack);
end;

{ TfcxPageContainer }

function TfcxPageContainer.AddFilterField(ASliceField: TfcxSliceField;
  AName, ACaption: TfcxString): integer;
begin
  Result := Fields.Count;
  if not InsertField(ASliceField, Result, AName, ACaption) then
    Result := -1;
end;

function TfcxPageContainer.DeleteFilterField(AIndex: integer): boolean;
begin
  Result := RemoveFieldByIndex(AIndex);
end;

function TfcxPageContainer.GetMeasuresLevel: TfcxSmallCount;
begin
  if FSlice.MeasuresContainer.Container = Self then
  begin
    if FSlice.MeasuresContainer.FPosition = -1 then
      Result := 0
    else
    if FSlice.MeasuresContainer.FPosition > Fields.Count then
      Result := Fields.Count
    else
      Result := FSlice.MeasuresContainer.FPosition;
  end
  else
    Result := -1;
end;

function TfcxPageContainer.GetMeasuresLevelVisibleIndex: TfcxSmallCount;
begin
  Result := MeasuresLevel;
end;

function TfcxPageContainer.InsertFilterField(ASliceField: TfcxSliceField;
  AIndex: integer; AName, ACaption: TfcxString): integer;
begin
  if InsertField(ASliceField, AIndex, AName, ACaption) then
    Result := AIndex
  else
    Result := -1;
end;

{ TfcxSliceContainer }

constructor TfcxSliceContainer.Create(ASlice: TfcxSlice; ARegion: TfcxRegionOfField);
begin
  FSlice := ASlice;
  FRegion := ARegion;
  FAlignment := taLeftJustify;
end;

destructor TfcxSliceContainer.Destroy;
begin
  inherited;
end;

procedure TfcxSliceContainer.LoadFromXML(AItem: TfcxXMLItem);
begin
  if AItem.Prop['alignment'] <> '' then
    Alignment := TAlignment(GetEnumValue(TypeInfo(TAlignment), AItem.Prop['alignment']))
  else
    Alignment := taLeftJustify;
end;

procedure TfcxSliceContainer.SaveToXML(AItem: TfcxXMLItem);
const
  RegionToName: array[TfcxRegionOfField] of String = (
 { rf_Page     } 'page_fields',
 { rf_CapXAx   } 'xaxis_fields',
 { rf_CapYAx   } 'yaxis_fields',
 { rf_CapFacts } 'measure_fields',
 { rf_None     } ''
  );
begin
  AItem.Name := RegionToName[Region];
  AItem.Prop['alignment'] := GetEnumName(TypeInfo(TAlignment), Ord(Alignment));
end;

procedure TfcxSliceContainer.SetAlignment(const Value: TAlignment);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_CapPageWidth,
    chs_CapXWidth,
    chs_CapYWidth,
    chs_None,
    chs_None
  );
begin
  if FAlignment <> Value then
  begin
    FSlice.StartChange;
    FAlignment := Value;
    if FRegion = rf_CapFacts then
      FSlice.StopChange([cChanges[FSlice.MeasuresContainer.Container.Region]])
    else
      FSlice.StopChange([cChanges[FRegion]]);
  end;
end;

{ TfcxCommonFieldOfRegion }

procedure TfcxCommonFieldOfRegion.BeginUpdate;
begin
  Owner.FContainer.FSlice.StartChange;
end;

procedure TfcxCommonFieldOfRegion.Changed;
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_Page,
    chs_XAxis,
    chs_YAxis,
    chs_Fact,
    chs_None
  );
begin
  Owner.FContainer.FSlice.StartChange;
  Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion]]);
end;

constructor TfcxCommonFieldOfRegion.Create(AOwner: TfcxCommonFieldsOfRegion; AName, ACaption: TfcxString);
begin
  Create(AOwner);
  FName := AName;
  FCaption := ACaption;
  FAlignment := taLeftJustify;
end;

constructor TfcxCommonFieldOfRegion.Create(AOwner: TfcxCommonFieldsOfRegion);
begin
  inherited Create;
  FFormat := nil;
  FOwner := AOwner;
end;

destructor TfcxCommonFieldOfRegion.Destroy;
begin
  inherited;
  FFormat.Free;
  FFormat := nil;
end;

procedure TfcxCommonFieldOfRegion.DoFormatChange(Sender: TObject);
begin
  Changed;
end;

function TfcxCommonFieldOfRegion.GetExpressions: TfcxExpressions;
begin
  Result := nil;
end;

function TfcxCommonFieldOfRegion.GetWidthSubGroup: SmallInt;
begin
  Result := 0;
end;

procedure TfcxCommonFieldOfRegion.SetWidthSubGroup(const AValue: SmallInt);
begin
  // override in descendant
end;

function TfcxCommonFieldOfRegion.GetHeightSubGroup: SmallInt;
begin
  Result := 0;
end;

procedure TfcxCommonFieldOfRegion.SetHeightSubGroup(const AValue: SmallInt);
begin
end;

procedure TfcxCommonFieldOfRegion.EndUpdate;
begin
  Owner.FContainer.FSlice.StopChange([]);
end;

procedure TfcxCommonFieldOfRegion.GeneralChanged;
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_CapPage,
    chs_CapXAxis,
    chs_CapYAxis,
    chs_CapMeasures,
    chs_None
  );
begin
  Owner.FContainer.FSlice.StartChange;
  Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion]]);
end;

procedure TfcxCommonFieldOfRegion.ExpressionChanged;
const
  cChanges: array[TfcxRegionOfField] of TfcxChangesInSlice =
  (
    [chs_CapPage, chs_Script],
    [chs_CapXAxis, chs_Script],
    [chs_CapYAxis, chs_Script],
    [chs_CapMeasures, chs_Script],
    [chs_None]
  );
begin
  Owner.FContainer.FSlice.StartChange;
  Owner.FContainer.FSlice.StopChange(cChanges[Owner.FContainer.FRegion]);
end;

function TfcxCommonFieldOfRegion.GetCaption: TfcxString;
begin
  if FCaption = '' then
    Result := FName
  else
    Result := FCaption;
end;

function TfcxCommonFieldOfRegion.GetCaptionWidth: SmallInt;
begin
  if FCaptionWidth = 0 then
    Result := Owner.FContainer.FSlice.DefaultColWidth
  else
    Result := FCaptionWidth;
end;

function TfcxCommonFieldOfRegion.GetIndex: Integer;
begin
  Result := FOwner.GetIndexOf(Self);
end;

function TfcxCommonFieldOfRegion.LoadFromXML(AItem: TfcxXMLItem): boolean;
var
  I: Integer;
  ADisplayFormat: TfcxFormat;
begin
  Result := False;
  if AItem.Name <> 'field' then
    Exit;
  Result := True;
  Name := AItem.Prop['name'];
  Caption := AItem.Prop['caption'];
  if Name = '' then
    Name := Caption;
  Alignment := TAlignment(GetEnumValue(TypeInfo(TAlignment), AItem.Prop['alignment']));
  CaptionWidth := AItem.IntProp['captionwidth'];
  for I := 0 to AItem.Count - 1 do
    if AItem[I].Name = 'display_format' then
    begin
      ADisplayFormat := TfcxFormat.Create;
      ADisplayFormat.FromXML(AItem[I]);
      DisplayFormat := ADisplayFormat;
      ADisplayFormat.Free;
    end
end;

procedure TfcxCommonFieldOfRegion.SaveToXML(AItem: TfcxXMLItem);
begin
  AItem.Name := 'field';
  AItem.Prop['name'] := Name;
  AItem.Prop['caption'] := Caption;
  AItem.Prop['alignment'] := GetEnumName(TypeInfo(TAlignment), Ord(Alignment));
  AItem.IntProp['captionwidth'] := CaptionWidth;
  if HasDisplayFormat then
  begin
    AItem := AItem.Add;
    AItem.Name := 'display_format';
    DisplayFormat.ToXML(AItem);
  end;
end;

procedure TfcxCommonFieldOfRegion.SetAlignment(const Value: TAlignment);
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_CapPageWidth,
    chs_CapXWidth,
    chs_CapYWidth,
    chs_None,
    chs_None
  );
begin
  if FAlignment <> Value then
  begin
    Owner.FContainer.FSlice.StartChange;
    FAlignment := Value;
    Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion]]);
  end;
end;

procedure TfcxCommonFieldOfRegion.SetCaption(const Value: TfcxString);
begin
  if FCaption <> Value then
  begin
    FCaption := Value;
    Changed;
  end
end;

procedure TfcxCommonFieldOfRegion.SetCaptionWidth(const Value: SmallInt);
var
  AValue: SmallInt;
const
  cChanges: array[TfcxRegionOfField] of TfcxChangeInSlice =
  (
    chs_CapPageWidth,
    chs_CapXWidth,
    chs_CapYWidth,
    chs_None,
    chs_None
  );
begin
  if Value = Owner.FContainer.FSlice.DefaultColWidth then
    AValue := 0
  else
    AValue := Value;
  if FCaptionWidth <> AValue then
  begin
    Owner.FContainer.FSlice.StartChange;
    FCaptionWidth := AValue;
    if (self is TfcxAxisField) and (Owner.FContainer = Owner.FContainer.FSlice.YAxisContainer) and Owner.FContainer.FSlice.SyncronizeYFieldsWidth then
    begin
      if TfcxAxisField(self).Width <> AValue then
        TfcxAxisField(self).Width := AValue;
    end;
    Owner.FContainer.FSlice.StopChange([cChanges[Owner.FContainer.FRegion]]);
  end
end;

procedure TfcxCommonFieldOfRegion.SetName(const Value: TfcxString);
begin
  FName := Value;
end;

{ TfcxCommonFieldsOfRegion }

function TfcxCommonFieldsOfRegion.Add(
  AField: TfcxCommonFieldOfRegion): TfcxSmallCount;
begin
  Result := FFields.Add(AField);
end;

procedure TfcxCommonFieldsOfRegion.Clear;
begin
  FFields.Clear;
end;

constructor TfcxCommonFieldsOfRegion.Create(AContainer: TfcxSliceContainer);
begin
  FContainer := AContainer;
  FFields := TfcxList.Create;
  FFields.AutoFree := True;
end;

procedure TfcxCommonFieldsOfRegion.Delete(AIndex: TfcxSmallCount);
begin
  FFields.Delete(AIndex);
end;

destructor TfcxCommonFieldsOfRegion.Destroy;
begin
  FreeAndNil(FFields);
  inherited;
end;

function TfcxCommonFieldsOfRegion.GetCount: TfcxSmallCount;
begin
  Result := FFields.Count;
end;

function TfcxCommonFieldsOfRegion.GetIndexByName(AFieldName: TfcxString): TfcxSmallCount;
var
  I: TfcxSmallCount;
begin
  for I := 0 to Count - 1 do
    if Items[I].Name = AFieldName then
    begin
      Result := I;
      Exit;
    end;
  Result := -1;
end;

function TfcxCommonFieldsOfRegion.GetIndexOf(AField: TfcxCommonFieldOfRegion): TfcxSmallCount;
begin
  for Result := 0 to FFields.Count - 1 do
    if FFields.List[Result] = AField then
      exit;
  Result := -1;
end;

function TfcxCommonFieldsOfRegion.GetItem(Index: TfcxSmallCount): TfcxCommonFieldOfRegion;
begin
  Result := FFields.List[Index];
end;

procedure TfcxCommonFieldsOfRegion.Insert(AField: TfcxCommonFieldOfRegion;
  AIndex: TfcxSmallCount);
begin
  FFields.Insert(AIndex, AField);
end;

{ TfcxAxisFields }

function TfcxAxisFields.FindSliceField(ASliceField: TfcxSliceField): integer;
var
  i: integer;
begin
  for i := 0 to FFields.Count - 1 do
  begin
    if TfcxAxisField(FFields.List[i]).FSliceField = ASliceField then
    begin
      Result := i;
      exit;
    end;
  end;
  Result := -1;
end;

function TfcxAxisFields.GetItem(Index: Integer): TfcxAxisField;
begin
  Result := FFields.List[Index];
end;

{ TfcxSliceDateSplitCubeField }

constructor TfcxSliceDateSplitCubeField.Create(ASlice: TfcxSlice;
  ACubeField: TfcxCommonField);
begin
  inherited Create(ASlice, ACubeField);
  FDateType := TfcxCommonDatePathField(ACubeField).DateType
end;

{ TfcxCommonSliceCubeField }

procedure TfcxCommonSliceCubeField.BeginUpdateFieldFilter;
begin
  FCubeFieldFilter.BeginUpdateField
end;

constructor TfcxCommonSliceCubeField.Create(ASlice: TfcxSlice;
  ACubeField: TfcxCommonField);
begin
  inherited Create(ASlice);
  FCubeField := ACubeField;
  FCaption := FCubeField.CubeFieldDisplayLabel;
  FCubeFieldFilter := FSlice.FFilterManager.UVFilter.CubeFieldFilter[FCubeField];
end;

function TfcxCommonSliceCubeField.EndUpdateFieldFilter: boolean;
begin
  Result := FCubeFieldFilter.EndUpdateField
end;

function TfcxCommonSliceCubeField.GetCanGroup: Boolean;
begin
  Result := FCubeField.CanGroup
end;

function TfcxCommonSliceCubeField.GetDisplayFormat: TfcxFormat;
begin
  Result := FCubeField.DisplayFormat
end;

function TfcxCommonSliceCubeField.GetFieldName: TfcxString;
begin
  Result := FCubeField.CubeFieldName;
end;

function TfcxCommonSliceCubeField.GetGroupCaption(
  AGroupIndex: integer): TfcxString;
begin
  Result := FCubeField.GroupCaption[AGroupIndex]
end;

function TfcxCommonSliceCubeField.GetGroupCount: Integer;
begin
  Result := FCubeField.GroupCount
end;

function TfcxCommonSliceCubeField.GetGroupIndexByOrder(
  AOrder: integer): integer;
begin
  Result := FCubeField.GroupIndexByOrder[AOrder]
end;

function TfcxCommonSliceCubeField.GetGroupManager: TfcxCommonGroupManager;
begin
  Result := FCubeField.GroupManager
end;

function TfcxCommonSliceCubeField.GetGroupUVCount(AGroupIndex: integer): integer;
begin
  Result := FCubeField.GroupUVCount[AGroupIndex]
end;

function TfcxCommonSliceCubeField.GetGroupUVIndexByOrder(AGroupIndex,
  AOrder: integer): integer;
begin
  Result := FCubeField.GroupUVIndexByOrder[AGroupIndex, AOrder]
end;

function TfcxCommonSliceCubeField.GetHasGroups: Boolean;
begin
  Result := FCubeField.HasGroups
end;

function TfcxCommonSliceCubeField.GetNonGroupUVCount: integer;
begin
  Result := FCubeField.NonGroupUVCount
end;

function TfcxCommonSliceCubeField.GetNonGroupUVIndexByOrder(
  AOrder: integer): integer;
begin
  Result := FCubeField.NonGroupUVIndexByOrder[AOrder]
end;

function TfcxCommonSliceCubeField.GetUVCaption(
  AUVIndex: integer): TfcxString;
begin
  Result := FCubeField.CaptionValueAtIndex[AUVIndex];
end;

function TfcxCommonSliceCubeField.GetUVCaptionFromRec(
  ARecIndex: integer): TfcxString;
begin
  Result := FSlice.FCube.SourceHolder.UniqueValueCaption[ARecIndex, FCubeField];
end;

function TfcxCommonSliceCubeField.GetUVCaptionInGroup(AGroupIndex,
  AOrder: integer): TfcxString;
begin
  Result := FCubeField.UVCaptionInGroup[AGroupIndex, AOrder]
end;

function TfcxCommonSliceCubeField.GetUVCaptionInNonGroups(
  AOrder: integer): TfcxString;
begin
  Result := FCubeField.UVCaptionInNonGroups[AOrder]
end;

function TfcxCommonSliceCubeField.GetUVFilterActive: boolean;
begin
  Result := FSlice.FilterManager.UVFilter.Active;
end;

function TfcxCommonSliceCubeField.GetUVFilteredValuesCount: integer;
begin
  Result := FCubeFieldFilter.FilteredValuesCount
end;

function TfcxCommonSliceCubeField.GetUVFilterOf(
  AUVIndex: integer): boolean;
begin
  Result := FCubeFieldFilter.Filter[AUVIndex]
end;

function TfcxCommonSliceCubeField.GetUVFilterOfValue(
  AVarValue: Variant): boolean;
begin
  Result := FCubeFieldFilter.FilterByValue[AVarValue]
end;

function TfcxCommonSliceCubeField.GetUVFilterStateOfGroup(AGroupIndex: integer): TfcxCheckState;
begin
  Result := FCubeFieldFilter.StateOfGroup[AGroupIndex];
end;

function TfcxCommonSliceCubeField.GetUVFilterType: TfcxUVFilterType;
begin
  Result := FCubeFieldFilter.UVFilterType
end;

function TfcxCommonSliceCubeField.GetUVAvailable(AUVIndex: integer): boolean;
begin
  Result := FCubeFieldFilter.Available[AUVIndex]
end;

function TfcxCommonSliceCubeField.GetUVIndexAndVariantFromRec(
  ARecIndex: integer; var AVariantValue: Variant): integer;
begin
  Result := FSlice.FCube.SourceHolder.GetUniqueValueIndexAndAsVariant(ARecIndex, FCubeField, AVariantValue);
end;

function TfcxCommonSliceCubeField.GetUVIndexFromRec(
  ARecIndex: integer): integer;
begin
  Result := FSlice.FCube.SourceHolder.UniqueValueIndex[ARecIndex, FCubeField];
end;

function TfcxCommonSliceCubeField.GetUVSingleIndex: Integer;
begin
  Result := FCubeFieldFilter.UVSingleIndex;
end;

function TfcxCommonSliceCubeField.GetUVValueFromRec(
  ARecIndex: integer): Variant;
begin
  Result := FSlice.FCube.SourceHolder.UniqueValueAsVariant[ARecIndex, FCubeField];
end;

function TfcxCommonSliceCubeField.GetUVAvailableCount: integer;
begin
  Result := FCubeFieldFilter.UVAvailableCount
end;

function TfcxCommonSliceCubeField.HasDisplayFormat: Boolean;
begin
  Result := FCubeField.DataTypeProcessor.HasDisplayFormat;
end;

procedure TfcxCommonSliceCubeField.InverseFilter;
begin
  FCubeFieldFilter.InverseFilter
end;

procedure TfcxCommonSliceCubeField.RollBackFieldFilter;
begin
  FCubeFieldFilter.RollBackField
end;

procedure TfcxCommonSliceCubeField.SetAllFilter;
begin
  FCubeFieldFilter.SetAllFilter
end;

procedure TfcxCommonSliceCubeField.SetCanGroup(const Value: Boolean);
begin
  FCubeField.CanGroup := Value
end;

procedure TfcxCommonSliceCubeField.SetDisplayFormat(
  const Value: TfcxFormat);
begin
  FCubeField.DisplayFormat := Value
end;

procedure TfcxCommonSliceCubeField.SetGroupCaption(AGroupIndex: integer;
  const Value: TfcxString);
begin
  FCubeField.GroupCaption[AGroupIndex] := Value
end;

procedure TfcxCommonSliceCubeField.SetNoneFilter;
begin
  FCubeFieldFilter.SetNoneFilter
end;

procedure TfcxCommonSliceCubeField.SetRangeFilter(ARange: TfcxRanges);
begin
  FCubeFieldFilter.SetRangeFilter(ARange);
end;

procedure TfcxCommonSliceCubeField.SetUVFilterOf(AUVIndex: integer;
  const Value: boolean);
begin
  FCubeFieldFilter.Filter[AUVIndex] := Value;
end;

procedure TfcxCommonSliceCubeField.SetUVFilterOfValue(AVarValue: Variant;
  const Value: boolean);
begin
  FCubeFieldFilter.FilterByValue[AVarValue] := Value;
end;

procedure TfcxCommonSliceCubeField.SetUVFilterStateOfGroup(
  AGroupIndex: integer; const Value: TfcxCheckState);
var
  i, AGroupUVCount: integer;
  AValue: boolean;
begin
  if Value in [csUnchecked, csChecked] then
  begin
    AValue := Value = csChecked;
    AGroupUVCount := GroupUVCount[AGroupIndex];
    HackTfcxFilterManager(FSlice.FilterManager).StartChangeExt;

    for i := 0 to AGroupUVCount - 1 do
      SetUVFilterOf(GroupUVIndexByOrder[AGroupIndex, i], AValue);

    if FSlice.FilterManager.UVFilter.Active then
      HackTfcxFilterManager(FSlice.FilterManager).StopChangeExt([chf_ActivedFilterChanged])
    else
      HackTfcxFilterManager(FSlice.FilterManager).StopChangeExt([chf_NonActivedFilterChanged]);
  end;
end;

procedure TfcxCommonSliceCubeField.SetUVFilterType(
  const Value: TfcxUVFilterType);
begin
  FCubeFieldFilter.UVFilterType := Value;
end;

procedure TfcxCommonSliceCubeField.SetUVGroupSingleIndex(const Value: Integer);
begin
  FCubeFieldFilter.GroupSingleIndex := Value;
end;

procedure TfcxCommonSliceCubeField.SetUVSingleIndex(const Value: Integer);
begin
  FCubeFieldFilter.UVSingleIndex := Value;
end;

function TfcxCommonSliceCubeField.GetUVValue(
  AUVIndex: integer): Variant;
begin
  Result := FCubeField.ValueAsVariantAtIndex[AUVIndex];
end;

{ TfcxSliceTimeSplitCubeField }

constructor TfcxSliceTimeSplitCubeField.Create(ASlice: TfcxSlice;
  ACubeField: TfcxCommonField);
begin
  inherited Create(ASlice, ACubeField);
  FTimeType := TfcxCommonTimePathField(ACubeField).TimeType
end;

{ TfcxFieldsContainer }

function TfcxFieldsContainer.AddMeasuresField: integer;
begin
  Result := InsertMeasuresField(Fields.Count)
end;

function TfcxFieldsContainer.ContainField(ASliceField: TfcxSliceField): boolean;
begin
  Result := (Fields.FindSliceField(ASliceField) <> -1);
end;

constructor TfcxFieldsContainer.Create(ASlice: TfcxSlice; ARegion: TfcxRegionOfField);
begin
  inherited;
  FFields := TfcxAxisFields.Create(Self);
end;

function TfcxFieldsContainer.DeleteMeasuresField: boolean;
begin
  if Region <> rf_Page then
    Result := FSlice.FPageContainer.InsertMeasuresField(0) <> -1
  else
    Result := False;
end;

destructor TfcxFieldsContainer.Destroy;
begin
  FreeAndNil(FFields);
  inherited;
end;

function TfcxFieldsContainer.GetLevelCount: TfcxSmallCount;
begin
  Result := Fields.Count;
end;

function TfcxFieldsContainer.GetMeasuresPosition: TfcxSmallCount;
begin
  if FSlice.MeasuresContainer.Container = Self then
  begin
    if FSlice.MeasuresContainer.FPosition = -1 then
      Result := Fields.Count
    else
    if FSlice.MeasuresContainer.FPosition > Fields.Count then
      Result := Fields.Count
    else
      Result := FSlice.MeasuresContainer.FPosition;
  end
  else
    Result := -1;
end;

function TfcxFieldsContainer.InsertField(ASliceField: TfcxSliceField;
  AIndexInList: TfcxSmallCount; AName, ACaption: TfcxString;
  ABeforeMeasures: Boolean): boolean;
var
  AOldIndexInList: integer;
  ARegion: TfcxRegionOfField;
  AFieldOfRegion: TfcxCommonFieldOfRegion;
  AChanges: TfcxChangesInSlice;
  AAllow: boolean;
//  AMeasurePos: TfcxSmallCount;
begin
  Result := False;
  if not Assigned(ASliceField) then
    Exit;
  AAllow := True;
  if not FSlice.Loading and Assigned(FSlice.FOnBeforeAddSliceFieldToRegion) then
  begin
    AAllow := True;
    FSlice.OnBeforeAddSliceFieldToRegion(AName, FRegion, AAllow);
    if not AAllow then
      Exit;
  end;
  Result := True;
  AOldIndexInList := Fields.FindSliceField(ASliceField);
  if AOldIndexInList <> -1 then
  begin
    ARegion := Region;
    AFieldOfRegion := Fields[AOldIndexInList];
  end
  else
  begin
    ARegion := rf_None;
    AFieldOfRegion := Nil;
  end;
  FSlice.StartChange;
  AChanges := RegionToChangeTypeMap[Region];
  if True {not FSlice.AllowDimInBothAxisAtOnce} and (AOldIndexInList = -1) then
  begin
// check in all containers
    if Region <> rf_Page then
    begin
      AOldIndexInList := FSlice.FPageContainer.Fields.FindSliceField(ASliceField);
      if AOldIndexInList <> -1 then
      begin
        AFieldOfRegion := FSlice.FPageContainer.Fields[AOldIndexInList];
        FSlice.FPageContainer.Fields.FFields.AutoFree := False;
        FSlice.FPageContainer.Fields.FFields.Delete(AOldIndexInList);
        FSlice.FPageContainer.Fields.FFields.AutoFree := True;
        if (FSlice.MeasuresContainer.Container = FSlice.FPageContainer) and (FSlice.MeasuresContainer.Position > AOldIndexInList) then
          dec(FSlice.MeasuresContainer.FPosition);
        AChanges := AChanges + [chs_CapPage];
      end;
    end;
    if AOldIndexInList = -1 then
      if Region <> rf_CapXAx then
      begin
        AOldIndexInList := FSlice.FXAxisContainer.Fields.FindSliceField(ASliceField);
        if AOldIndexInList <> -1 then
        begin
          AFieldOfRegion := FSlice.FXAxisContainer.Fields[AOldIndexInList];
          if Region = rf_Page then
            TfcxAxisField(AFieldOfRegion).FTopNProcessor.Active := False;
          FSlice.FXAxisContainer.Fields.FFields.AutoFree := False;
          FSlice.FXAxisContainer.Fields.FFields.Delete(AOldIndexInList);
          FSlice.FXAxisContainer.Fields.FFields.AutoFree := True;
          if (FSlice.MeasuresContainer.Container = FSlice.FXAxisContainer) and (FSlice.MeasuresContainer.Position > AOldIndexInList) then
            dec(FSlice.MeasuresContainer.FPosition);
          AChanges := AChanges + [chs_CapXAxis];
        end;
      end;
    if AOldIndexInList = -1 then
      if Region <> rf_CapYAx then
      begin
        AOldIndexInList := FSlice.FYAxisContainer.Fields.FindSliceField(ASliceField);
        if AOldIndexInList <> -1 then
        begin
          AFieldOfRegion := FSlice.FYAxisContainer.Fields[AOldIndexInList];
          if Region = rf_Page then
            TfcxAxisField(AFieldOfRegion).FTopNProcessor.Active := False;
          FSlice.FYAxisContainer.Fields.FFields.AutoFree := False;
          FSlice.FYAxisContainer.Fields.FFields.Delete(AOldIndexInList);
          FSlice.FYAxisContainer.Fields.FFields.AutoFree := True;
          if (FSlice.MeasuresContainer.Container = FSlice.FYAxisContainer) and (FSlice.MeasuresContainer.Position > AOldIndexInList) then
            dec(FSlice.MeasuresContainer.FPosition);
          AChanges := AChanges + [chs_CapYAxis];
        end;
      end;
  end;
  if AFieldOfRegion = nil then
  begin
// create new
    if AName = '' then
      AName := ASliceField.FieldName;
    if ACaption = '' then
      ACaption := ASliceField.Caption;
    AFieldOfRegion := TfcxAxisField.Create(Fields, ASliceField, AName, ACaption);
  end
  else
  if ARegion = rf_None then
  begin
    AFieldOfRegion.FOwner := Fields;
    if AFieldOfRegion is TfcxAxisField then
      if TfcxAxisField(AFieldOfRegion).TopNProcessor.Active and (FSlice.TopNs.ActiveCount > 1) then
        TfcxAxisField(AFieldOfRegion).TopNProcessor.Active := False;
  end;
  if Region = rf_CapYAx then
  begin
    if FSlice.SyncronizeYFieldsWidth then
      TfcxAxisField(AFieldOfRegion).CaptionWidth := TfcxAxisField(AFieldOfRegion).Width;
  end;
  if ARegion <> rf_None then
  begin
// field in this container
    if (AIndexInList = AOldIndexInList) then
    begin
      if (MeasuresLevel <> -1) then
      begin
        if (FSlice.MeasuresContainer.FPosition = AIndexInList) and ABeforeMeasures then
// move measures on position MeasuresContainer.FPosition + 1
          InsertMeasuresField(FSlice.MeasuresContainer.FPosition + 1)
        else
        if ((FSlice.MeasuresContainer.FPosition - 1) = AIndexInList) and not ABeforeMeasures then
// move measures on position MeasuresContainer.FPosition - 1
          InsertMeasuresField(FSlice.MeasuresContainer.FPosition - 1);
      end;
      FSlice.StopChange([]);
      exit;
    end;
    if AIndexInList >= Fields.FFields.Count then
      AIndexInList := Fields.FFields.Count - 1;
    Fields.FFields.Move(AOldIndexInList, AIndexInList);
    if (MeasuresLevel <> -1) and (FSlice.MeasuresContainer.FPosition <> -1) then
    begin
// measures in this container
      if AOldIndexInList > AIndexInList then
      begin
        if ((AIndexInList = FSlice.MeasuresContainer.Position) and ABeforeMeasures) or
           ((AOldIndexInList >= FSlice.MeasuresContainer.Position) and
            (AIndexInList < FSlice.MeasuresContainer.Position)) then
          inc(FSlice.MeasuresContainer.FPosition)
      end
      else
      begin
        if ((AIndexInList = FSlice.MeasuresContainer.Position) and ABeforeMeasures) then
          inc(FSlice.MeasuresContainer.FPosition)
        else
        if (AOldIndexInList < FSlice.MeasuresContainer.Position) and
           (AIndexInList > FSlice.MeasuresContainer.Position) then
          dec(FSlice.MeasuresContainer.FPosition)
      end;
    end;
  end
  else
  begin
    Fields.FFields.Insert(AIndexInList, AFieldOfRegion);
    if (MeasuresLevel <> -1) and (FSlice.MeasuresContainer.FPosition <> -1) then
// measures in this container
      if ((AIndexInList = FSlice.MeasuresContainer.Position) and ABeforeMeasures) or
         (AIndexInList < FSlice.MeasuresContainer.Position) then
        inc(FSlice.MeasuresContainer.FPosition);
  end;
  if not FSlice.Loading and Assigned(FSlice.FOnAfterAddSliceFieldToRegion) then
    FSlice.FOnAfterAddSliceFieldToRegion(AIndexInList, FRegion);
  FSlice.StopChange(AChanges);
end;

function TfcxFieldsContainer.InsertFieldInPosition(
  ASliceField: TfcxSliceField; AVisPosition: TfcxSmallCount; AName,
  ACaption: TfcxString): boolean;
var
  ABeforeMeasures: Boolean;
  AIndexInList, AOldIndexInList: TfcxSmallCount;
begin
  Result := False;
  if not Assigned(ASliceField) then
    Exit;
  AIndexInList := VisiblePositionInRegionToIndex(AVisPosition, ABeforeMeasures);
  AOldIndexInList := Fields.FindSliceField(ASliceField);
  if AOldIndexInList = -1 then
    Result := InsertField(ASliceField, AIndexInList, AName, ACaption, ABeforeMeasures)
  else
    if AOldIndexInList < AIndexInList then
      Result := InsertField(ASliceField, AIndexInList-1, AName, ACaption, ABeforeMeasures)
    else
      Result := InsertField(ASliceField, AIndexInList, AName, ACaption, ABeforeMeasures)
end;

function TfcxFieldsContainer.InsertMeasuresField(
  AIndex: TfcxSmallCount): integer;
var
  AChanges: TfcxChangesInSlice;
  AAllow: boolean;
begin
  AChanges := [];
  if not FSlice.Loading and Assigned(FSlice.FOnBeforeAddSliceFieldToRegion) then
  begin
    AAllow := True;
    FSlice.OnBeforeAddSliceFieldToRegion(FSlice.FMeasuresContainer.Caption, FRegion, AAllow);
    if not AAllow then
    begin
      Result := -1;
      Exit;
    end
  end;
  FSlice.StartChange;
  if FSlice.FMeasuresContainer.FContainer <> Self then
    FSlice.FMeasuresContainer.SetContainer(Self)
  else
  if FSlice.FMeasuresContainer.FPosition <> -1 then
  begin
    if (FSlice.FMeasuresContainer.FPosition = AIndex) then
    begin
      Result := AIndex;
      FSlice.StopChange([]);
      exit;
    end;
  end;
  if FSlice.FMeasuresContainer.FPosition <> -1 then
    FSlice.FMeasuresContainer.SetPosition(AIndex);
  if not FSlice.Loading and Assigned(FSlice.FOnAfterAddSliceFieldToRegion) then
    FSlice.OnAfterAddSliceFieldToRegion(AIndex, FRegion);
  FSlice.StopChange(AChanges);
  if FSlice.FMeasuresContainer.FPosition = -1 then
    Result := 0
  else
    Result := FSlice.FMeasuresContainer.FPosition;
end;

function TfcxFieldsContainer.InsertMeasuresFieldInPosition(
  AVisPosition: TfcxSmallCount): boolean;
var
  ABeforeMeasures: Boolean;
  AIndexInList: TfcxSmallCount;
begin
  Result := True;
  AIndexInList := VisiblePositionInRegionToIndex(AVisPosition, ABeforeMeasures);
  InsertMeasuresField(AIndexInList)
end;

procedure TfcxFieldsContainer.LoadFromXML(AItem: TfcxXMLItem);
var
  i, j: Integer;
  Field: TfcxAxisField;
begin
  inherited;
  Fields.Clear;
  for i := 0 to AItem.Count - 1 do
  begin
    if AItem[i].Name = 'fields' then
    begin
      for j := 0 to AItem[i].Count - 1 do
      begin
        Field := TfcxAxisField.Create(Fields);
        if not Field.LoadFromXML(AItem[i][j]) then
          Field.Free
        else
          Fields.Add(Field);
      end;
      break;
    end;
  end;
end;

function TfcxFieldsContainer.RemoveField(ASliceField: TfcxSliceField): boolean;
var
  AIndexInList: TfcxSmallCount;
begin
  AIndexInList := Fields.FindSliceField(ASliceField);
  if AIndexInList > -1 then
    Result := RemoveFieldByIndex(AIndexInList)
  else
    Result := False;
end;

function TfcxFieldsContainer.RemoveFieldByIndex(AIndexInList: TfcxSmallCount): boolean;
var
  AChanges: TfcxChangesInSlice;
  AAllow: boolean;
begin
  Result := False;
  if Fields.Count <= AIndexInList then
    Exit;
  AAllow := True;
  if not FSlice.Loading and Assigned(FSlice.FOnBeforeRemoveSliceField) then
    FSlice.OnBeforeRemoveSliceFieldFromRegion(FFields[AIndexInList].Name, FRegion, AAllow);
  if not AAllow then
    Exit;
  FSlice.StartChange;
  AChanges := RegionToChangeTypeMap[Region];
  Fields.FFields.Delete(AIndexInList);
  if (FSlice.MeasuresContainer.Container = Self) and (FSlice.MeasuresContainer.Position > AIndexInList) then
    dec(FSlice.MeasuresContainer.FPosition);
  FSlice.StopChange(AChanges);
end;

procedure TfcxFieldsContainer.SaveToXML(AItem: TfcxXMLItem);
var
  I: Integer;
  AItem1: TfcxXMLItem;
begin
  inherited SaveToXML(AItem);
  AItem1 := AItem.Add;
  AItem1.Name := 'fields';
  for I := 0 to Fields.Count - 1 do
    Fields[I].SaveToXML(AItem1.Add);
end;

function TfcxFieldsContainer.VisiblePositionInRegionToIndex(
  AVisPosition: TfcxSmallCount; var ABeforeMeasures: boolean): integer;
begin
  ABeforeMeasures := True;
  if (MeasuresLevel <> -1) then
// measures in this container
    if (FSlice.MeasuresContainer.FPosition <> -1) then
      if MeasuresLevel < AVisPosition then
      begin
        Result := AVisPosition - 1;
        ABeforeMeasures := False;
      end
      else
        Result := AVisPosition
    else
    begin
      Result := AVisPosition - 1;
      ABeforeMeasures := False;
    end
  else
    Result := AVisPosition;
end;

{ TfcxSliceAttributeCubeField }

constructor TfcxSliceAttributeCubeField.Create(ASlice: TfcxSlice;
  ACubeField: TfcxCommonField);
begin
  inherited Create(ASlice, ACubeField);
  FAttributeName := TfcxAttributeUniqueValues(TfcxCommonUVField(ACubeField).UniqueValues).AttributeName;
  FAttributeIndex := TfcxHackAttributesManager(TfcxCommonUVField(TfcxCommonUVField(ACubeField).MasterField).UniqueValues.SplitManager.AttributesManager).FindAttribute(FAttributeName);
end;

{ TfcxSliceMeasureField }

constructor TfcxSliceMeasureField.Create(ASlice: TfcxSlice);
begin
  inherited;
  FUVFilterSupport := False;
end;

function TfcxSliceMeasureField.GetUVFilterType: TfcxUVFilterType;
begin
  Result := uvft_None
end;

{ TfcxSliceCalcField }

constructor TfcxSliceCalcField.Create(ASlice: TfcxSlice);
begin
  inherited;
  FUVFilterSupport := False;
end;

function TfcxSliceCalcField.GetUVFilterType: TfcxUVFilterType;
begin
  Result := uvft_None
end;

{ TfcxAxisGroupMemberField }

constructor TfcxAxisGroupMemberField.CreateGroupMemberField(
  AAxisField: TfcxAxisField; AName, ACaption: TfcxString);
begin
  Create(AAxisField.Owner, AAxisField.SliceField, AName, ACaption);
  FGroupMemberField := True;
  FParentAxisField := AAxisField;
end;

function TfcxAxisGroupMemberField.GetGroupCaption(AGroupIndex: integer): TfcxString;
begin
  Result := '';
end;

function TfcxAxisGroupMemberField.GetGroupCount: Integer;
begin
  Result := 0;
end;

function TfcxAxisGroupMemberField.GetHasGroups: boolean;
begin
  Result := False;
end;

{ TfcxAxisSourceTree }

function TfcxAxisSourceTree.CalcLevelPosType(
  ALevelIndex: TfcxSmallCount): TfcxAxisTreeLevelPosType;
begin
  if FLevelCount = 0 then
  begin
    if FMeasuresLevel = -1 then
      Result := fcATLPT_WOMeasures_LastLevel
    else  // axis with measures
      Result := fcATLPT_WMeasures_BaseLevel_LastLevel
  end
  else
  begin
    if FMeasuresLevel = -1 then // axis without measures
      if ALevelIndex = (FLevelCount - 1) then // last level
        Result := fcATLPT_WOMeasures_LastLevel
      else // not last level
        Result := fcATLPT_WOMeasures_NotLastLevel
    else // axis with measures
    if ALevelIndex = (FMeasuresLevel - 1) then // based level for measures
      if ALevelIndex = (FLevelCount - 1) then // last level
        Result := fcATLPT_WMeasures_BaseLevel_LastLevel
      else // not last level
        Result := fcATLPT_WMeasures_BaseLevel_NotLastLevel
    else // not based level for measures
    if ALevelIndex = (FLevelCount - 1) then // last level
      Result := fcATLPT_WMeasures_NotBaseLevel_LastLevel
    else
    if ALevelIndex < (FMeasuresLevel - 1) then // above measures
      Result := fcATLPT_WMeasures_NotBaseLevel_High_NotLastLevel
    else // under measures
      Result := fcATLPT_WMeasures_NotBaseLevel_Low_NotLastLevel;
  end;
end;

procedure TfcxAxisSourceTree.ClearNode(
  AfcSourceTreeNode: PfcxSourceTreeNode; ALevelIndex: TfcxSmallCount);
var
  ANodeIndex: integer;
begin
  if ALevelIndex <> (FLevelCount - 1) then // not last level
    for ANodeIndex := 0 to AfcSourceTreeNode.Count - 1 do
      ClearNode(@(PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[ANodeIndex]), ALevelIndex + 1);
  FreeMem(PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes));
end;

procedure TfcxAxisSourceTree.ClearPropertiesByMask(AMask: byte);
var
  ALevelIndex: TfcxSmallCount;
  AIndex, AIndex2: integer;
begin
  for ALevelIndex := -1 to FLevelCount - 1 do
  begin
    for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropSelfCount - 1 do
      for AIndex2 := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        FSourceTreeLevels[ALevelIndex].FPropSelf[AIndex][AIndex2].State := FSourceTreeLevels[ALevelIndex].FPropSelf[AIndex][AIndex2].State and AMask;
    for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount - 1 do
      for AIndex2 := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AIndex][AIndex2].State := FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AIndex][AIndex2].State and AMask;
    for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount - 1 do
      for AIndex2 := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AIndex][AIndex2].State := FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AIndex][AIndex2].State and AMask;
    for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropMeasuresCount - 1 do
      for AIndex2 := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        FSourceTreeLevels[ALevelIndex].FPropMeasures[AIndex][AIndex2].State := FSourceTreeLevels[ALevelIndex].FPropMeasures[AIndex][AIndex2].State and AMask;
  end;
end;

procedure TfcxAxisSourceTree.ClearTree;
var
  ALevelIndex: TfcxSmallCount;
begin
  AFilled := False;
  if FRootNodeNew.Nodes = nil then
    Exit;
  ClearNode(FRootNodeNew, -2);
  FRootNodeNew.Nodes := nil;
  FRootNodeNew.Count := 0;
  FRootNodeNew.IndexInProp := 0;
  for ALevelIndex := -1 to FLevelCount - 1 do
    FreeMemForLevelProp(@(FSourceTreeLevels[ALevelIndex]));
  FLevelCount := 0;
  FreeMem(FSourceTreeLevels);
  FSourceTreeLevels := nil;
  FreeMem(FSourceOrderIndexes);
  FSourceOrderIndexes := nil;
  FGoodRecordCount := 0;
  FMeasuresCount := 0;
  FMeasuresLevel := -1;
  FreeMem(FFieldIndexOfLevel);
  FFieldIndexOfLevel := nil;
end;

constructor TfcxAxisSourceTree.Create(AAxisContainer: TfcxAxisContainer);
begin
  AFilled := False;
  FAxisContainer := AAxisContainer;
  GetMem(FRootNodeNew, SizeOf(_fcxSourceTreeNode));
  FillChar(FRootNodeNew^, SizeOf(_fcxSourceTreeNode), 0);
{
  FRootNodeNew.Nodes := nil;
  FRootNodeNew.Count := 0;
  FRootNodeNew.IndexInProp := 0;
  FRootNodeNew.NodeType := ntSimpleValue;
}
  FLevelCount := 0;
  FSourceTreeLevels := nil;
  FGoodRecordCount := 0;
  FSourceOrderIndexes := nil;
  FMeasuresCount := 0;
  FMeasuresLevel := -1;
  FFieldIndexOfLevel := nil;
end;

procedure TfcxAxisSourceTree.DeleteSubLevel(AMainLevelIndex: TfcxSmallCount);
var
  ALevelIndex: TfcxSmallCount;
  AStep: TfcxTraverseStep;
  ANode: PfcxSourceTreeNode;
  ANodeIndexArray, ADeltaIndexArray: Array of integer;
  ANodeArray: Array of PfcxSourceTreeNode;
  ACloneNodes: PfcxSourceTreeNodeArray;
begin
  SetLength(ANodeArray, FLevelCount + 1);
  SetLength(ANodeIndexArray, FLevelCount + 1);
  SetLength(ADeltaIndexArray, FLevelCount + 1);
  ADeltaIndexArray[0] := 0;
  for ALevelIndex := 1 to FLevelCount do
    ADeltaIndexArray[ALevelIndex] := -1;
  ALevelIndex := 0;
  ANodeArray[0] := @(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0]);
  ANodeIndexArray[0] := 0;
  AStep := fcts_Down;
  while True do
  begin
    case AStep of
      fcts_Next, fcts_Down:
        begin
// proc begin
          if ALevelIndex = (AMainLevelIndex + 1) then
          begin
            ANode := ANodeArray[ALevelIndex];
            ACloneNodes := ANode.Nodes;
            ANode.Nodes := ACloneNodes[0].Nodes;
            ANode.Count := ACloneNodes[0].Count;
            FreeMem(ACloneNodes);
          end;
// proc end
          if ALevelIndex = (AMainLevelIndex + 1) then
          begin
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end
          end
          else
          begin
            AStep := fcts_Down;
            inc(ALevelIndex);
            ANodeIndexArray[ALevelIndex] := 0;
            ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
          end;
        end;
      fcts_Up:
        begin
          if ALevelIndex = 0 then  // can stop - on this level ALWAYS only one element
          begin
            Break;
          end
          else
          if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
          begin
            AStep := fcts_Up;
            dec(ALevelIndex);
          end
          else
          begin
            AStep := fcts_Next;
            ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
            ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
          end
        end;
    end;
  end;
end;

destructor TfcxAxisSourceTree.Destroy;
begin
  ClearTree;
  FreeMem(FRootNodeNew);
  inherited;
end;

procedure TfcxAxisSourceTree.FillSourceTree;
var
  j2, j3: integer;
  AIndex: integer;
  AUVIndexArray: Array of integer;
  AUVTypeArray: Array of TfcxAxisTreeNodeType;
  AFieldWGroupArray: Array of boolean;
  ALevelTypeArray: Array of TfcxAxisTreeLevelType;
  ANode: PfcxSourceTreeNode;
  AEnd: boolean;
  ALevelIndex, AFieldIndex: TfcxSmallCount;
  ARecordIndex: integer;
  AGroupIndex: integer;
begin
  ClearTree;
  FLevelCount := FAxisContainer.Fields.Count;
  ReallocMem(FFieldIndexOfLevel, FLevelCount * SizeOf(TfcxSmallCount));
  SetLength(AFieldWGroupArray, FLevelCount);
  SetLength(ALevelTypeArray, FLevelCount);
  for ALevelIndex := 0 to FLevelCount - 1 do
  begin
    AFieldWGroupArray[ALevelIndex] := False;
    ALevelTypeArray[ALevelIndex] := fcATLT_Simple;
    FFieldIndexOfLevel[ALevelIndex] := ALevelIndex;
        if FAxisContainer.Fields[ALevelIndex].FSliceField.HasGroups then
        begin
          AFieldWGroupArray[ALevelIndex] := True;
          ALevelTypeArray[ALevelIndex] := fcATLT_HasGroup;
        end
  end;

  if FAxisContainer.FSlice.FMeasuresContainer.FContainer = FAxisContainer then
  begin
    if (FAxisContainer.FSlice.FMeasuresContainer.FPosition = -1) or (FAxisContainer.FSlice.FMeasuresContainer.FPosition > FLevelCount) then
      FMeasuresLevel := FLevelCount
    else
    begin
      FMeasuresLevel := FAxisContainer.FSlice.FMeasuresContainer.FPosition;
    end;
    FMeasuresCount := FAxisContainer.FSlice.FMeasuresContainer.Count;
  end
  else
  begin
    FMeasuresLevel := -1;
    FMeasuresCount := 0;
  end;
  GetMem(FSourceTreeLevels, (FLevelCount + 1) * SizeOf(_fcxSourceTreeLevelArray));
  for ALevelIndex := -1 to FLevelCount - 1 do
  begin
    if ALevelIndex = -1 then
      FSourceTreeLevels[ALevelIndex].FLevelType := fcATLT_Simple
    else
      FSourceTreeLevels[ALevelIndex].FLevelType := ALevelTypeArray[ALevelIndex];
    FSourceTreeLevels[ALevelIndex].FGSubLevelVisible := False;
    FSourceTreeLevels[ALevelIndex].FGroupExpandedCount := 0;
    FSourceTreeLevels[ALevelIndex].FHiddenCount := 0;
    FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount := 0;
    FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier := 0;
    FSourceTreeLevels[ALevelIndex].FUseCollapsedFromSliceField := True;
    FSourceTreeLevels[ALevelIndex].FTopNCheck := False;
    if ALevelIndex > -1 then
      if (FAxisContainer.FSlice.FTopNs.ActiveCount > 0) and (FAxisContainer.FSlice.FTopNs.FStep = 1) and (not FAxisContainer.FSlice.FTopNs.AsFilter) then
        if FAxisContainer.Fields[FFieldIndexOfLevel[ALevelIndex]].TopNProcessor.Active then
          FSourceTreeLevels[ALevelIndex].FTopNCheck := True;
    SetLevelPropCounts(ALevelIndex);
    GetMemForLevelProp(@(FSourceTreeLevels[ALevelIndex]));
  end;
  if Assigned(FAxisContainer.FSlice.FCube) then
    FFullRecordCount := FAxisContainer.FSlice.FCube.SourceHolder.RecordsCount
  else
    FFullRecordCount := 0;

  FRootNodeNew.Count := 1;
  GetMem(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes), {FRootNodeNew.Count * }SizeOf(_fcxSourceTreeNode));

  FillChar(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)^, SizeOf(_fcxSourceTreeNode), 0);
  FSourceTreeLevels[-1].FFullNodeCount := 1;
  FSourceTreeLevels[-1].FGoodNodeCount := 1;
  FSourceTreeLevels[-1].FCollapsedNodeCount := 0;
  FSourceTreeLevels[-1].FCanCollapseNodeMultiplier := 0;
  FSourceTreeLevels[-1].FUseCollapsedFromSliceField := False;

  if FFullRecordCount <> 0 then
  begin
    if FLevelCount = 0 then
    begin
// no fields in axis
      PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0].Count := FFullRecordCount;
      GetMem(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0].Nodes, PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0].Count * SizeOf(integer));
      for ARecordIndex := 0 to FFullRecordCount - 1 do
      begin
        PfcxIntegerArray(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0].Nodes)[ARecordIndex] := ARecordIndex;
      end;
    end
    else
    begin
      SetLength(AUVIndexArray, FLevelCount);
      SetLength(AUVTypeArray, FLevelCount);
      for ARecordIndex := 0 to FFullRecordCount - 1 do
      begin
// for row fill array of UV indexes by level
        for ALevelIndex := 0 to FLevelCount - 1 do
        begin
          AFieldIndex := FFieldIndexOfLevel[ALevelIndex];
          AUVTypeArray[ALevelIndex] := fcATNT_SimpleValue;
          AUVIndexArray[ALevelIndex] := FAxisContainer.FSlice.FCube.SourceHolder.UniqueValueIndex[ARecordIndex, TfcxCommonSliceCubeField(FAxisContainer.Fields[AFieldIndex].FSliceField).CubeField];
// Check TopN (Is Record in Others?)
          if FSourceTreeLevels[ALevelIndex].FTopNCheck then
          begin
            if FAxisContainer.FSlice.FFilterManager.GoodIndex[ARecordIndex] > -1 then
              if FAxisContainer.Fields[FFieldIndexOfLevel[ALevelIndex]].TopNProcessor.IsOther[FAxisContainer.FSlice.FFilterManager.GoodIndex[ARecordIndex]] then
              begin
                AUVTypeArray[ALevelIndex] := fcATNT_OtherValue;
                AUVIndexArray[ALevelIndex] := 0;
              end;
          end;
// check including in group
          if AFieldWGroupArray[ALevelIndex] and (AUVTypeArray[ALevelIndex] <> fcATNT_OtherValue) then
          begin
            if FAxisContainer.Fields[AFieldIndex].FSliceField is TfcxSliceStdSplitCubeField then
              AGroupIndex := TfcxCommonStdPathField(TfcxCommonSliceCubeField(FAxisContainer.Fields[AFieldIndex].FSliceField).CubeField).StdPathUniqueValues.GroupIndexOfUV[AUVIndexArray[ALevelIndex]]
            else
              AGroupIndex := TfcxCommonUVField(TfcxCommonSliceCubeField(FAxisContainer.Fields[AFieldIndex].FSliceField).CubeField).UniqueValues.GroupIndexOfUV[AUVIndexArray[ALevelIndex]];
            if AGroupIndex >= 0 then
            begin
              AUVTypeArray[ALevelIndex] := fcATNT_GroupValue;
              AUVIndexArray[ALevelIndex] := AGroupIndex;
            end;
          end;
        end;
        ANode := @(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0]);
        AEnd := False;
        for ALevelIndex := 0 to FLevelCount - 1 do
        begin
          if FindPlace(ANode, AUVIndexArray[ALevelIndex], AFieldWGroupArray[ALevelIndex], AUVTypeArray[ALevelIndex], FSourceTreeLevels[ALevelIndex].FTopNCheck, AIndex) then
          begin
// found. go down
            ANode := @(PfcxSourceTreeNodeArray(ANode.Nodes)[AIndex]);
          end
          else
          begin
// insert new branch
            if FAxisContainer.Fields[FFieldIndexOfLevel[ALevelIndex]].FSliceField.ShowAllValues and not AFieldWGroupArray[ALevelIndex] and (ALevelIndex = 0) then
            begin
// add nodes for all values
              ANode.Count := FAxisContainer.Fields[FFieldIndexOfLevel[ALevelIndex]].FSliceField.UVCount;
              ReallocMem(ANode.Nodes, ANode.Count * SizeOf(_fcxSourceTreeNode));
              FSourceTreeLevels[ALevelIndex].FFullNodeCount := FSourceTreeLevels[ALevelIndex].FFullNodeCount + ANode.Count;
              for j3 := 0 to ANode.Count - 1 do
              begin
                PfcxSourceTreeNodeArray(ANode.Nodes)[j3].Count := 0;
                PfcxSourceTreeNodeArray(ANode.Nodes)[j3].Index := j3;
                ANode.NodeType := fcATNT_SimpleValue;
                if (ALevelIndex = (FLevelCount - 1)) then
                  PfcxSourceTreeNodeArray(ANode.Nodes)[j3].Nodes := nil
                else
                begin
                  GetMem(PfcxSourceTreeNodeArray(ANode.Nodes)[j3].Nodes, 16 * SizeOf(_fcxSourceTreeNode));
                end;
              end;
              ANode := @(PfcxSourceTreeNodeArray(ANode.Nodes)[AUVIndexArray[ALevelIndex]]);
            end
            else
            begin
              if ANode.Count < 64 then
              begin
                if (ANode.Count and 15) = 0 then
                  ReallocMem(ANode.Nodes, (ANode.Count + 16) * SizeOf(_fcxSourceTreeNode))
              end
              else
              if ANode.Count < 1024 then
              begin
                if (ANode.Count and 63) = 0 then
                  ReallocMem(ANode.Nodes, (ANode.Count + 64) * SizeOf(_fcxSourceTreeNode))
              end
              else
              begin
                if (ANode.Count and 1023) = 0 then
                  ReallocMem(ANode.Nodes, (ANode.Count + 1024) * SizeOf(_fcxSourceTreeNode))
              end;
              Move(PfcxSourceTreeNodeArray(ANode.Nodes)[AIndex], PfcxSourceTreeNodeArray(ANode.Nodes)[AIndex + 1], (ANode.Count - AIndex) * SizeOf(_fcxSourceTreeNode));
              ANode.Count := ANode.Count + 1;
              FSourceTreeLevels[ALevelIndex].FFullNodeCount := FSourceTreeLevels[ALevelIndex].FFullNodeCount + 1;
              ANode := @(PfcxSourceTreeNodeArray(ANode.Nodes)[AIndex]);
              ANode.Count := 0;
              if (ALevelIndex = (FLevelCount - 1)) then
                ANode.Nodes := nil
              else
              begin
                GetMem(ANode.Nodes, 16 * SizeOf(_fcxSourceTreeNode));
              end;
              ANode.Index := AUVIndexArray[ALevelIndex];
              ANode.NodeType := AUVTypeArray[ALevelIndex];
            end;
            for j2 := ALevelIndex + 1 to FLevelCount - 1 do
            begin
// add new nodes in all levels
              if FAxisContainer.Fields[FFieldIndexOfLevel[j2]].FSliceField.ShowAllValues and not AFieldWGroupArray[j2] then
              begin
// add nodes for all values
                ANode.Count := FAxisContainer.Fields[FFieldIndexOfLevel[j2]].FSliceField.UVCount;
                FSourceTreeLevels[j2].FFullNodeCount := FSourceTreeLevels[j2].FFullNodeCount + ANode.Count;
                for j3 := 0 to ANode.Count - 1 do
                begin
                  PfcxSourceTreeNodeArray(ANode.Nodes)[j3].Count := 0;
                  PfcxSourceTreeNodeArray(ANode.Nodes)[j3].Index := j3;
                  ANode.NodeType := fcATNT_SimpleValue;
                  if (j2 = (FLevelCount - 1)) then
                    PfcxSourceTreeNodeArray(ANode.Nodes)[j3].Nodes := nil
                  else
                  begin
                    GetMem(PfcxSourceTreeNodeArray(ANode.Nodes)[j3].Nodes, 16 * SizeOf(_fcxSourceTreeNode));
                  end;
                end;
                ANode := @(PfcxSourceTreeNodeArray(ANode.Nodes)[AUVIndexArray[j2]]);
              end
              else
              begin
                ANode.Count := 1;
                FSourceTreeLevels[j2].FFullNodeCount := FSourceTreeLevels[j2].FFullNodeCount + 1;
                ANode := @(PfcxSourceTreeNodeArray(ANode.Nodes)[0]);
                ANode.Count := 0;
                if (j2 = (FLevelCount - 1)) then
                  ANode.Nodes := nil
                else
                begin
                  GetMem(ANode.Nodes, 16 * SizeOf(_fcxSourceTreeNode));
                end;
                ANode.Index := AUVIndexArray[j2];
                ANode.NodeType := AUVTypeArray[j2];
              end;
            end;
            AEnd := True;
          end;
          if AEnd or (ALevelIndex = (FLevelCount - 1)) then
          begin
            if ANode.Count < 64 then
            begin
              if (ANode.Count and 15) = 0 then
                ReallocMem(ANode.Nodes, (ANode.Count + 16) * SizeOf(integer))
            end
            else
            if ANode.Count < 1024 then
            begin
              if (ANode.Count and 63) = 0 then
                ReallocMem(ANode.Nodes, (ANode.Count + 64) * SizeOf(integer))
            end
            else
            begin
              if (ANode.Count and 1023) = 0 then
                ReallocMem(ANode.Nodes, (ANode.Count + 1024) * SizeOf(integer))
            end;
            PfcxIntegerArray(ANode.Nodes)[ANode.Count] := ARecordIndex;
            ANode.Count := ANode.Count + 1;
            break;
          end
        end;
      end;
    end;
    if FSourceTreeLevels[-1].FGoodNodeCount = 0 then
    begin
      FSourceTreeLevels[-1].FFullNodeCount := 1;
      FSourceTreeLevels[-1].FGoodNodeCount := 1;
      FSourceTreeLevels[-1].FCollapsedNodeCount := 0;
      FSourceTreeLevels[-1].FCanCollapseNodeMultiplier := 0;
      FSourceTreeLevels[-1].FUseCollapsedFromSliceField := False;
    end;

  end;
  Recapacity;
  AFilled := True;
end;

procedure TfcxAxisSourceTree.FillSourceOrderIndexes(ALevelsSaved: Boolean = False);
var
  ALevelIndex: TfcxSmallCount;
  AStep: TfcxTraverseStep;
  ANode: PfcxSourceTreeNode;
  ANodeIndexArray: Array of integer;
  ANodeArray: Array of PfcxSourceTreeNode;
  i, AGoodRecordIndex{, ARecordIndex}: integer;
  AGoodCountArray: Array of integer;
begin
  if (FAxisContainer.FSlice.FTopNs.FStep = 1) and (FAxisContainer.FSlice.FTopNs.ActiveCount > 0) and FAxisContainer.FSlice.FTopNs.FAsFilter then
    FGoodRecordCount := FAxisContainer.FSlice.FTopNs.FGoodRecCount
  else
    FGoodRecordCount := FAxisContainer.FSlice.FFilterManager.GoodRecCount;
  ReallocMem(FAxisContainer.FCoordinate, FGoodRecordCount * SizeOf(Integer));
  ReallocMem(FSourceOrderIndexes, FGoodRecordCount * SizeOf(integer));
  if FFullRecordCount > 0 then
  begin
    if FLevelCount = 0 then
    begin
// no fields in axis
      Move(PfcxIntegerArray(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0].Nodes)^, FSourceOrderIndexes^, FGoodRecordCount * SizeOf(Integer));
      Exit;
    end;
    for ALevelIndex := -1 to FLevelCount - 1 do
    begin
      FSourceTreeLevels[ALevelIndex].FGoodNodeCount := 0;
      FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount := 0;
    end;

    AGoodRecordIndex := 0;
    SetLength(ANodeArray, FLevelCount + 1);
    SetLength(ANodeIndexArray, FLevelCount + 1);
    SetLength(AGoodCountArray, FLevelCount + 1);
    for ALevelIndex := 0 to FLevelCount do
      AGoodCountArray[ALevelIndex] := 0;
    ALevelIndex := 0;
    ANodeArray[0] := @(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0]);
    ANodeIndexArray[0] := 0;
    AStep := fcts_Down;
    while True do
    begin
      case AStep of
        fcts_Next, fcts_Down:
          begin
// proc begin
            ANode := ANodeArray[ALevelIndex];
            AGoodCountArray[ALevelIndex] := 0;
            if ALevelIndex = FLevelCount then
            begin
              if FFullRecordCount = FGoodRecordCount then
              begin
                for i := 0 to ANode.Count - 1 do
                begin
                  FSourceOrderIndexes[PfcxIntegerArray(ANode.Nodes)[i]] := AGoodRecordIndex;
                  inc(AGoodRecordIndex);
                end;
                AGoodCountArray[ALevelIndex] := AGoodCountArray[ALevelIndex] + ANode.Count;
                inc(FSourceTreeLevels[ALevelIndex - 1].FGoodNodeCount);
                inc(AGoodCountArray[ALevelIndex - 1]);
              end
              else
              begin
                for i := 0 to ANode.Count - 1 do
                begin
                  if FAxisContainer.FSlice.FTopNs.GoodTopNRec[FAxisContainer.FSlice.FilterManager.GoodIndex[PfcxIntegerArray(ANode.Nodes)[i]]] > -1 then
                  begin
                    FSourceOrderIndexes[FAxisContainer.FSlice.FTopNs.GoodTopNRec[FAxisContainer.FSlice.FilterManager.GoodIndex[PfcxIntegerArray(ANode.Nodes)[i]]]] := AGoodRecordIndex;
                    inc(AGoodRecordIndex);
                    inc(AGoodCountArray[ALevelIndex]);
                  end;
                end;
                if AGoodCountArray[ALevelIndex] > 0 then
                begin
                  inc(FSourceTreeLevels[ALevelIndex - 1].FGoodNodeCount);
                  inc(AGoodCountArray[ALevelIndex - 1]);
                end;
              end;
            end;

// proc end
            if ALevelIndex = FLevelCount then // last level
            begin
              if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
              begin
                AStep := fcts_Up;
                dec(ALevelIndex);
              end
              else
              begin
                AStep := fcts_Next;
                ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
                ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
              end
            end
            else
            begin
              AStep := fcts_Down;
              inc(ALevelIndex);
              ANodeIndexArray[ALevelIndex] := 0;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end;
          end;
        fcts_Up:
          begin
            if AGoodCountArray[ALevelIndex] > 0 then
            begin
              if ALevelIndex <> 0 then
              begin
                inc(AGoodCountArray[ALevelIndex - 1]);
// calc fCollapsedNodeCount
                if FSourceTreeLevels[ALevelIndex - 1].FUseCollapsedFromSliceField then
                  for i := 0 to FSourceTreeLevels[ALevelIndex - 1].FCanCollapseNodeMultiplier - 1 do
                    if FAxisContainer.Fields[FFieldIndexOfLevel[ALevelIndex - 1]].FSliceField.FCollapsed then
                    begin
                      FSourceTreeLevels[ALevelIndex - 1].FPropSelf[i][ANodeArray[ALevelIndex].IndexInProp].State :=
                      (FSourceTreeLevels[ALevelIndex - 1].FPropSelf[i][ANodeArray[ALevelIndex].IndexInProp].State or stDimCollapsed);
                      inc(FSourceTreeLevels[ALevelIndex - 1].FCollapsedNodeCount);
                    end
                    else
                      FSourceTreeLevels[ALevelIndex - 1].FPropSelf[i][ANodeArray[ALevelIndex].IndexInProp].State :=
                      (FSourceTreeLevels[ALevelIndex - 1].FPropSelf[i][ANodeArray[ALevelIndex].IndexInProp].State and not stDimCollapsed)
                else
                  for i := 0 to FSourceTreeLevels[ALevelIndex - 1].FCanCollapseNodeMultiplier - 1 do
                    if ((FSourceTreeLevels[ALevelIndex - 1].FPropSelf[i][ANodeArray[ALevelIndex].IndexInProp].State and stDimCollapsed) <> 0) then
                      inc(FSourceTreeLevels[ALevelIndex - 1].FCollapsedNodeCount);
              end;
              inc(FSourceTreeLevels[ALevelIndex - 1].FGoodNodeCount);
            end;
            if ALevelIndex = 0 then  // can stop - on this level ALWAYS only one element
            begin
              Break;
            end
            else
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end
          end;
      end;
    end;
    for ALevelIndex := 0 to FLevelCount - 2 do
    begin
      FSourceTreeLevels[ALevelIndex].FUseCollapsedFromSliceField := False;
      if (FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier > 0) then
        if ALevelsSaved then
          if FAxisContainer.FAxisTree[ALevelIndex].LevelType <> fcATLT_SubGroup then
            FAxisContainer.Fields[FAxisContainer.FAxisTree[ALevelIndex].RegionFieldIndex].FSliceField.FCollapsed := (FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount = (FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier * FSourceTreeLevels[ALevelIndex].FGoodNodeCount))
          else
            FAxisContainer.Fields[FAxisContainer.FAxisTree[ALevelIndex].RegionFieldIndex].FSliceField.FCollapsedSubGroup := (FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount = (FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier * FSourceTreeLevels[ALevelIndex].FGoodNodeCount))
        else
          FAxisContainer.Fields[FFieldIndexOfLevel[ALevelIndex]].FSliceField.FCollapsed := (FSourceTreeLevels[ALevelIndex].FCollapsedNodeCount = (FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier * FSourceTreeLevels[ALevelIndex].FGoodNodeCount))
    end;
    if FSourceTreeLevels[-1].FGoodNodeCount = 0 then
    begin
      FSourceTreeLevels[-1].FFullNodeCount := 1;
      FSourceTreeLevels[-1].FGoodNodeCount := 1;
    end;
  end;
end;

function TfcxAxisSourceTree.FindPlace(AfcSourceTreeNode: PfcxSourceTreeNode;
  AUVIndex: integer; AWithGroup: boolean; AUVType: TfcxAxisTreeNodeType; ATopNCheck: Boolean; var AIndex: integer): boolean;
var
  ALeft, ARight, AMiddle, ACompare: Integer;
begin
  Result := False;
  if AfcSourceTreeNode.Count = 0 then
  begin
    AIndex := 0;
    exit;
  end;
  if ATopNCheck then
  begin
    if AUVType = fcATNT_OtherValue then
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AfcSourceTreeNode.Count - 1].NodeType = fcATNT_OtherValue then
      begin
        AIndex := AfcSourceTreeNode.Count - 1;
        // Found
        Result := True;
        exit;
      end
      else
      begin
        AIndex := AfcSourceTreeNode.Count;
        exit;
      end;
    if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[0].NodeType = fcATNT_OtherValue then
    begin
      AIndex := 0;
      exit;
    end;
    if not AWithGroup then
    begin
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AfcSourceTreeNode.Count - 1].NodeType = fcATNT_OtherValue then
        if AfcSourceTreeNode.Count = 1 then
        begin
          AIndex := AfcSourceTreeNode.Count - 1;
          exit;
        end
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AfcSourceTreeNode.Count - 2].Index < AUVIndex then
        begin
          AIndex := AfcSourceTreeNode.Count - 1;
          exit;
        end
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AfcSourceTreeNode.Count - 2].Index = AUVIndex then
        begin
          AIndex := AfcSourceTreeNode.Count - 2;
          // Found
          Result := True;
          exit;
        end;
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[0].Index > AUVIndex then
      begin
        AIndex := 0;
        exit;
      end
      else
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[0].Index = AUVIndex then
      begin
        AIndex := 0;
        // Found
        Result := True;
        exit;
      end;
      ALeft := 0;
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AfcSourceTreeNode.Count - 1].NodeType = fcATNT_OtherValue then
        ARight := AfcSourceTreeNode.Count - 2
      else
        ARight := AfcSourceTreeNode.Count - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].Index = AUVIndex then
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].Index > AUVIndex then
          ARight := AMiddle - 1
        else
          ALeft := AMiddle + 1;
      end;
    end
    else
    begin
      ALeft := 0;
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AfcSourceTreeNode.Count - 1].NodeType = fcATNT_OtherValue then
        ARight := AfcSourceTreeNode.Count - 2
      else
        ARight := AfcSourceTreeNode.Count - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].NodeType < AUVType then
          ACompare := 1
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].NodeType > AUVType then
          ACompare := -1
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].Index > AUVIndex then
          ACompare := 1
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].Index < AUVIndex then
          ACompare := -1
        else
          ACompare := 0;
        if ACompare = -1 then
          ALeft := AMiddle + 1
        else if ACompare = 1 then
          ARight := AMiddle - 1
        else
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end;
      end;
    end;
  end
  else
  begin
    if not AWithGroup then
    begin
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AfcSourceTreeNode.Count - 1].Index < AUVIndex then
      begin
        AIndex := AfcSourceTreeNode.Count;
        exit;
      end
      else
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AfcSourceTreeNode.Count - 1].Index = AUVIndex then
      begin
        AIndex := AfcSourceTreeNode.Count - 1;
        // Found
        Result := True;
        exit;
      end;
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[0].Index > AUVIndex then
      begin
        AIndex := 0;
        exit;
      end
      else
      if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[0].Index = AUVIndex then
      begin
        AIndex := 0;
        // Found
        Result := True;
        exit;
      end;
      ALeft := 0;
      ARight := AfcSourceTreeNode.Count - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].Index = AUVIndex then
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].Index > AUVIndex then
          ARight := AMiddle - 1
        else
          ALeft := AMiddle + 1;
      end;
    end
    else
    begin
      ALeft := 0;
      ARight := AfcSourceTreeNode.Count - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].NodeType < AUVType then
          ACompare := 1
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].NodeType > AUVType then
          ACompare := -1
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].Index > AUVIndex then
          ACompare := 1
        else
        if PfcxSourceTreeNodeArray(AfcSourceTreeNode.Nodes)[AMiddle].Index < AUVIndex then
          ACompare := -1
        else
          ACompare := 0;
        if ACompare = -1 then
          ALeft := AMiddle + 1
        else if ACompare = 1 then
          ARight := AMiddle - 1
        else
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end;
      end;
    end;
  end;
  AIndex := ALeft;
end;

procedure TfcxAxisSourceTree.FreeMemForLevelProp(
  ASourceTreeLevel: PfcxSourceTreeLevel);
var
  AIndex: integer;
begin
  for AIndex := 0 to ASourceTreeLevel.FPropSelfCount - 1 do
    FreeMem(ASourceTreeLevel.FPropSelf[AIndex]);
  for AIndex := 0 to ASourceTreeLevel.FPropTotalSelfCount - 1 do
    FreeMem(ASourceTreeLevel.FPropTotalSelf[AIndex]);
  for AIndex := 0 to ASourceTreeLevel.FPropAdditionalTotalSelfCount - 1 do
  begin
    FreeMem(ASourceTreeLevel.FPropAdditionalTotalSelf[AIndex]);
    FreeMem(ASourceTreeLevel.FAdditionalTotalWidths[AIndex]);
    FreeMem(ASourceTreeLevel.FAdditionalTotalHeights[AIndex]);
  end;
  for AIndex := 0 to ASourceTreeLevel.FPropMeasuresCount - 1 do
    FreeMem(ASourceTreeLevel.FPropMeasures[AIndex]);
  for AIndex := 0 to ASourceTreeLevel.FSizeCount - 1 do
  begin
    FreeMem(ASourceTreeLevel.FWidths[AIndex]);
    FreeMem(ASourceTreeLevel.FHeights[AIndex]);
  end;
  FreeMem(ASourceTreeLevel.FPropSelf);
  FreeMem(ASourceTreeLevel.FPropTotalSelf);
  FreeMem(ASourceTreeLevel.FPropAdditionalTotalSelf);
  FreeMem(ASourceTreeLevel.FAdditionalTotalWidths);
  FreeMem(ASourceTreeLevel.FAdditionalTotalHeights);
  FreeMem(ASourceTreeLevel.FPropMeasures);
  FreeMem(ASourceTreeLevel.FWidths);
  FreeMem(ASourceTreeLevel.FHeights);
end;

procedure TfcxAxisSourceTree.GetMemForLevelProp(
  ASourceTreeLevel: PfcxSourceTreeLevel);
begin
  GetMem(ASourceTreeLevel.FPropSelf, ASourceTreeLevel.FPropSelfCount * SizeOf(Pointer));
  GetMem(ASourceTreeLevel.FPropTotalSelf, ASourceTreeLevel.FPropTotalSelfCount * SizeOf(Pointer));
  GetMem(ASourceTreeLevel.FPropAdditionalTotalSelf, ASourceTreeLevel.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
  GetMem(ASourceTreeLevel.FAdditionalTotalWidths, ASourceTreeLevel.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
  GetMem(ASourceTreeLevel.FAdditionalTotalHeights, ASourceTreeLevel.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
  GetMem(ASourceTreeLevel.FPropMeasures, ASourceTreeLevel.FPropMeasuresCount * SizeOf(Pointer));
  GetMem(ASourceTreeLevel.FWidths, ASourceTreeLevel.FSizeCount * SizeOf(Pointer));
  GetMem(ASourceTreeLevel.FHeights, ASourceTreeLevel.FSizeCount * SizeOf(Pointer));
end;

procedure TfcxAxisSourceTree.GetMemForLevelPropDetail(
  ATarget, ASource: PfcxSourceTreeLevel);

  procedure DoProc(APropTargetArray: PfcxAxisTreeNodePropertiesListArray; APropCount: integer; ASize: integer);
  var
    AIndex: integer;
  begin
    for AIndex := 0 to APropCount - 1 do
    begin
      GetMem(APropTargetArray[AIndex], ATarget.FFullNodeCount * ASize);
      FillChar(APropTargetArray[AIndex]^, ATarget.FFullNodeCount * ASize, stDefault)
    end;
  end;
  procedure DoProc2(APropTargetArray, APropSourceArray: PfcxAxisTreeNodePropertiesListArray; APropCount: integer; ASize: integer);
  var
    AIndex: integer;
  begin
    for AIndex := 0 to APropCount - 1 do
    begin
      GetMem(APropTargetArray[AIndex], ATarget.FFullNodeCount * ASize);
      Move(APropSourceArray[AIndex]^, APropTargetArray[AIndex]^, ATarget.FFullNodeCount * ASize);
    end;
  end;
  procedure DoProcSize(ASizeTargetArray: PfcxAxisTreeNodeSizesListArray; ASizeCount: integer; ASize: integer);
  var
    AIndex: integer;
  begin
    for AIndex := 0 to ASizeCount - 1 do
    begin
      GetMem(ASizeTargetArray[AIndex], ATarget.FFullNodeCount * ASize);
      FillChar(ASizeTargetArray[AIndex]^, ATarget.FFullNodeCount * ASize, 0)
    end;
  end;
  procedure DoProcSize2(ASizeTargetArray, ASizeSourceArray: PfcxAxisTreeNodeSizesListArray; ASizeCount: integer; ASize: integer);
  var
    AIndex: integer;
  begin
    for AIndex := 0 to ASizeCount - 1 do
    begin
      GetMem(ASizeTargetArray[AIndex], ATarget.FFullNodeCount * ASize);
      Move(ASizeSourceArray[AIndex]^, ASizeTargetArray[AIndex]^, ATarget.FFullNodeCount * ASize);
    end;
  end;

begin
  if ASource = nil then
  begin
    DoProc(ATarget.FPropSelf, ATarget.FPropSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc(ATarget.FPropTotalSelf, ATarget.FPropTotalSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc(ATarget.FPropAdditionalTotalSelf, ATarget.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc(ATarget.FPropMeasures, ATarget.FPropMeasuresCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProcSize(ATarget.FAdditionalTotalWidths, ATarget.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize(ATarget.FAdditionalTotalHeights, ATarget.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize(ATarget.FWidths, ATarget.FSizeCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize(ATarget.FHeights, ATarget.FSizeCount, SizeOf(_fcxAxisTreeNodeSizesList));
  end
  else
  begin
    DoProc2(ATarget.FPropSelf, ASource.FPropSelf, ATarget.FPropSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc2(ATarget.FPropTotalSelf, ASource.FPropTotalSelf, ATarget.FPropTotalSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc2(ATarget.FPropAdditionalTotalSelf, ASource.FPropAdditionalTotalSelf, ATarget.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc2(ATarget.FPropMeasures, ASource.FPropMeasures, ATarget.FPropMeasuresCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProcSize2(ATarget.FAdditionalTotalWidths, ASource.FAdditionalTotalWidths, ATarget.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize2(ATarget.FAdditionalTotalHeights, ASource.FAdditionalTotalHeights, ATarget.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize2(ATarget.FWidths, ASource.FWidths, ATarget.FSizeCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize2(ATarget.FHeights, ASource.FHeights, ATarget.FSizeCount, SizeOf(_fcxAxisTreeNodeSizesList));
  end;
end;

procedure TfcxAxisSourceTree.InsertSubLevel(AMainLevelIndex: TfcxSmallCount);
var
  ALevelIndex: TfcxSmallCount;
  AStep: TfcxTraverseStep;
  ANode: PfcxSourceTreeNode;
  ANodeIndexArray, ADeltaIndexArray: Array of integer;
  ANodeArray: Array of PfcxSourceTreeNode;
  ACloneNodes: PfcxSourceTreeNodeArray;
begin
  SetLength(ANodeArray, FLevelCount + 1);
  SetLength(ANodeIndexArray, FLevelCount + 1);
  SetLength(ADeltaIndexArray, FLevelCount + 1);
  ADeltaIndexArray[0] := 0;
  for ALevelIndex := 1 to FLevelCount do
    ADeltaIndexArray[ALevelIndex] := -1;
  ALevelIndex := 0;
  ANodeArray[0] := @(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0]);
  ANodeIndexArray[0] := 0;
  AStep := fcts_Down;
  while True do
  begin
    case AStep of
      fcts_Next, fcts_Down:
        begin
// proc begin
          if ALevelIndex = (AMainLevelIndex + 1) then
          begin
            ANode := ANodeArray[ALevelIndex];
            GetMem(ACloneNodes, SizeOf(_fcxSourceTreeNode));
            ACloneNodes[0].Count := ANode.Count;
            ACloneNodes[0].Index := ANode.Index;
            ACloneNodes[0].IndexInProp := ANode.IndexInProp;
            ACloneNodes[0].Nodes := ANode.Nodes;
            ACloneNodes[0].NodeType := fcATNT_EmptyValue;
            ANode.Count := 1;
            ANode.Nodes := ACloneNodes;
          end;
// proc end
          if ALevelIndex = (AMainLevelIndex + 1) then
          begin
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end
          end
          else
          begin
            AStep := fcts_Down;
            inc(ALevelIndex);
            ANodeIndexArray[ALevelIndex] := 0;
            ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
          end;
        end;
      fcts_Up:
        begin
          if ALevelIndex = 0 then  // can stop - on this level ALWAYS only one element
          begin
            Break;
          end
          else
          if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
          begin
            AStep := fcts_Up;
            dec(ALevelIndex);
          end
          else
          begin
            AStep := fcts_Next;
            ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
            ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
          end
        end;
    end;
  end;
end;

procedure TfcxAxisSourceTree.ReallocMemForLevelPropDetail(ATarget,
  ASource: PfcxSourceTreeLevel);
var
  AIndex: integer;
begin
  if ATarget.FPropSelfCount < ASource.FPropSelfCount then
  begin
    for AIndex := ATarget.FPropSelfCount to ASource.FPropSelfCount - 1 do
      FreeMem(ATarget.FPropSelf[AIndex]);
    ReallocMem(ATarget.FPropSelf, ATarget.FPropSelfCount * SizeOf(Pointer));
  end
  else
  if ATarget.FPropSelfCount > ASource.FPropSelfCount then
  begin
    ReallocMem(ATarget.FPropSelf, ATarget.FPropSelfCount * SizeOf(Pointer));
    for AIndex := ASource.FPropSelfCount to ATarget.FPropSelfCount - 1 do
    begin
      GetMem(ATarget.FPropSelf[AIndex], ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties));
      if ASource.FPropSelfCount = 0 then
        FillChar(ATarget.FPropSelf[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties), stDefault)
      else
        Move(ATarget.FPropSelf[0]^, ATarget.FPropSelf[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties))
    end;
  end;

  if ATarget.FPropTotalSelfCount < ASource.FPropTotalSelfCount then
  begin
    for AIndex := ATarget.FPropTotalSelfCount to ASource.FPropTotalSelfCount - 1 do
      FreeMem(ATarget.FPropTotalSelf[AIndex]);
    ReallocMem(ATarget.FPropTotalSelf, ATarget.FPropTotalSelfCount * SizeOf(Pointer));
  end
  else
  if ATarget.FPropTotalSelfCount > ASource.FPropTotalSelfCount then
  begin
    ReallocMem(ATarget.FPropTotalSelf, ATarget.FPropTotalSelfCount * SizeOf(Pointer));
    for AIndex := ASource.FPropTotalSelfCount to ATarget.FPropTotalSelfCount - 1 do
    begin
      GetMem(ATarget.FPropTotalSelf[AIndex], ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties));
      if ASource.FPropTotalSelfCount = 0 then
        FillChar(ATarget.FPropTotalSelf[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties), stDefault)
      else
        Move(ATarget.FPropTotalSelf[0]^, ATarget.FPropTotalSelf[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties))
    end;
  end;

  if ATarget.FPropAdditionalTotalSelfCount < ASource.FPropAdditionalTotalSelfCount then
  begin
    for AIndex := ATarget.FPropAdditionalTotalSelfCount to ASource.FPropAdditionalTotalSelfCount - 1 do
    begin
      FreeMem(ATarget.FPropAdditionalTotalSelf[AIndex]);
      FreeMem(ATarget.FAdditionalTotalWidths[AIndex]);
      FreeMem(ATarget.FAdditionalTotalHeights[AIndex]);
    end;
    ReallocMem(ATarget.FPropAdditionalTotalSelf, ATarget.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
    ReallocMem(ATarget.FAdditionalTotalWidths, ATarget.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
    ReallocMem(ATarget.FAdditionalTotalHeights, ATarget.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
  end
  else
  if ATarget.FPropAdditionalTotalSelfCount > ASource.FPropAdditionalTotalSelfCount then
  begin
    ReallocMem(ATarget.FPropAdditionalTotalSelf, ATarget.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
    ReallocMem(ATarget.FAdditionalTotalWidths, ATarget.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
    ReallocMem(ATarget.FAdditionalTotalHeights, ATarget.FPropAdditionalTotalSelfCount * SizeOf(Pointer));
    for AIndex := ASource.FPropAdditionalTotalSelfCount to ATarget.FPropAdditionalTotalSelfCount - 1 do
    begin
      GetMem(ATarget.FPropAdditionalTotalSelf[AIndex], ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties));
      GetMem(ATarget.FAdditionalTotalWidths[AIndex], ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList));
      GetMem(ATarget.FAdditionalTotalHeights[AIndex], ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList));
      if ASource.FPropAdditionalTotalSelfCount = 0 then
      begin
        FillChar(ATarget.FPropAdditionalTotalSelf[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties), stDefault);
{
        FillChar(ATarget.FAdditionalTotalWidths[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), stDefault);
        FillChar(ATarget.FAdditionalTotalHeights[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), stDefault);
}
      end
      else
      begin
// ??? not need?
        Move(ATarget.FPropAdditionalTotalSelf[0]^, ATarget.FPropAdditionalTotalSelf[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties));
{
        Move(ATarget.FAdditionalTotalWidths[0]^, ATarget.FAdditionalTotalWidths[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList));
        Move(ATarget.FAdditionalTotalHeights[0]^, ATarget.FAdditionalTotalHeights[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList));
}
      end;
    end;
  end;
  for AIndex := 0 to ATarget.FPropAdditionalTotalSelfCount - 1 do
  begin
    FillChar(ATarget.FAdditionalTotalWidths[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), 0);
    FillChar(ATarget.FAdditionalTotalHeights[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), 0);
  end;

  if ATarget.FPropMeasuresCount < ASource.FPropMeasuresCount then
  begin
    for AIndex := ATarget.FPropMeasuresCount to ASource.FPropMeasuresCount - 1 do
      FreeMem(ATarget.FPropMeasures[AIndex]);
    ReallocMem(ATarget.FPropMeasures, ATarget.FPropMeasuresCount * SizeOf(Pointer));
  end
  else
  if ATarget.FPropMeasuresCount > ASource.FPropMeasuresCount then
  begin
    ReallocMem(ATarget.FPropMeasures, ATarget.FPropMeasuresCount * SizeOf(Pointer));
    for AIndex := ASource.FPropMeasuresCount to ATarget.FPropMeasuresCount - 1 do
    begin
      GetMem(ATarget.FPropMeasures[AIndex], ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties));
      if ASource.FPropMeasuresCount = 0 then
        FillChar(ATarget.FPropMeasures[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties), stDefault)
      else
        Move(ATarget.FPropMeasures[0]^, ATarget.FPropMeasures[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeProperties))
    end;
  end;

  if ATarget.FSizeCount < ASource.FSizeCount then
  begin
    for AIndex := ATarget.FSizeCount to ASource.FSizeCount - 1 do
    begin
      FreeMem(ATarget.FWidths[AIndex]);
      FreeMem(ATarget.FHeights[AIndex]);
    end;
    ReallocMem(ATarget.FWidths, ATarget.FSizeCount * SizeOf(Pointer));
    ReallocMem(ATarget.FHeights, ATarget.FSizeCount * SizeOf(Pointer));
  end
  else
  if ATarget.FSizeCount > ASource.FSizeCount then
  begin
    ReallocMem(ATarget.FWidths, ATarget.FSizeCount * SizeOf(Pointer));
    ReallocMem(ATarget.FHeights, ATarget.FSizeCount * SizeOf(Pointer));
    for AIndex := ASource.FSizeCount to ATarget.FSizeCount - 1 do
    begin
      GetMem(ATarget.FWidths[AIndex], ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList));
      GetMem(ATarget.FHeights[AIndex], ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList));
{
      if ASource.FSizeCount = 0 then
      begin
        FillChar(ATarget.FWidths[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), stDefault);
        FillChar(ATarget.FHeights[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), stDefault);
      end
      else
      begin
        Move(ATarget.FWidths[0]^, ATarget.FWidths[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList));
        Move(ATarget.FHeights[0]^, ATarget.FHeights[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList));
      end
}
    end;
  end;
  for AIndex := 0 to ATarget.FSizeCount - 1 do
  begin
    FillChar(ATarget.FWidths[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), 0);
    FillChar(ATarget.FHeights[AIndex]^, ATarget.FFullNodeCount * SizeOf(_fcxAxisTreeNodeSizesList), 0);
  end;
end;

procedure TfcxAxisSourceTree.ReallocMemForLevelPropDetailNewCount(
  ASourceTreeLevel: PfcxSourceTreeLevel; ANewCount, AOldCount, AStartIndex: Integer);
var
  AFullNodeCount, AStartIndexNew, AStartIndexOld, AMoveCount: integer;

  procedure DoProc(APropArray: PfcxAxisTreeNodePropertiesListArray; APropCount: integer; ASize: integer);
  var
    AIndex: integer;
  begin
    for AIndex := 0 to APropCount - 1 do
    begin
      ReallocMem(APropArray[AIndex], AFullNodeCount * ASize);
      Move(APropArray[AIndex][AStartIndexOld], APropArray[AIndex][AStartIndexNew], AMoveCount * ASize);
      FillChar(APropArray[AIndex][AStartIndex], ANewCount * ASize, stDefault)
    end;
  end;

  procedure DoProc2(APropArray: PfcxAxisTreeNodePropertiesListArray; APropCount: integer; ASize: integer);
  var
    AIndex: integer;
  begin
    for AIndex := 0 to APropCount - 1 do
    begin
      Move(APropArray[AIndex][AStartIndexOld], APropArray[AIndex][AStartIndexNew], AMoveCount * ASize);
      ReallocMem(APropArray[AIndex], AFullNodeCount * ASize);
    end;
  end;

  procedure DoProcSize(ASizeArray: PfcxAxisTreeNodeSizesListArray; ASizeCount: integer; ASize: integer);
  var
    AIndex: integer;
  begin
    for AIndex := 0 to ASizeCount - 1 do
    begin
      ReallocMem(ASizeArray[AIndex], AFullNodeCount * ASize);
      Move(ASizeArray[AIndex][AStartIndexOld], ASizeArray[AIndex][AStartIndexNew], AMoveCount * ASize);
      FillChar(ASizeArray[AIndex][AStartIndex], ANewCount * ASize, 0)
    end;
  end;

  procedure DoProcSize2(ASizeArray: PfcxAxisTreeNodeSizesListArray; ASizeCount: integer; ASize: integer);
  var
    AIndex: integer;
  begin
    for AIndex := 0 to ASizeCount - 1 do
    begin
      Move(ASizeArray[AIndex][AStartIndexOld], ASizeArray[AIndex][AStartIndexNew], AMoveCount * ASize);
      ReallocMem(ASizeArray[AIndex], AFullNodeCount * ASize);
    end;
  end;
begin
  AFullNodeCount := (ASourceTreeLevel.FFullNodeCount + ANewCount - AOldCount);
  AStartIndexOld := AStartIndex + AOldCount;
  AStartIndexNew :=  AStartIndex + ANewCount;
  AMoveCount := ASourceTreeLevel.FFullNodeCount - AStartIndexOld;
  if AFullNodeCount > ASourceTreeLevel.FFullNodeCount then
  begin
    DoProc(ASourceTreeLevel.FPropSelf, ASourceTreeLevel.FPropSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc(ASourceTreeLevel.FPropTotalSelf, ASourceTreeLevel.FPropTotalSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc(ASourceTreeLevel.FPropAdditionalTotalSelf, ASourceTreeLevel.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc(ASourceTreeLevel.FPropMeasures, ASourceTreeLevel.FPropMeasuresCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProcSize(ASourceTreeLevel.FAdditionalTotalWidths, ASourceTreeLevel.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize(ASourceTreeLevel.FAdditionalTotalHeights, ASourceTreeLevel.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize(ASourceTreeLevel.FWidths, ASourceTreeLevel.FSizeCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize(ASourceTreeLevel.FHeights, ASourceTreeLevel.FSizeCount, SizeOf(_fcxAxisTreeNodeSizesList));
    ASourceTreeLevel.FFullNodeCount := AFullNodeCount;
  end
  else
  if AFullNodeCount < ASourceTreeLevel.FFullNodeCount then
  begin
    DoProc2(ASourceTreeLevel.FPropSelf, ASourceTreeLevel.FPropSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc2(ASourceTreeLevel.FPropTotalSelf, ASourceTreeLevel.FPropTotalSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc2(ASourceTreeLevel.FPropAdditionalTotalSelf, ASourceTreeLevel.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProc2(ASourceTreeLevel.FPropMeasures, ASourceTreeLevel.FPropMeasuresCount, SizeOf(_fcxAxisTreeNodeProperties));
    DoProcSize2(ASourceTreeLevel.FAdditionalTotalWidths, ASourceTreeLevel.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize2(ASourceTreeLevel.FAdditionalTotalHeights, ASourceTreeLevel.FPropAdditionalTotalSelfCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize2(ASourceTreeLevel.FWidths, ASourceTreeLevel.FSizeCount, SizeOf(_fcxAxisTreeNodeSizesList));
    DoProcSize2(ASourceTreeLevel.FHeights, ASourceTreeLevel.FSizeCount, SizeOf(_fcxAxisTreeNodeSizesList));
    ASourceTreeLevel.FFullNodeCount := AFullNodeCount;
  end;
end;

procedure TfcxAxisSourceTree.Recapacity;
var
  ALevelIndex: TfcxSmallCount;
  AStep: TfcxTraverseStep;
  ANode: PfcxSourceTreeNode;
  ANodeIndexArray, ADeltaIndexArray: Array of integer;
  ANodeArray: Array of PfcxSourceTreeNode;
  i: integer;
begin
  for ALevelIndex := -1 to FLevelCount - 1 do
    GetMemForLevelPropDetail(@(FSourceTreeLevels[ALevelIndex]), nil);

  if FLevelCount = 0 then
  begin
    ANode := @(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0]);
    ReallocMem(ANode.Nodes, ANode.Count * SizeOf(integer));
    exit;
  end;
  SetLength(ANodeArray, FLevelCount + 1);
  SetLength(ANodeIndexArray, FLevelCount + 1);
  SetLength(ADeltaIndexArray, FLevelCount + 1);
  ADeltaIndexArray[0] := 0;
  for ALevelIndex := 1 to FLevelCount do
    ADeltaIndexArray[ALevelIndex] := -1;
  ALevelIndex := 0;
  ANodeArray[0] := @(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0]);
  ANodeIndexArray[0] := 0;
  AStep := fcts_Down;
  while True do
  begin
    case AStep of
      fcts_Next, fcts_Down:
        begin
// proc begin
            begin
              ANode := ANodeArray[ALevelIndex];
              if ALevelIndex = FLevelCount then
                ReallocMem(ANode.Nodes, ANode.Count * SizeOf(integer))
              else
              begin
                ReallocMem(ANode.Nodes, ANode.Count * SizeOf(_fcxSourceTreeNode));
                for i := 0 to ANode.Count - 1 do
                begin
                  inc(ADeltaIndexArray[ALevelIndex + 1]);
                  PfcxSourceTreeNodeArray(ANode.Nodes)[i].IndexInProp := ADeltaIndexArray[ALevelIndex + 1];
                end;
              end
            end;
// proc end
          if ANodeArray[ALevelIndex].Count = 0 then
          begin
            if ALevelIndex > 0 then
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
              Break;
          end
          else
          if (ALevelIndex = FLevelCount) then // last level
          begin
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end
          end
          else
          begin
            AStep := fcts_Down;
            inc(ALevelIndex);
            ANodeIndexArray[ALevelIndex] := 0;
            ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
          end;
        end;
      fcts_Up:
        begin
          if ALevelIndex = 0 then  // can stop - on this level ALWAYS only one element
          begin
            Break;
          end
          else
          if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
          begin
            AStep := fcts_Up;
            dec(ALevelIndex);
          end
          else
          begin
            AStep := fcts_Next;
            ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
            ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
          end
        end;
    end;
  end;
end;

procedure TfcxAxisSourceTree.ResetLevelsType;
var
  AOldSourceTreeLevels: PfcxSourceTreeLevelArray;
  ALevelIndex: TfcxSmallCount;
  AIndex, i: integer;
begin
  if not AFilled then
    Exit;
  if FAxisContainer.FSlice.FMeasuresContainer.FContainer = FAxisContainer then
  begin
    if (FAxisContainer.FSlice.FMeasuresContainer.FPosition = -1) or (FAxisContainer.FSlice.FMeasuresContainer.FPosition > FLevelCount) then
      FMeasuresLevel := FLevelCount
    else
    begin
      FMeasuresLevel := FAxisContainer.FSlice.FMeasuresContainer.FPosition;
      if (FMeasuresLevel <> FLevelCount) and (FSourceTreeLevels[FMeasuresLevel].FLevelType = fcATLT_SubGroup) then
        inc(FMeasuresLevel);
    end;
    FMeasuresCount := FAxisContainer.FSlice.FMeasuresContainer.Count;
  end
  else
  begin
    FMeasuresLevel := -1;
    FMeasuresCount := 0
  end;
  GetMem(AOldSourceTreeLevels, (FLevelCount + 1) * SizeOf(_fcxSourceTreeLevelArray));
  Move(FSourceTreeLevels^, AOldSourceTreeLevels^, (FLevelCount + 1) * SizeOf(_fcxSourceTreeLevelArray));
  for ALevelIndex := -1 to FLevelCount - 1 do
  begin
    SetLevelPropCounts(ALevelIndex);
    FSourceTreeLevels[ALevelIndex].FGoodNodeCount := AOldSourceTreeLevels[ALevelIndex].FGoodNodeCount;
    FSourceTreeLevels[ALevelIndex].FFullNodeCount := AOldSourceTreeLevels[ALevelIndex].FFullNodeCount;

    FSourceTreeLevels[ALevelIndex].FPropSelf := AOldSourceTreeLevels[ALevelIndex].FPropSelf;
    FSourceTreeLevels[ALevelIndex].FPropTotalSelf := AOldSourceTreeLevels[ALevelIndex].FPropTotalSelf;
    FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf := AOldSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf;
    FSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths := AOldSourceTreeLevels[ALevelIndex].FAdditionalTotalWidths;
    FSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights := AOldSourceTreeLevels[ALevelIndex].FAdditionalTotalHeights;
    FSourceTreeLevels[ALevelIndex].FPropMeasures := AOldSourceTreeLevels[ALevelIndex].FPropMeasures;
    FSourceTreeLevels[ALevelIndex].FWidths := AOldSourceTreeLevels[ALevelIndex].FWidths;
    FSourceTreeLevels[ALevelIndex].FHeights := AOldSourceTreeLevels[ALevelIndex].FHeights;
    ReallocMemForLevelPropDetail(@(FSourceTreeLevels[ALevelIndex]), @(AOldSourceTreeLevels[ALevelIndex]));

    FSourceTreeLevels[ALevelIndex].FHiddenCount := 0;
    for i := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
    begin
      for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropSelfCount - 1 do
        if (FSourceTreeLevels[ALevelIndex].FPropSelf[AIndex][i].State and stHidden) <> 0 then
          inc(FSourceTreeLevels[ALevelIndex].FHiddenCount);
      for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount - 1 do
        if (FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AIndex][i].State and stHidden) <> 0 then
          inc(FSourceTreeLevels[ALevelIndex].FHiddenCount);
      for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount - 1 do
        if (FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AIndex][i].State and stHidden) <> 0 then
          inc(FSourceTreeLevels[ALevelIndex].FHiddenCount);
    end;
// calc count of expanded group
    if FSourceTreeLevels[ALevelIndex].FLevelType = fcATLT_HasGroup then
    begin
      FSourceTreeLevels[ALevelIndex].FGroupExpandedCount := 0;
      for i := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropSelfCount - 1 do
          if (FSourceTreeLevels[ALevelIndex].FPropSelf[AIndex][i].State and stGExpanded) = stGExpanded then
            inc(FSourceTreeLevels[ALevelIndex].FGroupExpandedCount);
    end;
  end;
  FreeMem(AOldSourceTreeLevels);
end;

(*
procedure Obhod;
var
  AIndex, ALevelIndex: TfcxSmallCount;
  AStep: TfcxTraverseStep;
  ANode: PfcxSourceTreeNode;
  ANodeIndexArray: Array of integer;
  ANodeArray: Array of PfcxSourceTreeNode;
begin
  SetLength(ANodeArray, FLevelCount + 1);
  SetLength(ANodeIndexArray, FLevelCount + 1);
  ALevelIndex := 0;
  ANodeArray[0] := @(PfcxSourceTreeNodeArray(PfcxPointerArray(FRootNodeNew.Data)[0])[0]);
  ANodeIndexArray[0] := 0;
  AStep := fcts_Down;
  while True do
  begin
    case AStep of
      fcts_Next, fcts_Down:
        begin
// proc begin

// proc end
          if ALevelIndex = FLevelCount then // last level
          begin
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(PfcxPointerArray(ANodeArray[ALevelIndex - 1].Data)[0])[ANodeIndexArray[ALevelIndex]]);
            end
          end
          else
          begin
            AStep := fcts_Down;
            inc(ALevelIndex);
            ANodeIndexArray[ALevelIndex] := 0;
            ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(PfcxPointerArray(ANodeArray[ALevelIndex - 1].Data)[0])[ANodeIndexArray[ALevelIndex]]);
          end;
        end;
      fcts_Up:
        begin
          if ALevelIndex = 0 then  // can stop - on this level ALWAYS only one element
          begin
            Break;
          end
          else
          if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
          begin
            AStep := fcts_Up;
            dec(ALevelIndex);
          end
          else
          begin
            AStep := fcts_Next;
            ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
            ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(PfcxPointerArray(ANodeArray[ALevelIndex - 1].Data)[0])[ANodeIndexArray[ALevelIndex]]);
          end
        end;
    end;
  end;
end;
*)

procedure TfcxAxisSourceTree.SetGroupExpanded(ARealLevel: TfcxSmallCount;
  ANodeIndex: integer; AMeasureIndex: TfcxSmallCount; const Value: Boolean);
var
  ALevel: TfcxSmallCount;
begin
  ALevel := ARealLevel;
  if FMeasuresLevel <> -1 then // axis with measures
  begin
    if FMeasuresLevel = ARealLevel then
      exit
    else
    if FMeasuresLevel < ARealLevel then
      dec(ALevel);
    if (ALevel <> ARealLevel) or (ALevel = -1) then
      SetGroupExpandedInternal(ALevel, ARealLevel, ANodeIndex, AMeasureIndex, Value, False)
    else
      SetGroupExpandedInternal(ALevel, ARealLevel, ANodeIndex, 0, Value, False);
  end
  else
    SetGroupExpandedInternal(ALevel, ARealLevel, ANodeIndex, 0, Value, False);
end;

procedure TfcxAxisSourceTree.SetGroupExpandedInternal(ALevel,
  ARealLevel: TfcxSmallCount; ANodeIndex: integer;
  AMeasureIndex: TfcxSmallCount; const Value: Boolean; ANonChange: boolean);
var
 ASourceTreeNode, AOldSourceTreeNode: PfcxSourceTreeNode;
 AOldNodes: PfcxSourceTreeNodeArray;
 AOldCountInLevelArray, ANewCountInLevelArray, AStartIndexInPropArray: Array of integer;
 AOldNodesCount: integer;

  procedure CreateNewSubTree(AOldNode, ANewNode: PfcxSourceTreeNode; AStartLevel: TfcxSmallCount);
  var
    AFieldIndex, ALevelIndex, ALevelIndex2: TfcxSmallCount;
    AStep: TfcxTraverseStep;
    ANode, ANodeNew: PfcxSourceTreeNode;
    ANodeIndexArray: Array of integer;
    ANodeArray: Array of PfcxSourceTreeNode;
    AIndex, ARecordIndex, i, j2, AGroupIndex: integer;
    AEnd: boolean;
    AUVIndexArray: Array of integer;
    AUVTypeArray: Array of TfcxAxisTreeNodeType;
  begin
    SetLength(AUVIndexArray, FLevelCount);
    SetLength(AUVTypeArray, FLevelCount);
    SetLength(AOldCountInLevelArray, FLevelCount);
    SetLength(ANewCountInLevelArray, FLevelCount);
    SetLength(AStartIndexInPropArray, FLevelCount);
    for ALevelIndex := 0 to FLevelCount - 1 do
    begin
      AOldCountInLevelArray[ALevelIndex] := 0;
      ANewCountInLevelArray[ALevelIndex] := 0;
      AStartIndexInPropArray[ALevelIndex] := -1;
    end;


    SetLength(ANodeArray, FLevelCount + 1);
    SetLength(ANodeIndexArray, FLevelCount + 1);
    ALevelIndex := AStartLevel+1;
    ANodeArray[AStartLevel+1] := AOldNode;
    GetMem(ANewNode.Nodes, 16 * SizeOf(_fcxSourceTreeNode));
    ANewNode.Count := 0;
    ANodeIndexArray[AStartLevel+1] := 0;
    AStep := fcts_Down;
    while True do
    begin
      case AStep of
        fcts_Next, fcts_Down:
          begin
  // proc begin
              begin
                if ALevelIndex >= AStartLevel then
                begin
                  ANode := ANodeArray[ALevelIndex];
                  if (ALevelIndex-1) <> AStartLevel then
                  begin
                    inc(AOldCountInLevelArray[ALevelIndex-1]);
                  end;
                  if (AStartIndexInPropArray[ALevelIndex-1] = -1) or
                     (AStartIndexInPropArray[ALevelIndex-1] > ANode.IndexInProp) then
                    AStartIndexInPropArray[ALevelIndex-1] := ANode.IndexInProp;
                end
                else
                  ANode := nil;
                if ALevelIndex = FLevelCount then
                begin
                  for i := 0 to ANode.Count - 1 do
                  begin
//index of source record. need process this record
                    ARecordIndex := PfcxIntegerArray(ANode.Nodes)[i];

// for row fill array of UV indexes by level
                    for ALevelIndex2 := 0 to FLevelCount - 1 do
                    begin
                      AFieldIndex := FFieldIndexOfLevel[ALevelIndex2];
                      AUVTypeArray[ALevelIndex2] := fcATNT_SimpleValue;

                      AUVIndexArray[ALevelIndex2] := FAxisContainer.FSlice.FCube.SourceHolder.UniqueValueIndex[ARecordIndex, TfcxCommonSliceCubeField(FAxisContainer.Fields[AFieldIndex].FSliceField).CubeField];
// check including in group
                      if FSourceTreeLevels[ALevelIndex2].FLevelType = fcATLT_HasGroup then
                      begin
                        if FAxisContainer.Fields[AFieldIndex].FSliceField is TfcxSliceStdSplitCubeField then
                          AGroupIndex := TfcxCommonStdPathField(TfcxCommonSliceCubeField(FAxisContainer.Fields[AFieldIndex].FSliceField).CubeField).StdPathUniqueValues.GroupIndexOfUV[AUVIndexArray[ALevelIndex2]]
                        else
                          AGroupIndex := TfcxCommonUVField(TfcxCommonSliceCubeField(FAxisContainer.Fields[AFieldIndex].FSliceField).CubeField).UniqueValues.GroupIndexOfUV[AUVIndexArray[ALevelIndex2]];
                        if AGroupIndex >= 0 then
                        begin
                          AUVTypeArray[ALevelIndex2] := fcATNT_GroupValue;
                          AUVIndexArray[ALevelIndex2] := AGroupIndex;
                        end;
                      end
                      else
                      if FSourceTreeLevels[ALevelIndex2].FLevelType = fcATLT_SubGroup then
                      begin
// lavel of expanded group
// for descendants of simple cell and nonexpanded group create empty node with parent value
                        if AUVTypeArray[ALevelIndex2 - 1] = fcATNT_SimpleValue then
                        begin
                          AUVTypeArray[ALevelIndex2] := fcATNT_EmptyValue;
                          AUVIndexArray[ALevelIndex2] := AUVIndexArray[ALevelIndex2 - 1];
                        end
                        else
                        begin
                          if (ALevelIndex2 = (AStartLevel + 1)) then
                          begin
// our group
                            if (FSourceTreeLevels[ALevelIndex2-1].FPropSelf[AMeasureIndex][ASourceTreeNode.IndexInProp].State and stGExpanded) = 0 then
                            begin
// expand
                              AUVTypeArray[ALevelIndex2] := fcATNT_SimpleValue;
                            end
                            else
                            begin
// collaps
                              AUVTypeArray[ALevelIndex2] := fcATNT_EmptyValue;
                              AUVIndexArray[ALevelIndex2] := AUVIndexArray[ALevelIndex2 - 1];
                            end;
                          end
                          else
                          begin
// default all groups are collapsed
                            AUVTypeArray[ALevelIndex2] := fcATNT_EmptyValue;
                            AUVIndexArray[ALevelIndex2] := AUVIndexArray[ALevelIndex2 - 1];
                          end
                        end
                      end;
                    end;

                    ANodeNew := ANewNode;
                    AEnd := False;
                    for ALevelIndex2 := AStartLevel+1 to FLevelCount - 1 do
                    begin
{todo was error one time}
                      if FindPlace(ANodeNew, AUVIndexArray[ALevelIndex2], FSourceTreeLevels[ALevelIndex2].FLevelType = fcATLT_HasGroup, AUVTypeArray[ALevelIndex2], FSourceTreeLevels[ALevelIndex].FTopNCheck, AIndex) then
                      begin
// found. go down
                        ANodeNew := @(PfcxSourceTreeNodeArray(ANodeNew.Nodes)[AIndex]);
                      end
                      else
                      begin
// not found, insert new brach
                        if ANodeNew.Count < 64 then
                        begin
                          if (ANodeNew.Count and 15) = 0 then
                            ReallocMem(ANodeNew.Nodes, (ANodeNew.Count + 16) * SizeOf(_fcxSourceTreeNode))
                        end
                        else
                        if ANodeNew.Count < 1024 then
                        begin
                          if (ANodeNew.Count and 63) = 0 then
                            ReallocMem(ANodeNew.Nodes, (ANodeNew.Count + 64) * SizeOf(_fcxSourceTreeNode))
                        end
                        else
                        begin
                          if (ANodeNew.Count and 1023) = 0 then
                            ReallocMem(ANodeNew.Nodes, (ANodeNew.Count + 1024) * SizeOf(_fcxSourceTreeNode))
                        end;
                        Move(PfcxSourceTreeNodeArray(ANodeNew.Nodes)[AIndex], PfcxSourceTreeNodeArray(ANodeNew.Nodes)[AIndex + 1], (ANodeNew.Count - AIndex) * SizeOf(_fcxSourceTreeNode));
                        ANodeNew.Count := ANodeNew.Count + 1;
                        inc(ANewCountInLevelArray[ALevelIndex2]);
                        ANodeNew := @(PfcxSourceTreeNodeArray(ANodeNew.Nodes)[AIndex]);
                        ANodeNew.Count := 0;
                        if (ALevelIndex2 = (FLevelCount - 1)) then
                          ANodeNew.Nodes := nil
                        else
                        begin
                          GetMem(ANodeNew.Nodes, 16 * SizeOf(_fcxSourceTreeNode));
                        end;
                        ANodeNew.Index := AUVIndexArray[ALevelIndex2];
                        ANodeNew.NodeType := AUVTypeArray[ALevelIndex2];
                        for j2 := ALevelIndex2 + 1 to FLevelCount - 1 do
                        begin
                          ANodeNew.Count := 1;
                          inc(ANewCountInLevelArray[j2]);
                          ANodeNew := @(PfcxSourceTreeNodeArray(ANodeNew.Nodes)[0]);
                          ANodeNew.Count := 0;
                          if (j2 = (FLevelCount - 1)) then
                            ANodeNew.Nodes := nil
                          else
                          begin
                            GetMem(ANodeNew.Nodes, 16 * SizeOf(_fcxSourceTreeNode));
                          end;
                          ANodeNew.Index := AUVIndexArray[j2];
                          ANodeNew.NodeType := AUVTypeArray[j2];
                        end;
                        AEnd := True;
                      end;
                      if AEnd or (ALevelIndex2 = (FLevelCount - 1)) then
                      begin
                        if ANodeNew.Count < 64 then
                        begin
                          if (ANodeNew.Count and 15) = 0 then
                            ReallocMem(ANodeNew.Nodes, (ANodeNew.Count + 16) * SizeOf(integer))
                        end
                        else
                        if ANodeNew.Count < 1024 then
                        begin
                          if (ANodeNew.Count and 63) = 0 then
                            ReallocMem(ANodeNew.Nodes, (ANodeNew.Count + 64) * SizeOf(integer))
                        end
                        else
                        begin
                          if (ANodeNew.Count and 1023) = 0 then
                            ReallocMem(ANodeNew.Nodes, (ANodeNew.Count + 1024) * SizeOf(integer))
                        end;
                        PfcxIntegerArray(ANodeNew.Nodes)[ANodeNew.Count] := ARecordIndex;
                        ANodeNew.Count := ANodeNew.Count + 1;
                        break;
                      end
                    end;
                  end;
                end;
              end;
// proc end
            if ALevelIndex = FLevelCount then // last level
            begin
              if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
              begin
                AStep := fcts_Up;
                dec(ALevelIndex);
              end
              else
              begin
                AStep := fcts_Next;
                ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
                ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
              end
            end
            else
            begin
              AStep := fcts_Down;
              inc(ALevelIndex);
              ANodeIndexArray[ALevelIndex] := 0;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end;
          end;
        fcts_Up:
          begin
            if ALevelIndex = (AStartLevel+1) then  // can stop - on this level ALWAYS only one element
            begin
              Break;
            end
            else
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end
          end;
      end;
    end;
  end;

  procedure RecapacityProperties(AStartLevel: TfcxSmallCount);
  var
    ALevelIndex: TfcxSmallCount;
    AStep: TfcxTraverseStep;
    ANode: PfcxSourceTreeNode;
    ANodeIndexArray, ADeltaIndexArray: Array of integer;
    ANodeArray: Array of PfcxSourceTreeNode;
    i: integer;
  begin
    for ALevelIndex := AStartLevel to FLevelCount - 1 do
      ReallocMemForLevelPropDetailNewCount(@(FSourceTreeLevels[ALevelIndex]), ANewCountInLevelArray[ALevelIndex], AOldCountInLevelArray[ALevelIndex], AStartIndexInPropArray[ALevelIndex]);

    SetLength(ANodeArray, FLevelCount + 1);
    SetLength(ANodeIndexArray, FLevelCount + 1);
    SetLength(ADeltaIndexArray, FLevelCount + 1);
    ADeltaIndexArray[0] := 0;
    for ALevelIndex := 1 to FLevelCount do
      ADeltaIndexArray[ALevelIndex] := -1;
    ALevelIndex := 0;
    ANodeArray[0] := @(PfcxSourceTreeNodeArray(FRootNodeNew.Nodes)[0]);
    ANodeIndexArray[0] := 0;
    AStep := fcts_Down;
    while True do
    begin
      case AStep of
        fcts_Next, fcts_Down:
          begin
  // proc begin
              begin
                ANode := ANodeArray[ALevelIndex];
                if ALevelIndex = FLevelCount then
//                  ReallocMem(ANode.Nodes, ANode.Count * SizeOf(integer))
                else
                begin
//                  ReallocMem(ANode.Nodes, ANode.Count * SizeOf(_fcxSourceTreeNode));
                  for i := 0 to ANode.Count - 1 do
                  begin
                    inc(ADeltaIndexArray[ALevelIndex + 1]);
                    PfcxSourceTreeNodeArray(ANode.Nodes)[i].IndexInProp := ADeltaIndexArray[ALevelIndex + 1];
                  end;
                end
              end;
  // proc end
            if ALevelIndex = FLevelCount then // last level
            begin
              if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
              begin
                AStep := fcts_Up;
                dec(ALevelIndex);
              end
              else
              begin
                AStep := fcts_Next;
                ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
                ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
              end
            end
            else
            begin
              AStep := fcts_Down;
              inc(ALevelIndex);
              ANodeIndexArray[ALevelIndex] := 0;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end;
          end;
        fcts_Up:
          begin
            if ALevelIndex = 0 then  // can stop - on this level ALWAYS only one element
            begin
              Break;
            end
            else
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end
          end;
      end;
    end;
  end;

  procedure RecapacityNewTree(AStartNode: PfcxSourceTreeNode; AStartLevel: TfcxSmallCount);
  var
    ALevelIndex: TfcxSmallCount;
    AStep: TfcxTraverseStep;
    ANode: PfcxSourceTreeNode;
    ANodeIndexArray: Array of integer;
    ANodeArray: Array of PfcxSourceTreeNode;
  begin

    SetLength(ANodeArray, FLevelCount + 1);
    SetLength(ANodeIndexArray, FLevelCount + 1);
    ALevelIndex := AStartLevel+1;
    ANodeArray[AStartLevel+1] := AStartNode;
    ANodeIndexArray[AStartLevel+1] := 0;
    AStep := fcts_Down;
    while True do
    begin
      case AStep of
        fcts_Next, fcts_Down:
          begin
  // proc begin
              begin
                ANode := ANodeArray[ALevelIndex];
                if ALevelIndex = FLevelCount then
                  ReallocMem(ANode.Nodes, ANode.Count * SizeOf(integer))
                else
                begin
                  ReallocMem(ANode.Nodes, ANode.Count * SizeOf(_fcxSourceTreeNode));
                end
              end;
  // proc end
            if ALevelIndex = FLevelCount then // last level
            begin
              if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
              begin
                AStep := fcts_Up;
                dec(ALevelIndex);
              end
              else
              begin
                AStep := fcts_Next;
                ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
                ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
              end
            end
            else
            begin
              AStep := fcts_Down;
              inc(ALevelIndex);
              ANodeIndexArray[ALevelIndex] := 0;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end;
          end;
        fcts_Up:
          begin
            if ALevelIndex = (AStartLevel+1) then  // can stop - on this level ALWAYS only one element
            begin
              Break;
            end
            else
            if ANodeIndexArray[ALevelIndex] = (ANodeArray[ALevelIndex - 1].Count - 1) then // last right child
            begin
              AStep := fcts_Up;
              dec(ALevelIndex);
            end
            else
            begin
              AStep := fcts_Next;
              ANodeIndexArray[ALevelIndex] := ANodeIndexArray[ALevelIndex] + 1;
              ANodeArray[ALevelIndex] := @(PfcxSourceTreeNodeArray(ANodeArray[ALevelIndex - 1].Nodes)[ANodeIndexArray[ALevelIndex]]);
            end
          end;
      end;
    end;
  end;
var
  i: integer;
begin
  if FSourceTreeLevels[ALevel].FLevelType <> fcATLT_HasGroup then
    exit;
  ASourceTreeNode := FAxisContainer.FAxisTree[ALevel].Nodes[ANodeIndex].SSelfNode;
  if ASourceTreeNode.NodeType <> fcATNT_GroupValue then
    exit;
  if ((FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][ASourceTreeNode.IndexInProp].State and stGExpanded) = stGExpanded) = Value then
    exit;
  if ANonChange then
    inc(FAxisContainer.FSlice.FChangeSemaphore); 
  FAxisContainer.FSlice.StartChange;
  if Value then
  begin
    if FSourceTreeLevels[ALevel].FGroupExpandedCount = 0 then
      SetSubGroupVisible(ALevel, True);
    AOldNodes := ASourceTreeNode.Nodes;
    GetMem(AOldSourceTreeNode, SizeOf(_fcxSourceTreeNode));
    AOldSourceTreeNode.Count := ASourceTreeNode.Count;
    AOldSourceTreeNode.Index := ASourceTreeNode.Index;
    AOldSourceTreeNode.Nodes := ASourceTreeNode.Nodes;
    AOldSourceTreeNode.IndexInProp := ASourceTreeNode.IndexInProp;
    AOldSourceTreeNode.NodeType := ASourceTreeNode.NodeType;
    CreateNewSubTree(AOldSourceTreeNode, ASourceTreeNode, ALevel);
    RecapacityNewTree(ASourceTreeNode, ALevel);
    RecapacityProperties(ALevel + 1);

    ClearNode(@(AOldNodes[0]), ALevel + 1);
    FreeMem(AOldNodes);
    FreeMem(AOldSourceTreeNode);
    FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][ASourceTreeNode.IndexInProp].State := FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][ASourceTreeNode.IndexInProp].State or stGExpanded;
    inc(FSourceTreeLevels[ALevel].FGroupExpandedCount);
  end
  else
  begin

    AOldNodes := ASourceTreeNode.Nodes;
    AOldNodesCount := ASourceTreeNode.Count;
    GetMem(AOldSourceTreeNode, SizeOf(_fcxSourceTreeNode));
    AOldSourceTreeNode.Count := ASourceTreeNode.Count;
    AOldSourceTreeNode.Index := ASourceTreeNode.Index;
    AOldSourceTreeNode.Nodes := ASourceTreeNode.Nodes;
    AOldSourceTreeNode.IndexInProp := ASourceTreeNode.IndexInProp;
    AOldSourceTreeNode.NodeType := ASourceTreeNode.NodeType;
    CreateNewSubTree(AOldSourceTreeNode, ASourceTreeNode, ALevel);
    RecapacityNewTree(ASourceTreeNode, ALevel);
    RecapacityProperties(ALevel + 1);

    for i := 0 to AOldNodesCount - 1 do
    begin
      ClearNode(@(AOldNodes[i]), ALevel + 1);
    end;
    FreeMem(AOldNodes);
    FreeMem(AOldSourceTreeNode);
    FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][ASourceTreeNode.IndexInProp].State := FSourceTreeLevels[ALevel].FPropSelf[AMeasureIndex][ASourceTreeNode.IndexInProp].State xor stGExpanded;
    dec(FSourceTreeLevels[ALevel].FGroupExpandedCount);
    if FSourceTreeLevels[ALevel].FGroupExpandedCount = 0 then
      SetSubGroupVisible(ALevel, False);
  end;
  if FAxisContainer.FSlice.XAxisContainer = FAxisContainer then
    FAxisContainer.FSlice.StopChange([chs_ExpandXGroup])
  else
    FAxisContainer.FSlice.StopChange([chs_ExpandYGroup]);
  if ANonChange then
    dec(FAxisContainer.FSlice.FChangeSemaphore);
end;

procedure TfcxAxisSourceTree.SetLevelPropCounts(
  ALevelIndex: TfcxSmallCount);
begin
  FSourceTreeLevels[ALevelIndex].FGoodNodeCount := 0;
  FSourceTreeLevels[ALevelIndex].FFullNodeCount := 0;
  FSourceTreeLevels[ALevelIndex].FLevelPosType := CalcLevelPosType(ALevelIndex);
  FSourceTreeLevels[ALevelIndex].FPropSelfCount := cfcSelfPropertiesArrayCount[FSourceTreeLevels[ALevelIndex].FLevelPosType];
  if FSourceTreeLevels[ALevelIndex].FPropSelfCount = 2 then
    FSourceTreeLevels[ALevelIndex].FPropSelfCount := FMeasuresCount;
  FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount := cfcTotalSelfPropertiesArrayCount[FSourceTreeLevels[ALevelIndex].FLevelPosType];
  if FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount = 2 then
    FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount := FMeasuresCount;
  FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount := cfcAdditionalTotalSelfPropertiesArrayCount[FSourceTreeLevels[ALevelIndex].FLevelPosType];
  if FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount = 4 then
    FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount := FMeasuresCount * FAxisContainer.CountAdditionalTotalFunctionsInLevel[ALevelIndex]
  else
  if FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount = 3 then
    FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount := FAxisContainer.CountAdditionalTotalFunctionsInLevel[ALevelIndex];
  FSourceTreeLevels[ALevelIndex].FPropMeasuresCount := cfcMeasuresPropertiesArrayCount[FSourceTreeLevels[ALevelIndex].FLevelPosType];
  if FSourceTreeLevels[ALevelIndex].FPropMeasuresCount = 2 then
    FSourceTreeLevels[ALevelIndex].FPropMeasuresCount := FMeasuresCount;
  if FMeasuresCount > 1 then
    FSourceTreeLevels[ALevelIndex].FSizeCount := FMeasuresCount
  else
    FSourceTreeLevels[ALevelIndex].FSizeCount := 1;
  FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier := cfcCanCollapseMultiplier[FSourceTreeLevels[ALevelIndex].FLevelPosType];
  if FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier = 2 then
    FSourceTreeLevels[ALevelIndex].FCanCollapseNodeMultiplier := FMeasuresCount;
end;

procedure TfcxAxisSourceTree.SetPropertiesByMask(AMask: byte);
var
  ALevelIndex: TfcxSmallCount;
  AIndex, AIndex2: integer;
begin
  for ALevelIndex := -1 to FLevelCount - 1 do
  begin
    for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropSelfCount - 1 do
      for AIndex2 := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        FSourceTreeLevels[ALevelIndex].FPropSelf[AIndex][AIndex2].State := FSourceTreeLevels[ALevelIndex].FPropSelf[AIndex][AIndex2].State or AMask;
    for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount - 1 do
      for AIndex2 := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AIndex][AIndex2].State := FSourceTreeLevels[ALevelIndex].FPropTotalSelf[AIndex][AIndex2].State or AMask;
    for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount - 1 do
      for AIndex2 := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AIndex][AIndex2].State := FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelf[AIndex][AIndex2].State or AMask;
    for AIndex := 0 to FSourceTreeLevels[ALevelIndex].FPropMeasuresCount - 1 do
      for AIndex2 := 0 to FSourceTreeLevels[ALevelIndex].FFullNodeCount - 1 do
        FSourceTreeLevels[ALevelIndex].FPropMeasures[AIndex][AIndex2].State := FSourceTreeLevels[ALevelIndex].FPropMeasures[AIndex][AIndex2].State or AMask;
  end;
end;

function TfcxAxisSourceTree.SetSubGroupVisible(ALevelIndex: TfcxSmallCount;
  AValue: Boolean): Boolean;
var
  ASubLevelIndex: TfcxSmallCount;
  AOldSourceTreeLevel: PfcxSourceTreeLevel;
begin
  Result := False;
  ASubLevelIndex := ALevelIndex + 1;
  if (ALevelIndex < FLevelCount) and
     (FSourceTreeLevels[ALevelIndex].FLevelType = fcATLT_HasGroup) then
  begin
    if (AValue xor FSourceTreeLevels[ALevelIndex].FGSubLevelVisible) then
    begin
      if AValue then
      begin
        inc(FLevelCount);
        ReallocMem(FFieldIndexOfLevel, FLevelCount * SizeOf(TfcxSmallCount));
        ReallocMem(FSourceTreeLevels, (FLevelCount + 1) * SizeOf(_fcxSourceTreeLevelArray));
        if ASubLevelIndex < (FLevelCount - 1) then
        begin
          System.Move(FFieldIndexOfLevel[ASubLevelIndex], FFieldIndexOfLevel[ALevelIndex+2], (FLevelCount - ALevelIndex - 2) * SizeOf(TfcxSmallCount));
          System.Move(FSourceTreeLevels[ASubLevelIndex], FSourceTreeLevels[ALevelIndex+2], (FLevelCount - ALevelIndex - 2) * SizeOf(_fcxSourceTreeLevelArray));
        end;
        if FAxisContainer.FSlice.FMeasuresContainer.FContainer = FAxisContainer then
          if FMeasuresLevel > ALevelIndex then
            inc(FMeasuresLevel);

        FFieldIndexOfLevel[ASubLevelIndex] := FFieldIndexOfLevel[ALevelIndex];
        FSourceTreeLevels[ASubLevelIndex].FLevelType := fcATLT_SubGroup;
        FSourceTreeLevels[ASubLevelIndex].FGSubLevelVisible := False;
        SetLevelPropCounts(ASubLevelIndex);
        FSourceTreeLevels[ASubLevelIndex].FGoodNodeCount := FSourceTreeLevels[ALevelIndex].FGoodNodeCount;
        FSourceTreeLevels[ASubLevelIndex].FFullNodeCount := FSourceTreeLevels[ALevelIndex].FFullNodeCount;
        GetMemForLevelProp(@(FSourceTreeLevels[ASubLevelIndex]));
        GetMemForLevelPropDetail(@(FSourceTreeLevels[ASubLevelIndex]), @(FSourceTreeLevels[ALevelIndex]));

        FSourceTreeLevels[ALevelIndex].FGSubLevelVisible := True;
        SetLevelPropCounts(ALevelIndex);
        FSourceTreeLevels[ALevelIndex].FGoodNodeCount := FSourceTreeLevels[ASubLevelIndex].FGoodNodeCount;
        FSourceTreeLevels[ALevelIndex].FFullNodeCount := FSourceTreeLevels[ASubLevelIndex].FFullNodeCount;
        ReallocMemForLevelPropDetail(@(FSourceTreeLevels[ALevelIndex]), @(FSourceTreeLevels[ASubLevelIndex]));
        InsertSubLevel(ALevelIndex);
        Result := True;
      end
      else
      begin
        DeleteSubLevel(ALevelIndex);
        FreeMemForLevelProp(@(FSourceTreeLevels[ASubLevelIndex]));
        dec(FLevelCount);
        if ASubLevelIndex < FLevelCount then
        begin
          System.Move(FFieldIndexOfLevel[ASubLevelIndex+1], FFieldIndexOfLevel[ASubLevelIndex], (FLevelCount - ASubLevelIndex) * SizeOf(TfcxSmallCount));
          System.Move(FSourceTreeLevels[ASubLevelIndex+1], FSourceTreeLevels[ASubLevelIndex], (FLevelCount - ASubLevelIndex) * SizeOf(_fcxSourceTreeLevelArray));
        end;
        ReallocMem(FFieldIndexOfLevel, FLevelCount * SizeOf(TfcxSmallCount));
        ReallocMem(FSourceTreeLevels, (FLevelCount + 1) * SizeOf(_fcxSourceTreeLevelArray));
        if FAxisContainer.FSlice.FMeasuresContainer.FContainer = FAxisContainer then
          if FMeasuresLevel >= ASubLevelIndex then
            dec(FMeasuresLevel);
        FSourceTreeLevels[ALevelIndex].FGSubLevelVisible := False;
        GetMem(AOldSourceTreeLevel, SizeOf(_fcxSourceTreeLevel));
        AOldSourceTreeLevel.FLevelPosType := FSourceTreeLevels[ALevelIndex].FLevelPosType;
        AOldSourceTreeLevel.FPropMeasuresCount := FSourceTreeLevels[ALevelIndex].FPropMeasuresCount;
        AOldSourceTreeLevel.FPropSelfCount := FSourceTreeLevels[ALevelIndex].FPropSelfCount;
        AOldSourceTreeLevel.FPropTotalSelfCount := FSourceTreeLevels[ALevelIndex].FPropTotalSelfCount;
        AOldSourceTreeLevel.FPropAdditionalTotalSelfCount := FSourceTreeLevels[ALevelIndex].FPropAdditionalTotalSelfCount;
        AOldSourceTreeLevel.FSizeCount := FSourceTreeLevels[ALevelIndex].FSizeCount;
        AOldSourceTreeLevel.FFullNodeCount := FSourceTreeLevels[ALevelIndex].FFullNodeCount;
        AOldSourceTreeLevel.FGoodNodeCount := FSourceTreeLevels[ALevelIndex].FGoodNodeCount;
        SetLevelPropCounts(ALevelIndex);

        FSourceTreeLevels[ALevelIndex].FGoodNodeCount := AOldSourceTreeLevel.FGoodNodeCount;
        FSourceTreeLevels[ALevelIndex].FFullNodeCount := AOldSourceTreeLevel.FFullNodeCount;

        ReallocMemForLevelPropDetail(@(FSourceTreeLevels[ALevelIndex]), AOldSourceTreeLevel);

        FreeMem(AOldSourceTreeLevel);

        Result := True;
      end
    end;
  end;
//
end;

{ TfcxFormulaMeasureProcessor }

class procedure TfcxFormulaMeasureProcessor.Func(
  const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  AMeasureValue.Value := AValue;
end;

{ TfcxCountMeasureProcessor }

class procedure TfcxCountMeasureProcessor.Func(
  const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  if TVarData(AMeasureValue.Value).VType <= 1 then
    AMeasureValue.Value := 0;
  if not (TVarData(AValue).VType <= 1) then
    AMeasureValue.Value := AMeasureValue.Value + 1;
end;

{ TfcxFormulaDetailMeasureProcessor }

class procedure TfcxFormulaDetailMeasureProcessor.Func(
  const AMeasureValue: PfcxMeasureValue; AValue: Variant; AStep,
  AIndexValue: Integer; AMeasureField: TfcxMeasureField);
begin
  AMeasureValue.Value := AValue;
end;

class function TfcxFormulaDetailMeasureProcessor.GetSize: Integer;
begin
  Result := SizeOf(_fcxMeasureValueWithPointer);
end;

class function TfcxFormulaDetailMeasureProcessor.NewMeasureValue(
  AMeasureField: TfcxMeasureField): PfcxMeasureValue;
begin
  Result := inherited NewMeasureValue(AMeasureField);
  PfcxMeasureValueWithPointer(Result).PValue := Nil;
end;

{ TfcxSliceStdSplitCubeField }

function TfcxSliceStdSplitCubeField.GetDataType: TfcxDataType;
begin
  Result := TfcxCommonStdPathField(FCubeField).DataType
end;

function TfcxSliceStdSplitCubeField.GetUVCount: integer;
begin
  Result := TfcxCommonStdPathDTP(TfcxCommonStdPathField(FCubeField).DataTypeProcessor).CountUV
end;

function TfcxSliceStdSplitCubeField.GetUVIndexByOrder(
  AOrder: integer): integer;
begin
  Result := {TfcxCommonStdPathDTP(TfcxCommonStdPathField(FCubeField).DataTypeProcessor).ValueAtIndex[}AOrder{]};
end;

function TfcxSliceStdSplitCubeField.GetUVOrderByIndex(
  AIndex: integer): integer;
begin
  Result := {TfcxCommonStdPathDTP(TfcxCommonStdPathField(FCubeField).DataTypeProcessor).IndexAtValue[}AIndex{]};
end;

{ TfcxMeasureHighlights }

function TfcxCustomHighlights.Add(AClass: TfcxCustomHighlightClass): TfcxCustomHighlight;
begin
  Result := AClass.Create(Self);
end;

function TfcxCustomHighlights.AddByName(AClassName: String): TfcxCustomHighlight;
begin
  Result := fcxRegisteredHighlights.FindByName(AClassName).ClassType.Create(Self);
end;

procedure TfcxCustomHighlights.Assign(Source: TPersistent);
var
  I: Integer;
begin
  if Source is TCollection then
  begin
    BeginUpdate;
    try
      Clear;
      for I := 0 to TCollection(Source).Count - 1 do
        Add(TfcxCustomHighlightClass(TCollection(Source).Items[I].ClassType)).Assign(TCollection(Source).Items[I]);
    finally
      EndUpdate;
    end;
    Exit;
  end;
  inherited Assign(Source);
end;

constructor TfcxCustomHighlights.Create(AMeasure: TfcxMeasureField);
begin
  inherited Create(TfcxCustomHighlight);
  FMeasure := AMeasure;
end;

destructor TfcxCustomHighlights.Destroy;
begin
{$IFDEF FPC}
  if Count > 0 then
  begin
    BeginUpdate;
    Clear;
  end;
{$ENDIF}
  inherited Destroy;
end;

function TfcxCustomHighlights.GetItem(AIndex: Integer): TfcxCustomHighlight;
begin
  Result := TfcxCustomHighlight(inherited GetItem(AIndex));
end;

procedure TfcxCustomHighlights.LoadFromXML(AItem: TfcxXMLItem);
var
  I: Integer;
  HC: TfcxCustomHighlightClass;
  Highlight: TfcxCustomHighlight;
begin
  Clear;
  if UpperCase(AItem.Name) = 'HIGHLIGHTS' then
  begin
    for I := 0 to AItem.Count - 1 do
    begin
      HC := fcxRegisteredHighlights.FindHighlightClass(AItem[I].Name);
      if Assigned(HC) then
      begin
        Highlight := Add(HC);
        Highlight.LoadFromXML(AItem[I]);
      end;
    end;
  end;
end;

procedure TfcxCustomHighlights.LoadRangeXML(AItem: TfcxXMLItem);
var
  I: integer;
begin
  for I := 0 to AItem.Count - 1 do
    AddByName('TfcxRangeHighlight').LoadRangeXML(AItem[I]);
end;

function TfcxCustomHighlights.RequestMinMax(AValue: Pointer; AMax: Boolean; ByCol: Boolean): Double;
var
  MeasureCell: PfcxMeasureCell absolute AValue;
  AFindIndex, ABaseIndexTotal, ASecondIndexTotal, ABaseIndexInLevel, AIndexInSecondLevel: Integer;
  ABaseLevel, ASecondLevel: TfcxSmallCount;
begin

  with FMeasure.FMeasuresContainer do
  begin
    if (FMeasure.FCellHighlights = nil) then
      CalcMaxMins(MeasureCell.MeasureIndex);

    ASecondLevel := FSecondContainer.FLevelOf[MeasureCell.SecondIndex];
    ABaseLevel := FBaseContainer.FLevelOf[MeasureCell.BaseIndex];
    if not ByCol xor FBaseAxisIsX then
    begin
      if ASecondLevel = -1 then
        Result := MeasureCell^.Value
      else
      begin
        ABaseIndexInLevel := FBaseContainer.FIndexInLevel[MeasureCell.BaseIndex];
        ASecondIndexTotal := FSecondContainer.FIndexOfParent[MeasureCell.SecondIndex];
        AIndexInSecondLevel := FSecondContainer.FIndexInLevel[ASecondIndexTotal];
        AFindIndex := AIndexInSecondLevel;
        if not (FLevelsProp[ABaseLevel][ASecondLevel-1][ABaseIndexInLevel].Simple or FMeasureFields[FMeasure.Index].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel-1].CreateAllCells) then
          FindByIndex(ABaseLevel, ASecondLevel-1, ABaseIndexInLevel, AFindIndex, AFindIndex);
        if AMax then
          Result := PfcxArrayMeasureCellHighlightsArray(FMeasure.FCellHighlights[ABaseLevel][ASecondLevel-1])[ABaseIndexInLevel][AFindIndex].MaxSecond
        else
          Result := PfcxArrayMeasureCellHighlightsArray(FMeasure.FCellHighlights[ABaseLevel][ASecondLevel-1])[ABaseIndexInLevel][AFindIndex].MinSecond
      end
    end
    else
    begin
      ABaseIndexTotal := FBaseContainer.FIndexOfParent[MeasureCell.BaseIndex];
      if ABaseIndexTotal = -1 then
        Result := MeasureCell^.Value
      else
      begin
        ABaseIndexInLevel := FBaseContainer.FIndexInLevel[ABaseIndexTotal];
        AIndexInSecondLevel := FSecondContainer.FIndexInLevel[MeasureCell.SecondIndex];
        AFindIndex := AIndexInSecondLevel;
        if not (FLevelsProp[ABaseLevel-1][ASecondLevel][ABaseIndexInLevel].Simple or FMeasureFields[FMeasure.Index].FMeasurePropInLevelsListArray[ABaseLevel-1][ASecondLevel].CreateAllCells) then
          FindByIndex(ABaseLevel-1, ASecondLevel, ABaseIndexInLevel, AFindIndex, AFindIndex);
        if AMax then
          Result := PfcxArrayMeasureCellHighlightsArray(FMeasure.FCellHighlights[ABaseLevel-1][ASecondLevel])[ABaseIndexInLevel][AFindIndex].MaxBase
        else
          Result := PfcxArrayMeasureCellHighlightsArray(FMeasure.FCellHighlights[ABaseLevel-1][ASecondLevel])[ABaseIndexInLevel][AFindIndex].MinBase
      end
    end;
  end;
end;

function TfcxCustomHighlights.RequestPercent(AValue: Pointer; APercent: Double; ByCol: Boolean): Double;
var
  AMin, AMax: Double;
begin
  AMin := RequestMinMax(AValue, False, ByCol);
  AMax := RequestMinMax(AValue, True, ByCol);
  Result := AMin + APercent * (AMax - AMin) / 100;
end;

function TfcxCustomHighlights.RequestPercentile(AValue: Pointer; APercentile: Double; ByCol: Boolean): Double;
var
  MeasureCell: PfcxMeasureCell absolute AValue;
  AFindIndex: Integer;
  ABaseIndexTotal, ABaseIndexInLevel, ASecondIndexTotal, AIndexInSecondLevel: integer;
  ABaseLevel, ASecondLevel: TfcxSmallCount;
  i, APercentileIndex: Integer;
begin
  with FMeasure.FMeasuresContainer do
  begin
    if (FMeasure.FCellHighlights = nil) then
      CalcMaxMins(MeasureCell.MeasureIndex);
    APercentileIndex := -1;
    for i := 0 to FMeasure.FPercentilesCount - 1 do
    begin
      if FMeasure.FPercentiles[i] = APercentile then
      begin
        APercentileIndex := i;
        Break;
      end;
    end;
    if APercentileIndex = - 1 then
    begin
      CalcPercentile(MeasureCell.MeasureIndex, APercentile);
      APercentileIndex := FMeasure.FPercentilesCount - 1;
    end;

    ASecondLevel := FSecondContainer.FLevelOf[MeasureCell.SecondIndex];
    ABaseLevel := FBaseContainer.FLevelOf[MeasureCell.BaseIndex];
    if not ByCol xor FBaseAxisIsX then
    begin
      if ASecondLevel = -1 then
        Result := MeasureCell^.Value
      else
      begin
        ABaseIndexInLevel := FBaseContainer.FIndexInLevel[MeasureCell.BaseIndex];
        ASecondIndexTotal := FSecondContainer.FIndexOfParent[MeasureCell.SecondIndex];
        AIndexInSecondLevel := FSecondContainer.FIndexInLevel[ASecondIndexTotal];
        AFindIndex := AIndexInSecondLevel;
        if not (FLevelsProp[ABaseLevel][ASecondLevel-1][ABaseIndexInLevel].Simple or FMeasureFields[FMeasure.Index].FMeasurePropInLevelsListArray[ABaseLevel][ASecondLevel-1].CreateAllCells) then
          FindByIndex(ABaseLevel, ASecondLevel-1, ABaseIndexInLevel, AFindIndex, AFindIndex);
        Result := PfcxArrayMeasureCellHighlightsArray(FMeasure.FCellHighlights[ABaseLevel][ASecondLevel-1])[ABaseIndexInLevel][AFindIndex].PercentilesSecond[APercentileIndex];
      end
    end
    else
    begin
      ABaseIndexTotal := FBaseContainer.FIndexOfParent[MeasureCell.BaseIndex];
      if ABaseIndexTotal = -1 then
        Result := MeasureCell^.Value
      else
      begin
        ABaseIndexInLevel := FBaseContainer.FIndexInLevel[ABaseIndexTotal];
        AIndexInSecondLevel := FSecondContainer.FIndexInLevel[MeasureCell.SecondIndex];
        AFindIndex := AIndexInSecondLevel;
        if not (FLevelsProp[ABaseLevel-1][ASecondLevel][ABaseIndexInLevel].Simple or FMeasureFields[FMeasure.Index].FMeasurePropInLevelsListArray[ABaseLevel-1][ASecondLevel].CreateAllCells) then
          FindByIndex(ABaseLevel-1, ASecondLevel, ABaseIndexInLevel, AFindIndex, AFindIndex);
        Result := PfcxArrayMeasureCellHighlightsArray(FMeasure.FCellHighlights[ABaseLevel-1][ASecondLevel])[ABaseIndexInLevel][AFindIndex].PercentilesBase[APercentileIndex];
      end
    end;
  end;
end;

function TfcxCustomHighlights.RequestValue(AValue: Pointer): Double;
var
  MeasureCell: PfcxMeasureCell absolute AValue;
begin
  Result := VarToDouble(MeasureCell^.Value);
end;

{ TfcxCommonUVSliceCubeField }

function TfcxCommonUVSliceCubeField.GetDataType: TfcxDataType;
begin
  Result := TfcxCommonUVField(FCubeField).DataType
end;

function TfcxCommonUVSliceCubeField.GetSplitManager: TfcxSplitManager;
begin
  Result := TfcxCommonUVField(FCubeField).UniqueValues.SplitManager;
end;

function TfcxCommonUVSliceCubeField.GetUniqueValues: TfcxBaseUniqueValues;
begin
  Result := TfcxCommonUVField(FCubeField).UniqueValues;
end;

function TfcxCommonUVSliceCubeField.GetUVCount: integer;
begin
  Result := TfcxCommonUVField(FCubeField).UniqueValues.Count;
end;

{ TfcxSliceTimeStat }

procedure TfcxSliceTimeStat.Clear;
begin
  XBuildTime := 0;
  YBuildTime := 0;
  DataBuildTime := 0;
end;

{ TfcxTopNProcessor }

constructor TfcxTopNProcessor.Create(AAxisField: TfcxAxisField);
begin
// ACount = 0 if not top-N dim
  FRankList := Nil;
  FRankListCount := 0;
  FActive := False;
  FCount := 0;
  FAxisField := AAxisField;
  FList := Nil;
  FTopCount := 0;
  FTopType := ttMax;
  FMeasure := Nil;
  FCreateOthers := True;
{
  if FCount > 0 then
  begin
    GetMem(FList, SizeOf(_fcxByteArray) * (FCount shr 3 + 1));
    FillChar(FList^, SizeOf(_fcxByteArray) * (FCount shr 3 + 1), 0);
  end;
}
end;

destructor TfcxTopNProcessor.Destroy;
begin
  if FActive and Assigned(FAxisField.FSliceField.FSlice.FTopNs) then
    Active := False;
  FreeMem(FRankList);
  FRankList := Nil;
  FRankListCount := 0;
  FreeMem(FList);
  FList := Nil;
  FCount := 0;
  inherited;
end;

function TfcxTopNProcessor.GetAsString: String;
const
  sTopType: array[TfcxTopType] of String = (
 { ttMax } 'sTopMax',
 { ttMin } 'sTopMin'
  );
begin
  if CreateOthers then
    Result := Format('%s %d %s / %s (%s)', [fcxResources.Get(sTopType[TopType]), TopCount, Measure.Caption, AxisField.Caption, fcxResources.Get('sCreateOthers')])
  else
    Result := Format('%s %d %s / %s', [fcxResources.Get(sTopType[TopType]), TopCount, Measure.Caption, AxisField.Caption]);
end;

function TfcxTopNProcessor.GetIsOther(ARecIndex: Integer): Boolean;
begin
  Result := (FCount > 0) and ((FList[ARecIndex shr 3] and BitmapSet[ARecIndex mod 8]) <> 0)
end;

procedure TfcxTopNProcessor.LoadFromXML(AItem: TfcxXMLItem);
var
  AMeasureIndex: integer;
begin
  AMeasureIndex := FAxisField.FSliceField.FSlice.FMeasuresContainer.MeasureFields.IndexByName[AItem.Prop['measure']];
  if AMeasureIndex >= 0 then
  begin
    FMeasure := FAxisField.FSliceField.FSlice.FMeasuresContainer.MeasureFields[AMeasureIndex];
    FTopType := TfcxTopType(GetEnumValue(TypeInfo(TfcxTopType), AItem.Prop['toptype']));
    FTopCount := AItem.IntProp['topcount'];
    FCreateOthers := AItem.BoolProp['createother'];
    Active := True;
  end
end;

procedure TfcxTopNProcessor.Prepare;
begin
  if FActive then
    SetCount(FAxisField.FSliceField.FSlice.FFilterManager.GoodRecCount)
  else
    SetCount(0);
  FreeMem(FRankList);
  FRankList := Nil;
  FRankListCount := 0;
end;

procedure TfcxTopNProcessor.SaveToXML(AItem: TfcxXMLItem);
begin
  AItem.Name := 'topnprocessor';
  AItem.Prop['dimension'] := AxisField.Name;
  AItem.Prop['measure'] := Measure.Name;
  AItem.IntProp['topcount'] := TopCount;
  AItem.BoolProp['createother'] := CreateOthers;
  AItem.Prop['toptype'] := GetEnumName(TypeInfo(TfcxTopType), Ord(TopType));
end;

procedure TfcxTopNProcessor.SetActive(const Value: Boolean);
begin
  if Active <> Value then
  begin
    if not Assigned(FMeasure) then
      Exit;
    FAxisField.FSliceField.FSlice.StartChange;
    if Value then
    begin
      if not CreateOthers or FAxisField.FSliceField.FSlice.FTopNs.FAsFilter then
        FAxisField.FSliceField.FSlice.FTopNs.ClearTopN
      else
        FAxisField.FSliceField.FSlice.FTopNs.ClearTopNInAxis(TfcxAxisContainer(FAxisField.FOwner.FContainer));
      inc(FAxisField.FSliceField.FSlice.FTopNs.FActiveCount)
    end
    else
      dec(FAxisField.FSliceField.FSlice.FTopNs.FActiveCount);
    FActive := Value;
    FAxisField.FSliceField.FSlice.FTopNs.FAsFilter := not CreateOthers;
    if FAxisField.FSliceField.FSlice.FTopNs.FAsFilter then
      FAxisField.FSliceField.FSlice.FTopNs.FFilterTopN := Self
    else
      FAxisField.FSliceField.FSlice.FTopNs.FFilterTopN := nil;
    FAxisField.FSliceField.FSlice.StopChange([chs_TopNChanged]);
  end;
end;

procedure TfcxTopNProcessor.SetCount(ACount: Integer);
begin
  FreeMem(FList);
  FList := Nil;
  FCount := ACount;
  if FActive and (FCount > 0) then
  begin
    GetMem(FList, SizeOf(_fcxByteArray) * (FCount shr 3 + 1));
    FillChar(FList^, SizeOf(_fcxByteArray) * (FCount shr 3 + 1), 0);
  end;
end;

procedure TfcxTopNProcessor.SetCreateOthers(const Value: Boolean);
begin
  if FCreateOthers <> Value then
  begin
    FAxisField.FSliceField.FSlice.StartChange;
    if not Value then
      FAxisField.FSliceField.FSlice.FTopNs.ClearTopN;
    FCreateOthers := Value;
    FAxisField.FSliceField.FSlice.StopChange([chs_TopNChanged]);
  end
end;

procedure TfcxTopNProcessor.SetIsOther(ARecIndex: Integer;
  const Value: Boolean);
begin
  if FCount > ARecIndex then
    if Value then
    begin
      if FList[ARecIndex shr 3] <> (FList[ARecIndex shr 3] or BitmapSet[ARecIndex mod 8]) then
      begin
        dec(FAxisField.FSliceField.FSlice.FTopNs.FGoodRecCount);
        FList[ARecIndex shr 3] := FList[ARecIndex shr 3] or BitmapSet[ARecIndex mod 8]
      end
    end
    else
      if FList[ARecIndex shr 3] <> (FList[ARecIndex shr 3] and BitmapUnSet[ARecIndex mod 8]) then
      begin
        inc(FAxisField.FSliceField.FSlice.FTopNs.FGoodRecCount);
        FList[ARecIndex shr 3] := FList[ARecIndex shr 3] and BitmapUnSet[ARecIndex mod 8];
      end
end;

procedure TfcxTopNProcessor.SetMeasure(const Value: TfcxMeasureField);
begin
  if Measure <> Value then
  begin
    FAxisField.FSliceField.FSlice.StartChange;
    FMeasure := Value;
    FAxisField.FSliceField.FSlice.StopChange([chs_TopNChanged]);
  end
end;

procedure TfcxTopNProcessor.SetTopCount(const Value: integer);
begin
  if TopCount <> Value then
  begin
    FAxisField.FSliceField.FSlice.StartChange;
    FTopCount := Value;
    FAxisField.FSliceField.FSlice.StopChange([chs_TopNChanged]);
  end;
end;

procedure TfcxTopNProcessor.SetTopType(const Value: TfcxTopType);
begin
  if TopType <> Value then
  begin
    FAxisField.FSliceField.FSlice.StartChange;
    FTopType := Value;
    FAxisField.FSliceField.FSlice.StopChange([chs_TopNChanged]);
  end;
end;

{ TfcxTopNs }

function TfcxTopNs.BuildTree: TfcxTree;
var
  I: integer;
  Node: PfcxTreeNode;
begin
  Result := TfcxTree.Create;
//  if ActiveCount < 1 then
//    Exit;
  for I := 0 to FSlice.XAxisContainer.Fields.Count - 1 do
    if Assigned(FSlice.XAxisContainer.Fields[i].TopNProcessor.Measure) then
//    if FSlice.XAxisContainer.Fields[i].TopNProcessor.Active then
    begin
      Node := Result.AddChild(nil);
      Node^.Data := FSlice.XAxisContainer.Fields[i].TopNProcessor;
    end;
  for i := 0 to FSlice.YAxisContainer.Fields.Count - 1 do
    if Assigned(FSlice.YAxisContainer.Fields[i].TopNProcessor.Measure) then
//    if FSlice.YAxisContainer.Fields[i].TopNProcessor.Active then
    begin
      Node := Result.AddChild(nil);
      Node^.Data := FSlice.YAxisContainer.Fields[i].TopNProcessor;
    end;
end;

procedure TfcxTopNs.CalcTopInBaseAxis(ABaseLevel: TfcxSmallCount; ATopNProcessor: TfcxTopNProcessor);
var
  AAxisTreeLevel: ^_fcxAxisTreeLevel;
  AWorkArray: PfcxArrPointerArray;

  procedure CalcTopRankInternal;
  var
    i1, AIndex: integer;
    AParentIndex: integer;
    ASortArray: PfcxIntegerArray;
    ACount, ACapacity: integer;
    AMeasureValue: PfcxMeasureValue;

    function Find(AValue: Variant; var AIndex: Integer): Boolean;
    var
      ALeft, ARight, AMiddle: Integer;
      AValue2: Variant;
    begin
      Result := False;
      ALeft := 0;
      ARight := ACount - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        AValue2 := PfcxMeasureValue(AWorkArray[ASortArray[AMiddle]][0]).Value;
        if AValue2 = AValue then
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end
        else
        if AValue2 > AValue then
          ARight := AMiddle - 1
        else
          ALeft := AMiddle + 1;
      end;
      AIndex := ALeft;
    end;

    procedure SetRankMax(ATopCount: integer);
    var
      ARank, j1: integer;
    begin
      ARank := 0;
      for j1 := ACount - 1 downto 0 do
      begin
        if j1 = (ACount - 1) then
          inc(ARank)
        else
          if PfcxMeasureValue(AWorkArray[ASortArray[j1]][0]).Value <>
             PfcxMeasureValue(AWorkArray[ASortArray[j1+1]][0]).Value then
            inc(ARank);
        if ARank > ATopCount then
          Exit;
        ATopNProcessor.FRankList[ASortArray[j1]] := ARank;
      end;
    end;

    procedure SetRankMin(ATopCount: integer);
    var
      ARank, j1: integer;
    begin
      ARank := 0;
      for j1 := 0 to ACount - 1 do
      begin
        if j1 = 0 then
          inc(ARank)
        else
          if PfcxMeasureValue(AWorkArray[ASortArray[j1]][0]).Value <>
             PfcxMeasureValue(AWorkArray[ASortArray[j1-1]][0]).Value then
            inc(ARank);
        if ARank > ATopCount then
          Exit;
        ATopNProcessor.FRankList[ASortArray[j1]] := ARank;
      end;
    end;

  begin
    ASortArray := nil;
    ACapacity := 0;
    if ACapacity < AAxisTreeLevel.Count then
      ACapacity := AAxisTreeLevel.Count;
    GetMem(ASortArray, ACapacity * SizeOf(Integer));
    ACount := 0;
    AParentIndex := -2;
    for i1 := 0 to AAxisTreeLevel.Count - 1 do
    begin
      AMeasureValue := AWorkArray[i1][0];
      if AMeasureValue <> nil then
      begin
        if (AParentIndex = -2) then
          AParentIndex := AAxisTreeLevel.Nodes[i1].IndParent
        else
        if  (AAxisTreeLevel.Nodes[i1].IndParent <> AParentIndex) then
        begin
          if ATopNProcessor.FTopType = ttMax then
            SetRankMax(ATopNProcessor.FTopCount)
          else
            SetRankMin(ATopNProcessor.FTopCount);
          AParentIndex := AAxisTreeLevel.Nodes[i1].IndParent;
          ACount := 0;
        end;

        Find(AMeasureValue.Value, AIndex);
        Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
        ASortArray[AIndex] := i1;
        ACount := ACount + 1;
      end;
    end;
    if ACount > 0 then
      if ATopNProcessor.FTopType = ttMax then
        SetRankMax(ATopNProcessor.FTopCount)
      else
        SetRankMin(ATopNProcessor.FTopCount);

    FreeMem(ASortArray);
  end;

begin
  AAxisTreeLevel := @(FSlice.FMeasuresContainer.FBaseContainer.FAxisTree[ABaseLevel]);
  AWorkArray := PfcxArrPointerArray(ATopNProcessor.FMeasure.FMeasureCells[ABaseLevel][-1]);
  ATopNProcessor.FRankListCount := AAxisTreeLevel.Count;
  GetMem(ATopNProcessor.FRankList, ATopNProcessor.FRankListCount * SizeOf(Integer));
  FillChar(ATopNProcessor.FRankList^, ATopNProcessor.FRankListCount * SizeOf(Integer), 0);
  CalcTopRankInternal;
end;

procedure TfcxTopNs.CalcTopInSecondAxis(ASecondLevel: TfcxSmallCount;
  ATopNProcessor: TfcxTopNProcessor);
var
  AAxisTreeLevel: ^_fcxAxisTreeLevel;
  AWorkArray: PfcxArrPointerArray;
  ALevelProp: ^_fcxLevelInCellList;
  ACountInSecondLevel: integer;

  procedure CalcTopRankInternal;
  var
    j1, AIndex: integer;
    AParentIndex: integer;
    ASortArray: PfcxIntegerArray;
    ACount, ACapacity: integer;

    function Find(AValue: Variant; var AIndex: Integer): Boolean;
    var
      ALeft, ARight, AMiddle: Integer;
      AValue2: Variant;
    begin
      Result := False;
      ALeft := 0;
      ARight := ACount - 1;
      while ALeft <= ARight do
      begin
        AMiddle := (ALeft + ARight) shr 1; // middle
        AValue2 := PfcxMeasureValue(AWorkArray[0][ASortArray[AMiddle]]).Value;
        if AValue2 = AValue then
        begin
          Result := True;
          // Found
          AIndex := AMiddle;
          Exit;
        end
        else
        if AValue2 > AValue then
          ARight := AMiddle - 1
        else
          ALeft := AMiddle + 1;
      end;
      AIndex := ALeft;
    end;

    procedure SetRankMax(ATopCount: integer);
    var
      ARank, j1: integer;
    begin
      ARank := 0;
      for j1 := ACount - 1 downto 0 do
      begin
        if j1 = (ACount - 1) then
          inc(ARank)
        else
          if PfcxMeasureValue(AWorkArray[0][ASortArray[j1]]).Value <>
             PfcxMeasureValue(AWorkArray[0][ASortArray[j1 + 1]]).Value then
            inc(ARank);
        if ARank > ATopCount then
          Exit;
        ATopNProcessor.FRankList[ASortArray[j1]] := ARank;
      end;
    end;

    procedure SetRankMin(ATopCount: integer);
    var
      ARank, j1: integer;
    begin
      ARank := 0;
      for j1 := 0 to ACount - 1 do
      begin
        if j1 = 0 then
          inc(ARank)
        else
          if PfcxMeasureValue(AWorkArray[0][ASortArray[j1]]).Value <>
             PfcxMeasureValue(AWorkArray[0][ASortArray[j1 - 1]]).Value then
            inc(ARank);
        if ARank > ATopCount then
          Exit;
        ATopNProcessor.FRankList[ASortArray[j1]] := ARank;
      end;
    end;

  begin
    ASortArray := nil;
    ACapacity := 0;

    if ACapacity < ACountInSecondLevel then
      ACapacity := ACountInSecondLevel;
    GetMem(ASortArray, ACapacity * SizeOf(Integer));
    ACount := 0;
    AParentIndex := -2;
    if ALevelProp.Simple or FSlice.FMeasuresContainer.FMeasureFields[ATopNProcessor.FMeasure.Index].FMeasurePropInLevelsListArray[-1][ASecondLevel].CreateAllCells then
    begin
      for j1 := 0 to ACountInSecondLevel - 1 do
      begin
        if AWorkArray[0][j1] <> nil then
        begin
          if (AParentIndex = -2) then
            AParentIndex := AAxisTreeLevel.Nodes[j1].IndParent
          else
          if (AAxisTreeLevel.Nodes[j1].IndParent <> AParentIndex) then
          begin
            if ATopNProcessor.FTopType = ttMax then
              SetRankMax(ATopNProcessor.FTopCount)
            else
              SetRankMin(ATopNProcessor.FTopCount);
            AParentIndex := AAxisTreeLevel.Nodes[j1].IndParent;
            ACount := 0;
          end;
          Find(PfcxMeasureValue(AWorkArray[0][j1]).Value, AIndex);
          Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
          ASortArray[AIndex] := j1;
          ACount := ACount + 1;
        end
      end
    end
    else
    begin
      for j1 := 0 to ACountInSecondLevel - 1 do
      begin
        if (AParentIndex = -2) then
          AParentIndex := AAxisTreeLevel.Nodes[ALevelProp.IndexList[j1]].IndParent
        else
        if  (AAxisTreeLevel.Nodes[ALevelProp.IndexList[j1]].IndParent <> AParentIndex) then
        begin
          if ATopNProcessor.FTopType = ttMax then
            SetRankMax(ATopNProcessor.FTopCount)
          else
            SetRankMin(ATopNProcessor.FTopCount);
          AParentIndex := AAxisTreeLevel.Nodes[ALevelProp.IndexList[j1]].IndParent;
          ACount := 0;
        end;
        Find(PfcxMeasureValue(AWorkArray[0][j1]).Value, AIndex);
        Move(ASortArray[AIndex], ASortArray[AIndex + 1], (ACount - AIndex) * SizeOf(integer));
        ASortArray[AIndex] := j1;
        ACount := ACount + 1;
      end
    end;
    if ACount > 0 then
      if ATopNProcessor.FTopType = ttMax then
        SetRankMax(ATopNProcessor.FTopCount)
      else
        SetRankMin(ATopNProcessor.FTopCount);
    FreeMem(ASortArray);
  end;
begin
  AAxisTreeLevel := @(FSlice.FMeasuresContainer.FSecondContainer.FAxisTree[ASecondLevel]);
  AWorkArray := PfcxArrPointerArray(ATopNProcessor.FMeasure.FMeasureCells[-1][ASecondLevel]);
  ALevelProp := @(FSlice.FMeasuresContainer.FLevelsProp[-1][ASecondLevel][0]);
  if ALevelProp.Simple or FSlice.FMeasuresContainer.FMeasureFields[ATopNProcessor.FMeasure.Index].FMeasurePropInLevelsListArray[-1][ASecondLevel].CreateAllCells then
    ACountInSecondLevel := AAxisTreeLevel.Count
  else
    ACountInSecondLevel := ALevelProp.Count;
  ATopNProcessor.FRankListCount := AAxisTreeLevel.Count;
  GetMem(ATopNProcessor.FRankList, ATopNProcessor.FRankListCount * SizeOf(Integer));
  FillChar(ATopNProcessor.FRankList^, ATopNProcessor.FRankListCount * SizeOf(Integer), 0);
  CalcTopRankInternal;
end;

procedure TfcxTopNs.Calculate;
  procedure CalculateAxis(AAxisContainer: TfcxAxisContainer);
  var
    i, j, k: integer;
  begin
    for i := 0 to AAxisContainer.Fields.Count - 1 do
    begin
      if AAxisContainer.Fields[i].TopNProcessor.Active then
      begin
// All in others
        if (AAxisContainer.Fields[i].TopNProcessor.FCount > 0) then
          FillChar(AAxisContainer.Fields[i].TopNProcessor.FList^, SizeOf(_fcxByteArray) * (AAxisContainer.Fields[i].TopNProcessor.FCount shr 3 + 1), 255);
// calc top rank
        if AAxisContainer = FSlice.MeasuresContainer.FBaseContainer then
          CalcTopInBaseAxis(i, AAxisContainer.Fields[i].TopNProcessor)
        else
          CalcTopInSecondAxis(i, AAxisContainer.Fields[i].TopNProcessor);
// set 0 for not Others records
        for j := 0 to AAxisContainer.Fields[i].TopNProcessor.FRankListCount - 1 do
          if AAxisContainer.Fields[i].TopNProcessor.FRankList[j] > 0 then
          begin
            if AAxisContainer = FSlice.MeasuresContainer.FBaseContainer then
              for k := 0 to FSlice.MeasuresContainer.DetailRecordsCount[i, -1, j, 0] - 1 do
                AAxisContainer.Fields[i].TopNProcessor.IsOther[FSlice.FilterManager.GoodIndex[FSlice.MeasuresContainer.DetailRecords[i, -1, j, 0, k]]] := False
            else
              for k := 0 to FSlice.MeasuresContainer.DetailRecordsCount[-1, i, 0, j] - 1 do
                AAxisContainer.Fields[i].TopNProcessor.IsOther[FSlice.FilterManager.GoodIndex[FSlice.MeasuresContainer.DetailRecords[-1, i, 0, j, k]]] := False
          end;
      end;
    end;
  end;
var
  i, j: integer;
begin
  FStep := 1;
  CalculateAxis(FSlice.XAxisContainer);
  CalculateAxis(FSlice.YAxisContainer);
  if FAsFilter then
  begin
    GetMem(FGoodRecIndex, FGoodRecCount * SizeOf(Integer));
    GetMem(FGoodRecIndexReverse, FFilterTopN.FCount * SizeOf(Integer));
    j := 0;
    for i := 0 to FFilterTopN.FCount - 1 do
    begin
      if not FFilterTopN.IsOther[i] then
      begin
        FGoodRecIndex[j] := i;
        FGoodRecIndexReverse[i] := j;
        inc(j);
      end
      else
        FGoodRecIndexReverse[i] := -1;
    end;
  end;
end;

procedure TfcxTopNs.ClearTopN;
var
  i: integer;
begin
  if FActiveCount > 0 then
  begin
    FSlice.StartChange;
    FStep := 0;
    for i := 0 to FSlice.XAxisContainer.Fields.Count - 1 do
      FSlice.XAxisContainer.Fields[i].TopNProcessor.Active := False;
    for i := 0 to FSlice.YAxisContainer.Fields.Count - 1 do
      FSlice.YAxisContainer.Fields[i].TopNProcessor.Active := False;
    FAsFilter := False;
    FFilterTopN := Nil;
    FActiveCount := 0;
    FreeMem(FGoodRecIndex);
    FGoodRecIndex := Nil;
    FGoodRecCount := 0;
    FreeMem(FGoodRecIndexReverse);
    FGoodRecIndexReverse := Nil;
    FSlice.StopChange([chs_TopNChanged]);
  end;
end;

procedure TfcxTopNs.ClearTopNInAxis(AAxis: TfcxAxisContainer);
var
  i: integer;
begin
  if FActiveCount > 0 then
  begin
    FSlice.StartChange;
    FStep := 0;
    for i := 0 to AAxis.Fields.Count - 1 do
      AAxis.Fields[i].TopNProcessor.Active := False;
    FSlice.StopChange([chs_TopNChanged]);
  end;
end;

constructor TfcxTopNs.Create(ASlice: TfcxSlice);
begin
  FSlice := ASlice;
  FAsFilter := False;
  FFilterTopN := Nil;
  FActiveCount := 0;
  FStep := 0;
  FGoodRecIndex := Nil;
  FGoodRecCount := 0;
  FGoodRecIndexReverse := Nil;
end;

destructor TfcxTopNs.Destroy;
begin
  FreeMem(FGoodRecIndex);
  FGoodRecIndex := Nil;
  FGoodRecCount := 0;
  FreeMem(FGoodRecIndexReverse);
  FGoodRecIndexReverse := Nil;
  inherited;
end;

function TfcxTopNs.GetGoodIndex(AGoodTopNRec: integer): integer;
begin
  if (FStep = 1) and FAsFilter and (AGoodTopNRec > -1) then
    Result := FGoodRecIndex[AGoodTopNRec]
  else
    Result := AGoodTopNRec
end;

function TfcxTopNs.GetGoodTopNRec(AGoodIndex: integer): integer;
begin
  if (FStep = 1) and FAsFilter and (AGoodIndex > -1) then
    Result := FGoodRecIndexReverse[AGoodIndex]
  else
    Result := AGoodIndex
end;

function TfcxTopNs.GetNeedReSortXAxis: Boolean;
var
  i: integer;
begin
  Result := False;
  for i := 0 to FSlice.FXAxisContainer.Fields.Count - 1 do
    if FSlice.FXAxisContainer.FAxisSourceTree.FSourceTreeLevels[i].FTopNCheck then
    begin
      Result := True;
      Exit;
    end
end;

function TfcxTopNs.GetNeedReSortYAxis: Boolean;
var
  i: integer;
begin
  Result := False;
  for i := 0 to FSlice.FYAxisContainer.Fields.Count - 1 do
    if FSlice.FYAxisContainer.FAxisSourceTree.FSourceTreeLevels[i].FTopNCheck then
    begin
      Result := True;
      Exit;
    end
end;

function TfcxTopNs.GetNeedSortXAxis: Boolean;
var
  i: integer;
begin
  Result := False;
  if not FAsFilter and (FActiveCount > 0) then
    for i := 0 to FSlice.XAxisContainer.Fields.Count - 1 do
      if FSlice.XAxisContainer.Fields[i].TopNProcessor.Active then
      begin
        Result := True;
        Exit;
      end;
end;

function TfcxTopNs.GetNeedSortYAxis: Boolean;
var
  i: integer;
begin
  Result := False;
  if not FAsFilter and (FActiveCount > 0) then
    for i := 0 to FSlice.YAxisContainer.Fields.Count - 1 do
      if FSlice.YAxisContainer.Fields[i].TopNProcessor.Active then
      begin
        Result := True;
        Exit;
      end;
end;

procedure TfcxTopNs.LoadFromXML(AItem: TfcxXMLItem);
var
  i, ADimIndex: Integer;
begin
  for i := 0 to AItem.Count - 1 do
  begin
    ADimIndex := FSlice.FXAxisContainer.Fields.IndexByName[AItem[i].Prop['dimension']];
    if ADimIndex >= 0 then
      FSlice.FXAxisContainer.Fields[ADimIndex].FTopNProcessor.LoadFromXML(AItem[i])
    else
    begin
      ADimIndex := FSlice.FYAxisContainer.Fields.IndexByName[AItem[i].Prop['dimension']];
      if ADimIndex >= 0 then
        FSlice.FYAxisContainer.Fields[ADimIndex].FTopNProcessor.LoadFromXML(AItem[i]);
    end;
  end;
end;

procedure TfcxTopNs.MeasureDeleting(AMeasure: TfcxMeasureField);
var
  i: integer;
  ADeleted: Boolean;
begin
  ADeleted := False;
  FSlice.StartChange;
  for i := 0 to FSlice.XAxisContainer.Fields.Count - 1 do
    if FSlice.XAxisContainer.Fields[i].TopNProcessor.FMeasure = AMeasure then
    begin
      if FSlice.XAxisContainer.Fields[i].TopNProcessor.Active and not FSlice.XAxisContainer.Fields[i].TopNProcessor.FCreateOthers then
      begin
        FAsFilter := False;
        FFilterTopN := Nil;
      end;
      ADeleted := ADeleted or FSlice.XAxisContainer.Fields[i].TopNProcessor.Active;
      FSlice.XAxisContainer.Fields[i].TopNProcessor.Active := False;
      FSlice.XAxisContainer.Fields[i].TopNProcessor.Measure := Nil;
    end;
  for i := 0 to FSlice.YAxisContainer.Fields.Count - 1 do
    if FSlice.YAxisContainer.Fields[i].TopNProcessor.FMeasure = AMeasure then
    begin
      if FSlice.YAxisContainer.Fields[i].TopNProcessor.Active and not FSlice.YAxisContainer.Fields[i].TopNProcessor.FCreateOthers then
      begin
        FAsFilter := False;
        FFilterTopN := Nil;
      end;
      ADeleted := ADeleted or FSlice.YAxisContainer.Fields[i].TopNProcessor.Active;
      FSlice.YAxisContainer.Fields[i].TopNProcessor.Active := False;
      FSlice.YAxisContainer.Fields[i].TopNProcessor.Measure := Nil;
    end;
  if ADeleted then
    FSlice.StopChange([chs_TopNChanged])
  else
    FSlice.StopChange([])
end;

procedure TfcxTopNs.Prepare;
var
  i: integer;
begin
  for i := 0 to FSlice.XAxisContainer.Fields.Count - 1 do
    FSlice.XAxisContainer.Fields[i].TopNProcessor.Prepare;
  for i := 0 to FSlice.YAxisContainer.Fields.Count - 1 do
    FSlice.YAxisContainer.Fields[i].TopNProcessor.Prepare;
  FStep := 0;
  FreeMem(FGoodRecIndex);
  FGoodRecIndex := Nil;
  FGoodRecCount := 0;
  FreeMem(FGoodRecIndexReverse);
  FGoodRecIndexReverse := Nil;
end;

procedure TfcxTopNs.SaveToXML(AItem: TfcxXMLItem);
var
  I: Integer;
begin
  AItem.Name := 'topn';
  for i := 0 to FSlice.XAxisContainer.Fields.Count - 1 do
    if FSlice.XAxisContainer.Fields[i].TopNProcessor.Active then
      FSlice.XAxisContainer.Fields[i].TopNProcessor.SaveToXML(AItem.Add);
  for i := 0 to FSlice.YAxisContainer.Fields.Count - 1 do
    if FSlice.YAxisContainer.Fields[i].TopNProcessor.Active then
      FSlice.YAxisContainer.Fields[i].TopNProcessor.SaveToXML(AItem.Add);
end;

procedure TfcxTopNs.SetTopN(ADimension: TfcxAxisField;
  AMeasure: TfcxMeasureField; ATopType: TfcxTopType; ATopCount: Integer;
  ACreateOthers: Boolean);
begin
  FSlice.StartChange;
  if not ACreateOthers or FAsFilter then
    ClearTopN
  else
  if ACreateOthers then
    ClearTopNInAxis(TfcxAxisContainer(ADimension.FOwner.FContainer));
  ADimension.TopNProcessor.Measure := AMeasure;
  ADimension.TopNProcessor.TopType := ATopType;
  ADimension.TopNProcessor.TopCount := ATopCount;
  ADimension.TopNProcessor.CreateOthers := ACreateOthers;
  ADimension.TopNProcessor.Active := True;
  FAsFilter := not ACreateOthers;
  if FAsFilter then
    FFilterTopN := ADimension.TopNProcessor;
  FSlice.StopChange([chs_TopNChanged]);
end;

{ TCustomAxisTextBuilder }

function TCustomAxisTextBuilder.BuildText: TfcxString;
var
  i: integer;
begin
  FAxisContainer.TraverseAxis(0, FAxisContainer.VisibleLevelCount - 1, FStartCell, PlainTextProc);
// delete final #9
  for i := 0 to FPlainText.Count - 1 do
    FPlainText[i] := Copy(FPlainText[i], 1, length(FPlainText[i]) - 1);
  Result := FPlainText.DelimitedText;
end;

constructor TCustomAxisTextBuilder.Create(AxisContainer: TfcxAxisContainer; StartCell, StopCell: Integer; ACloneHeaders: Boolean);
begin
  inherited Create;
  FAxisContainer := AxisContainer;
  FStartCell := StartCell;
  FStopCell := StopCell;
  FPlainText := TfcxStringList.Create;
  FCloneHeaders := ACloneHeaders;
end;

destructor TCustomAxisTextBuilder.Destroy;
begin
  FPlainText.Free;
  inherited;
end;

{ TXAxisPlainTextBuilder }

function TXAxisPlainTextBuilder.PlainTextProc(Sender: TfcxAxisContainer; const ARec: TfcxSliceDrawHeader): Boolean;
var
  I, J: Integer;
  S: TfcxString;
begin
  Result := (ARec.TreeRect.Cell + FStartCell) >= FStopCell;
  if (ARec.TreeRect.Cell + FStartCell) > FStopCell then
    Exit;
  for I := ARec.TreeRect.Level to ARec.TreeRect.Level + ARec.TreeRect.SizeLevel - 1 do
  begin
    while FPlainText.Count <= I do
      FPlainText.Add('');
    if FCloneHeaders or (I = ARec.TreeRect.Level) then
      S := ARec.Text + #9
    else
      S := #9;
    for J := ARec.TreeRect.Cell + 1 to Min(FStopCell - FStartCell, ARec.TreeRect.Cell + ARec.TreeRect.SizeCell - 1) do
      S := S + #9;
    FPlainText[I] := FPlainText[I] + S;
  end;
end;

{ TYAxisPlainTextBuilder }

function TYAxisPlainTextBuilder.PlainTextProc(Sender: TfcxAxisContainer; const ARec: TfcxSliceDrawHeader): Boolean;
var
  I, J: Integer;
  S: TfcxString;
begin
  Result := (ARec.TreeRect.Cell + FStartCell) >= FStopCell;
  if (ARec.TreeRect.Cell + FStartCell) > FStopCell then
    Exit;
  for I := ARec.TreeRect.Cell to ARec.TreeRect.Cell + ARec.TreeRect.SizeCell - 1 do
  begin
    while FPlainText.Count <= I do
      FPlainText.Add('');
    if FCloneHeaders or (I = ARec.TreeRect.Cell) then
      S := ARec.Text + #9
    else
      S := #9;
    for J := ARec.TreeRect.Level + 1 to Min(FStopCell - FStartCell, ARec.TreeRect.Level + ARec.TreeRect.SizeLevel - 1) do
      S := S + #9;
    FPlainText[I] := S + FPlainText[I];
  end;
end;

{ TfcxChartsManager }

constructor TfcxChartsManager.Create(ASlice: TfcxSlice);
begin
  FSlice := ASlice;
end;

function TfcxChartsManager.GetChart(AIndex: Integer): IfcxChart;
var
  AChartsCount: Integer;
begin
  Result := Nil;
  AChartsCount := FSlice.ListnersManager.ListnerByInterfaceCount[IfcxChart];
  if AChartsCount > AIndex then
  begin
    Result := FSlice.ListnersManager.ListnerByInterface[AIndex, IfcxChart] as IfcxChart;
    Exit;
  end;
end;

function TfcxChartsManager.GetCount: Integer;
begin
  Result := FSlice.ListnersManager.ListnerByInterfaceCount[IfcxChart];
end;

function TfcxChartsManager.LoadFromXMLItem(AItem: TfcxXMLItem): Boolean;
var
  i, j: integer;
begin
  Result := False;
  if AItem.Name = 'charts' then
    if AItem.IntProp['version'] = 2 then
    begin
      if AItem.Count > Count then
        NeedChart(AItem.Count);
      for i := 0 to Min(AItem.Count, Count) - 1 do
        Chart[i].LoadFromXMLItem(AItem[i]);
      Result := True;
    end
    else
    begin
// ver 1
      j := 0;
      for i := 0 to AItem.Count - 1 do
        if copy(AItem[i].Name, 1, 5) = 'chart' then
        begin
          if j >= Count then
            NeedChart(j+1);
          if j < Count then
            Chart[j].LoadFromXMLItem(AItem[i])
          else
            Break;
          inc(j);
        end;
      Result := True;
    end;
end;

procedure TfcxChartsManager.NeedChart(ACount: Integer);
begin
  if Assigned(OnNeedChart) then
    OnNeedChart(Self, ACount);
end;

procedure TfcxChartsManager.SaveToXMLItem(AItem: TfcxXMLItem);
var
  i: integer;
begin
  AItem.Name := 'charts';
  AItem.IntProp['version'] := 2;
  AItem.Prop['timestamp'] := DateTimeToStr(Now);
  for i := 0 to Count - 1 do
    Chart[i].SaveToXMLItem(AItem.Add)
end;

{ TfcxCustomHighlight }

function TfcxCustomHighlight.AcceptValue(AValue: Variant): Boolean;
begin
  Result := False;
end;

procedure TfcxCustomHighlight.AssignTo(Dest: TPersistent);
begin
  if Dest is TfcxCustomHighlight then
  begin
    TfcxCustomHighlight(Dest).FApplyTo := FApplyTo;
    TfcxCustomHighlight(Dest).DoChange;
  end
  else
    inherited;
end;

function TfcxCustomHighlight.AsString: String;
begin
  Result := '';
end;

procedure TfcxCustomHighlight.BeginUpdate;
begin
  inc(FUpdateCount);
end;

constructor TfcxCustomHighlight.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  FApplyTo := [hatCells];
  FUpdateCount := 0;
end;

function TfcxCustomHighlight.CustomDrawn: Boolean;
begin
  Result := False;
end;

procedure TfcxCustomHighlight.DoChange;
begin
  if FUpdateCount <> 0 then
    Exit;

  Changed(False);
  if Assigned(OnChange) then
    OnChange(Self);
end;

procedure TfcxCustomHighlight.EndUpdate;
begin
  dec(FUpdateCount);
  if FUpdateCount = 0 then
    DoChange;
end;

class function TfcxCustomHighlight.GetCaptionForEditor: String;
begin
  Result := '<none>';
end;

function TfcxCustomHighlight.HideValue: Boolean;
begin
  Result := False;
end;

procedure TfcxCustomHighlight.LoadFromXML(AItem: TfcxXMLItem);
begin
  ApplyTo := TfcxHighlightApplyTo(Byte(AItem.IntProp['ApplyTo']));
end;

procedure TfcxCustomHighlight.LoadRange(ARange: TfcxLoadedRange);
begin
end;

procedure TfcxCustomHighlight.LoadRangeXML(AItem: TfcxXMLItem);
begin
end;

procedure TfcxCustomHighlight.SaveToXML(AItem: TfcxXMLItem);
begin
  AItem.Name := ClassName;
  AItem.IntProp['ApplyTo'] := Byte(ApplyTo);
end;

procedure TfcxCustomHighlight.SetApplyTo(const Value: TfcxHighlightApplyTo);
begin
  if FApplyTo <> Value then
  begin
    FApplyTo := Value;
    DoChange;
  end;
end;

procedure TfcxCustomHighlights.SaveToXML(AItem: TfcxXMLItem);
var
  I: Integer;
begin
  AItem.Name := 'HIGHLIGHTS';
  for I := 0 to Count - 1 do
    Items[I].SaveToXML(AItem.Add);
end;

procedure TfcxCustomHighlights.Update(Item: TCollectionItem);
begin
  inherited;
  if Assigned(OnChange) then
    OnChange(Self);
end;

{ TfcxRegisteredHighlights }

function TfcxRegisteredHighlights.Add(AHighlightClass: TfcxCustomHighlightClass; AFrameClass: TClass): PfcxRegisteredHighlight;
begin
  New(Result);
  Result^.ClassType := AHighlightClass;
  Result^.FrameType := AFrameClass;
  inherited Add(Result);
end;

function TfcxRegisteredHighlights.FindByName(AClassName: String): PfcxRegisteredHighlight;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    if Items[I].ClassType.ClassName = AClassName then
    begin
      Result := Items[I];
      Exit;
    end;
  Result := nil;
end;

function TfcxRegisteredHighlights.FindEditorClass(AHighlightClass: TfcxCustomHighlightClass): TClass;
var
  Rec: PfcxRegisteredHighlight;
begin
  Rec := FindItem(AHighlightClass);
  if Assigned(Rec) then
    Result := Rec^.FrameType
  else
    Result := nil;
end;

function TfcxRegisteredHighlights.FindHighlightClass(AClassName: String): TfcxCustomHighlightClass;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    if Items[I].ClassType.ClassName = AClassName then
    begin
      Result := Items[I].ClassType;
      Exit;
    end;
  Result := nil;
end;

function TfcxRegisteredHighlights.FindItem(AHighlightClass: TfcxCustomHighlightClass): PfcxRegisteredHighlight;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    if Items[I].ClassType = AHighlightClass then
    begin
      Result := Items[I];
      Exit;
    end;
  Result := nil;
end;

function TfcxRegisteredHighlights.GetItem(AIndex: Integer): PfcxRegisteredHighlight;
begin
  Result := inherited Get(AIndex);
end;

procedure TfcxRegisteredHighlights.Notify(Ptr: Pointer; Action: TListNotification);
begin
  if Action = lnDeleted then
    Dispose(PfcxRegisteredHighlight(Ptr));
  inherited;
end;

procedure TfcxRegisteredHighlights.SetItem(AIndex: Integer; const Value: PfcxRegisteredHighlight);
begin
  inherited Put(AIndex, Value)
end;

{ TfcxDetailOrderedRecordSetProvider }

function TfcxDetailOrderedRecordSetProvider.CompareUVIndexes(AIndex1,
  AIndex2: integer): integer;
var
  AFieldIndex, AUVIndex1, AUVIndex2: integer;
begin
  Result := 0;
  for AFieldIndex := 0 to CubeFieldsInIndex.Count - 1 do
  begin
    AUVIndex1 := FSlice.Cube.SourceHolder.UniqueValueIndex[AIndex1, CubeFieldsInIndex.Items[AFieldIndex].CubeField];
    AUVIndex2 := FSlice.Cube.SourceHolder.UniqueValueIndex[AIndex2, CubeFieldsInIndex.Items[AFieldIndex].CubeField];
    if AUVIndex1 = AUVIndex2 then
      Result := 0
    else
    begin
      Result := fcxCompareHelperDirection[CubeFieldsInIndex.Items[AFieldIndex].SortDirection, AUVIndex1 > AUVIndex2];
      break;
    end;
  end;
end;

constructor TfcxDetailOrderedRecordSetProvider.Create(ASlice: TfcxSlice);
begin
  inherited Create;
  FPosition := Point(-1, -1);
  SetSlice(ASlice);
end;

procedure TfcxDetailOrderedRecordSetProvider.FillRecordIndex;
var
  i: integer;
begin
  for i := 0 to RecordCount - 1 do
    FRecordIndex[i] := FSlice.FMeasuresContainer.DetailRecords[FBaseLevel, FSecondLevel, FIndexInBaseLevel, FIndexInSecondLevel, i];
end;

function TfcxDetailOrderedRecordSetProvider.GetRecordCount: Integer;
begin
  if ValidProperties then
    Result := FSlice.FMeasuresContainer.DetailRecordsCount[FBaseLevel, FSecondLevel, FIndexInBaseLevel, FIndexInSecondLevel]
  else
    Result := 0;
end;

function TfcxDetailOrderedRecordSetProvider.GetRowIndexDefault(
  ARow: Integer): Integer;
begin
  Result := FSlice.FMeasuresContainer.DetailRecords[FBaseLevel, FSecondLevel, FIndexInBaseLevel, FIndexInSecondLevel, ARow]
end;

function TfcxDetailOrderedRecordSetProvider.GetText(ACol,
  ARowIndex: Integer): String;
begin
  if ValidProperties then
    Result := StringToControl(FSlice.FCube.SourceHolder.UniqueValueCaption[ARowIndex, Columns.VisItems[ACol].Field])
  else
    Result := '';
end;

procedure TfcxDetailOrderedRecordSetProvider.SetPosition(APosition: TPoint);
begin
  FPosition := APosition;
  if ValidProperties then
    if not FSlice.ColRowToDataCellPos(FPosition.X, FPosition.Y, FBaseLevel, FSecondLevel, FIndexInBaseLevel, FIndexInSecondLevel) then
      FPosition := Point(-1, -1);
  Update;
end;

procedure TfcxDetailOrderedRecordSetProvider.SetSlice(ASlice: TfcxSlice);
begin
  if FSlice <> ASlice then
  begin
    CubeFieldsInIndex.Clear;
    FPosition := Point(-1, -1);
  end;
  FSlice := ASlice;
  if Assigned(FSlice) then
    Columns.Update(FSlice.Cube)
  else
  begin
    Columns.Update(nil);
    FPosition := Point(-1, -1);
  end;
  Update;
end;

function TfcxDetailOrderedRecordSetProvider.ValidPosition: Boolean;
begin
  Result := (FPosition.X <> -1) and (FPosition.Y <> -1);
end;

function TfcxDetailOrderedRecordSetProvider.ValidProperties: Boolean;
begin
  Result := Assigned(FSlice) and Assigned(FSlice.FCube) and FSlice.FCube.Active and ValidPosition;
end;

initialization
  fcxRegisteredHighlights := TfcxRegisteredHighlights.Create;
finalization
  fcxRegisteredHighlights.Free;
end.
