{ ------------------------------------------------------------- }
{ Purpose : NxInspector 6                                       }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2015. All Rights Reserved.         }
{ ------------------------------------------------------------- }
{$I '..\NxSuite6.inc'}
unit NxInspector6;

interface

{$IFDEF REGIONS}{$REGION 'Uses'}{$ENDIF}
uses
  { Standard }
  Classes, Types, Messages, Windows, Graphics,
  { Controls }
  Controls, Forms, ImgList,
  { TypeInfo }
  TypInfo,
  { Next Standard 6 }
  NxClasses6, NxIntf6, NxTypes6, NxControls6, NxConsts6, NxEdit6,
  { NextInspector 6 }
  NxAssociates6, NxInspectorNode6;
{$IFDEF REGIONS}{$ENDREGION}{$ENDIF}

{$IFDEF REGIONS}{$REGION 'Constants'}{$ENDIF}
const
  { Sizes }
  szMarginWidth = 14;
  szMovingThreshold = 2;

  { Positions }
  posSplitter = 100;
  posSplitterMin = 40;
{$IFDEF REGIONS}{$ENDREGION}{$ENDIF}

type
{$IFDEF REGIONS}{$REGION 'Enums & Sets'}{$ENDIF}
  TNxInspectorGridLinesStyle = (gsSolid, gsDotted, gsInset, gsOutset);
  TNxInspectorOptions = set of (ioBoldNonDefault, ioCanSelectNone,
    ioEnterSelectNext, ioMouseMovingSelect, ioPreviewPopup, ioModifiedIndicators,
    ioSplitterMoving, ioEndEditOnUnfocus, ioAssignEditOnLast); // ioTopLevelOverflow
  TNxInspectorState = set of (isMouseDown, isSplitterMoving);
  TNxInspectorNodePart = (npAll, npCaption, npMargin, npValue, npValueText);
  TNxInspectorNodeParts = set of TNxInspectorNodePart;
{$IFDEF REGIONS}{$ENDREGION}{$ENDIF}

{$IFDEF REGIONS}{$REGION 'Records'}{$ENDIF}
  TNxPropInfo = record
    Instance: TObject;
    Value: Variant;
    Name: string;
    Kind: TTypeKind;
  end;
{$IFDEF REGIONS}{$ENDREGION}{$ENDIF}

{$IFDEF REGIONS}{$REGION 'Event declarations'}{$ENDIF}
  { Base Node Event }
  TNxInspectorNodeNotifyEvent = procedure (Sender: TObject;
    Node: TNxInspectorNode6) of object;

  { Trigger on Assigning InplaceEdit back to Node }
  TNxInspectorEditAssignEvent = procedure (Sender: TObject;
    Node: TNxInspectorNode6; var Text: WideString; var CanAssign: Boolean) of object;

  { Trigger on InplaceEditing OnChange }
  TNxInspectorEditingEvent = procedure (Sender: TObject; Text: WideString) of object;

  { Trigger before Editing start }
  TNxInspectorEditQueryEvent = procedure (Sender: TObject; Node: TNxInspectorNode6;
    var Text: WideString; var CanEdit: Boolean) of object;

  { Trigger before Node is selected }
  TNxInspectorSelectQueryEvent = procedure (Sender: TObject; Node: TNxInspectorNode6;
    var CanSelect: Boolean) of object;

  { Trigger when editing is started }
  TNxEditEnterEvent = procedure (Sender: TObject; Node: TNxInspectorNode6;
    InplaceEdit: INxInplaceEdit) of object;

  { Chance for creation of different InplaceEdit }
  TNxGetNodeInplaceEditEvent = procedure (Sender: TObject; Node: TNxInspectorNode6;
    var InplaceEdit: INxInplaceEdit) of object;

  TNxBeforeAddPropertyEvent = procedure (Sender: TObject; Info: TNxPropInfo;
    var NodeClass: TNxInspectorNodeClass; var Accept: Boolean) of object;
{$IFDEF REGIONS}{$ENDREGION}{$ENDIF}

  { TNxNodeHintWindow6 }

  TNxNodeHintWindow6 = class(THintWindow)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    function GetContentRect: TRect;
    procedure NCPaint(DC: HDC); override;
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure ActivateHint(Rect: TRect; const AHint: string); override;
  end;

  { TNxCustomInspector6 }

  TNxCustomInspector6 = class(TNxScrollControl6,
    INxNodeNotifyControl, INxInspectorControl, INxVersionControl)
  private
    { Fields }
    FActiveStyle: INxInspectorStyle;
    FAppearanceOptions: TNxAppearanceOptions;
    FAssociates: TNxAssociates6;
    FTextFitHint: THintWindow;
    FCaptionHotNode: TNxInspectorNode6;
    FCapturedView: INxInspectorNodeView;
    FDetailsButtonPressed: Boolean;
    FDetailsHotNode: TNxInspectorNode6;
    FEditingNode: TNxInspectorNode6;
    FEditorUpdating: Boolean;
    FFilter: WideString;
    FFirstIndex: Integer;
    FGlyphCollapse: TBitmap;
    FGlyphExpand: TBitmap;
    FGridColor: TColor;
    FGridLines: Boolean;
    FGridLinesStyle: TNxInspectorGridLinesStyle;
    FGridPadding: Integer;
    FHideTopLevel: Boolean;
    FHotNode: TNxInspectorNode6;
    FImagePosition: TNxNodeIconPosition;
    FImages: TCustomImageList;
    FInplaceEdit: INxInplaceEdit;
    FMarginWidth: Integer;
    FNodeHeight: Integer;
    FNodeHint: TNxNodeHintWindow6;
    FNodes: TNxInspectorNodes;
    FOnAfterEdit: TNxInspectorNodeNotifyEvent;
    FOnBeforeAddProperty: TNxBeforeAddPropertyEvent;
    FOnEditAssign: TNxInspectorEditAssignEvent;
    FOnEditEnter: TNxEditEnterEvent;
    FOnEditing: TNxInspectorEditingEvent;
    FOnEditQuery: TNxInspectorEditQueryEvent;
    FOnExpandChange: TNxInspectorNodeNotifyEvent;
    FOnGetInplaceEdit: TNxGetNodeInplaceEditEvent;
    FOnPropertyNodeAdded: TNxInspectorNodeNotifyEvent;
    FOnSelect: TNotifyEvent;
    FOnSelectQuery: TNxInspectorSelectQueryEvent;
    FOptions: TNxInspectorOptions;
    FPreciseScroling: Boolean;
    FPreciseScrolling: Boolean;
    FPressedNode: TNxInspectorNode6;
    FPrevSelectedNode: TNxInspectorNode6;
    FResizingNode: TNxInspectorNode6;
    FResizingReadyNode: TNxInspectorNode6;
    FSelectedNode: TNxInspectorNode6;
    FSelectionColor: TColor;
    FShowMargin: Boolean;
    FSpacing: Integer;
    FSplitterColor: TColor;
    FSplitterHot: Boolean;
    FSplitterMinPosition: Integer;
    FSplitterPosition: Integer;
    FState: TNxInspectorState;
    FStyle: TNxAppearanceStyle;
    FToggleStyle: TNxToggleButtonStyle;
    FTopLevelColor: TColor;
    FUpdateCount: Integer;
    FUpdateOptions: TNxUpdateOptions;
    FUseTopLevelColor: Boolean;
    FValueHotNode: TNxInspectorNode6;
    FMarginColor: TColor;
    FSelectionTextColor: Tcolor;
    FInplaceEditColor: TColor;
    FInactiveSelectionColor: TColor;
    FUserDefinedColorPalette: TNxUserDefinedColorPalette;
    function GetAssociates: TNxAssociates6;
    function GetControlName: string;
    function GetFiltered: Boolean;
    function GetFirstIndex: Integer;
    function GetGridSize: Integer;
    function GetInplaceEditActive: Boolean;
    function GetMarginIndent: Integer;
    function GetPinned(Index: Integer): TNxInspectorNode6;
    function GetPinnedCount: Integer;
    function GetPinnedHeight: Integer;
    function GetScrollableRect: TRect;
    function GetSplitterResizingRect: TRect;
    function GetValueWidth: Integer;
    function GetVersion: string;
    procedure SetAppearanceOptions(const Value: TNxAppearanceOptions);
    procedure SetCaptionHotNode(const Value: TNxInspectorNode6);
    procedure SetDetailsButtonPressed(const Value: Boolean);
    procedure SetDetailsHotNode(const Value: TNxInspectorNode6);
    procedure SetFilter(const Value: WideString);
    procedure SetGlyphCollapse(const Value: TBitmap);
    procedure SetGlyphExpand(const Value: TBitmap);
    procedure SetGridColor(const Value: TColor);
    procedure SetGridLines(const Value: Boolean);
    procedure SetGridLinesStyle(const Value: TNxInspectorGridLinesStyle);
    procedure SetGridPadding(const Value: Integer);
    procedure SetHideTopLevel(const Value: Boolean);
    procedure SetHotNode(const Value: TNxInspectorNode6);
    procedure SetImagePosition(const Value: TNxNodeIconPosition);
    procedure SetImages(const Value: TCustomImageList);
    procedure SetMarginWidth(const Value: Integer);
    procedure SetNodeHeight(const Value: Integer);
    procedure SetOptions(const Value: TNxInspectorOptions);
    procedure SetPreciseScrolling(const Value: Boolean);
    procedure SetResizingReadyNode(const Value: TNxInspectorNode6);
    procedure SetSelectedNode(const Value: TNxInspectorNode6);
    procedure SetSelectionColor(const Value: TColor);
    procedure SetShowMargin(const Value: Boolean);
    procedure SetSpacing(const Value: Integer);
    procedure SetSplitterColor(const Value: TColor);
    procedure SetSplitterHot(const Value: Boolean);
    procedure SetSplitterMinPosition(const Value: Integer);
    procedure SetSplitterPosition(const Value: Integer);
    procedure SetStyle(const Value: TNxAppearanceStyle);
    procedure SetToggleStyle(const Value: TNxToggleButtonStyle);
    procedure SetTopLevelColor(const Value: TColor);
    procedure SetUseTopLevelColor(const Value: Boolean);
    procedure SetValueHotNode(const Value: TNxInspectorNode6);
    procedure SetMarginColor(const Value: TColor);
    procedure SetSelectionTextColor(const Value: Tcolor);
    procedure SetInplaceEditColor(const Value: TColor);
    procedure SetInactiveSelectionColor(const Value: TColor);
    procedure SetUserDefinedColorPalette(
      const Value: TNxUserDefinedColorPalette);
  protected
    procedure CreateWnd; override;
    { Delphi Messages }
    procedure CMChildKey(var AMsg: TCMChildKey); message CM_CHILDKEY;
    procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED;
    procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure CMParentFontChanged(var Message: TMessage); message CM_PARENTFONTCHANGED;
    { Event Handlers }
    procedure DoAfterEdit(Node: TNxInspectorNode6); dynamic;
    procedure DoEditAssign(Node: TNxInspectorNode6; var Text: WideString; var CanAssign: Boolean); dynamic;
    procedure DoEditEnter(Node: TNxInspectorNode6; InplaceEdit: INxInplaceEdit); dynamic;
    procedure DoEditing(Text: WideString); dynamic;
    procedure DoEditQuery(Node: TNxInspectorNode6; var Text: WideString; var CanEdit: Boolean); dynamic;
    procedure DoExit; override;
    procedure DoExpandChange(Node: TNxInspectorNode6); dynamic;
    procedure DoGetInplaceEdit(Node: TNxInspectorNode6; var InplaceEdit: INxInplaceEdit); dynamic;
    procedure DoGlyphChange(Sender: TObject);
    procedure DoInplaceEditChange(Sender: TObject); dynamic;
    procedure DoNodeChange(Sender: TNxInspectorNode6); dynamic;
    procedure DoNodesChange(Sender: TNxInspectorNode6; Kind: TNxNodesChangeKind;
      Attr1, Attr2: Integer); dynamic;
    procedure DoNodeViewChange(Sender: INxInspectorNodeView); dynamic;
    procedure DoUserDefinedColorPaletteChanged(Sender: TObject);
    { Property-binding Events }
    procedure DoBeforeAddProperty(Info: TNxPropInfo; var NodeClass:
      TNxInspectorNodeClass; var Accept: Boolean); dynamic;
    procedure DoPropertyNodeAdded(Node: TNxInspectorNode6); dynamic;
    { Selection Events }
    procedure DoSelect; dynamic;
    procedure DoSelectQuery(Node: TNxInspectorNode6; var CanSelect: Boolean); dynamic;
    { Drawing Methods }
    procedure DrawGrid(const Y: Integer; const ANode: TNxInspectorNode6); virtual;
    { Editing Methods }
    function AssignEdit: Boolean;
    procedure AssignEditToView;
    procedure AssignView;
    procedure Clear;
    procedure EditNode(const Node: TNxInspectorNode6); virtual;
    procedure EndEdit; virtual;
    procedure EndSplitterMoving; virtual;
    { INxInspectorControl }
    procedure AssignNodeView(const View: INxInspectorNodeView);
    function GetFont: TFont;
    procedure SetNodeView(View: INxInspectorNodeView);
    { INxNodeNotifyControl }
    procedure NodeNotification(Node: TNxInspectorNode6; Kind: TNxNodeNotificationKind);
    { DFM }
    function GetChildOwner: TComponent; override;
    { Mouse & Keyboard }
    function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    { Overriden Methods }
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure ScrollContentBy(DeltaX, DeltaY: Integer); override;
    procedure ScrollRect(DeltaX, DeltaY: Integer; Rect, ClipRect: TRect); override;
    { Main Paint Methods }
    procedure Paint; override;
    procedure PaintNode(const ANode: TNxInspectorNode6; NodeRect: TRect); virtual;
    function PaintNodes(Location: TPoint): TPoint; virtual;
    function PaintPinned: TPoint; virtual;

    procedure StyleChanged; override;

    { Hint Windows }
    procedure ActivateCaptionFitHint(Node: TNxInspectorNode6);
    procedure ActivateNodeHint(Node: TNxInspectorNode6);
    procedure ActivateValueFitHint(Node: TNxInspectorNode6);
    procedure DeactivateTextFitHint;
    procedure DeactivateHints;
    procedure DeactivateNodeHint;
    { Update }
    procedure UpdateInplaceEdit; virtual;
    procedure UpdateVertScrollBar; virtual;
    { Windows Messages }
    procedure WMActivateApp(var Message: TWMActivateApp); message WM_ACTIVATEAPP;
    procedure WMEraseBkGnd(var Message: TWMEraseBkGnd); message WM_ERASEBKGND;
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
    procedure WMKillFocus(var Message: TWMSetFocus); message WM_KILLFOCUS;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
    procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
    { Properties }
    property MarginIndent: Integer read GetMarginIndent;
    property PinnedHeight: Integer read GetPinnedHeight;
    property ScrollableRect: TRect read GetScrollableRect;
    property SplitterHot: Boolean read FSplitterHot write SetSplitterHot;
    property State: TNxInspectorState read FState;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Methods }
    procedure BeginUpdate(Options: TNxUpdateOptions = [uoScrollBar]);
    procedure BestFitSplitter;
    function CanUpdate(Options: TNxUpdateOptions): Boolean;
    procedure ClearAllStates;
    procedure Delete(const Index: Integer);
    procedure EndUpdate(Update: Boolean = True; Immediate: Boolean = False);
    function Focused: Boolean; override;
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
    function GetNodeAtPos(const X, Y: Integer; SizeGrip: Boolean = False): TNxInspectorNode6;
    function GetNodeHeight(const Node: TNxInspectorNode6): Integer;
    function GetNodePart(const X, Y: Integer): TNxInspectorNodePart;
    function GetNodeRect(const Node: TNxInspectorNode6; Part: TNxInspectorNodePart = npAll): TRect;
    function GetNodeTop(const Node: TNxInspectorNode6): Integer;
    function HasPinned: Boolean;
    procedure InvalidateNode(const Node: TNxInspectorNode6; Parts: TNxInspectorNodeParts);
    function IsNodeShowing(const Node: TNxInspectorNode6): Boolean;
    procedure ReadProperties(const AParent: TNxInspectorNode6);
    procedure RefreshNode(const Node: TNxInspectorNode6);
    procedure SelectNode(const Node: TNxInspectorNode6; WantFocus: Boolean);
    procedure SetAllSaved;
    procedure ScrollToNode(const Node: TNxInspectorNode6);
    procedure SelectNext(CanFocusNextControl: Boolean = False);
    procedure SelectNone;
    procedure SelectPrevious;
    procedure UpdateScrollBars;
    { Public Properties }
    property Associates: TNxAssociates6 read GetAssociates;
    property Canvas;
    property CaptionHotNode: TNxInspectorNode6 read FCaptionHotNode write SetCaptionHotNode;
    property ControlName: string read GetControlName;
    property DetailsButtonPressed: Boolean read FDetailsButtonPressed write SetDetailsButtonPressed;
    property DetailsHotNode: TNxInspectorNode6 read FDetailsHotNode write SetDetailsHotNode;
    property EditingNode: TNxInspectorNode6 read FEditingNode;
    property Filtered: Boolean read GetFiltered;
    property FirstIndex: Integer read GetFirstIndex;
    property GridSize: Integer read GetGridSize;
    property HotNode: TNxInspectorNode6 read FHotNode write SetHotNode;
    property InplaceEdit: INxInplaceEdit read FInplaceEdit;
    property InplaceEditActive: Boolean read GetInplaceEditActive;
    property Pinned[Index: Integer]: TNxInspectorNode6 read GetPinned;
    property PinnedCount: Integer read GetPinnedCount;
    property ResizingReadyNode: TNxInspectorNode6 read FResizingReadyNode write SetResizingReadyNode;
    property SelectedNode: TNxInspectorNode6 read FSelectedNode write SetSelectedNode;
    property SplitterResizingRect: TRect read GetSplitterResizingRect;
    property ValueHotNode: TNxInspectorNode6 read FValueHotNode write SetValueHotNode;
    property ValueWidth: Integer read GetValueWidth;
  published
    property Align;
    property Anchors;
    property AppearanceOptions: TNxAppearanceOptions read FAppearanceOptions
      write SetAppearanceOptions default [aoStyleColors];
    property BorderSize default 1;
    property Constraints;
    property Enabled;
    property Filter: WideString read FFilter write SetFilter;
    property GlyphCollapse: TBitmap read FGlyphCollapse write SetGlyphCollapse;
    property GlyphExpand: TBitmap read FGlyphExpand write SetGlyphExpand;
    property GridColor: TColor read FGridColor write SetGridColor default clBtnFace;
    property GridLines: Boolean read FGridLines write SetGridLines default True;
    property GridLinesStyle: TNxInspectorGridLinesStyle read FGridLinesStyle
      write SetGridLinesStyle default gsSolid;
    property GridPadding: Integer read FGridPadding write SetGridPadding default 0;
    property HideTopLevel: Boolean read FHideTopLevel write SetHideTopLevel default False;
    property ImagePosition: TNxNodeIconPosition read FImagePosition write SetImagePosition default ipLeft;
    property Images: TCustomImageList read FImages write SetImages;
    property InactiveSelectionColor: TColor read FInactiveSelectionColor write SetInactiveSelectionColor default clBtnFace;
    property InplaceEditColor: TColor read FInplaceEditColor write SetInplaceEditColor default clWindow;
    property MarginColor: TColor read FMarginColor write SetMarginColor default clBtnFace;
    property MarginWidth: Integer read FMarginWidth write SetMarginWidth default szMarginWidth;
    property NodeHeight: Integer read FNodeHeight write SetNodeHeight default szItemHeight;
    property Nodes: TNxInspectorNodes read FNodes;
    property Options: TNxInspectorOptions read FOptions write SetOptions default [ioEnterSelectNext, ioPreviewPopup, ioSplitterMoving];
    property ParentColor default False;
    property PreciseScrolling: Boolean read FPreciseScrolling write SetPreciseScrolling default False;
    property SelectionColor: TColor read FSelectionColor write SetSelectionColor default clHighlight;
    property SelectionTextColor: Tcolor read FSelectionTextColor write SetSelectionTextColor default clHighlightText;
    property ShowMargin: Boolean read FShowMargin write SetShowMargin default True;
    property Spacing: Integer read FSpacing write SetSpacing default 0;
    property SplitterColor: TColor read FSplitterColor write SetSplitterColor default clGray;
    property SplitterMinPosition: Integer read FSplitterMinPosition write SetSplitterMinPosition default posSplitterMin;
    property SplitterPosition: Integer read FSplitterPosition write SetSplitterPosition default posSplitter;
    property Style: TNxAppearanceStyle read FStyle write SetStyle default stNative;
    property TabOrder;
    property TabStop;
    property ToggleStyle: TNxToggleButtonStyle read FToggleStyle write SetToggleStyle default tsStyleNative;
    property TopLevelColor: TColor read FTopLevelColor write SetTopLevelColor default clBtnFace;
    property UserDefinedColorPalette: TNxUserDefinedColorPalette read FUserDefinedColorPalette write SetUserDefinedColorPalette;
    property UseTopLevelColor: Boolean read FUseTopLevelColor write SetUseTopLevelColor default True;
    property Version: string read GetVersion stored False;
    property Visible;
    { Events }
    property OnAfterEdit: TNxInspectorNodeNotifyEvent read FOnAfterEdit write FOnAfterEdit;
    property OnEditAssign: TNxInspectorEditAssignEvent read FOnEditAssign write FOnEditAssign;
    property OnEditEnter: TNxEditEnterEvent read FOnEditEnter write FOnEditEnter;
    property OnEditing: TNxInspectorEditingEvent read FOnEditing write FOnEditing;
    property OnEditQuery: TNxInspectorEditQueryEvent read FOnEditQuery write FOnEditQuery;
    property OnExpandChange: TNxInspectorNodeNotifyEvent read FOnExpandChange write FOnExpandChange;
    property OnBeforeAddProperty: TNxBeforeAddPropertyEvent read FOnBeforeAddProperty write FOnBeforeAddProperty;
    property OnGetInplaceEdit: TNxGetNodeInplaceEditEvent read FOnGetInplaceEdit write FOnGetInplaceEdit;
    property OnPropertyNodeAdded: TNxInspectorNodeNotifyEvent read FOnPropertyNodeAdded write FOnPropertyNodeAdded;
    property OnSelect: TNotifyEvent read FOnSelect write FOnSelect;
    property OnSelectQueryf: TNxInspectorSelectQueryEvent read FOnSelectQuery write FOnSelectQuery;
  end;

  TNextInspector6 = class(TNxCustomInspector6);

implementation

uses
  { Standard }
  SysUtils, Dialogs, StdCtrls, Math,
  { Next Suite 6 }
  NxGraphics6,
  { NextInspector 6 }
  NxInspectorNodeClasses6, NxInspectorStylePaint6;

{ TNxNodeHintWindow6 }

procedure TNxNodeHintWindow6.ActivateHint(Rect: TRect; const AHint: string);
begin
  inherited;

end;

constructor TNxNodeHintWindow6.Create(AOwner: TComponent);
begin
  inherited;
  ControlStyle := ControlStyle + [csOpaque];
end;

procedure TNxNodeHintWindow6.CreateParams(var Params: TCreateParams);
begin
  inherited;
  with Params do
  begin
    if CheckWin32Version(5, 1) then
      WindowClass.Style := WindowClass.Style and not CS_DROPSHADOW;
  end;
end;

function TNxNodeHintWindow6.GetContentRect: TRect;
begin

end;

procedure TNxNodeHintWindow6.NCPaint(DC: HDC);
var
  Pen: HPEN;
  R: TRect;
  BorderColor: TColor;
begin
  GetWindowRect(Handle, R);
  OffsetRect(R, -R.Left, -R.Top);
  BorderColor := clGray;
  Pen := CreatePen(PS_SOLID, 1, ColorToRGB(BorderColor));
  try
    SelectObject(DC, Pen);
    Rectangle(DC, R.Left, R.Top, R.Right, R.Bottom);
  finally
    DeleteObject(Pen);
  end;
end;

procedure TNxNodeHintWindow6.Paint;
begin
  inherited;
end;

{ TNxCustomInspector6 }

procedure TNxCustomInspector6.ActivateCaptionFitHint(Node: TNxInspectorNode6);
var
  NodeRect, HintRect: TRect;
  HintWidth: Integer;
  HintText: WideString;
begin
  FTextFitHint := TNxTextFitHintWindow.Create(Self);

  { Calculate Rect }
  NodeRect := GetNodeRect(Node, npCaption);

  if Node.CaptionWrapKind = wkWordWrap then
  begin
    HintWidth := Node.View.CaptionTextSize.cx;
  end
  else HintWidth := Screen.Width;

  if Node.CaptionTextFitHint <> '' then HintText := Node.CaptionTextFitHint
    else HintText := Node.Caption;

  HintRect := FTextFitHint.CalcHintRect(HintWidth, HintText, nil);

  { Convert to "Screen" }
  HintRect.TopLeft := ClientToScreen(NodeRect.TopLeft);

  HintRect.Bottom := HintRect.Top + HintRect.Bottom;
  HintRect.Right := HintRect.Left + HintRect.Right;

  { Move Hint to the Right }
  OffsetRect(HintRect, Node.Indent * MarginWidth, 0);

  if Assigned(Images) and (ImagePosition = ipLeft) then
  begin
    OffsetRect(HintRect, Images.Width + pdStandard, 0);
  end;

  { Vertical Position }
  OffsetRect(HintRect, 0, RectHeight(NodeRect) div 2 - RectHeight(HintRect) div 2);

  { Activate }
  FTextFitHint.ActivateHint(HintRect, HintText);
  FreeNotification(FTextFitHint);
end;

procedure TNxCustomInspector6.ActivateNodeHint(Node: TNxInspectorNode6);
var
  HintRect: TRect;
  Location: TPoint;
begin
  { Create Hint Window }
  FNodeHint := TNxNodeHintWindow6.Create(Self);
  FNodeHint.Color := Color;

  { Calculate Rect }
  HintRect := FNodeHint.CalcHintRect(Screen.Width, Node.Hint, nil);

  { Set Location & Activate }
  Location := GetNodeRect(Node).TopLeft;
  OffsetPoint(Location, MarginIndent + SplitterPosition, -RectHeight(HintRect));

  { Cordinates need to be Screen }
  HintRect.TopLeft := ClientToScreen(Location);
  HintRect.Bottom := HintRect.Top + HintRect.Bottom;
  HintRect.Right := HintRect.Left + HintRect.Right;

  { Show Hint }
  FNodeHint.ActivateHint(HintRect, Node.Hint);
end;

procedure TNxCustomInspector6.ActivateValueFitHint(Node: TNxInspectorNode6);
var
  NodeRect, HintRect: TRect;
  HintWidth: Integer;
  HintText: WideString;
begin
  FTextFitHint := TNxTextFitHintWindow.Create(Self);
  FreeNotification(FTextFitHint);

  HintText := Node.Value;

  { Calculate Rect }
  NodeRect := GetNodeRect(Node, npValueText);
  HintWidth := ClientWidth;
  HintRect := FTextFitHint.CalcHintRect(HintWidth, HintText, nil);

  { Convert to "Screen" }
  HintRect.TopLeft := ClientToScreen(NodeRect.TopLeft);
  HintRect.Bottom := HintRect.Top + HintRect.Bottom;
  HintRect.Right := HintRect.Left + HintRect.Right;

  { Center Verticaly }
  OffsetRect(HintRect, 0, RectHeight(NodeRect) div 2 - RectHeight(HintRect) div 2);

  { Activate }
  FTextFitHint.ActivateHint(HintRect, HintText);
end;

function TNxCustomInspector6.AssignEdit: Boolean;
var
  Text: WideString;
  PropertyIntf: INxProperty;
begin
  { Note: Transfer InplaceEdit.Text to Item }
  if Assigned(FInplaceEdit)
    and Assigned(FSelectedNode) then
  begin
    if Supports(InplaceEdit, INxProperty, PropertyIntf) then
    begin
      Text := PropertyIntf.AsString;
    end else Text := FInplaceEdit.Text;

    { Default }
    Result := FSelectedNode.AllowEmpty or (Text <> FSelectedNode.EmptyValue);

    { Trigger Event, Adjust Text }
    DoEditAssign(FSelectedNode, Text, Result);

    { Editing Approved }
    if Result then
    begin
      FSelectedNode.Value := Text;

      { Refresh }
      if FSelectedNode.View.HasContent then
        FSelectedNode.View.Refresh;
    end;

  end;

end;

procedure TNxCustomInspector6.AssignEditToView;
var
  PropertyIntf: INxProperty;
  Value: WideString;
begin
  if Assigned(FSelectedNode)
    and Assigned(FInplaceEdit)
    and not FEditorUpdating then
  begin

      if Supports(InplaceEdit, INxProperty, PropertyIntf) then
      begin
        Value := PropertyIntf.AsString;
      end else Value := FInplaceEdit.Text;

    FSelectedNode.View.AsString := Value;
  end;
end;

procedure TNxCustomInspector6.AssignNodeView(const View: INxInspectorNodeView);
begin
  View.Canvas := Canvas;
  View.ImagePosition := ImagePosition;
  View.Images := Images;
  View.UseTopLevelColor := UseTopLevelColor;
  View.MarginColor := MarginColor;
  View.MarginWidth := MarginWidth;
  View.ShowMargin := ShowMargin;
  View.SplitterPosition := SplitterPosition;
  View.ToggleStyle := ToggleStyle;
end;

procedure TNxCustomInspector6.AssignView;
begin
  if Assigned(FSelectedNode)
    and Assigned(FSelectedNode.View) then
  begin
    FSelectedNode.View.AssignValue;
  end;
end;

procedure TNxCustomInspector6.BeginUpdate(Options: TNxUpdateOptions);
begin
  FUpdateOptions := Options;

  { Don't Update ScrollBar }
  if uoScrollBar in FUpdateOptions then
  begin
    { Set Position Field, but not Update }
    VertScrollBar.Lock;
  end;

  { Increase }
  Inc(FUpdateCount);
end;

procedure TNxCustomInspector6.BestFitSplitter;
var
  i, Position: Integer;
  NodeView: INxInspectorNodeView;
begin
  { Min position }
  Position := MarginIndent;

  for i := 0 to Pred(Nodes.Count) do
  begin

    { Don't include top-level since they
      overflow by default }
    if not Nodes[i].IsTopLevel then
    begin
      NodeView := Nodes[i].View;
      NodeView.Canvas := Canvas;
      NodeView.Control := Self;
      Position := Max(Position, Nodes[i].View.PreferedSize([etCaption]).cx);
    end;

  end;

  { New one }
  SplitterPosition := Position;
end;

function TNxCustomInspector6.CanUpdate(Options: TNxUpdateOptions): Boolean;
begin
  Result := (FUpdateCount = 0) or (Options * FUpdateOptions <> []);
end;

procedure TNxCustomInspector6.Clear;
begin
  FCapturedView := nil;
  FPressedNode := nil;
  FEditingNode := nil;
  FFirstIndex := 0;
  FHotNode := nil;
  FResizingNode := nil;
  FResizingReadyNode := nil;
  FSelectedNode := nil;
  FInplaceEdit := nil;
end;

procedure TNxCustomInspector6.ClearAllStates;
var
  i: Integer;
begin
  for i := 0 to Pred(Nodes.Count) do
  begin
    Nodes[i].ClearState;
  end;
end;

procedure TNxCustomInspector6.CMChildKey(var AMsg: TCMChildKey);
var
  Ref: IInterfaceComponentReference;
begin
  inherited;

  if Supports(FInplaceEdit, IInterfaceComponentReference, Ref) then
  begin
    { 10/23/11: If Child is FInplaceEdit then send KeyDown and
                stop further processing if FInplaceEdit is released
                within KeyDown (!) }
    if Ref.GetComponent = AMsg.Sender then
    begin
      { Ask InplaceEdit first (!) }
      if FInplaceEdit.CanDispatchKey(AMsg.CharCode)
        then KeyDown(AMsg.CharCode, []);

      { 2/18/12: Potential problem with Tab key
                 and Button InplaceEdit's  }
      if AMsg.CharCode = VK_TAB then AMsg.Result := 1;      

      { 10/22/11: Must be set in order to stop processing
                  if FInplaceEdit is nil (!) }
      if FInplaceEdit = nil then AMsg.Result := 1;
    end;
  end;
end;

procedure TNxCustomInspector6.CMColorChanged(var Message: TMessage);
begin
  inherited;
end;

procedure TNxCustomInspector6.CMFontChanged(var Message: TMessage);
var
  i: Integer;
begin
  inherited;
  for i := 0 to Pred(Nodes.Count) do
    with Nodes[i] do
    begin
      if ParentCaptionFont then CaptionFont := Font;
      if ParentValueFont then ValueFont := Font;
    end;
end;

procedure TNxCustomInspector6.CMMouseLeave(var Message: TMessage);
  function MouseInHint: Boolean;
  var
    Pos: TPoint;
  begin
    GetCursorPos(Pos);
    Result := Assigned(FTextFitHint) and PtInRect(FTextFitHint.BoundsRect, Pos);
  end;
begin
  inherited;

  { NOTE: AV if Hint receive CM_MOUSEENTER
    but destroying it here (!) }
  if not MouseInHint then
  begin
    { Hint destroyed }
    HotNode := nil;
  end;

  ResizingReadyNode := nil;
end;

procedure TNxCustomInspector6.CMParentFontChanged(var Message: TMessage);
var
  i: Integer;
begin
  inherited;
  for i := 0 to Pred(Nodes.Count) do
  begin
    { Caption Font }
    if Nodes[i].ParentCaptionFont then Nodes[i].CaptionFont.Assign(Font);

    { Value Font }
    if Nodes[i].ParentValueFont then Nodes[i].ValueFont.Assign(Font);
  end;
end;

constructor TNxCustomInspector6.Create(AOwner: TComponent);
begin
  inherited;
  FAppearanceOptions := [aoStyleColors];
  FAssociates := TNxAssociates6.Create;
  FCaptionHotNode := nil;
  { 12/22/11: Indicate View captured with Mouse
              and/or Keyboard }
  FCapturedView := nil;
  FDetailsButtonPressed := False;
  FDetailsHotNode := nil;
  FPressedNode := nil;
  FEditingNode := nil;
  FEditorUpdating := False;
  FGlyphCollapse := TBitmap.Create;
  FGlyphCollapse.OnChange := DoGlyphChange;
  FGlyphExpand := TBitmap.Create;
  FGlyphExpand.OnChange := DoGlyphChange;
  FGridColor := clBtnFace;
  FGridLines := True;
  FGridLinesStyle := gsSolid;
  FGridPadding := 0;
  FHideTopLevel := False;
  FHotNode := nil;
  FImagePosition := ipLeft;
  FImages := nil;
  FInactiveSelectionColor := clBtnFace;
  FInplaceEdit := nil;
  FInplaceEditColor := clWindow;
  FMarginColor := clBtnFace;
  FMarginWidth := szMarginWidth;
  FNodeHeight := szItemHeight;
  FNodeHint := nil;
  FNodes := TNxInspectorNodes.Create(Self);
  FNodes.OnNodeChanged := DoNodeChange;
  FNodes.OnNodesChanged := DoNodesChange;
  { Add Self into Notifications List }
  FNodes.ChangeNotification(Self);
  FOptions := [ioEnterSelectNext, ioPreviewPopup, ioSplitterMoving];
  FPreciseScroling := False;
  FResizingNode := nil;
  FResizingReadyNode := nil;
  FSelectedNode := nil;
  FSelectionColor := clHighlight;
  FSelectionTextColor := clHighlightText;
  FShowMargin := True;
  FSpacing := 0;
  FSplitterColor := clGray;
  FSplitterHot := False;
  FSplitterMinPosition := posSplitterMin;
  FSplitterPosition := posSplitter;
  FState := [];
  FToggleStyle := tsStyleNative;
  FTopLevelColor := clBtnFace;
  FUserDefinedColorPalette := TNxUserDefinedColorPalette.Create;
  FUserDefinedColorPalette.OnChanged := DoUserDefinedColorPaletteChanged;
  FUseTopLevelColor := True;
  FUpdateCount := 0;
  FValueHotNode := nil;

  { Set Properties }
  Color := clWindow;
  BorderSize := 1;
  Height := 200;
  ParentColor := False;
  TabStop := True;
  Width := 245;

  { Set Default Style }
  Style := stNative;
end;

procedure TNxCustomInspector6.CreateWnd;
begin
  inherited;
  HorzScrollBar.Visible := False;
end;

procedure TNxCustomInspector6.DeactivateTextFitHint;
begin
  if Assigned(FTextFitHint) then
  begin
    FTextFitHint.ReleaseHandle;
    FTextFitHint.Free;
    FTextFitHint := nil;
  end;
end;

procedure TNxCustomInspector6.DeactivateHints;
begin
  DeactivateNodeHint;
  DeactivateTextFitHint;
end;

procedure TNxCustomInspector6.DeactivateNodeHint;
begin
  if FNodeHint <> nil then
  begin
    FNodeHint.ReleaseHandle;
    FNodeHint := nil;
  end;
end;

procedure TNxCustomInspector6.Delete(const Index: Integer);
begin
  Nodes.Delete(Index);
end;

destructor TNxCustomInspector6.Destroy;
begin
  DeactivateNodeHint;
  DeactivateTextFitHint;

  FInplaceEdit := nil;

  { Fields }
  FreeAndNil(FAssociates);
  FreeAndNil(FGlyphCollapse);
  FreeAndNil(FGlyphExpand);
  FreeAndNil(FUserDefinedColorPalette);

  { InterfacedObjects }
  FreeAndNil(FNodes);

  inherited;
end;

procedure TNxCustomInspector6.DoAfterEdit(Node: TNxInspectorNode6);
begin
  if Assigned(FOnAfterEdit) then FOnAfterEdit(Self, Node);
end;

procedure TNxCustomInspector6.DoBeforeAddProperty(Info: TNxPropInfo;
  var NodeClass: TNxInspectorNodeClass; var Accept: Boolean);
begin
  if Assigned(FOnBeforeAddProperty) then FOnBeforeAddProperty(Self, Info, NodeClass, Accept);
end;

procedure TNxCustomInspector6.DoEditAssign(Node: TNxInspectorNode6; var Text: WideString; var CanAssign: Boolean);
begin
  if Assigned(FOnEditAssign) then FOnEditAssign(Self, Node, Text, CanAssign);
end;

procedure TNxCustomInspector6.DoEditEnter(Node: TNxInspectorNode6;
  InplaceEdit: INxInplaceEdit);
begin
  if Assigned(FOnEditEnter) then FOnEditEnter(Self, Node, InplaceEdit);
end;

procedure TNxCustomInspector6.DoEditing(Text: WideString);
begin
  if Assigned(FOnEditing) then FOnEditing(Self, Text);
end;

procedure TNxCustomInspector6.DoEditQuery(Node: TNxInspectorNode6; var Text: WideString; var CanEdit: Boolean);
begin
  if Assigned(FOnEditQuery) then FOnEditQuery(Self, Node, Text, CanEdit);
end;

procedure TNxCustomInspector6.DoExit;
begin
  inherited;
  if Assigned(SelectedNode) then InvalidateNode(SelectedNode, [npCaption]);
  if ioEndEditOnUnfocus in Options then
  begin
    AssignEdit;
    EndEdit;
  end;
end;

procedure TNxCustomInspector6.DoExpandChange(Node: TNxInspectorNode6);
begin
  if Assigned(FOnExpandChange) then FOnExpandChange(Self, Node);
end;

procedure TNxCustomInspector6.DoGetInplaceEdit(Node: TNxInspectorNode6; var InplaceEdit: INxInplaceEdit);
begin
  if Assigned(FOnGetInplaceEdit) then FOnGetInplaceEdit(Self, Node, InplaceEdit);  
end;

procedure TNxCustomInspector6.DoGlyphChange(Sender: TObject);
begin
  Invalidate;
end;

procedure TNxCustomInspector6.DoInplaceEditChange(Sender: TObject);
var
  PropertyIntf: INxProperty;
  Text: WideString;
begin
  if not FEditorUpdating
    and Assigned(SelectedNode) then
  begin
    { Item automaticaly assign Text from InplaceEdit }
    if SelectedNode.AutoAssign then
      try
        { Prevent Recursion }
        FEditorUpdating := True;

        { Trigger Event }
        if Supports(InplaceEdit, INxProperty, PropertyIntf) then
        begin
          Text := PropertyIntf.AsString;
        end else Text := FInplaceEdit.Text;

        DoEditing(Text);

        { Trigger OnEditAssign event & transfer InplaceEdit.Text into Item }
        AssignEdit;

      finally
        { Turn Flag back }
        FEditorUpdating := False;
      end
  end;
end;

procedure TNxCustomInspector6.DoNodeChange(Sender: TNxInspectorNode6);
begin
  if not FEditorUpdating
    and (Sender = SelectedNode)
    and Assigned(InplaceEdit) then
  begin
    try
      FEditorUpdating := True;

      { Transfer Item's Value to InplaceEdit }
      InplaceEdit.Text := Sender.DisplayText;
    finally
      FEditorUpdating := False;
    end;
  end;

  { Update Associated }
  if Sender.BindProperty <> EmptyStr then
  begin

    { Already reading property? }
    if not (ntPropertyReading in Sender.State) then
      FAssociates.SetPropValue(Sender.BindProperty, Sender);

  end;
end;

procedure TNxCustomInspector6.DoNodesChange(Sender: TNxInspectorNode6;
  Kind: TNxNodesChangeKind; Attr1, Attr2: Integer);
begin

  if Assigned(FNodes) then
  begin

    case Kind of
      ncAdd:
        if (FNodes.ShowingCount = 1)
          and Sender.Showing then
        begin
          FFirstIndex := Sender.AbsoluteIndex;
        end;
      ncClear:
        begin
          Clear;
        end;
      ncDelete:
        if Sender.Showing and (Sender = FSelectedNode) then
        begin
          EndEdit;
          FSelectedNode := nil;
        end;
      ncPin: ;
    end;

    if CanUpdate([uoScrollBar])
      then UpdateVertScrollBar;

    UpdateInplaceEdit;

    if CanUpdate([uoRepaint])
      then Invalidate;

  end;

end;

procedure TNxCustomInspector6.DoNodeViewChange(Sender: INxInspectorNodeView);
begin
  if not FEditorUpdating
    and Assigned(SelectedNode)
    and (Sender = SelectedNode.View)
    and Assigned(InplaceEdit) then
  begin
    { Transfer Item's Value to InplaceEdit }
    { 12/25/11: OnInplaceEditChange not occur if Text is same,
                and InplaceEdit impact View }
    InplaceEdit.Text := Sender.Value;
  end;
end;

procedure TNxCustomInspector6.DoPropertyNodeAdded(Node: TNxInspectorNode6);
begin
  if Assigned(FOnPropertyNodeAdded) then FOnPropertyNodeAdded(Self, Node);
end;

function TNxCustomInspector6.DoMouseWheelDown(Shift: TShiftState;
  MousePos: TPoint): Boolean;
begin
  Result := inherited DoMouseWheelDown(Shift, MousePos);

  if Result then
  begin

    { Do Scroll }
    if ssCtrl in Shift
      then VertScrollBar.PageDown
        else VertScrollBar.Next;

  end;

end;

function TNxCustomInspector6.DoMouseWheelUp(Shift: TShiftState;
  MousePos: TPoint): Boolean;
begin
  Result := inherited DoMouseWheelUp(Shift, MousePos);

  if Result then
  begin

    { Do Scroll }
    if ssCtrl in Shift
      then VertScrollBar.PageUp
        else VertScrollBar.Prior;

  end;

end;

procedure TNxCustomInspector6.DoSelect;
begin
  if Assigned(FOnSelect) then FOnSelect(Self);
end;

procedure TNxCustomInspector6.DoSelectQuery(Node: TNxInspectorNode6;
  var CanSelect: Boolean);
begin
  if Assigned(FOnSelectQuery) then FOnSelectQuery(Self, Node, CanSelect);
end;

procedure TNxCustomInspector6.DoUserDefinedColorPaletteChanged(Sender: TObject);
begin

end;

procedure TNxCustomInspector6.DrawGrid(const Y: Integer; const ANode: TNxInspectorNode6);
var
  X: Integer;
begin
  X := FGridPadding;
  with Canvas do
  begin
    if aoIndentGridLines in AppearanceOptions then
    begin
      Pen.Color := FActiveStyle.GetMarginColor(ANode, MarginColor);
      Polyline([Point(X, Y), Point(ClientWidth - FGridPadding, Y)]);
      Inc(X, FMarginWidth);
    end;
    case FGridLinesStyle of
      gsSolid:
      begin
        Pen.Color := FActiveStyle.GetGridColor;
        Polyline([Point(X, Y), Point(ClientWidth - FGridPadding, Y)]);
      end;
      gsDotted:
      begin
        if ShowMargin then
        begin
          Pen.Color := MarginColor;
          Polyline([
            Point(X, Y),
            Point(X + MarginWidth, Y)]);
          Inc(X, MarginWidth);
        end;
        Pen.Color := Color;
        Polyline([Point(X, Y), Point(ClientWidth - FGridPadding, Y)]);
        DrawDotLine(Canvas, X, Y, ClientWidth - FGridPadding, Y, FActiveStyle.GetGridColor);
      end;
      gsInset:
      begin
        Pen.Color := GridColor;
        Polyline([Point(X, Y), Point(ClientWidth - FGridPadding, Y)]);
        Pen.Color := Color;
        Polyline([Point(X, Y + 1), Point(ClientWidth - FGridPadding, Y + 1)]);
      end;
      gsOutset:
      begin
        Pen.Color := Color;
        Polyline([Point(X, Y), Point(ClientWidth - FGridPadding, Y)]);
        Pen.Color := GridColor;
        Polyline([Point(X, Y + 1), Point(ClientWidth - FGridPadding, Y + 1)]);
      end;
    end;
  end;
end;

procedure TNxCustomInspector6.EditNode(const Node: TNxInspectorNode6);
var
  CanEdit: Boolean;
  Text: WideString;
  UserStyled: INxUserStyled;
begin
  if Assigned(Node) and Node.CanEdit then
  begin
    CanEdit := True;

    { May be Adjusted later }
    Text := Node.DisplayText;

    { Trigger Event. Contine or Cancel? }
    DoEditQuery(Node, Text, CanEdit);

    if CanEdit then
    begin
      FInplaceEdit := Node.InplaceEdit;

      { Call event }
      DoGetInplaceEdit(Node, FInplaceEdit);

      { Create InplaceEdit? }
      if FInplaceEdit = nil then
      begin
        FInplaceEdit := Node.BeginEdit;
      end;

      if Assigned(FInplaceEdit) then
      begin
        { Editing Started ! }

        { ! Set EditingNode Property }
        FEditingNode := Node;

        { 11/1/11: It is important to first Hide control
                   before setting Parent }

        { Hide it before setting properties }
        FInplaceEdit.Hide;

        { InplaceEdit will set some properties, e.g. BorderStyle }
        FInplaceEdit.BeginEdit;

        { Set Parent }
        FInplaceEdit.Parent := Self;

        { Adjust Edit to the Node's properties (such as Font) }
        FEditingNode.AssignEdit(FInplaceEdit);

        { Transfer Value }
        FInplaceEdit.Text := Text;

        { Style Edit }
        FInplaceEdit.Color := FInplaceEditColor;

        { Set padding inside InplaceEdit }
        FInplaceEdit.Padding.SetBounds(0, 2, 0, 0);

        { Leading & Trailing }
        FInplaceEdit.SetEditPadding(0, 0);

        { User defined styles }
        if Supports(FInplaceEdit, INxUserStyled, UserStyled) then
        begin
          UserStyled.ChangeStyle(Style);
          UserStyled.UserDefinedColorPalette := UserDefinedColorPalette;
        end;

        { Attach Event }
        FInplaceEdit.OnChange := DoInplaceEditChange;

        { Set Position, Size, call Show & Focus }
        UpdateInplaceEdit;

        { Editing began event }
        DoEditEnter(Node, FInplaceEdit);
      end;

    end; { CanEdit }

    { Turn back Focus to Control, if
      Editing not started }
    if not Assigned(FInplaceEdit)
      and CanFocus
      and not Focused then SetFocus;

  end;
end;

procedure TNxCustomInspector6.EndEdit;
begin
  if Assigned(FInplaceEdit) then
  begin
    { Detach Event }
    FInplaceEdit.OnChange := nil;

    FInplaceEdit.Parent := nil;

    try
      FInplaceEdit.Hide;
    finally
      { Editing Ended }
      FInplaceEdit := nil;
      FEditingNode := nil;

      { Trigger Event }
      DoAfterEdit(FSelectedNode);
    end;

  end;
end;

procedure TNxCustomInspector6.EndSplitterMoving;
begin
  Exclude(FState, isSplitterMoving);
end;

procedure TNxCustomInspector6.EndUpdate(Update, Immediate: Boolean);
begin
  Dec(FUpdateCount);

  { Finished Updating? }
  if (Update and (FUpdateCount = 0)) or Immediate then
  begin
    { ToDo: Check AutoCalculate? }
//    if not (uoRecalculate in FUpdateOptions)
//      then Recalculate;

    if not (uoRecount in FUpdateOptions) then
    begin
      { Recount FirstVisible & LastVisible }
//        Recount;

      { Recount LastFixedRow }
//        ActiveView.Recount;
    end;

    if not (uoScrollBar in FUpdateOptions) then
    begin
      { Unlock ScrollBar & Update }
      UpdateScrollBars;
    end
    else VertScrollBar.Unlock;

    { Refresh }
    Invalidate;
  end;
end;

function TNxCustomInspector6.Focused: Boolean;
begin
  { Return True and if InplaceEdit focued }
  Result := inherited Focused or (Assigned(FInplaceEdit) and FInplaceEdit.Focused);
end;

function TNxCustomInspector6.GetAssociates: TNxAssociates6;
begin
  Result := FAssociates;
end;

function TNxCustomInspector6.GetChildOwner: TComponent;
begin
  Result := nil;
end;

procedure TNxCustomInspector6.GetChildren(Proc: TGetChildProc;
  Root: TComponent);
var
  i: Integer;
begin
  inherited;
  for i := 0 to Pred(FNodes.Count) do
    if FNodes[i].ParentNode = nil then Proc(FNodes[i]);
end;

function TNxCustomInspector6.GetControlName: string;
begin
  Result := 'NextInspector';
end;

function TNxCustomInspector6.GetFiltered: Boolean;
begin
  Result := FFilter <> EmptyStr;
end;

function TNxCustomInspector6.GetFirstIndex: Integer;
begin
  Result := 0;
  if not PreciseScrolling then Result := FFirstIndex;
end;

function TNxCustomInspector6.GetFont: TFont;
begin
  Result := Font;
end;

function TNxCustomInspector6.GetGridSize: Integer;
begin
  Result := 0;
  if FGridLines then
    case FGridLinesStyle of
      gsSolid, gsDotted: Result := 1;
      gsInset, gsOutset: Result := 2;
    end;
end;

function TNxCustomInspector6.GetInplaceEditActive: Boolean;
begin
  Result := Assigned(FInplaceEdit) and FInplaceEdit.Showing;
end;

function TNxCustomInspector6.GetNodeAtPos(const X, Y: Integer;
  SizeGrip: Boolean = False): TNxInspectorNode6;
var
  Index, PosY: Integer;

  function ProcessNode(Node: TNxInspectorNode6): TNxInspectorNode6;
  var
    NodeRect: TRect;
  begin
    Result := nil;

    { Top Spacing }
    Inc(PosY, Spacing);

    { Hit-test }
    { 10/29/11: Hit-test include GridSize & Spacing }
    NodeRect := Rect(0, PosY, ClientWidth, PosY + GetNodeHeight(Node) + GridSize + Spacing);

    if SizeGrip then
    begin
      NodeRect := Rect(ClientWidth - szSizeGrip, PosY + GetNodeHeight(Node) - szSizeGrip, ClientWidth, PosY + GetNodeHeight(Node));
    end;

    if PtInRect(NodeRect, Point(X, Y)) then
    begin
      Result := Node;
      Exit;
    end;

    { Actual Height of Item }
    Inc(PosY, GetNodeHeight(Node));

    Inc(PosY, GridSize);

    { Bottom Spacing }
    Inc(PosY, Spacing);
  end;

begin
  PosY := 0;

  Index := 0;

  { Processing Pinned }

  while Index < FNodes.PinnedCount do
  begin
    Result := FNodes.Pinned[Index];

    if Result.Visible then
    begin
      Result := ProcessNode(Result);

      if Assigned(Result)
        then Exit;
    end;

    Inc(Index);
  end;

  { Processing Regular }

  if PreciseScrolling then Dec(PosY, VertScrollBar.Position);

  Index := FirstIndex;

  while Index < FNodes.Count do
  begin
    Result := FNodes[Index];

    if Result.Showing and not Result.Pinned then
    begin
      if Assigned(ProcessNode(Result)) then Exit;
    end;

    Inc(Index);
  end;

  { Node not found }
  Result := nil;
end;

function TNxCustomInspector6.GetNodeHeight(const Node: TNxInspectorNode6): Integer;
begin
  if Node.ActualHeight = 0
    then Result := FNodeHeight
    else Result := Node.ActualHeight;
end;

function TNxCustomInspector6.GetNodePart(const X, Y: Integer): TNxInspectorNodePart;
begin
  if ShowMargin and (X <= MarginWidth) then Result := npMargin
  else if InRange(X, MarginIndent, MarginIndent + SplitterPosition) then Result := npCaption
  else Result := npValue;
end;

function TNxCustomInspector6.GetNodeRect(const Node: TNxInspectorNode6;
  Part: TNxInspectorNodePart): TRect;
begin
  with Result do
  begin
    case Part of
      npAll, npMargin: Left := 0;
      npCaption: Left := MarginIndent;
      npValueText: Left := Node.View.TextRect.Left;
      else Left := MarginIndent + SplitterPosition + 1;
    end;

    Top := GetNodeTop(Node);

    case Part of
      npAll, npValue: Right := ClientWidth;
      npCaption: Right := MarginIndent + SplitterPosition;
      npMargin: Right := MarginIndent;
      npValueText: Right := Node.View.TextRect.Right;
    end;
    Bottom := Top + GetNodeHeight(Node);
  end;
end;

function TNxCustomInspector6.GetNodeTop(const Node: TNxInspectorNode6): Integer;
var
  Index: Integer;
  CurrentNode: TNxInspectorNode6;
begin
  { Start from Top }
  Result := 0;

  Index := 0;

  { Processing Pinned }

  while Index < FNodes.PinnedCount do
  begin
    CurrentNode := FNodes.Pinned[Index];

    if CurrentNode.Visible then
    begin
      if CurrentNode = Node
        then Exit; { Mactch }

      { Top Spacing }
      Inc(Result, Spacing);

      { Actual Height of Node }
      Inc(Result, GetNodeHeight(CurrentNode));

      Inc(Result, GridSize);

      { Bottom Spacing }
      Inc(Result, Spacing);
    end;

    Inc(Index);
  end;

  { Processing Un-pinned }

  if Node.AbsoluteIndex < FFirstIndex then
  begin
    Index := FFirstIndex;

    while Index >= 0 do
    begin
      CurrentNode := FNodes[Index];

      if CurrentNode.Showing then
      begin

        { Got it? }
        if CurrentNode = Node
          then Exit;

        { Top Spacing }
        Dec(Result, Spacing);

        { Actual Height of Node }
        Dec(Result, GetNodeHeight(CurrentNode));

        Dec(Result, GridSize);

        { Bottom Spacing }
        Dec(Result, Spacing);
      end;

      { Go to prev }
      Dec(Index);

    end;

  end { before first }
  else
  begin

    if PreciseScrolling then Dec(Result, VertScrollBar.Position);

    Index := FirstIndex;

    while Index < FNodes.Count do
    begin
      CurrentNode := FNodes[Index];

      if CurrentNode.Showing then
      begin

        { Got it? }
        if CurrentNode = Node
          then Exit;

        { Top Spacing }
        Inc(Result, Spacing);

        { Actual Height of Node }
        Inc(Result, GetNodeHeight(CurrentNode));

        Inc(Result, GridSize);

        { Bottom Spacing }
        Inc(Result, Spacing);
      end;

      { Go to next }
      Inc(Index);
    end;

  end;

end;

function TNxCustomInspector6.GetMarginIndent: Integer;
begin
  if ShowMargin then Result := MarginWidth else Result := 0;
end;

function TNxCustomInspector6.GetPinned(Index: Integer): TNxInspectorNode6;
begin
  Result := FNodes.Pinned[Index];
end;

function TNxCustomInspector6.GetPinnedCount: Integer;
begin
  Result := FNodes.PinnedCount;
end;

function TNxCustomInspector6.GetPinnedHeight: Integer;
var
  Index: Integer;
  MeasuringNode: TNxInspectorNode6;
begin
  { Default }
  Result := 0;

  for Index := 0 to Pred(FNodes.PinnedCount) do
  begin
    MeasuringNode := FNodes.Pinned[Index];

    if MeasuringNode.Visible then
    begin
      { Upper Spacing }
      Inc(Result, FSpacing);

      Inc(Result, GetNodeHeight(MeasuringNode));

      { Lower Spacing }
      Inc(Result, FSpacing);

      if GridLines
        then Inc(Result);
    end;
  end;
end;

function TNxCustomInspector6.GetScrollableRect: TRect;
begin
  Result := ClientRect;
  Inc(Result.Top, PinnedHeight);
end;

function TNxCustomInspector6.GetSplitterResizingRect: TRect;
begin
  Result := Rect(SplitterPosition - szMovingThreshold, 0, SplitterPosition + szMovingThreshold, ClientHeight);

  { Include Margin, if set }
  OffsetRect(Result, MarginIndent, 0);
end;

function TNxCustomInspector6.GetValueWidth: Integer;
begin
  Result := ClientWidth - MarginIndent - SplitterPosition - 1;
end;

function TNxCustomInspector6.GetVersion: string;
begin
  Result := vrNextInspector;
end;

function TNxCustomInspector6.HasPinned: Boolean;
begin
  Result := FNodes.PinnedCount > 0;
end;

procedure TNxCustomInspector6.InvalidateNode(const Node: TNxInspectorNode6; Parts: TNxInspectorNodeParts);
var
  NodeRect, InvalidationRect: TRect;
begin
  NodeRect := GetNodeRect(Node);
  if npAll in Parts then InvalidateRect(NodeRect) else
  begin
    if npMargin in Parts then
    begin
      InvalidationRect := NodeRect;
      InvalidationRect.Right := MarginIndent;
      InvalidateRect(InvalidationRect);
    end;
    if npCaption in Parts then
    begin
      InvalidationRect := NodeRect;
      InvalidationRect.Left := MarginIndent;
      if icValue in Node.VisibleCells then
      begin
        InvalidationRect.Right := InvalidationRect.Left + SplitterPosition;
      end;
      InvalidateRect(InvalidationRect);
    end;
    if npValue in Parts then
    begin
      InvalidationRect := NodeRect;
      if icCaption in Node.VisibleCells then
      begin
        InvalidationRect.Left := InvalidationRect.Left + SplitterPosition;
      end;
      InvalidateRect(InvalidationRect);
    end;
  end;
end;

function TNxCustomInspector6.IsNodeShowing(const Node: TNxInspectorNode6): Boolean;
begin
  Result := Node.Visible;
  Result := Result and (not Filtered or (Pos(Filter, Node.Caption) > -1));
end;

procedure TNxCustomInspector6.NodeNotification(Node: TNxInspectorNode6;
  Kind: TNxNodeNotificationKind);
var
  AbsoluteIndex, AFirst: Integer;

  procedure ShiftNodes(Index, Delta: Integer);
  var
    i: Integer;
  begin

    i := 1;

    while i <= Abs(Delta) do
    begin

      if VertScrollBar.Showing and VertScrollBar.IsLast then
      begin
        if Delta < 0 then
        begin
          if FFirstIndex > 0 then Dec(FFirstIndex)
        end
        else Inc(FFirstIndex);

      end else
      begin

        if Delta < 0 then
        begin

          if AbsoluteIndex < AFirst then
          begin
            Inc(Index);
            if Index >= AFirst then
            begin
              Inc(FFirstIndex);
            end else
            begin
              VertScrollBar.Lock;
              VertScrollBar.MoveBy(-1);
              VertScrollBar.Unlock(False);
            end;
          end;

        end else
        begin

          if AbsoluteIndex < AFirst then
          begin
            VertScrollBar.Lock;
            VertScrollBar.MoveBy(1);
            VertScrollBar.Unlock(False);
          end;

        end;

      end;

      UpdateVertScrollBar;

      Inc(i);
    end;

  end;

begin
  AFirst := FFirstIndex;

  case Kind of
    ntCaptionChanged:
      if CanUpdate([uoRepaint]) then
      begin
        InvalidateRect(GetNodeRect(Node, npCaption));
      end;

    ntResized:
      begin
        UpdateInplaceEdit;

        if CanUpdate([uoScrollBar])
          then UpdateVertScrollBar;

        Invalidate;
      end;

    ntVisibleChanged:
    begin
      AbsoluteIndex := Node.AbsoluteIndex;

      if AbsoluteIndex < FFirstIndex then
      begin
        ShiftNodes(AbsoluteIndex, FNodes.ShowingDelta);
      end
      else if AbsoluteIndex = FFirstIndex then
      begin
        UpdateVertScrollBar;
      end
      else if AbsoluteIndex > FFirstIndex then
      begin
        ShiftNodes(AbsoluteIndex, FNodes.ShowingDelta);
      end;

    end;

    ntExpanded:
      begin


        AbsoluteIndex := Node.AbsoluteIndex;
        if AbsoluteIndex < FFirstIndex then
        begin
          ShiftNodes(AbsoluteIndex, FNodes.ShowingDelta);
        end
        else if AbsoluteIndex = FFirstIndex then
        begin

        end
        else if AbsoluteIndex > FFirstIndex then
        begin
          ShiftNodes(AbsoluteIndex, FNodes.ShowingDelta);
        end;

      end;

    ntLayoutChanged:
    begin
      if CanUpdate([uoScrollBar]) then UpdateVertScrollBar;
      if CanUpdate([uoRepaint]) then Invalidate;
      UpdateInplaceEdit;
    end;

    ntCollapsed:
      begin
        AbsoluteIndex := Node.AbsoluteIndex;
        if AbsoluteIndex < FFirstIndex then
        begin
          ShiftNodes(AbsoluteIndex, FNodes.ShowingDelta);
        end
        else if AbsoluteIndex = FFirstIndex then
        begin

        end
        else if AbsoluteIndex > FFirstIndex then
        begin
          ShiftNodes(AbsoluteIndex, FNodes.ShowingDelta);
        end;

      end;

    ntToggle:
      begin

        if CanUpdate([uoScrollBar]) then UpdateVertScrollBar;
        if CanUpdate([uoRepaint]) then Invalidate;
        UpdateInplaceEdit;

        { Event `OnExpandChange` }
        DoExpandChange(Node);
      end;
  end;
end;

procedure TNxCustomInspector6.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;
  case Key of
    VK_TAB:
      begin
        SelectNext(True);
        RemoveKeyMessage(Handle);
      end;
    VK_RETURN:
      begin
        if ioEnterSelectNext in Options then
        begin
          if not(ssShift in Shift) then SelectNext;
        end else
        begin
          AssignEdit;
        end;
      end;
    VK_DOWN:  SelectNext;
    VK_UP:    SelectPrevious;
    else
      if Assigned(FSelectedNode)
        and not InplaceEditActive then
      begin
        { Capture View }
        FCapturedView := FSelectedNode.View;

        { Key is var }
        FCapturedView.KeyDown(Key, Shift);

        { 12/26/11: View may set Key to 0, to prevent toggle }
        if not FSelectedNode.ExpandLock then
        begin
          case Key of
            VK_LEFT: FSelectedNode.Expanded := False;
            VK_RIGHT: FSelectedNode.Expanded := True;
          end;
          case Chr(Key) of
            '-':  FSelectedNode.Expanded := False;
            '+':  FSelectedNode.Expanded := True;
          end;
        end;
      end;

  end;
end;

procedure TNxCustomInspector6.KeyUp(var Key: Word; Shift: TShiftState);
begin
  inherited;
  if Key in [VK_RETURN, VK_DOWN, VK_UP]  then
  begin

  end else
    if Assigned(SelectedNode) then
    begin
      if Assigned(FCapturedView) then FCapturedView.KeyUp(Key, Shift);

      { No captured View }
      FCapturedView := nil;
    end;

end;

procedure TNxCustomInspector6.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited;
  { 11/1/11: SetFocus don't check
             focus state }
  if not Focused and CanFocus then SetFocus;

  { Mouse Button Down }
  if Button = mbLeft then Include(FState, isMouseDown);

  if SplitterHot then
  begin
    if ssDouble in Shift then
    begin
      BestFitSplitter;
      Exit;
    end;

    Include(FState, isSplitterMoving);

    Exit;
  end;

  if Assigned(ResizingReadyNode)
    and ResizingReadyNode.Resizeable then
  begin
    FResizingNode := ResizingReadyNode;

    Exit;
  end;

  { Set SelectedNode and WantFocus }
  SelectNode(GetNodeAtPos(X, Y), True);
//  SelectedNode := GetNodeAtPos(X, Y);

  FPressedNode := SelectedNode;

  if Assigned(SelectedNode) then
  begin
    { 11/10/11: Indicate INxItemView of "Down" Item }
    FCapturedView := SelectedNode.View;

    { Click on Toggle Button }
    if not SelectedNode.ExpandLock
      and not SelectedNode.Pinned
      and
            (PtInRect(FCapturedView.ToggleRect, Point(X, Y))
        or  (PtInRect(FCapturedView.CaptionRect, Point(X, Y)) and (ssDouble in Shift)))
    then
    begin
      SelectedNode.Expanded := not SelectedNode.Expanded;
    end;

    if itoDetailsButton in SelectedNode.Options then
    begin
      { Details Button State }
      DetailsButtonPressed := PtInRect(FCapturedView.DetailsButtonRect, Point(X, Y));
    end;

    { 8/5/11: If Readonly/Disabled still catch
              Clicks outside ContentRect }
    if Assigned(FCapturedView)
      and PtInRect(FCapturedView.ValueRect, Point(X, Y)) then
    begin

      { 12/22/11: Not ReadOnly & Enabled }
      if SelectedNode.CanInteract
        or not PtInRect(FCapturedView.ContentRect, Point(X, Y)) then
      begin

        { Not inside EditRect }
        if not SelectedNode.CanEdit
          or not PtInRect(FCapturedView.EditRect, Point(X, Y)) then
        begin
          { Process click to View }
          FCapturedView.MouseDown(Button, Shift, X, Y);
        end;

      end; { if CanInteract }

    end; { if PtInRect }

  end;

end;

procedure TNxCustomInspector6.MouseMove(Shift: TShiftState; X, Y: Integer);

  procedure SetPartialNode;
  begin
    if (icCaption in HotNode.VisibleCells)
      and PtInRect(HotNode.View.CaptionTextRect, Point(X, Y)) then
    begin
      CaptionHotNode := HotNode;
      Exit;
    end else CaptionHotNode := nil;

    if (icValue in HotNode.VisibleCells)
      and PtInRect(HotNode.View.ValueRect, Point(X, Y)) then
    begin
      ValueHotNode := HotNode;
      Exit;
    end else ValueHotNode := nil;
  end;

begin
  inherited;

  if not Assigned(FNodes) then Exit;

  { Splitter Moving is in progress }
  if isSplitterMoving in FState then
  begin
    SplitterPosition := X - MarginIndent;
    Exit;
  end;

  if Assigned(FResizingNode) then
  begin
    FResizingNode.NodeHeight := Y - GetNodeTop(FResizingNode);
    Invalidate;
    Exit;
  end;

  { Splitter Moving is enabled }
  if ioSplitterMoving in FOptions then
  begin
    { 10/31/11: Can't move, if View captured a Mouse }
    SplitterHot := PtInRect(SplitterResizingRect, Point(X, Y))
      and not (Assigned(FCapturedView) and FCapturedView.CaptureMouse);
  end;

  if SplitterHot then
  begin
    if Assigned(HotNode) then
      HotNode.View.MouseLeave;

    { Hide eventual Hint }
    CaptionHotNode := nil;
    ValueHotNode := nil;

    Exit; { Don't do any further actions }
  end;

  { Get Item if over SizeGrip }
  ResizingReadyNode := GetNodeAtPos(X, Y, True);

  if Assigned(FSelectedNode) and FSelectedNode.View.CaptureMouse then
  begin
    FSelectedNode.View.MouseMove(Shift, X, Y);
  end else
  begin
    if Assigned(HotNode) then
    begin
      if X < MarginIndent + SplitterPosition then HotNode.View.MouseLeave;
    end;

    HotNode := GetNodeAtPos(X, Y);

    { Delphi ObjectInspector behaviour }
    if Assigned(HotNode)
      and (X < SplitterPosition + MarginWidth)
      and (ioMouseMovingSelect in FOptions)
      and (isMouseDown in FState) then
    begin
      SelectedNode := HotNode;
    end;

    if Assigned(HotNode)
      and (itoDetailsButton in HotNode.Options) then
    begin
      if PtInRect(HotNode.View.DetailsButtonRect, Point(X, Y)) then
      begin
        DetailsHotNode := HotNode;

        Exit;
      end else DetailsHotNode := nil;

    end else DetailsHotNode := nil;

    if Assigned(HotNode) then
    begin

      { Set CaptionHotNode, ValueHotNode }
      SetPartialNode;

    end else
    begin
      CaptionHotNode := nil;
      ValueHotNode := nil;
    end;

    if Assigned(HotNode)
      and (not Assigned(FPressedNode) or (HotNode = FPressedNode)) then
    begin
      if PtInRect(HotNode.View.ValueRect, Point(X, Y)) then
      begin
        HotNode.View.MouseMove(Shift, X, Y);
      end;
    end;
  end;
end;

procedure TNxCustomInspector6.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;

  Exclude(FState, isMouseDown);

  { Splitter moving is ended }
  EndSplitterMoving;

  if Assigned(FCapturedView) then
  begin
    { Click on Details Button }
    if DetailsButtonPressed
      and PtInRect(FCapturedView.DetailsButtonRect, Point(X, Y)) then
    begin
      FCapturedView.Node.ShowDetails; { Trigger Event }
    end;

    { Un-press }
    DetailsButtonPressed := False;

    { Process MouseUp }
    FCapturedView.MouseUp(Button, Shift, X, Y);

    { 10/31/11: FView is not captured with
                Mouse anymore (!) }
    FCapturedView := nil;
  end;

  { Unlock }
  FPressedNode := nil;
  FResizingNode := nil;
end;

procedure TNxCustomInspector6.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if Operation = opRemove then
  begin
    if AComponent = FImages then Images := nil;
    if AComponent = FTextFitHint then FTextFitHint := nil;

  end;
end;

procedure TNxCustomInspector6.Paint;
begin
  inherited;

  { Hide hint on repaints }
  DeactivateHints;

  try
    PaintNodes(PaintPinned);
  finally
    { Set ClipRect back }
    SetClipRect(Canvas, ClientRect);
  end;
end;

procedure TNxCustomInspector6.PaintNode(const ANode: TNxInspectorNode6;
  NodeRect: TRect);
begin
  if Assigned(ANode.View) then
    with ANode.View do
    begin
      { 10/29/11: Owner will be set inside
                  TNxItemView constructor }

      { Set Canvas & other properties }
      SetNodeView(ANode.View);

      Control := Self;
      ANode.View.Style := Self.FActiveStyle;
      Style.AppearanceOptions := Self.AppearanceOptions;
      ClientRect := NodeRect;

      NodeState := [];
      if (SelectedNode = ANode) and InplaceEditActive then
      begin
        NodeState := [isEditing];
      end;

      DetailsButtonState := [];
      if (DetailsHotNode = ANode) then DetailsButtonState := DetailsButtonState + [bsHover];

      if SelectedNode = ANode then
      begin
        if FDetailsButtonPressed then DetailsButtonState := DetailsButtonState + [bsPressed];
      end;

      Selected := ANode = FSelectedNode;

      Paint;
    end;
end;

function TNxCustomInspector6.PaintNodes(Location: TPoint): TPoint;
var
  Y, i: Integer;
  NodeRect, ClipRect: TRect;
  PaintingNode: TNxInspectorNode6;
  PaintSpacing: Boolean;
begin
  Y := Location.Y;
  PaintSpacing := False;

  if PreciseScrolling then
    Dec(Y, VertScrollBar.Position);

  for i := FirstIndex to Pred(FNodes.Count) do
  begin
    PaintingNode := FNodes[i];

    if PaintingNode.Showing and
      not PaintingNode.Pinned then
    begin

      if PaintSpacing then
      begin
        with Canvas do
        begin
          Brush.Color := Color;
          FillRect(Rect(0, Y, ClientWidth, Y + FSpacing));
        end;
        Inc(Y, FSpacing);
      end else PaintSpacing := True;

      NodeRect := Bounds(0, Y, ClientWidth, GetNodeHeight(FNodes[i]));

      { Inside Invalidation rect ? }
      if RectVisible(Canvas.Handle, NodeRect) then
      begin

        try
          ClipRect := NodeRect;
          IntersectRect(ClipRect, ClipRect, ScrollableRect);

          SetClipRect(Canvas, ClipRect);

          PaintNode(PaintingNode, NodeRect);

        finally
          SetClipRect(Canvas, ClientRect);
        end;

      end;

      Inc(Y, GetNodeHeight(FNodes[i]));

      if Y > ClientHeight
        then Break;

      with Canvas do
      begin
        Brush.Color := Color;
        FillRect(Bounds(0, Y, ClientWidth, FSpacing));
      end;

      Inc(Y, FSpacing);

      if Y > ClientHeight
        then Break;

      if GridLines then
      begin
        if Y >= ScrollableRect.Top
          then DrawGrid(Y, FNodes[i]);

        Inc(Y, GridSize);
      end;

    end;

  end;

  if Y < ClientHeight then
  begin
    with Canvas do
    begin
      Brush.Color := Color;
      FillRect(Rect(0, Y, ClientWidth, ClientHeight));
    end;
  end;

end;

function TNxCustomInspector6.PaintPinned: TPoint;
var
  Index: Integer;
  UpdateRect, NodeRect: TRect;
  PaintingNode: TNxInspectorNode6;
  PaintSpacing: Boolean;
begin
  PaintPinned := Point(0, 0);
  PaintSpacing := False;

  { Get Invalidation Rect }
  GetClipBox(Canvas.Handle, UpdateRect);

  for Index := 0 to Pred(FNodes.PinnedCount) do
  begin
    PaintingNode := FNodes.Pinned[Index];

    if PaintingNode.Visible then
    begin

      { Upper Spacing }
      if PaintSpacing then
      begin
        with Canvas do
        begin
          Brush.Color := Color;
          FillRect(Rect(0, Result.Y, ClientWidth, Result.Y + FSpacing));
        end;
        Inc(Result.Y, FSpacing);
      end else PaintSpacing := True;

      NodeRect := Rect(0, Result.Y, ClientWidth, Result.Y + GetNodeHeight(PaintingNode));

      { Note: Draw Item only if is inside
              Invalidation Rect }
      if RectIntersect(NodeRect, UpdateRect) then
      begin
        PaintNode(PaintingNode, NodeRect);
      end;

      Inc(Result.Y, GetNodeHeight(PaintingNode));

      { Lower Spacing }
      with Canvas do
      begin
        Brush.Color := Color;
        FillRect(Rect(0, Result.Y, ClientWidth, Result.Y + FSpacing));
      end;

      Inc(Result.Y, FSpacing);

      if GridLines then
      begin
        DrawGrid(Result.Y, PaintingNode);

        Inc(Result.Y, GridSize);
      end;
    end;

  end;

  with Canvas do
  begin
    Pen.Color := BorderColor;
    Polyline([
      Point(0, Result.Y - GridSize),
      Point(ClientWidth, Result.Y - GridSize)
    ]);
  end;
end;

{$WARNINGS OFF}
procedure TNxCustomInspector6.ReadProperties(const AParent: TNxInspectorNode6);
var
  o: Integer;
  TypeKinds: TTypeKinds;

  procedure ReadProperty(const Instance: TObject; ANode: TNxInspectorNode6);
  var
    Info: TNxPropInfo;
    Accept: Boolean;
    PropName: string;
    PropValue: Variant;
    PropCount, i: Integer;
    PropList: PPropList;
    PropInfo: PPropInfo;
    TypeKind: TTypeKind;
    AClass: TClass;
    AObj: TObject;
    ClassItem, ANewNode: TNxInspectorNode6;
    NodeClass: TNxInspectorNodeClass;
  begin
    { Can add }
    Accept := True;

    PropCount := GetPropList(Instance.ClassInfo, TypeKinds, nil);

    GetMem(PropList, PropCount * SizeOf(Pointer));

    GetPropList(Instance.ClassInfo, TypeKinds, PropList);

    for i := 0 to Pred(PropCount) do
    begin
      PropName := PropList[i]^.Name;
      PropValue := GetPropValue(Instance, PropName, True);

      { Read Info }
      PropInfo := GetPropInfo(Instance, PropName);

      TypeKind := PropType(Instance, PropName);

      NodeClass := TNxInspectorNode6;

      case TypeKind of
        tkClass:
        begin
          AClass := GetObjectPropClass(Instance, PropList[i]);
          AObj := GetObjectProp(Instance, PropList[i], AClass);

          if Assigned(AObj) then
          begin
            NodeClass := TNxInspectorNode6;

            { Trigger event }
            //DoPropertyLoad(PropName, TypeKind, PropValue, AItemClass, Accept);

            { Should add? }
            if Accept then
            begin
              ClassItem := FNodes.Add(ANode, NodeClass);

              ClassItem.Caption := PropName;

              if ANode.BindProperty <> EmptyStr
                then ClassItem.BindProperty := ANode.BindProperty + '.';

              ClassItem.BindProperty := ClassItem.BindProperty + PropName;

              { Trigger event }
//              DoPropertyLoaded(ClassItem);

              { Recurse }
              ReadProperty(AObj, ClassItem);
            end;

          end;

          NodeClass := nil;
        end;

        tkString,
        tkWString,
        tkLString,
        {$IFDEF UNICODE}tkUString,{$ENDIF}
        tkWChar,
        tkChar,
        tkVariant: NodeClass := TNxInspectorNode6;

        tkInteger,
        tkFloat,
        tkInt64: NodeClass := TNxSpinInspectorNode6;

        tkSet: NodeClass := TNxToolbarInspectorNode6;

        tkEnumeration: NodeClass := TNxComboBoxInspectorNode6;
      end;

      if Assigned(NodeClass) then
      begin
        Accept := True;

        Info.Name := PropName;
        Info.Value := PropValue;
        Info.Kind := TypeKind;
        Info.Instance := Instance;

        { Trigger event }
        DoBeforeAddProperty(Info, NodeClass, Accept);

        { Should add? }
        if Accept then
        begin

          ANewNode := FNodes.Add(ANode, NodeClass);

          ANewNode.Caption := PropName;
          ANewNode.ReadOnly := PropInfo^.SetProc = nil;

          with ANewNode do
          begin
            { Sub-property }
            if ANewNode.BindProperty <> EmptyStr
              then BindProperty := ANode.BindProperty + '.';

            { Set BindProperty }
            BindProperty := BindProperty + PropName;

            AssignProperty(TypeKind);

            { Trigger event }
            DoPropertyNodeAdded(ANewNode);
          end;

        end;

      end; { Not sub-item }

    end; { for }

    FreeMem(PropList);
  end;


begin
  TypeKinds := [
    tkUnknown,
    tkInteger,
    tkChar,
    tkEnumeration,
    tkFloat,
    tkString,
    tkSet,
    tkClass,
    tkMethod,
    tkWChar,
    tkLString,
    tkWString,
    {$IFDEF UNICODE}tkUString,{$ENDIF}
    tkVariant,
    tkArray,
    tkRecord,
    tkInterface,
    tkInt64,
    tkDynArray
  ];

  for o := 0 to Pred(FAssociates.Count) do
    ReadProperty(FAssociates[o], AParent);
end;
{$WARNINGS ON}

procedure TNxCustomInspector6.RefreshNode(const Node: TNxInspectorNode6);
var
  Regions: TNxInspectorNodeParts;
begin
  Regions := [];
  if icCaption in Node.VisibleCells then Include(Regions, npCaption);
  if not (icValue in Node.VisibleCells) then Include(Regions, npValue);
  InvalidateNode(Node, Regions);
end;

procedure TNxCustomInspector6.ScrollContentBy(DeltaX, DeltaY: Integer);
var
  PixelDeltaY: Integer;
  ScrollingRect: TRect;
begin
  inherited;

  PixelDeltaY := 0;

  if PreciseScrolling then
  begin
    PixelDeltaY := -DeltaY;
  end
  else
  begin

    if DeltaY > 0 then

      repeat

        if FNodes[FFirstIndex].Showing then
        begin
          { Top spacing }
          Dec(PixelDeltaY, Spacing);

          Dec(PixelDeltaY, GetNodeHeight(FNodes[FFirstIndex]));

          { Grid }
          Dec(PixelDeltaY, GridSize);

          { Bottom spacing }
          Dec(PixelDeltaY, Spacing);

          Dec(DeltaY);
        end;

        Inc(FFirstIndex);

      until DeltaY = 0

    else if DeltaY < 0 then

      repeat
        Dec(FFirstIndex);

        if FNodes[FFirstIndex].Showing then
        begin
          { Top spacing }
          Inc(PixelDeltaY, Spacing);

          Inc(PixelDeltaY, GetNodeHeight(FNodes[FFirstIndex]));

          { Grid }
          Inc(PixelDeltaY, GridSize);

          { Bottom spacing }
          Inc(PixelDeltaY, Spacing);

          Inc(DeltaY);
        end;

      until DeltaY = 0

    else Exit; { Nothing to scroll }

  end;

  FCapturedView := nil;
  HotNode := nil;

  { Exclude Pinned }
  ScrollingRect := ClientRect;
  ScrollingRect.Top := PinnedHeight;

  { Do scroll }
  Self.ScrollRect(0, PixelDeltaY, ScrollingRect, ScrollingRect);
end;

procedure TNxCustomInspector6.ScrollRect(DeltaX, DeltaY: Integer; Rect,
  ClipRect: TRect);
begin
{$IFDEF UNSAFE}
  ScrollWindowEx(Handle, DeltaX, DeltaY, @Rect, @ClipRect, 0, nil, SW_INVALIDATE);
{$ELSE}
  ScrollWindowEx(Handle, DeltaX, DeltaY, Rect, ClipRect, 0, nil, SW_INVALIDATE);
{$ENDIF}
end;

procedure TNxCustomInspector6.ScrollToNode(const Node: TNxInspectorNode6);
var
  i, Y, NodeHeight: Integer;
  NodeRect: TRect;
begin
  NodeRect := GetNodeRect(Node);

  { On screen already? }
  if not RectInRect(ScrollableRect, NodeRect) then
  begin

    if NodeRect.Top < ScrollableRect.Top then
    begin
      i := FFirstIndex;

      Y := NodeRect.Top;

      if PreciseScrolling
        then VertScrollBar.MoveBy(NodeRect.Top - ScrollableRect.Top)
      else
        while (Y < ScrollableRect.Top) and (i >= 0) do
        begin

          if FNodes[i].Showing then
          begin
            NodeHeight := GetNodeHeight(FNodes[i]);

            { Top Spacing }
            Inc(Y, Spacing);

            { Actual Height of Item }
            Inc(Y, NodeHeight);

            Inc(Y, GridSize);

            { Bottom Spacing }
            Inc(Y, Spacing);

            { Note: This call a ScrollContentBy method. This
                    method don't call VM_VSCROLL }
            VertScrollBar.MoveBy(-1);

            { Warning: Re-calc Max & PageSize }
            UpdateVertScrollBar;
          end;

          Dec(i);

        end;

    end else
    begin
      i := FFirstIndex;

      Y := NodeRect.Top + GetNodeHeight(Node);

      if PreciseScrolling
        then VertScrollBar.MoveBy(NodeRect.Bottom - ScrollableRect.Bottom)
      else
        while (Y > ClientHeight) and (i < FNodes.Count) do
        begin

          if FNodes[i].Showing then
          begin
            NodeHeight := GetNodeHeight(FNodes[i]);

            { Top Spacing }
            Dec(Y, Spacing);

            { Actual Height of Item }
            Dec(Y, NodeHeight);

            Dec(Y, GridSize);

            { Bottom Spacing }
            Dec(Y, Spacing);

            { Note: This call a ScrollContentBy method }
            VertScrollBar.MoveBy(1);

            UpdateVertScrollBar;
          end;

          Inc(i);

        end;

    end;
  end;

end;

procedure TNxCustomInspector6.SelectNext;
var
  i, Start: Integer;
begin
  if Assigned(FSelectedNode) then
  begin
    { Start }
    if FSelectedNode.Pinned
      then Start := Succ(FSelectedNode.PinnedIndex)
      else Start := Succ(FSelectedNode.AbsoluteIndex);
  end
  else Start := 0;

  { Pinned is Selected, or Selected is NIL }
  if not Assigned(FSelectedNode)
      or FSelectedNode.Pinned then
  begin
    for i := Start to Pred(FNodes.PinnedCount) do
      if FNodes.Pinned[i].Visible then
      begin
        SelectNode(FNodes.Pinned[i], True);

        Exit; { Done }
      end;

    { Reach Last Pinned }
    Start := 0;
  end;

  { Processing Regular }
  for i := Start to Pred(FNodes.Count) do
    if FNodes[i].Showing then
    begin
      SelectNode(FNodes[i], True);

      Exit; { Done }
    end;

  { Last node reached }
  if CanFocusNextControl then SelectNextControl else
    if ioAssignEditOnLast in FOptions then AssignEdit;
end;

procedure TNxCustomInspector6.SelectNode(const Node: TNxInspectorNode6; WantFocus: Boolean);
var
  CanSelect: Boolean;
begin
  if Node <> FSelectedNode then
  begin
    CanSelect := True;

    DoSelectQuery(Node, CanSelect); { Trigger Event }

    if CanSelect then
    begin

      { Editing is in Progress }
      if Assigned(FInplaceEdit) then
      begin
        { 10/21/11: Focust must be moved from InplaceEdit
                    since it will be destroyed (!) }
        { 10/22/11: Commented at the moment. }

        { Apply & End Previous Session }
        AssignEdit;

        { Set Focus back to Inspector, only if requested
          (e.g. by selecting with Mouse) }
        if CanFocus and WantFocus then
        begin
          { DoExit will not called now if we switch focus
            between InplaceEdit & Inspector }

          SetFocus;
        end;

        { Trigger: OnAfterEdit & Destroy }
        EndEdit;

      end else
      begin
        { 12/22/11: InplaceEdit having priority than View.
                    If Item don't have InplaceEdit, Assign View }
        { Apply View of SelectedNode to Value }
        AssignView;

      end;

      { 8/13/2016 SetFocus back to Inspector now called
                  before EndEditing }

      if Assigned(FCapturedView) then
      begin
        FCapturedView.KeyUp;
        FCapturedView := nil;
      end;

      { Hide Hint, if set }
      DeactivateTextFitHint;

      if Assigned(Node)
        or (ioCanSelectNone in Options) then
      begin
        FPrevSelectedNode := FSelectedNode;

        { Set New SelectedNode }
        FSelectedNode := Node;

        { Refresh Old }
        if Assigned(FPrevSelectedNode) then RefreshNode(FPrevSelectedNode);

        if Assigned(FSelectedNode) then
        begin
          ScrollToNode(FSelectedNode);

          { Start Editing }
          EditNode(FSelectedNode);

          FSelectedNode.View.OnChange := DoNodeViewChange;

          RefreshNode(FSelectedNode);
        end;
      end;

      DoSelect; { Trigger Event }

    end; { CanSelect }

  end else
  begin
    { Selection not changed, but
      not editing (!) }

    if Assigned(FSelectedNode)
      and not Assigned(FInplaceEdit) then
    begin
      { Start Editing }
      EditNode(FSelectedNode);
    end;

  end;

end;

procedure TNxCustomInspector6.SelectNone;
begin
  SelectedNode := nil;
end;

procedure TNxCustomInspector6.SelectPrevious;
var
  i, Start: Integer;
begin
  if Assigned(FSelectedNode) then
  begin
    { Start }
    if FSelectedNode.Pinned
      then Start := Pred(FSelectedNode.PinnedIndex)
      else Start := Pred(FSelectedNode.AbsoluteIndex);
  end
  else Start := Pred(FNodes.Count);

  { Regular is Selected, or Selected is NIL }
  if  not Assigned(FSelectedNode)
      or not FSelectedNode.Pinned then
  begin
    for i := Start downto 0 do
      if FNodes[i].Showing then
      begin
        SelectNode(FNodes[i], True);

        Exit; { Done }
      end;

    Start := Pred(FNodes.PinnedCount); { Reach top }
  end;

  { Process Pinned }
  for i := Start downto 0 do
    if FNodes.Pinned[i].Visible then
    begin
      SelectNode(FNodes.Pinned[i], True);

      Exit; { Done }
    end;
end;

procedure TNxCustomInspector6.SetAllSaved;
var
  i: Integer;
begin
  for i := 0 to Pred(Nodes.Count) do
  begin
    Nodes[i].Saved := True;
  end;
end;

procedure TNxCustomInspector6.SetAppearanceOptions(const Value: TNxAppearanceOptions);
begin
  FAppearanceOptions := Value;
  Invalidate;
end;

procedure TNxCustomInspector6.SetCaptionHotNode(const Value: TNxInspectorNode6);
begin
  if Value <> FCaptionHotNode then
  begin
    DeactivateNodeHint;
    DeactivateTextFitHint;

    FCaptionHotNode := Value;

    if Assigned(FCaptionHotNode) then
    begin

      { Text-fit Hint first }
      if (FCaptionHotNode.Caption <> '')
        and not FCaptionHotNode.View.CanCaptionFit then
      begin
        ActivateCaptionFitHint(FCaptionHotNode);
        Exit;
      end;

      { `Hint` Prop. Hint }
      if FCaptionHotNode.Hint <> '' then
      begin
        ActivateNodeHint(FCaptionHotNode);
      end;

    end;

  end;
end;

procedure TNxCustomInspector6.SetDetailsButtonPressed(const Value: Boolean);
begin
  if Value <> FDetailsButtonPressed then
  begin
    FDetailsButtonPressed := Value;
    if Assigned(FCapturedView) then FCapturedView.RefreshDetailsButton;
  end;
end;

procedure TNxCustomInspector6.SetDetailsHotNode(const Value: TNxInspectorNode6);
var
  OldDetailsHoverItem: TNxInspectorNode6;
begin
  if Value <> FDetailsHotNode then
  begin

    OldDetailsHoverItem := FDetailsHotNode;

    FDetailsHotNode := Value;

    if Assigned(FDetailsHotNode)
      then CaptionHotNode := nil;

    if Assigned(OldDetailsHoverItem) then
      OldDetailsHoverItem.View.Refresh(OldDetailsHoverItem.View.DetailsButtonRect);

    if Assigned(FDetailsHotNode) then
      FDetailsHotNode.View.Refresh(FDetailsHotNode.View.DetailsButtonRect);
  end;
end;

procedure TNxCustomInspector6.SetFilter(const Value: WideString);
begin
  if Value <> FFilter then
  begin
    EndEdit;

    // TODO: Maybe call it better?
    Clear;

    FFilter := Value;

    FNodes.FilterText := FFilter;

    { Set Position, Max & PageSize to 0, but
      not update }
    VertScrollBar.Clear;

    UpdateVertScrollBar;

    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetGlyphCollapse(const Value: TBitmap);
begin
  if FGlyphCollapse <> Value then
  begin
    FGlyphCollapse.Assign(Value);
    FActiveStyle.SetToggleGlyphs(FGlyphExpand, FGlyphCollapse);

    if Assigned(FGlyphCollapse)
      then FGlyphCollapse.OnChange := DoGlyphChange;
  end;
end;

procedure TNxCustomInspector6.SetGlyphExpand(const Value: TBitmap);
begin
  if FGlyphExpand <> Value then
  begin
    FGlyphExpand.Assign(Value);
    FActiveStyle.SetToggleGlyphs(FGlyphExpand, FGlyphCollapse);

    if Assigned(FGlyphExpand)
      then FGlyphExpand.OnChange := DoGlyphChange;
  end;
end;

procedure TNxCustomInspector6.SetGridColor(const Value: TColor);
begin
  if Value <> FGridColor then
  begin
    FGridColor := Value;
    StyleChanged;
  end;
end;

procedure TNxCustomInspector6.SetGridLines(const Value: Boolean);
begin
  if Value <> FGridLines then
  begin
    FGridLines := Value;
    StyleChanged;
  end;
end;

procedure TNxCustomInspector6.SetGridLinesStyle(const Value: TNxInspectorGridLinesStyle);
begin
  FGridLinesStyle := Value;
  Invalidate;
end;

procedure TNxCustomInspector6.SetGridPadding(const Value: Integer);
begin
  if Value <> FGridPadding then
  begin
    FGridPadding := Value;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetHideTopLevel(const Value: Boolean);
begin
  FHideTopLevel := Value;
  { Notify FNodes }
  FNodes.HideTopLevel := FHideTopLevel;
  Invalidate;
end;

procedure TNxCustomInspector6.SetHotNode(const Value: TNxInspectorNode6);
begin
  if Value <> FHotNode then
  begin
    DeactivateNodeHint;

    if Assigned(FHotNode) then FHotNode.View.MouseLeave;

    { HotNode is changed, so and FCaptionHoverItem }
    CaptionHotNode := nil;

    { HotNode is changed, so and FDetailsHoverItem }
    DetailsHotNode := nil;

    ValueHotNode := nil;

    { Set New }
    FHotNode := Value;

    if Assigned(FHotNode) then
    begin
      FHotNode.View.Canvas := Canvas;
      FHotNode.View.ClientRect := GetNodeRect(FHotNode);

      FHotNode.View.PreviewPopup := ioPreviewPopup in Options;
    end;

  end;
end;

procedure TNxCustomInspector6.SetImagePosition(const Value: TNxNodeIconPosition);
begin
  FImagePosition := Value;
  Invalidate;
end;

procedure TNxCustomInspector6.SetImages(const Value: TCustomImageList);
begin
  if FImages <> Value then
  begin
    if Assigned(FImages) and not Assigned(Value)
      then RemoveFreeNotification(FImages);
    FImages := Value;
    if Assigned(FImages) then FreeNotification(FImages);
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetInactiveSelectionColor(const Value: TColor);
begin
  if Value <> FInactiveSelectionColor then
  begin
    FInactiveSelectionColor := Value;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetInplaceEditColor(const Value: TColor);
begin
  if Value <> FInplaceEditColor then
  begin
    FInplaceEditColor := Value;
    if InplaceEditActive then FInplaceEdit.Color := FInplaceEditColor;
  end;
end;

procedure TNxCustomInspector6.SetNodeHeight(const Value: Integer);
begin
  if Value <> FNodeHeight then
  begin
    FNodeHeight := Value;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetNodeView(View: INxInspectorNodeView);
begin
  AssignNodeView(View);

  View.Options := [];
  if ioModifiedIndicators in Options
    then View.Options := View.Options + [voModifiedIndicators];
end;

procedure TNxCustomInspector6.SetMarginColor(const Value: TColor);
begin
  if Value <> FMarginColor then
  begin
    FMarginColor := Value;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetMarginWidth(const Value: Integer);
begin
  if Value <> FMarginWidth then
  begin
    FMarginWidth := Value;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetOptions(const Value: TNxInspectorOptions);
begin
  FOptions := Value;
  Invalidate;
end;

procedure TNxCustomInspector6.SetPreciseScrolling(const Value: Boolean);
begin
  if Value <> FPreciseScroling then
  begin
    FPreciseScrolling := Value;
    VertScrollBar.Clear;
    UpdateVertScrollBar;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetResizingReadyNode(const Value: TNxInspectorNode6);
begin
  if FResizingReadyNode <> Value then
  begin
    if Assigned(Value) and Value.Resizeable then
    begin
      { Property is Set }
      FResizingReadyNode := Value;
    end
    else FResizingReadyNode := nil;

    if Assigned(FResizingReadyNode)
      then Cursor := crSizeNS
      else Cursor := crDefault;
  end;
end;

procedure TNxCustomInspector6.SetSelectedNode(const Value: TNxInspectorNode6);
begin
  SelectNode(Value, False);
end;

procedure TNxCustomInspector6.SetSelectionColor(const Value: TColor);
begin
  FSelectionColor := Value;
  Invalidate;
end;

procedure TNxCustomInspector6.SetSelectionTextColor(const Value: Tcolor);
begin
  if Value <> FSelectionTextColor then
  begin
    FSelectionTextColor := Value;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetShowMargin(const Value: Boolean);
begin
  FShowMargin := Value;
  Invalidate;
end;

procedure TNxCustomInspector6.SetSpacing(const Value: Integer);
begin
  FSpacing := Value;
  Invalidate;
end;

procedure TNxCustomInspector6.SetSplitterColor(const Value: TColor);
begin
  if Value <> FSplitterColor then
  begin
    FSplitterColor := Value;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetSplitterHot(const Value: Boolean);
begin
  if Value <> FSplitterHot then
  begin
    FSplitterHot := Value;
    if FSplitterHot
      then Cursor := crSizeWE { ToDo: Add SplitterCursor property }
      else Cursor := crDefault;

    { Deactivate hint if shown }
    CaptionHotNode := nil;
    ValueHotNode := nil;

    DetailsHotNode := nil;
  end;
end;

procedure TNxCustomInspector6.SetSplitterMinPosition(const Value: Integer);
begin
  if Value <> FSplitterMinPosition then
  begin
    FSplitterMinPosition := Value;
    if SplitterPosition < FSplitterMinPosition then
      SplitterPosition := FSplitterMinPosition;
  end;
end;

procedure TNxCustomInspector6.SetSplitterPosition(const Value: Integer);
begin
  if Value <> FSplitterPosition then
  begin
    FSplitterPosition := Value;

    { Limit }
    FSplitterPosition := Max(FSplitterMinPosition, FSplitterPosition);

    { Resize InplaceEdit }
    if InplaceEditActive then
    begin
      UpdateInplaceEdit;
    end;

    UpdateVertScrollBar;

    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetStyle(const Value: TNxAppearanceStyle);
var
  UserStyled: INxUserStyled;
begin
  if (Value <> FStyle)
    or not Assigned(FActiveStyle) then
  begin
    FStyle := Value;

    { Release existing Style }
    FActiveStyle := nil;

    { Create new Style object }
    case FStyle of
      stNative: FActiveStyle := TNxNativeInspectorStyle.Create(Self);
      stModern: FActiveStyle := TNxModernInspectorStyle.Create(Self);
      stUserDefined:
      begin
        FActiveStyle := TNxUserDefinedInspectorStyle.Create(Self);
        if Supports(FActiveStyle, INxUserStyled, UserStyled) then
          UserStyled.UserDefinedColorPalette := UserDefinedColorPalette;
      end;
    end;

    if Assigned(FActiveStyle) then
    begin
      FActiveStyle.Control := Self;
      FActiveStyle.Canvas := Self.Canvas;
    end;

    StyleChanged;
  end;
end;

procedure TNxCustomInspector6.SetToggleStyle(const Value: TNxToggleButtonStyle);
begin
  if Value <> FToggleStyle then
  begin
    FToggleStyle := Value;
    if Assigned(FActiveStyle) then
    begin
      FActiveStyle.ToggleStyle := FToggleStyle;
    end;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetTopLevelColor(const Value: TColor);
begin
  if Value <> FTopLevelColor then
  begin
    FTopLevelColor := Value;
    FActiveStyle.TopLevelColor := FTopLevelColor;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetUserDefinedColorPalette(
  const Value: TNxUserDefinedColorPalette);
begin
  FUserDefinedColorPalette := Value;
end;

procedure TNxCustomInspector6.SetUseTopLevelColor(const Value: Boolean);
begin
  if Value <> FUseTopLevelColor then
  begin
    FUseTopLevelColor := Value;
    Invalidate;
  end;
end;

procedure TNxCustomInspector6.SetValueHotNode(const Value: TNxInspectorNode6);
begin
  if Value <> FValueHotNode then
  begin
    DeactivateTextFitHint;

    FValueHotNode := Value;

    if Assigned(FValueHotNode) then
    begin

      { Text-fit Hint first }
      if (FValueHotNode.Value <> '')
        and not FValueHotNode.View.CanValueFit
          then ActivateValueFitHint(FValueHotNode);

    end;
  end;
end;

procedure TNxCustomInspector6.StyleChanged;
begin
  if Assigned(FActiveStyle) then
  begin
    FActiveStyle.GridColor := GridColor;
    FActiveStyle.InactiveSelectionColor := InactiveSelectionColor;
    FActiveStyle.SelectionColor := SelectionColor;
    FActiveStyle.SelectionTextColor := SelectionTextColor;
    FActiveStyle.SetToggleGlyphs(GlyphExpand, GlyphCollapse);
    FActiveStyle.SplitterColor := SplitterColor;
    FActiveStyle.ToggleStyle := ToggleStyle;
    FActiveStyle.TopLevelColor := TopLevelColor;
  end;
  inherited StyleChanged;
end;

procedure TNxCustomInspector6.UpdateInplaceEdit;
var
  EditRect, ContainerRect: TRect;
begin
  if Assigned(FSelectedNode)
    and not FSelectedNode.IsDestroying
    and Assigned(FInplaceEdit) then
  begin

    if FSelectedNode.Showing
      or FSelectedNode.PinnedShowing then
    begin
      FSelectedNode.View.ClientRect := GetNodeRect(FSelectedNode);

      { Assign global Inspector properties to View
        before calculating `EditRect` }
      AssignNodeView(FSelectedNode.View);

      EditRect := FSelectedNode.View.EditRect;

      if FSelectedNode.Pinned then
        ContainerRect := ClientRect
      else
        ContainerRect := ScrollableRect;

      { Update Position & Size of InplaceEdit }
      if RectInRect(ContainerRect, EditRect) then
      begin

        FInplaceEdit.SetBounds(EditRect.Left, EditRect.Top,
          RectWidth(EditRect), RectHeight(EditRect));

        FInplaceEdit.Show;

        if not FInplaceEdit.Focused
          then FInplaceEdit.SetFocus;

      end else
      begin
        FInplaceEdit.Hide;
        SetFocus;
      end;
    end
    else
      EndEdit;

  end;
end;

procedure TNxCustomInspector6.UpdateScrollBars;
begin
  UpdateVertScrollBar;
end;

procedure TNxCustomInspector6.UpdateVertScrollBar;

  function GetPixelHeight: Integer;
  var
    Index: Integer;
    CurrentItem: TNxInspectorNode6;
  begin
    Result := 0;

    Index := 0;

    while Index < FNodes.Count do
    begin
      CurrentItem := FNodes[Index];

      if CurrentItem.Showing then
      begin

        { Top Spacing }
        Inc(Result, Spacing);

        { Actual Height of Item }
        Inc(Result, GetNodeHeight(CurrentItem));

        { Bottom Spacing }
        Inc(Result, Spacing);

        Inc(Result, GridSize);

      end;

      Inc(Index);
    end;
  end;

  function GetPageSize: Integer;
  var
    Index, Pos: Integer;
    CurrentItem: TNxInspectorNode6;
  begin
    { Start from Top }
    Pos := PinnedHeight;

    Result := 0;

    Index := FFirstIndex;

    while Index < FNodes.Count do
    begin
      CurrentItem := FNodes[Index];

      if CurrentItem.Showing then
      begin

        { Top Spacing }
        Inc(Pos, Spacing);

        { Actual Height of Item }
        Inc(Pos, GetNodeHeight(CurrentItem));

        { Bottom Spacing }
        Inc(Pos, Spacing);

        Inc(Pos, GridSize);

        if Pos > ClientHeight then
        begin
          Dec(Result);

          Exit;
        end;

        Inc(Result);
      end;

      Inc(Index);
    end;
  end;

begin
  if PreciseScrolling then
  begin
    VertScrollBar.SetValues(GetPixelHeight, ClientHeight - PinnedHeight);
  end else
  begin
    VertScrollBar.SetValues(Pred(FNodes.ShowingCount), GetPageSize);
  end;
end;

procedure TNxCustomInspector6.WMActivateApp(var Message: TWMActivateApp);
begin
  inherited;
	if not Message.Active then
  begin
    { Reset all Stuff when ALT + TAB is pressed }

    { Hide Hint, eventualy }
    CaptionHotNode := nil;
    ValueHotNode := nil;

    { Release Mouse }
    Exclude(FState, isMouseDown);
  end;
end;

procedure TNxCustomInspector6.WMEraseBkGnd(var Message: TWMEraseBkGnd);
begin
  Message.Result := 1;
end;

procedure TNxCustomInspector6.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  inherited;
  Message.Result := Message.Result or DLGC_WANTCHARS or DLGC_WANTARROWS;
end;

procedure TNxCustomInspector6.WMKillFocus(var Message: TWMSetFocus);
begin
  inherited;
  if not Focused then
  begin
    { Release Mouse }
    Exclude(FState, isMouseDown);
  end;
end;

procedure TNxCustomInspector6.WMSize(var Message: TWMSize);
begin
  inherited;
  UpdateInplaceEdit;
  UpdateVertScrollBar;
end;

procedure TNxCustomInspector6.WMVScroll(var Message: TWMVScroll);
begin
  inherited;

  UpdateInplaceEdit;

  { ToDo: Add BeginUpdate/EndUpdate check }
  UpdateVertScrollBar;
end;

end.
