{ ------------------------------------------------------------- }
{ Purpose : NextGrid 6                                          }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2012. All Rights Reserved.         }
{ ------------------------------------------------------------- }
{$I '..\NxSuite6.inc'}

unit NxColumnViewClasses6;

interface

uses
  { Standard }
  Classes, Types, Controls, Windows, Graphics, ImgList, ExtCtrls,
  {$IFDEF UI_TYPES}UITypes,{$ENDIF}
  { Next Standard 6 }
  NxIntf6, NxClasses6, NxTypes6, NxConsts6, NxViews6, NxHTML6,
  { NextGrid 6 }
  NxColumns6, NxCells6, NxGridView6;

const
  szCheckBox = 11;
  szCheckBoxThemed = 13;
  szTreeLevelIndent = 19;

const
  DefaultCheckBoxSize: array[Boolean] of Integer = (szCheckBox, szCheckBoxThemed);

type
  INxRowView = interface
    ['{63AE1ACF-9D8C-4FE1-8882-4B82F52915F5}']
    function GetRow: INxRow;
    procedure SetRow(const Value: INxRow);
    property Row: INxRow read GetRow write SetRow;
  end;

  TNxColumnViewClass = class of TNxColumnView;

  { TNxTextColumnView }

  TNxTextColumnView = class(TNxColumnView)
  private
    function GetWrapable: INxWrapable;
  protected
    procedure Changed; override;
    function GetBestSize: TSize; override;
    function GetTextRect: TRect; virtual;
  public
    procedure PaintContent; override;
    property Wrapable: INxWrapable read GetWrapable;
  end;

  { TNxButtonColumnView }

  TNxButtonColumnView = class(TNxTextColumnView)
  private
    function GetAlwaysShowButton: Boolean;
    function GetButtonColumn: TNxButtonColumn6;
    function GetButtonRect: TRect;
  protected
    function GetBestSize: TSize; override;
    function GetInteractiveRect: TRect; override;
    function GetTextRect: TRect; override;
    procedure StateChanged(Value: TNxButtonState); override;
  public
    function IsResponsive: Boolean; override;
    { Mouse }
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseLeave; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); overload; override;
    procedure MouseUp; overload; override;
    { Paint }
    procedure PaintContent; override;
    { Properties }
    property AlwaysShowButton: Boolean read GetAlwaysShowButton;
    property ButtonColumn: TNxButtonColumn6 read GetButtonColumn;
    property ButtonRect: TRect read GetButtonRect;
  end;

  { TNxMemoColumnView }

  TNxMemoColumnView = class(TNxColumnView)
  protected
    function GetBestSize: TSize; override;
  public
    procedure PaintContent; override;
  end;

  { TNxCheckBoxColumnView }

  TNxCheckBoxColumnView = class(TNxColumnView)
  private
    function GetAsBoolean: Boolean;
    function GetCheckBoxColumn: INxCheckBoxColumn;
    function GetCheckBoxSize: TSize;
    procedure SetAsBoolean(const Value: Boolean);
  protected
    function GetBestSize: TSize; override;
    function GetCheckBoxRect: TRect; virtual;
    function GetInteractiveRect: TRect; override;
    procedure Changed; override;
    procedure StateChanged(Value: TNxButtonState); override;
  public
    { Overrided Methods }
    function CanDispatchKey(var Key: Word): Boolean; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp; overload; override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); overload; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseLeave; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); overload; override;
    procedure MouseUp; overload; override;
    function IsResponsive: Boolean; override;
    procedure PaintContent; override;
    { Properties }
    property AsBoolean: Boolean read GetAsBoolean write SetAsBoolean;
    property CheckBoxRect: TRect read GetCheckBoxRect;
    property CheckBoxSize: TSize read GetCheckBoxSize;
    property CheckBoxColumn: INxCheckBoxColumn read GetCheckBoxColumn;
  end;

  { TNxToolbarColumnView }

  TNxToolbarColumnView = class(TNxColumnView)
  private
    FButtonHint: THintWindow;
    FHintDelayTimer: TTimer;
    FHintHideTimer: TTimer;
    FHoverButton: Integer;
    FDownButton: Integer;
    function GetButton(Index: Integer): TNxToolbarColumnButton;
    function GetCollection: INxToolbarCollection;
    procedure SetDownButton(const Value: Integer);
    procedure SetHoverButton(const Value: Integer);
  protected
    procedure ActivateButtonHint(Button: TNxToolbarColumnButton);
    procedure DoHintDelayTimer(Sender: TObject);
    procedure DoHintHideTimer(Sender: TObject);
    procedure ReleaseButtonHint;
    procedure PaintDivider(DividerRect: TRect);
    procedure PaintButton(ButtonRect: TRect; Text: WideString;
      ButtonState: TNxButtonState; ImageIndex: TImageIndex);
    procedure Reset; override;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure Assign(Source: TObject); override;
    function GetButtonAtPos(Location: TPoint): Integer;
    function GetButtonRect(const Index: Integer): TRect;
    function GetButtonWidth(const Index: Integer): Integer; virtual;
    { Overrided Methods }
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseLeave; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); overload; override;
    procedure MouseUp; overload; override;
    function IsResponsive: Boolean; override;
    procedure PaintContent; override;
    procedure RefreshButton(const Index: Integer);
    { Properties }
    property Button[Index: Integer]: TNxToolbarColumnButton read GetButton;
    property Collection: INxToolbarCollection read GetCollection;
    property DownButton: Integer read FDownButton write SetDownButton;
    property HoverButton: Integer read FHoverButton write SetHoverButton;
  end;

  { TNxIconColumnView }

  TNxIconColumnView = class(TNxColumnView)
  private
    FHintDelayTimer: TTimer;
    FHintWindow: THintWindow;
    function GetButtonTextRect: TRect;
    function GetIconColumn: TNxIconColumn6;
    function GetIconRect: TRect;
  protected
    procedure ActivateHint;
    procedure Changed; override;
    procedure DoHintDelayTimer(Sender: TObject);
    function GetBestSize: TSize; override;
    function HasIcon: Boolean;
    procedure ReleaseHint;
    procedure StateChanged(Value: TNxButtonState); override;
    procedure UpdateHintWindow;
  public
    destructor Destroy; override;
    function IsResponsive: Boolean; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); overload; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseLeave; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure PaintContent; override;
    { Properties }
    property ButtonTextRect: TRect read GetButtonTextRect;
    property IconColumn: TNxIconColumn6 read GetIconColumn;
    property IconRect: TRect read GetIconRect;
  end;

  { TNxSideEditColumnView }

  TNxSideEditColumnView = class(TNxColumnView)
  protected
    procedure Changed; override;
    function GetBestSize: TSize; override;
    function GetInteractiveRect: TRect; override;
  public
    procedure PaintContent; override;
  end;

  { TNxIconColumnView }

  TNxProgressColumnView = class(TNxSideEditColumnView)
  private
    FView: TNxProgressView;
    function GetProgressColumn: TNxProgressColumn6;
  protected
    procedure Changed; override;
    function GetBestSize: TSize; override;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure PaintContent; override;
    { Properties }
    property ProgressColumn: TNxProgressColumn6 read GetProgressColumn;
  end;

  { TNxGraphicColumnView }

  TNxGraphicColumnView = class(TNxColumnView)
  private
    function GetGraphic: TGraphic;
    function GetGraphicColumn: TNxGraphicColumn6;
  protected
    function GetBestSize: TSize; override;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure PaintContent; override;
    { Properties }
    property Graphic: TGraphic read GetGraphic;
    property GraphicColumn: TNxGraphicColumn6 read GetGraphicColumn;
  end;

  { TNxTrackBarColumnView }

  TNxTrackBarColumnView = class(TNxSideEditColumnView)
  private
    FSliderHint: THintWindow;
    function GetAsFloat: Double;
    function GetTrackRect: TRect;
    procedure SetAsFloat(const Value: Double);
  protected
    function GetSliderRect: TRect; virtual;
    function GetSliderSize: TSize; virtual;
    function GetTrackWidth: Integer; virtual;
    procedure ActivateSliderHint;
    procedure Changed; override;
    procedure DrawClassicTrackBar; virtual;
    procedure DrawDelphiStyledTrackBar; virtual;
    procedure DrawNativeStyledTrackBar; virtual;
    procedure ReleaseSliderHint;
    procedure StateChanged(Value: TNxButtonState); override;
    procedure UpdateSliderHint;
  protected
    property AsFloat: Double read GetAsFloat write SetAsFloat;
  public
    destructor Destroy; override;
    function GetPositionAtPos(const X, Y: Integer): Double;
    function IsResponsive: Boolean; override;
    { Mouse }
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseLeave; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); overload; override;
    { Painting }
    procedure PaintContent; override;
    { Properties }
    property TrackRect: TRect read GetTrackRect;
    property SliderRect: TRect read GetSliderRect;
  end;

  { TNxHTMLColumnView }

  TNxHTMLColumnView = class(TNxColumnView)
  private
    FView: TNxHTMLView;
  protected
    function GetBestSize: TSize; override;
    function GetDefaultStyle: TNxCSSStyle;
  public
    constructor Create; override;
    destructor Destroy; override;
    function IsResponsive: Boolean; override;
    { Mouse }
    procedure MouseLeave; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); overload; override;
    { Painting }
    procedure PaintContent; override;
  end;

  { TNxTreeColumnView }

  TNxTreeColumnView = class(TNxTextColumnView, INxRowView, INxTreeNodeView)
  private
    FIsFirst: Boolean;
    FIsLast: Boolean;
    FRow: IWeakReference;
    FToggleState: TNxButtonState;
    function GetImageRect: TRect;
    function GetImageSize: TSize;
    function GetIsFirst: Boolean;
    function GetIsLast: Boolean;
    function GetLevel: Integer;
    function GetLevelIndent: Integer;
    function GetRow: INxRow;
    function GetTreeProperties: INxTreeProperties;
    procedure SetIsFirst(const Value: Boolean);
    procedure SetIsLast(const Value: Boolean);
    procedure SetRow(const Value: INxRow);
    procedure SetToggleState(const Value: TNxButtonState);
  protected
    function GetBestSize: TSize; override;
    function GetInteractiveRect: TRect; override;
    function GetNonTextSpace: Integer;
    function GetTextRect: TRect; override;
    function GetToggleRect: TRect; virtual;
    function GetToggleSize: TSize; virtual;
    procedure PaintImage;
    procedure PaintLines;
    procedure PaintToggleButton;
    { Properties }
    property ImageSize: TSize read GetImageSize;
    property ImageRect: TRect read GetImageRect;
    property ToggleRect: TRect read GetToggleRect;
    property ToggleSize: TSize read GetToggleSize;
    property ToggleState: TNxButtonState read FToggleState write SetToggleState;
  public
    procedure AdjustEditRect(var EditRect: TRect); override;
    procedure Assign(Source: TObject); override;
    destructor Destroy; override;
    function HasImage: Boolean;
    function HasImageSpace: Boolean;
    function HasLines: Boolean;
    function HasToggleButton: Boolean;
    function HasToggleButtonSpace: Boolean;
    function IsClientRectToggle: Boolean;
    function IsResponsive: Boolean; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseLeave; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure PaintContent; override;
    { Properties }
    property LevelIndent: Integer read GetLevelIndent;
    property Level: Integer read GetLevel;
    property Row: INxRow read GetRow write SetRow;
    property TreeProperties: INxTreeProperties read GetTreeProperties;
  end;

  { TNxRateColumnView }

  TNxRateColumnView = class(TNxColumnView)
  private
    function GetRateViewOwner: INxRateViewOwner;
    function GetPosition: Integer;
    procedure SetPosition(const Value: Integer);
  protected
    function GetBestSize: TSize; override;
    function GetGlyphsRect: TRect;
    property RateViewOwner: INxRateViewOwner read GetRateViewOwner;
  public
    function GetPositionAtPos(X, Y: Integer): Integer;
    function IsResponsive: Boolean; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure PaintContent; override;
    property Position: Integer read GetPosition write SetPosition;
  end;

  { TNxColorColumnView }

  TNxColorColumnView = class(TNxColumnView)
  protected
    procedure PaintContent; override;
  end;

implementation

uses
  { Standard }
  StdCtrls, Math, SysUtils, StrUtils, Forms,
  { Next Standard 6 }
  NxGraphics6, NxStyles6, NxStylePaint6, NxEdit6,
  { Themes }
{$IFDEF THEMES}
  UxTheme
{$ELSE}
  NxTheme6
{$ENDIF}
;

{ TNxTextColumnView }

procedure TNxTextColumnView.Changed;
begin
  InvalidateRect(ClientRect);
end;

function TNxTextColumnView.GetBestSize: TSize;
var
  DisplayText: WideString;
begin
  Result := inherited GetBestSize;
  with Result do
  begin
    DisplayText := Column.GetDisplayText(Cell, Location.X, Location.Y);
    if Column.Resizeable then
    begin
      cx := GetTextWidth(Canvas, DisplayText, wkNone);
      cy := GetTextHeight(Canvas, DisplayText, Wrapable.WrapKind = wkWordWrap);
    end else
    begin
      cx := GetTextWidth(Canvas, DisplayText, Column.ClientWidth);
      cy := GetTextHeight(Canvas, Column.ClientWidth, DisplayText);
    end;
  end;
end;

function TNxTextColumnView.GetTextRect: TRect;
begin
  Result := ContentRect;
end;

function TNxTextColumnView.GetWrapable: INxWrapable;
begin
  Supports(Column, INxWrapable, Result);
end;

procedure TNxTextColumnView.PaintContent;
begin
  DrawTextRect(Canvas, GetTextRect, Column.Alignment, Column.VerticalAlignment, Column.GetDisplayText(Cell, Location.X, Location.Y), Wrapable.WrapKind);
end;

{ TNxButtonColumnView }

function TNxButtonColumnView.GetAlwaysShowButton: Boolean;
begin
  Result := TNxButtonColumn6(Column).AlwaysShowButton;
end;

function TNxButtonColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  if AlwaysShowButton then Inc(Result.cx, ButtonColumn.ButtonWidth);
end;

function TNxButtonColumnView.GetButtonColumn: TNxButtonColumn6;
begin
  Result := TNxButtonColumn6(Column);
end;

function TNxButtonColumnView.GetButtonRect: TRect;
begin
  Result := ContentRect;
  if ButtonColumn.ButtonHeight > 0 then
  begin
    Result.Bottom := Result.Top + ButtonColumn.ButtonHeight;
    OffsetRect(Result, 0, RectHeight(ContentRect) div 2 - RectHeight(Result) div 2);
  end;
  Result.Left := Result.Right - ButtonColumn.ButtonWidth;
end;

function TNxButtonColumnView.GetInteractiveRect: TRect;
begin
  Result := ButtonRect;
end;

function TNxButtonColumnView.GetTextRect: TRect;
begin
  Result := inherited GetTextRect;
  if AlwaysShowButton then Result.Right := InteractiveRect.Left;
end;

function TNxButtonColumnView.IsResponsive: Boolean;
begin
  Result := AlwaysShowButton;
end;

procedure TNxButtonColumnView.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if PtInRect(ButtonRect, Point(X, Y)) then State := State + [bsPressed]
    else State := State - [bsPressed];
end;

procedure TNxButtonColumnView.MouseLeave;
begin
  State := State - [bsHover];
end;

procedure TNxButtonColumnView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if PtInRect(ButtonRect, Point(X, Y)) then State := State + [bsHover]
    else State := State - [bsHover];
end;

procedure TNxButtonColumnView.MouseUp;
begin
  State := State - [bsPressed];
end;

procedure TNxButtonColumnView.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if bsPressed in State then
  begin
    State := State - [bsPressed];
    if PtInRect(InteractiveRect, Point(X, Y)) then
    begin
      if Assigned(ButtonColumn.OnButtonClick)
        then ButtonColumn.OnButtonClick(Self);
    end;
  end;
end;

procedure TNxButtonColumnView.PaintContent;
var
  ButtonState: TNxButtonState;
begin
  inherited;
  with ButtonColumn do
  begin
    if AlwaysShowButton then
    begin
      ButtonState := State;
      if not Column.Enabled then Include(ButtonState, bsDisabled);

      TNxButtonStyle6.Paint(Canvas, Control.Handle, ButtonRect, ButtonState,
        [soVCLStyles, soNativeStyles], False);

      TNxExecuteButton6.PaintInRect(Canvas, ButtonRect, ButtonGlyph, ButtonCaption);
    end;
  end;
end;

procedure TNxButtonColumnView.StateChanged(Value: TNxButtonState);
begin
  InvalidateRect(ButtonRect);
end;

{ TNxMemoColumnView }

function TNxMemoColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  with Result, Location do
  begin
    cx := GetTextWidth(Canvas, Column.GetDisplayText(Cell, X, Y), wkNone);
    cy := GetTextHeight(Canvas, Column.GetDisplayText(Cell, X, Y), True);
  end;
end;

procedure TNxMemoColumnView.PaintContent;
begin
  DrawTextRect(Canvas, ContentRect, Column.Alignment, Column.VerticalAlignment, Column.GetDisplayText(Cell, Location.X, Location.Y), wkWordWrap);
end;

{ TNxCheckBoxColumnView }

function TNxCheckBoxColumnView.CanDispatchKey(var Key: Word): Boolean;
begin
  Result := (Key = VK_SPACE)
    or (WideChar(Chr(Key)) = LeftStr(BoolToStr(True, True), 1))
    or (WideChar(Chr(Key)) = LeftStr(BoolToStr(False, True), 1));
end;

procedure TNxCheckBoxColumnView.Changed;
begin
  InvalidateRect(CheckBoxRect);
end;

function TNxCheckBoxColumnView.GetAsBoolean: Boolean;
begin
  Result := (Column as INxCheckBoxColumn).GetValue(Cell, Location.X, Location.Y);
end;

function TNxCheckBoxColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  with Result do
  begin
    cx := DefaultCheckBoxSize[Themed];
    cy := DefaultCheckBoxSize[Themed];
  end;
end;

function TNxCheckBoxColumnView.GetCheckBoxColumn: INxCheckBoxColumn;
begin
  Result := Column as INxCheckBoxColumn;
end;

function TNxCheckBoxColumnView.GetCheckBoxRect: TRect;
var
  Point: TPoint;
begin
  Point := PosInRect(CheckBoxSize.cx, CheckBoxSize.cy, ContentRect, Column.Alignment, Column.VerticalAlignment);
  Result := Bounds(Point.X, Point.Y, CheckBoxSize.cx, CheckBoxSize.cy);
end;

function TNxCheckBoxColumnView.GetCheckBoxSize: TSize;
begin
  with Result do
  begin
    cx := DotToPixel(DefaultCheckBoxSize[CanStyle(soVCLStyles) or CanStyle(soNativeStyles)]);
    cy := cx;
  end;
end;

function TNxCheckBoxColumnView.GetInteractiveRect: TRect;
begin
  Result := CheckBoxRect;
end;

function TNxCheckBoxColumnView.IsResponsive: Boolean;
begin
  Result := True;
end;

procedure TNxCheckBoxColumnView.KeyDown(var Key: Word; Shift: TShiftState);
begin
  if Column.Interactive then
  begin
    State := State + [bsPressed];

    { Prevent other view to be captured }
    Captured := True;
  end;
end;

procedure TNxCheckBoxColumnView.KeyUp;
begin
  State := State - [bsPressed];
end;

procedure TNxCheckBoxColumnView.KeyUp(var Key: Word; Shift: TShiftState);
begin
  { Pressing in progress? }
  if bsPressed in State then
  begin
    { Event OnCheckedChanged is called
      inside AsBoolean setter }
    if Key = VK_SPACE then
    begin
      AsBoolean := not AsBoolean;
    end;

    if WideChar(Chr(Key)) = LeftStr(BoolToStr(True, True), 1) then AsBoolean := True;
    if WideChar(Chr(Key)) = LeftStr(BoolToStr(False, True), 1) then AsBoolean := False;

    State := State - [bsPressed];
  end;
end;

procedure TNxCheckBoxColumnView.MouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if PtInRect(CheckBoxRect, Point(X, Y)) then
  begin
    State := State + [bsPressed];

    { Prevent other view to be captured }
    Captured := True;
  end;
end;

procedure TNxCheckBoxColumnView.MouseLeave;
begin
  State := State - [bsHover];
end;

procedure TNxCheckBoxColumnView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  if PtInRect(CheckBoxRect, Point(X, Y)) then
  begin
    State := State + [bsHover];
  end
  else State := State - [bsHover];
end;

procedure TNxCheckBoxColumnView.MouseUp;
begin
  State := State - [bsPressed];
end;

procedure TNxCheckBoxColumnView.MouseUp(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  { Change Value }
  if bsPressed in State then
    if PtInRect(CheckBoxRect, Point(X, Y)) then
    begin
      { Event is called inside setter }
      AsBoolean := not AsBoolean;
    end;

  inherited;
end;

procedure TNxCheckBoxColumnView.PaintContent;
var
  CheckBoxState: TCheckBoxState;
  Images: TCustomImageList;
  ImageIndex: TImageIndex;
  TextRect: TRect;
begin
  if AsBoolean then CheckBoxState := cbChecked else CheckBoxState := cbUnchecked;
  if CheckBoxColumn.DoPaintImage(AsBoolean, Images, ImageIndex) then
  begin
    if Assigned(Images) and InRange(ImageIndex, 0, Pred(Images.Count))
      then Images.Draw(Canvas, CheckBoxRect.Left, CheckBoxRect.Top, ImageIndex);
  end else
  begin
    Style.PaintCheckBox(CheckBoxRect, CheckBoxState, Self.State);
  end;

  if CheckBoxColumn.ShowText then
  begin
    TextRect := ContentRect;
    TextRect.Left := CheckBoxRect.Right + pdDouble;
    DrawTextRect(Canvas, TextRect, taLeftJustify, Column.VerticalAlignment, Column.GetDisplayText(Cell, Location.X, Location.Y), wkEllipsis);
  end;
end;

procedure TNxCheckBoxColumnView.SetAsBoolean(const Value: Boolean);
var
  Accept: Boolean;
begin
  Owner.LocateCell(Location.X, Location.Y);

  Accept := True;

  { Trigger event }
  CheckBoxColumn.DoCheckedQuery(Location.X, Location.Y, Accept);

  if Accept then
  begin
    { `OnCheckedQuery` can change location }
    Owner.LocateCell(Location.X, Location.Y);

    CheckBoxColumn.SetValue(Cell, Location.X, Location.Y, Value);

    { Trigger event }
    CheckBoxColumn.DoCheckedChanged(Location.X, Location.Y);
  end;
end;

procedure TNxCheckBoxColumnView.StateChanged(Value: TNxButtonState);
begin
  Changed;
end;

{ TNxToolbarColumnView }

procedure TNxToolbarColumnView.ActivateButtonHint(
  Button: TNxToolbarColumnButton);
var
  ButtonRect, HintRect: TRect;
begin
  if Assigned(Button)
    and (Button.Hint <> EmptyStr) then
  begin
    FButtonHint := HintWindowClass.Create(Control);
    FButtonHint.Color := clInfoBk;

    { Calculate Rect }
    ButtonRect := GetButtonRect(Button.Index);
    HintRect := FButtonHint.CalcHintRect(Screen.Width, Button.Hint, nil);

    { Cordinates must be "Screen" }
    HintRect.TopLeft := Control.ClientToScreen(ButtonRect.TopLeft);

    { Move Hint bellow Button }
    Inc(HintRect.Top, ButtonRect.Bottom - ButtonRect.Top);

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

    { Show Hint }
    FButtonHint.ActivateHint(HintRect, Button.Hint);
  end;
end;

procedure TNxToolbarColumnView.Assign(Source: TObject);
begin
  inherited;
  if Source is TNxToolbarColumnView then
  begin
    DownButton := TNxToolbarColumnView(Source).DownButton;
    HoverButton := TNxToolbarColumnView(Source).HoverButton;
  end;
end;

constructor TNxToolbarColumnView.Create;
begin
  inherited;
  FDownButton := Undefined;
  FHoverButton := Undefined;
end;

destructor TNxToolbarColumnView.Destroy;
begin
  ReleaseButtonHint;
  FreeAndNil(FHintHideTimer);
  FreeAndNil(FHintDelayTimer);
  inherited;
end;

procedure TNxToolbarColumnView.DoHintDelayTimer(Sender: TObject);
begin
  { Stop Timer }
  FHintDelayTimer.Enabled := False;

  if InRange(FHoverButton, 0, Pred(Collection.Count)) then
  begin
    ActivateButtonHint(Button[FHoverButton]);

    if not Assigned(FHintHideTimer) then
    begin
      FHintHideTimer := TTimer.Create(nil);
      FHintHideTimer.Interval := Application.HintHidePause;
      FHintHideTimer.OnTimer := DoHintHideTimer;
      FHintHideTimer.Enabled := True;
    end;
  end;
end;

procedure TNxToolbarColumnView.DoHintHideTimer(Sender: TObject);
begin
  FreeAndNil(FHintHideTimer);
  ReleaseButtonHint;
end;

function TNxToolbarColumnView.GetButton(Index: Integer): TNxToolbarColumnButton;
begin
  Result := Collection.Items[Index];
end;

function TNxToolbarColumnView.GetButtonAtPos(Location: TPoint): Integer;
var
  i, ButtonWidth: Integer;
  Pos: TPoint;
  ButtonRect: TRect;
begin
  { Default }
  Result := Undefined;

  { Start Pos. }
  Pos := ContentRect.TopLeft;

  for i := 0 to Pred(Collection.Count) do
  begin
    if Button[i].Visible then
    begin
      ButtonWidth := GetButtonWidth(i);

      { Build Rect }
      ButtonRect := Bounds(Pos.X, Pos.Y, ButtonWidth, ContentRect.Bottom - ContentRect.Top);

      if PtInRect(ButtonRect, Location) then
      begin
        Result := i;
        Exit; { Done }
      end;

      { Shift Pos. }
      Inc(Pos.X, ButtonWidth);
    end;
  end;
end;

function TNxToolbarColumnView.GetButtonRect(const Index: Integer): TRect;
var
  i, ButtonWidth: Integer;
  Pos: TPoint;
begin
  if InRange(Index, 0, Pred(Collection.Count)) then
  begin
    { Start Pos. }
    Pos := ContentRect.TopLeft;
    ButtonWidth := 0;

    for i := 0 to Index do
    begin
      if Button[i].Visible then
      begin
        ButtonWidth := GetButtonWidth(i);
        if i < Index then
        begin
          { Shift Pos. }
          Inc(Pos.X, ButtonWidth);
        end;
      end;
    end;

    { Build Rect }
    Result := Bounds(Pos.X, Pos.Y, ButtonWidth, ContentRect.Bottom - ContentRect.Top);
  end;
end;

function TNxToolbarColumnView.GetButtonWidth(const Index: Integer): Integer;
begin
  if Button[Index].Divider
    then Result := pdStandard * 2 + 1 else
  begin
    Result := GetTextSize(Canvas, Button[Index].Text, True).cx;

    if Assigned(Collection.Images)
      and (Button[Index].ImageIndex <> Undefined) then
    begin
      Inc(Result, Collection.Images.Width);

      { Image to Text Padding }
      Inc(Result, pdStandard);
    end;

    { Add Padding }
    Inc(Result, 8);
  end;

end;

function TNxToolbarColumnView.GetCollection: INxToolbarCollection;
begin
  Result := Column as INxToolbarCollection;
end;

function TNxToolbarColumnView.IsResponsive: Boolean;
begin
  Result := True;
end;

procedure TNxToolbarColumnView.MouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    DownButton := GetButtonAtPos(Point(X, Y));
  end;
end;

procedure TNxToolbarColumnView.MouseLeave;
begin
  HoverButton := Undefined;
end;

procedure TNxToolbarColumnView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  HoverButton := GetButtonAtPos(Point(X, Y));
end;

procedure TNxToolbarColumnView.MouseUp;
begin
  DownButton := Undefined;
end;

procedure TNxToolbarColumnView.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
var
  UpButton, PrevDownButton: Integer;
begin
  { Temp. Var }
  PrevDownButton := DownButton;

  { Release Pressed }
  DownButton := Undefined;

  if Button = mbLeft then
  begin

    UpButton := GetButtonAtPos(Point(X, Y));

    if InRange(UpButton, 0, Pred(Collection.Count))
      and (UpButton = PrevDownButton)
      and not Self.Button[UpButton].Divider then
    begin
      { Trigger Event }
      Collection.DoButtonClick(UpButton);
    end;

  end;

end;

procedure TNxToolbarColumnView.PaintButton(ButtonRect: TRect; Text: WideString;
  ButtonState: TNxButtonState; ImageIndex: TImageIndex);
var
  ImagePos: TPoint;
  TextRect: TRect;
begin
  { Background provided by Style }
  Style.PaintToolButton(ButtonRect, ButtonState);

  TextRect := ButtonRect;

  { Draw Image }
  if Assigned(Collection.Images)
    and (ImageIndex <> Undefined) then
  begin
    ImagePos := ButtonRect.TopLeft;

    Inc(ImagePos.Y, ContentHeight div 2 - Collection.Images.Height div 2);

    Inc(ImagePos.X, 4);

    Collection.Images.Draw(Canvas, ImagePos.X, ImagePos.Y, ImageIndex);

    Inc(TextRect.Left, Collection.Images.Width + 4);
  end;

  Canvas.Font.Color := clBtnText;

  { Draw Text }
  DrawTextRect(Canvas, TextRect, Classes.taCenter, taVerticalCenter, Text, wkNone);
end;

procedure TNxToolbarColumnView.PaintContent;
var
  i, ButtonWidth: Integer;
  ButtonRect: TRect;
  ButtonState: TNxButtonState;
  Pos: TPoint;
begin
  { Starting Pos }
  Pos := ContentRect.TopLeft;

  for i := 0 to Pred(Collection.Items.Count) do
  begin
    if Button[i].Visible then
    begin
      ButtonWidth := GetButtonWidth(i);
      ButtonRect := Bounds(Pos.X, Pos.Y, ButtonWidth, ContentRect.Bottom - ContentRect.Top);

      if Button[i].Divider then PaintDivider(ButtonRect) else
      begin
        { Set State }
        ButtonState := [];
        if FHoverButton = i then Include(ButtonState, bsHover);
        if FDownButton = i then Include(ButtonState, bsPressed);

        { Paint }
        PaintButton(ButtonRect, Button[i].Text, ButtonState, Collection.Items[i].ImageIndex);
      end;

      { Shift Pos. }
      Inc(Pos.X, ButtonWidth);
    end;
  end;
end;

procedure TNxToolbarColumnView.PaintDivider(DividerRect: TRect);
var
  Middle: Integer;
begin
  Middle := DividerRect.Left + (DividerRect.Right - DividerRect.Left) div 2;

  with Canvas do
  begin
    Pen.Color := clSilver;
    Polyline([
      Point(Middle, DividerRect.Top + pdStandard),
      Point(Middle, DividerRect.Bottom - pdStandard)
    ]);
  end;
end;

procedure TNxToolbarColumnView.RefreshButton(const Index: Integer);
begin
  if InRange(Index, 0, Pred(Collection.Count))
    and not Button[Index].Divider
      then InvalidateRect(GetButtonRect(Index));
end;

procedure TNxToolbarColumnView.ReleaseButtonHint;
begin
  if Assigned(FButtonHint) then
  begin
    FButtonHint.ReleaseHandle; { Required }
    FreeAndNil(FButtonHint);
  end;
end;

procedure TNxToolbarColumnView.Reset;
begin
  FHoverButton := Undefined;
  FDownButton := Undefined;
end;

procedure TNxToolbarColumnView.SetDownButton(const Value: Integer);
begin
  if FDownButton <> Value then
  begin
    Captured := False;

    RefreshButton(FDownButton);

    FDownButton := Value;

    if InRange(FDownButton, 0, Pred(Collection.Count)) then
      Captured := True;

    RefreshButton(FDownButton);
  end;
end;

procedure TNxToolbarColumnView.SetHoverButton(const Value: Integer);
begin
  if FHoverButton <> Value then
  begin
    { Since new Button become hover, destroy Hint }
    ReleaseButtonHint;

    RefreshButton(FHoverButton);
    FHoverButton := Value;
    RefreshButton(FHoverButton);

    { Create Timer }
    if not Assigned(FHintDelayTimer) then
    begin
      FHintDelayTimer := TTimer.Create(nil);
      FHintDelayTimer.Interval := Application.HintPause;
      FHintDelayTimer.OnTimer := DoHintDelayTimer;
    end;

    { Start Timer }
    FHintDelayTimer.Enabled := True;
  end;
end;

{ TNxIconColumnView }

procedure TNxIconColumnView.ActivateHint;
begin
  if not Assigned(FHintWindow) then
  begin
    { Create Hint Window }
    FHintWindow := HintWindowClass.Create(Control);
    FHintWindow.Color := clInfoBk;
  end;

  { Move Hint }
  UpdateHintWindow;
end;

procedure TNxIconColumnView.Changed;
begin
  inherited;
  InvalidateRect(IconRect);
end;

destructor TNxIconColumnView.Destroy;
begin
  ReleaseHint;
  FreeAndNil(FHintDelayTimer);
  inherited;
end;

procedure TNxIconColumnView.DoHintDelayTimer(Sender: TObject);
begin
  { Stop Timer }
  FHintDelayTimer.Enabled := False;

  if not (bsPressed in State)
    then ActivateHint;
end;

function TNxIconColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  with IconColumn do
    if Assigned(Images) then
    begin
      Result.cx := Images.Width;
      Result.cy := Images.Height;
    end;
end;

function TNxIconColumnView.GetButtonTextRect: TRect;
begin
  { Default, fill content }
  Result := ContentRect;

  { Have Icon? }
  if HasIcon then
  begin
    Result.Left := IconRect.Right + pdTextToIcon;
  end;
end;

function TNxIconColumnView.GetIconColumn: TNxIconColumn6;
begin
  Result := TNxIconColumn6(GetComponent(Column));
end;

function TNxIconColumnView.GetIconRect: TRect;
var
  Point: TPoint;
begin
  with IconColumn do
    if Assigned(Images) then
    begin
      { Align & Pos }
      Point := PosInRect(Images.Width, Images.Height, ContentRect, Alignment, VerticalAlignment);

      Result := Bounds(Point.X, Point.Y, Images.Width, Images.Height);
    end;
end;

function TNxIconColumnView.HasIcon: Boolean;
var
  IconIndex: Integer;
begin
  { Get Cell's Value or trigger OnGetValue event }
  IconIndex := IconColumn.GetValue(Cell, Location.X, Location.Y);

  with IconColumn do
    Result := Assigned(Images) and InRange(IconIndex, 0, Images.Count - 1);

end;

function TNxIconColumnView.IsResponsive: Boolean;
begin
  Result := IconColumn.Button;
end;

procedure TNxIconColumnView.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;
  if IconColumn.Button then
    if (Key = VK_SPACE) and not (bsPressed in State) then
    begin
      Captured := True;

      State := State + [bsPressed];
    end;
end;

procedure TNxIconColumnView.KeyUp(var Key: Word; Shift: TShiftState);
begin
  if bsPressed in State then
    with IconColumn do
    begin

      { Trigger event }
      if Button then
        if Assigned(OnButtonClick) then OnButtonClick(IconColumn);

      if CycleImages and Assigned(Images) then
      begin
        Cell.AsInteger := Cell.AsInteger + 1;

        if Cell.AsInteger > Pred(Images.Count) then
          Cell.AsInteger := 0;
      end;

    end;

  { Un-press }
  inherited;
end;

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

  { Hide hint }
  ReleaseHint;

  State := State + [bsPressed];

  { Prevent other view to be captured }
  Captured := True;
end;

procedure TNxIconColumnView.MouseLeave;
begin

  { Hide hint }
  ReleaseHint;

  State := State - [bsHover];
end;

procedure TNxIconColumnView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  State := State + [bsHover];

  { Show Hint? }
  if  not (bsPressed in State)
      and not Assigned(FHintWindow)
      and (IconColumn.Hint <> EmptyStr) then
  begin
    if not Assigned(FHintDelayTimer) then
    begin
      FHintDelayTimer := TTimer.Create(nil);
      FHintDelayTimer.Interval := Application.HintPause;
      FHintDelayTimer.OnTimer := DoHintDelayTimer;
    end;

    { Start Timer }
    FHintDelayTimer.Enabled := True;
  end;
end;

procedure TNxIconColumnView.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  { Already pressed? }
  if bsPressed in State then
    with IconColumn do
    begin

      { Inside cell? }
      if PtInRect(ClientRect, Point(X, Y)) then
      begin
      
        { Trigger event }
        if Button then
          if Assigned(OnButtonClick) then OnButtonClick(IconColumn);

        if CycleImages and Assigned(Images) then
        begin
          Cell.AsInteger := Cell.AsInteger + 1;

          if Cell.AsInteger > Pred(Images.Count) then
            Cell.AsInteger := 0;
        end;
      end;

    end;

  inherited;
end;

procedure TNxIconColumnView.PaintContent;
var
  IconPos: TPoint;
  IconIndex, ScaledHeight, ScaledWidth: Integer;
begin
  if IconColumn.Button
    then Style.PaintToolButton(ContentRect, FState);

  { Get Cell's Value or trigger OnGetValue event }
  IconIndex := IconColumn.GetValue(Cell, Location.X, Location.Y);

  if Assigned(IconColumn.Images)
    and InRange(IconIndex, 0, Pred(IconColumn.Images.Count)) then
  begin
    ScaledHeight := IconColumn.Images.Height;
    ScaledWidth := IconColumn.Images.Width;

    { Calc. Pos }
    IconPos := PosInRect(ScaledWidth, ScaledHeight, ContentRect, Column.Alignment, Column.VerticalAlignment);

    { Draw }
    IconColumn.Images.Draw(Canvas, IconPos.X, IconPos.Y, IconIndex);
  end;

  if IconColumn.Button then
  begin
    DrawTextRect(Canvas, ButtonTextRect, taLeftJustify, taVerticalCenter, IconColumn.ButtonText, wkEllipsis);
  end;
end;

procedure TNxIconColumnView.ReleaseHint;
begin
  if Assigned(FHintWindow) then
  begin
    FHintWindow.ReleaseHandle; { Required }
    { Free }
    FreeAndNil(FHintWindow);
  end;
end;

procedure TNxIconColumnView.StateChanged(Value: TNxButtonState);
begin
  InvalidateRect(ContentRect);
end;

procedure TNxIconColumnView.UpdateHintWindow;
var
  CursorPos: TPoint;
  HintRect: TRect;
  DisplayText: string;
begin
  if Assigned(FHintWindow) then
    with IconColumn do
  begin
    DisplayText := Hint;

    { Calculate Rect }
    HintRect := FHintWindow.CalcHintRect(Screen.Width, DisplayText, nil);

    GetCursorPos(CursorPos);

    OffsetPoint(CursorPos, 16, 16);

    { Cordinates must be "Screen" }
    HintRect.TopLeft := CursorPos;

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

    { Show Hint }
    FHintWindow.ActivateHint(HintRect, DisplayText);
  end;
end;

{ TNxSideEditColumnView }

procedure TNxSideEditColumnView.Changed;
begin
  inherited;

end;

function TNxSideEditColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  with Result, Column as INxSideEditColumn do
  begin
    cx := IfThen(ShowText, TextWidth + 40, 40);
    cy := 14;
  end;
end;

function TNxSideEditColumnView.GetInteractiveRect: TRect;
begin
  { Default }
  Result := ContentRect;

  with Column as INxSideEditColumn do
    if ShowText then
    begin
      Inc(Result.Left, TextWidth);

      { Add padding }
      Inc(Result.Left, pdDouble);
    end;
end;

procedure TNxSideEditColumnView.PaintContent;
var
  TextRect: TRect;
begin
  with Column, Column as INxSideEditColumn, Self.Location do
    if ShowText and not Self.Editing then
    begin
      TextRect := ContentRect;
      TextRect.Right := TextRect.Left + TextWidth;

      DrawTextRect(Canvas, TextRect, Alignment, VerticalAlignment, GetDisplayText(Cell, X, Y), wkEllipsis);
    end;
end;

{ TNxProgressColumnView }

procedure TNxProgressColumnView.Changed;
begin
  inherited;

end;

constructor TNxProgressColumnView.Create;
begin
  inherited;
  FView := TNxProgressView.Create;
end;

destructor TNxProgressColumnView.Destroy;
begin
  FreeAndNil(FView);
  inherited;
end;

function TNxProgressColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  with Result, ProgressColumn do
  begin
    cx := IfThen(ShowText, TextWidth + 40, 40);
    cy := ProgressHeight;
  end;
end;

function TNxProgressColumnView.GetProgressColumn: TNxProgressColumn6;
begin
  Result := TNxProgressColumn6(Column);
end;

procedure TNxProgressColumnView.PaintContent;
var
  Value: Double;
begin
  inherited;

  with ProgressColumn do
  begin
    FView.Max := ProgressColumn.Max;
    FView.Min := ProgressColumn.Min;
    FView.ProgressHeight := ProgressHeight;
    FView.ProgressColor := IfThen(bsDisabled in FState, Color, ProgressColor);
    FView.ProgressMargin := ProgressMargin;
    FView.ProgressStyle := ProgressStyle;
    FView.Transparent := Transparent;
    FView.Style := Self.Style;

    { Note: Cell.AsFloat ignores OnGetValue event }
    Value := GetValue(Cell, Self.Location.X, Self.Location.Y);
  end;

  FView.ClientRect := InteractiveRect;
  FView.Canvas := Canvas;
  FView.AsFloat := Value;

  FView.BorderColor := ColorOf(seDivider);
  FView.Color := IfThen(bsDisabled in State, FView.BorderColor, Column.Color);

  FView.Paint;
end;

{ TNxGraphicColumnView }

constructor TNxGraphicColumnView.Create;
begin
  inherited;
end;

destructor TNxGraphicColumnView.Destroy;
begin

  inherited;
end;

function TNxGraphicColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  with Result do
  begin
    cx := Graphic.Width;
    cy := Graphic.Height;
  end;
end;

function TNxGraphicColumnView.GetGraphic: TGraphic;
var
  DataCell: INxCell;
begin
  Result := nil;

  if Supports(Cell, INxCell, DataCell) then
  begin
    if Assigned(DataCell.Data) and (TObject(DataCell.Data) is TGraphic)
      then Result := DataCell.Data;
  end;
end;

function TNxGraphicColumnView.GetGraphicColumn: TNxGraphicColumn6;
begin
  Result := TNxGraphicColumn6(Column);
end;

procedure TNxGraphicColumnView.PaintContent;
var
  EffectsRect, ClipRect: TRect;
  i: Integer;
begin

  if Assigned(Graphic)
    and not Graphic.Empty then
  begin

    { Clip TGraphic painting inside
      ContentRect }
    GetClipBox(Canvas.Handle, ClipRect);
    try
      SetClipRect(Canvas, ContentRect);

      { Paint TGraphic & get TRect }
      EffectsRect := PaintGraphic(Canvas, Graphic, ContentRect, GraphicColumn.FillMode);

    finally
      SetClipRect(Canvas, ClipRect);
    end;

    PaintEffects(Canvas, EffectsRect, GraphicColumn.Effects);

    { Border }
    for i := 1 to GraphicColumn.BorderWidth do
      with Canvas do
      begin
        if Selected then
        begin
          Brush.Color := GraphicColumn.SelectedBorderColor;
        end else
        begin
          Brush.Color := GraphicColumn.BorderColor;
        end;
        FrameRect(EffectsRect);
        InflateRect(EffectsRect, -1, -1);
      end;

  end;

end;

{ TNxTrackBarColumnView }

procedure TNxTrackBarColumnView.ActivateSliderHint;
begin
  if not Assigned(FSliderHint) then
  begin
    { Create Hint Window }
    FSliderHint := HintWindowClass.Create(Control);
    FSliderHint.Color := clInfoBk;
  end;

  { Move Hint }
  UpdateSliderHint;
end;

procedure TNxTrackBarColumnView.Changed;
begin
  InvalidateRect(ClientRect);
end;

destructor TNxTrackBarColumnView.Destroy;
begin
  ReleaseSliderHint;
  inherited;
end;

procedure TNxTrackBarColumnView.DrawClassicTrackBar;
var
  w: Integer;
  RailRect: TRect;
begin
  RailRect := TrackRect;

  Canvas.Brush.Color := clBtnFace;
  Canvas.FillRect(RailRect);
  Frame3D(Canvas, RailRect, cl3DDkShadow, clBtnHighlight, 1);

  with Canvas, SliderRect do
  begin
    w := Right - Left;
    Brush.Color := clBtnFace;
    Pen.Color := clBtnFace;
    Polygon([Point(Left, Top),
      Point(Right, Top), Point(Right, Bottom - w div 2),
      Point(Left + w div 2, Bottom), Point(Left, Bottom - w div 2),
      Point(Left, Top)]);
    Pen.Color := clBtnHighlight;
    MoveTo(Right - 1, Top);
    LineTo(Left, Top);
    LineTo(Left, Bottom - w div 2);
    LineTo(Left + w div 2, Bottom);
    Pen.Color := cl3DDkShadow;
    LineTo(Right, Bottom - w div 2);
    LineTo(Right, Top - 1);
    Pen.Color := cl3DLight;
    MoveTo(Right - 2, Top + 1);
    LineTo(Left + 1, Top + 1);
    LineTo(Left + 1, Bottom - w div 2);
    LineTo(Left + w div 2, Bottom - 1);
    Pen.Color := clBtnShadow;
    LineTo(Right - 1, Bottom - w div 2);
    LineTo(Right - 1, Top);
  end;
end;

procedure TNxTrackBarColumnView.DrawDelphiStyledTrackBar;
begin
  TNxTrackBarStyle6.PaintThumb(Canvas, Control.Handle, SliderRect,
    State, [soVCLStyles]);
end;

procedure TNxTrackBarColumnView.DrawNativeStyledTrackBar;
var
  Theme: HTHEME;
  SliderState: Integer;
begin
  Theme := OpenThemeData(Control.Handle, teTrackBar);

  { Draw Trackbar Rail }
  DrawThemeBackground(Theme, Canvas.Handle, tcTrackbarRail, 1, TrackRect, nil);

  SliderState := tiTrackbarSliderNormal;
  if bsHover in State then SliderState := tiTrackbarSliderHot;
  if bsPressed in State then SliderState := tiTrackbarSliderPressed;
  if bsDisabled in State then SliderState := tiTrackbarSliderDisabled;

  { Draw Slider }
  DrawThemeBackground(Theme, Canvas.Handle, tcTrackbarSlider, SliderState, SliderRect, nil);

  CloseThemeData(Theme);
end;

function TNxTrackBarColumnView.GetAsFloat: Double;
var
  Intf: INxFloatCellValue;
begin
  if Supports(Column, INxFloatCellValue, Intf) then
  begin
    Result := Intf.GetValue(Cell, Location.X, Location.Y);
  end
  else Result := 0;
end;

function TNxTrackBarColumnView.GetPositionAtPos(const X, Y: Integer): Double;
var
  Spot: Integer;
begin
  with Column as INxRanged do
  begin
    Spot := X - TrackRect.Left;
    Result := Spot / GetTrackWidth * (Max - Min) + Min;

    if Result > Max then Result := Max;
    if Result < Min then Result := Min;
  end;
end;

function TNxTrackBarColumnView.GetSliderRect: TRect;
var
  Distance: Integer;
  TrackSize, SliderSize: TSize;
begin
  TrackSize := Size(RectWidth(TrackRect), RectHeight(TrackRect));

  with Column as INxRanged, Column as INxTrackBar do
  begin
    Distance := Round(TrackSize.cx * (AsFloat - Min) / (Max - Min));
  end;

  SliderSize := GetSliderSize;

  with Result do
  begin
    Left := TrackRect.Left + Distance - SliderSize.cx div 2;
    Top := TrackSize.cy div 2 - SliderSize.cy div 2 + TrackRect.Top;
    Right := Left + SliderSize.cx;
    Bottom := Top + SliderSize.cy;
  end;
end;

function TNxTrackBarColumnView.GetSliderSize: TSize;
begin
  if StyledControl.CanStyle(soVCLStyles) then
  begin
    Result.cy := 14 + 3;
    Result.cx := Result.cy - 8;
  end else if StyledControl.CanStyle(soNativeStyles) then
  begin
    Result.cy := 14 + 3;
    Result.cx := Result.cy - 8;
  end else
  begin
    Result.cy := 14;
    Result.cx := Result.cy - 6;
  end;
  Result := DotToPixel(Result);
end;

function TNxTrackBarColumnView.GetTrackRect: TRect;
begin
  Result := InteractiveRect;

  { Half-size of Slider left }
  Inc(Result.Left, GetSliderSize.cx div 2);

  { Half-size of Slider right }
  Dec(Result.Right, GetSliderSize.cx div 2 + 1);

  { Apply Padding }
  with Result, Column as INxTrackBar do
  begin
    Top := Top + (Bottom - Top) div 2 - DotToPixel(TrackHeight) div 2;
    Bottom := Top + DotToPixel(TrackHeight);
  end;
end;

function TNxTrackBarColumnView.GetTrackWidth: Integer;
begin
  Result := RectWidth(TrackRect);
end;

function TNxTrackBarColumnView.IsResponsive: Boolean;
begin
  Result := True;
end;

procedure TNxTrackBarColumnView.MouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  Ranged: INxRanged;
  Piece, Spot: Double;
begin
  if ssDouble in Shift then
  begin
    if Supports(Column, INxRanged, Ranged) then
    begin
      Piece := RectWidth(TrackRect) / (Ranged.Max - Ranged.Min);
      Spot := (X - TrackRect.Left) / Piece;
      AsFloat := Round(Spot) + Ranged.Min;
    end;
  end else
  begin
    AsFloat := GetPositionAtPos(X, Y);

    { Activate Hint too }
    State := State + [bsPressed];

    Captured := True;

    ActivateSliderHint;
  end;
end;

procedure TNxTrackBarColumnView.MouseLeave;
begin
  State := State - [bsHover];
end;

procedure TNxTrackBarColumnView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  if PtInRect(GetSliderRect, Point(X, Y)) then
    State := State + [bsHover]
  else
    State := State - [bsHover];

  if bsPressed in State then
  begin
    AsFloat := GetPositionAtPos(X, Y);

    { Repositionate Hint }
    UpdateSliderHint;
  end;

end;

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

procedure TNxTrackBarColumnView.PaintContent;
begin
  { Paint text }
  inherited;

  if CanStyle(soVCLStyles) then DrawDelphiStyledTrackBar
  else if CanStyle(soNativeStyles) then DrawNativeStyledTrackBar
  else DrawClassicTrackBar;
end;

procedure TNxTrackBarColumnView.ReleaseSliderHint;
begin
  if Assigned(FSliderHint) then
  begin
    FSliderHint.ReleaseHandle; { Required }
    { Free }
    FreeAndNil(FSliderHint);
  end;
end;

procedure TNxTrackBarColumnView.SetAsFloat(const Value: Double);
begin
  Owner.LocateCell(Location.X, Location.Y);

  Cell.AsFloat := Value;
end;

procedure TNxTrackBarColumnView.StateChanged(Value: TNxButtonState);
begin
  Changed;
end;

procedure TNxTrackBarColumnView.UpdateSliderHint;
var
  HintWidth: Integer;
  SliderRect, HintRect: TRect;
  DisplayText: string;
begin
  if Assigned(FSliderHint) then
    with Column, Self.Location do
  begin
    DisplayText := GetDisplayText(Cell, X, Y);

    { Calculate Rect }
    HintRect := FSliderHint.CalcHintRect(Screen.Width, DisplayText, nil);

    SliderRect := GetSliderRect;

    HintWidth := HintRect.Right - HintRect.Left;

    { Cordinates must be "Screen" }
    HintRect.TopLeft := Control.ClientToScreen(SliderRect.TopLeft);

    { Center Hint }
    Dec(HintRect.Left, HintWidth div 2);
    Inc(HintRect.Left, (SliderRect.Right - SliderRect.Left) div 2);

    { Move Hint bellow Slider }
    Inc(HintRect.Top, SliderRect.Bottom - SliderRect.Top);

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

    { Show Hint }
    FSliderHint.ActivateHint(HintRect, DisplayText);
  end;
end;

{ TNxHTMLColumnView }

constructor TNxHTMLColumnView.Create;
begin
  inherited;
  FView := TNxHTMLView.Create;
end;

destructor TNxHTMLColumnView.Destroy;
begin
  FView.Free;
  inherited;
end;

function TNxHTMLColumnView.GetBestSize: TSize;
begin
  Result := FView.BestSize;
end;

function TNxHTMLColumnView.GetDefaultStyle: TNxCSSStyle;
begin
  Result.Color := Column.Font.Color;
  Result.FontFamily := Column.Font.Name;
  Result.FontSize := IntToStr(Column.Font.Size);
  Result.TextAlign := taLeft;
end;

function TNxHTMLColumnView.IsResponsive: Boolean;
begin
  Result := True;
end;

procedure TNxHTMLColumnView.MouseLeave;
begin
  inherited;
  Screen.Cursor := crDefault;
end;

procedure TNxHTMLColumnView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  FView.HTML := Column.GetDisplayText(Cell, Location.X, Location.Y);
  FView.DefaultStyle := GetDefaultStyle;
  FView.Operation := [hoHitTest];
  FView.StyleSheet := TNxHTMLColumn6(Column).StylesSheet;
  FView.HitLocation := Point(X, Y);
  FView.PaintTo(Canvas, ClientRect);

  if FView.HitHref <> EmptyStr then
    Screen.Cursor := crHandPoint
  else
    Screen.Cursor := crDefault;
end;

procedure TNxHTMLColumnView.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  FView.HTML := Column.GetDisplayText(Cell, Location.X, Location.Y);
  FView.DefaultStyle := GetDefaultStyle;
  FView.Operation := [hoHitTest];
  FView.StyleSheet := TNxHTMLColumn6(Column).StylesSheet;
  FView.HitLocation := Point(X, Y);
  FView.PaintTo(Canvas, ClientRect);

  if FView.HitHref <> EmptyStr then
  begin
    TNxHTMLColumn6(Column).DoLinkClick(FView.HitHref);
  end;

end;

procedure TNxHTMLColumnView.PaintContent;
var
  Style: TNxCSSStyle;
begin
  Style.TextAlign := AlignmentToTextAlign(Column.Alignment);

  FView.StyleSheet := (Column as INxHTML).StylesSheet;
  FView.HTML := Column.GetDisplayText(Cell, Location.X, Location.Y);
  FView.DefaultStyle := GetDefaultStyle;
  FView.Operation := [hoPaint];
  FView.PaintTo(Canvas, ContentRect);
end;

{ TNxTreeColumnView }

procedure TNxTreeColumnView.AdjustEditRect(var EditRect: TRect);
begin
  EditRect.Left := EditRect.Left + GetNonTextSpace;
end;

procedure TNxTreeColumnView.Assign(Source: TObject);
begin
  inherited;
  if Source is TNxTreeColumnView then
  begin
    Row := TNxTreeColumnView(Source).Row;
  end;
end;

destructor TNxTreeColumnView.Destroy;
begin
//  FRow := nil;
  inherited;
end;

function TNxTreeColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  Inc(Result.cx, GetNonTextSpace);
end;

function TNxTreeColumnView.GetToggleSize: TSize;
begin
  case TreeProperties.ToggleButtonStyle of
    tsGlyph: Result := Size(9, 9);
    tsSquare: Result := Size(9, 9);
    tsStyleNative: Result := Size(9, 9);
    tsTriangle: if Row.Expanded then Result := Size(6, 6) else Result := Size(5, 9);
  end;
  Result := DotToPixel(Result);
end;

function TNxTreeColumnView.GetTreeProperties: INxTreeProperties;
begin
  Supports(Column, INxTreeProperties, Result);
end;

function TNxTreeColumnView.HasImage: Boolean;
begin
  Result := HasImageSpace and InRange(Row.ImageIndex, 0, Pred(TreeProperties.Images.Count));
end;

function TNxTreeColumnView.HasImageSpace: Boolean;
begin
  Result := Assigned(TreeProperties.Images) and Assigned(Row);
end;

function TNxTreeColumnView.HasLines: Boolean;
begin
  Result := TreeProperties.ShowLines;
end;

function TNxTreeColumnView.HasToggleButton: Boolean;
begin
  Result := HasToggleButtonSpace and Row.HasChildren;
end;

/// <summary>
/// Check if spacing for toggle is needed
/// </summary>
function TNxTreeColumnView.HasToggleButtonSpace: Boolean;
begin
  Result := (TreeProperties.ToggleButtonStyle <> tsHidden) and Assigned(Row);
end;

function TNxTreeColumnView.IsClientRectToggle: Boolean;
begin
  Result := TreeProperties.ClientRectToggle;
end;

function TNxTreeColumnView.IsResponsive: Boolean;
begin
  Result := True;
end;

procedure TNxTreeColumnView.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;
  if Key in [187, 189] then Row.ToggleExpanded;
  if Key = 56 then Row.Expand(True);
end;

procedure TNxTreeColumnView.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
var
  CanToggle: Boolean;
begin
  inherited;
  CanToggle := False;

  if HasToggleButton and PtInRect(ToggleRect, Point(X, Y))
    then CanToggle := True;

  if IsClientRectToggle then CanToggle := True;

  { Toggle }
  if CanToggle then Row.ToggleExpanded;
end;

procedure TNxTreeColumnView.MouseLeave;
begin
  inherited;
  ToggleState := ToggleState - [bsHover];
end;

procedure TNxTreeColumnView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if HasToggleButton and PtInRect(ToggleRect, Point(X, Y)) then
  begin
    ToggleState := ToggleState + [bsHover];
  end
  else ToggleState := ToggleState - [bsHover];
end;

function TNxTreeColumnView.GetImageRect: TRect;
begin
  Result := ContentRect;
  with Result do
  begin
    Left := ContentRect.Left + LevelIndent;
    if HasToggleButtonSpace then Inc(Left, RectWidth(ToggleRect) + pdDouble)
      else Inc(Result.Left, pdStandard);
    Right := Left + ImageSize.cx;
  end;
end;

function TNxTreeColumnView.GetImageSize: TSize;
begin
  Result := Size(0, 0);
  if HasImageSpace then
    with TreeProperties do Result := Size(Images.Width, Images.Height);
end;

function TNxTreeColumnView.GetLevelIndent: Integer;
begin
  Result := Level * szTreeLevelIndent;
end;

function TNxTreeColumnView.GetNonTextSpace: Integer;
const
  ToggleOrIconSpacing = 5;
var
  TextSpacing: Integer;
begin
  Result := LevelIndent;

  { No Toggle or Icon, no spacing }
  TextSpacing := 0;

  if HasToggleButtonSpace then
  begin
    Result := Result + pdStandard + RectWidth(ToggleRect);
    TextSpacing := ToggleOrIconSpacing;
  end;

  if HasImageSpace then
  begin
    Result := Result + pdStandard + RectWidth(ImageRect);
    TextSpacing := ToggleOrIconSpacing;
  end;

  { Spacing used for Toggle Button and/or Icon before Text }
  Result := Result + TextSpacing;
end;

function TNxTreeColumnView.GetInteractiveRect: TRect;
begin
  if IsClientRectToggle then Result := ClientRect else Result := ToggleRect;
end;

function TNxTreeColumnView.GetIsFirst: Boolean;
begin
  Result := FIsFirst;
end;

function TNxTreeColumnView.GetIsLast: Boolean;
begin
  Result := FIsLast;
end;

function TNxTreeColumnView.GetLevel: Integer;
begin
  if Assigned(Row) then Result := Row.Level else Result := 0;
end;

function TNxTreeColumnView.GetRow: INxRow;
begin
  if Assigned(FRow) then
    Result := FRow.GetIntf as INxRow
  else
    Result := nil;
end;

function TNxTreeColumnView.GetTextRect: TRect;
begin
  Result := inherited GetTextRect;
  Result.Left := Result.Left + GetNonTextSpace;
end;

function TNxTreeColumnView.GetToggleRect: TRect;
var
  Container: TRect;
  Location: TPoint;
begin
  Container := ContentRect;
  Container.Right := Container.Left + ToggleSize.cx;

  Location := CenterInRect(Container, ToggleSize.cx, ToggleSize.cy);

  Inc(Location.X, LevelIndent);
  Result := Bounds(Location.X, Location.Y, ToggleSize.cx, ToggleSize.cy);
end;

procedure TNxTreeColumnView.PaintContent;
begin
  inherited;
  if HasToggleButton then PaintToggleButton;
  if HasImage then PaintImage;
  if HasLines then PaintLines;
end;

procedure TNxTreeColumnView.PaintImage;
var
  DrawingRect: TRect;
begin
  DrawingRect := CenteredRect(ImageRect, Bounds(0, 0, ImageSize.cx, ImageSize.cy));
  TreeProperties.Images.Draw(Canvas, DrawingRect.Left, DrawingRect.Top, Row.ImageIndex);
end;

procedure TNxTreeColumnView.PaintLines;
var
  i, m, n, X: Integer;
  NextSibling, CurrentRow: INxRow;
begin
  m := Round(ClientRect.Top + (ClientRect.Bottom - ClientRect.Top) / 2);
  with Canvas do
  begin
    Pen.Color := TreeProperties.LinesColor;
//    ARow := Row.Index;

    { Vertical lines }
    if GetIsLast then
    begin
      X := ClientRect.Left + (Level * 19) + 6;
      if Row.ChildRowCount > 0 then
      begin
        if not GetIsFirst then Polyline([Point(X, ClientRect.Top), Point(X, m - 4)])
      end else
      begin
        if not GetIsFirst then Polyline([Point(X, ClientRect.Top), Point(X, m)])
      end;
    end;

    CurrentRow := Row;
    for i := Level downto 0 do
    begin
      X := ClientRect.Left + (i * 19) + 6;

      NextSibling := CurrentRow.NextSibling;

      CurrentRow := CurrentRow.ParentRow;

      //      OnParentRow(ARow, NextSibling);

      if Assigned(NextSibling) then
      begin
        if (i = Level) and (Row.ChildRowCount > 0) then
        begin
          if not GetIsFirst then Polyline([Point(X, ClientRect.Top), Point(X, m - 4)]);
          if not GetIsLast then Polyline([Point(X, m + 4), Point(X, ClientRect.Bottom)]);
        end else
        begin
          if not GetIsFirst then Polyline([Point(X, ClientRect.Top), Point(X, ClientRect.Bottom)])
            else Polyline([Point(X, m), Point(X, ClipRect.Bottom)]);
        end;
      end;
    end;

    { Horizontal line - }
    X := ClientRect.Left + (Level * 19) + 6;
    n := X;

    if Row.ChildRowCount > 0
      then Inc(n, 4);

    Polyline([Point(n, m), Point(X + 9, m)]);
  end;
end;

procedure TNxTreeColumnView.PaintToggleButton;
begin
  case TreeProperties.ToggleButtonStyle of
    tsSquare, tsStyleNative: PaintSquareToggle(Canvas, ToggleRect, Row.Expanded);
    tsTriangle: PaintTriangleToggle(Canvas, ToggleRect, Row.Expanded, ToggleState);
  end;
end;

procedure TNxTreeColumnView.SetIsFirst(const Value: Boolean);
begin
  FIsFirst := Value;
end;

procedure TNxTreeColumnView.SetIsLast(const Value: Boolean);
begin
  FIsLast := Value;
end;

procedure TNxTreeColumnView.SetRow(const Value: INxRow);
begin
  if Assigned(Value) then
    FRow := (Value as IWeakly).WeakReference
  else
    FRow := nil;
end;

procedure TNxTreeColumnView.SetToggleState(const Value: TNxButtonState);
var
  T: TRect;
begin
  if Value <> FToggleState then
  begin
    T := ToggleRect;
    FToggleState := Value;
    InvalidateRect(ToggleRect);
  end;
end;

{ TNxRateColumnView }

function TNxRateColumnView.GetBestSize: TSize;
begin
  Result := inherited GetBestSize;
  with RateViewOwner do
  begin
    if HasGraphic(Glyph) then
    begin
      Inc(Result.cx, Cell.AsInteger * Glyph.Width);
      Inc(Result.cy, Glyph.Height);

      if HasGraphic(GlyphEmpty) then
      begin
        Inc(Result.cx, (Max - Cell.AsInteger) * GlyphEmpty.Width);
        Result.cy := Math.Max(Result.cy, GlyphEmpty.Height);
      end;
      Inc(Result.cx, Pred(Max) * Spacing);
    end;
  end;
end;

function TNxRateColumnView.GetGlyphsRect: TRect;
var
  Location: TPoint;
  RectSize: TSize;
begin
  RectSize := GetBestSize;
  Location := PosInRect(RectSize.cx, RectSize.cy, ContentRect, Column.Alignment, Column.VerticalAlignment);
  Result := Bounds(Location.X, Location.Y, RectSize.cx, RectSize.cy);
end;

function TNxRateColumnView.GetRateViewOwner: INxRateViewOwner;
begin
  if not Supports(Column, INxRateViewOwner, Result) then Result := nil;
end;

function TNxRateColumnView.GetPosition: Integer;
begin
  Result := Cell.AsInteger;
end;

function TNxRateColumnView.GetPositionAtPos(X, Y: Integer): Integer;
var
  i: Integer;
  GlyphRect: TRect;
begin
  Result := -1;
  with RateViewOwner do
  begin
    if HasGraphic(Glyph) then
    begin
      { Start }
      GlyphRect := Bounds(GetGlyphsRect.Left, GetGlyphsRect.Top, Glyph.Width, Glyph.Height);

      for i := 1 to Max do
      begin
        if i <= Cell.AsInteger then
        begin
          if PtInRect(GlyphRect, Point(X, Y)) then
          begin
            Result := i;
            Exit;
          end;
          OffsetRect(GlyphRect, Glyph.Width, 0);
        end else
        begin
          if HasGraphic(GlyphEmpty) then
          begin
            if PtInRect(GlyphRect, Point(X, Y)) then
            begin
              Result := i;
              Exit;
            end;
            OffsetRect(GlyphRect, GlyphEmpty.Width, 0);
          end;
        end;
        if i < Max then OffsetRect(GlyphRect, Spacing, 0);
      end;
    end;
  end;
end;

function TNxRateColumnView.IsResponsive: Boolean;
begin
  Result := True;
end;

procedure TNxRateColumnView.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if PtInRect(GetGlyphsRect, Point(X, Y)) then
  begin
    Position := GetPositionAtPos(X, Y);
    Captured := True;
    State := State + [bsPressed];
  end;
end;

procedure TNxRateColumnView.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if (bsPressed in State)
    and PtInRect(GetGlyphsRect, Point(X, Y)) then
  begin
    Position := GetPositionAtPos(X, Y);
  end;
end;

procedure TNxRateColumnView.PaintContent;
var
  i, X, Y: Integer;
begin
  inherited;
  with RateViewOwner do
  begin
    if HasGraphic(Glyph) then
    begin
      X := GetGlyphsRect.Left;
      Y := GetGlyphsRect.Top;
      for i := 1 to Max do
      begin
        if i <= Cell.AsInteger then
        begin
          DrawGraphic(Canvas, X, Y, Glyph.Graphic);
          Inc(X, Glyph.Width);
        end else
        begin
          if HasGraphic(GlyphEmpty) then
          begin
            DrawGraphic(Canvas, X, Y, GlyphEmpty.Graphic);
            Inc(X, GlyphEmpty.Width);
          end;
        end;
        if i < Max then Inc(X, Spacing);
      end;
    end;
  end;
end;

procedure TNxRateColumnView.SetPosition(const Value: Integer);
begin
  if (Value <> -1) and (Value <> Cell.AsInteger) then
  begin
    Owner.LocateCell(Location.X, Location.Y);
    Cell.AsInteger := Value;
    InvalidateRect(ContentRect);
  end;
end;

{ TNxColorColumnView }

procedure TNxColorColumnView.PaintContent;
var
  Color: TColor;
begin
  Color := StrToColor(Cell.AsString);
  with Canvas do
  begin
    Brush.Color := Color;
    FillRect(ContentRect);
  end;
end;

end.
