{ ------------------------------------------------------------- }
{ Purpose : Next Standard 6                                     }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2012. All Rights Reserved.         }
{ ------------------------------------------------------------- }
{$I NxSuite6.inc}
{_$UNDEF WININET}

unit NxStdCtrls6;

interface

uses
  { Standard }
  Classes, Types, Messages, Windows, Graphics,
  { Controls }
  Controls, StdCtrls, ExtCtrls, Forms, ImgList, WinInet,
  {$IFDEF UI_TYPES}UITypes,{$ENDIF}
  { Next Standard 6 }
  NxTypes6, NxClasses6, NxConsts6, NxGraphics6, NxControls6, NxEdit6, NxStylePaint6;

const
  szToolButtonWidth = 22;
  szToolButtonHeight = 23;

const
  clWord = $009a572b;
  clWordTabBorder = $00D4D4D4;

type
  { TNxToolPalette }

  TNxPaletteButtonClickEvent = procedure (Sender: TObject; Index: Integer) of object;

  TNxPalette = class;

  TNxPaletteButton = class
  private
    FCollection: TNxPalette;
    FHint: string;
    FImageIndex: TImageIndex;
    FReferenceType: TComponentClass;
    FShowHint: Boolean;
    { Property Accessors }
    function GetIndex: Integer;
  public
    constructor Create(AOwner: TNxPalette);
    { Properties }
    property ReferenceType: TComponentClass read FReferenceType write FReferenceType;
    property ImageIndex: TImageIndex read FImageIndex write FImageIndex;
    property Index: Integer read GetIndex;
    property Hint: string read FHint write FHint;
    property ShowHint: Boolean read FShowHint write FShowHint default False;
  end;

  TNxPalette = class
  private
    FButtons: TList;
    FCaption: TCaption;
    function GetCount: Integer;
    function GetButton(Index: Integer): TNxPaletteButton;
  public
    constructor Create(Caption: TCaption);
    destructor Destroy; override;
    function AddButton(ReferenceType: TComponentClass; ImageIndex: TImageIndex): TNxPaletteButton;
  public
    property Button[Index: Integer]: TNxPaletteButton read GetButton;
    property Caption: TCaption read FCaption write FCaption;
    property Count: Integer read GetCount;
  end;

  TNxToolPalette6 = class(TCustomControl)
  private
    FBottomBorder: Boolean;
    FButtonHeight: Integer;
    FButtonHint: THintWindow;
    FButtonWidth: Integer;
    FHintDelayTimer: TTimer;
    FHoverButton: TNxPaletteButton;
    FHoverPalette: TNxPalette;
    FImages: TImageList;
    FIndent: TSize;
    FItemIndex: Integer;
    FOnButtonClick: TNxPaletteButtonClickEvent;
    FPalettes: TList;
    FPressedButton: TNxPaletteButton;
    FSelectedFont: TFont;
    FTabHeight: Integer;
    function GetCount: Integer;
    function GetPaletteObject(const Index: Integer): TNxPalette;
    function GetPaletteByText(const Text: WideString): TNxPalette;
    function GetPaletteText(const Index: Integer): WideString;
    procedure SetBottomBorder(const Value: Boolean);
    procedure SetButtonHeight(const Value: Integer);
    procedure SetButtonWidth(const Value: Integer);
    procedure SetHoverButton(const Value: TNxPaletteButton);
    procedure SetHoverPalette(const Value: TNxPalette);
    procedure SetImages(const Value: TImageList);
    procedure SetIndent(const Value: TSize);
    procedure SetItemIndex(const Value: Integer);
    procedure SetPressedButton(const Value: TNxPaletteButton);
    procedure SetSelectedFont(const Value: TFont);
    procedure SetTabHeight(const Value: Integer);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    { Delphi Messages }
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    { Handlers }
    procedure DoButtonClick(Index: Integer); dynamic;
    procedure DoHintDelayTimer(Sender: TObject);
    { TCustomControl }
    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;
    { Virtual Methods }
    procedure Paint; override;
    procedure PaintButton(const Tab, Index: Integer; ButtonRect: TRect; State: TNxButtonState);
    procedure PaintButtons(const Tab: Integer); virtual;
    procedure PaintTab(const Tab: Integer; TabRect: TRect);
    procedure PaintTabs; virtual;
    { Properties }
    property HoverButton: TNxPaletteButton read FHoverButton write SetHoverButton;
    property HoverPalette: TNxPalette read FHoverPalette write SetHoverPalette;
    property PressedButton: TNxPaletteButton read FPressedButton write SetPressedButton;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Methods }
    function Add(Palette: string; ReferenceType: TComponentClass;
      ImageIndex: Integer): TNxPaletteButton;
    function GetButtonAtPos(const X, Y: Integer): TNxPaletteButton;
    function GetButtonRectangle(const Index: Integer): TRect;
    function GetPaletteAtPos(const X, Y: Integer): TNxPalette;
    function GetPaletteRectangle(const Index: Integer): TRect;
    procedure RefreshButton(Button: TNxPaletteButton);
    procedure RefreshPalette(Palette: TNxPalette);
    function PaletteExist(const Index: Integer): Boolean;
    { Properties }
    property Count: Integer read GetCount;
    property Palette[const Index: Integer]: TNxPalette read GetPaletteObject;
    property PaletteByText[const Text: WideString]: TNxPalette read GetPaletteByText;
    property PaletteText[const Index: Integer]: WideString read GetPaletteText;
  published
    property Align;
    property BottomBorder: Boolean read FBottomBorder write SetBottomBorder default True;
    property ButtonHeight: Integer read FButtonHeight write SetButtonHeight default szToolButtonHeight;
    property ButtonWidth: Integer read FButtonWidth write SetButtonWidth default szToolButtonWidth;
    property Color;
    property Constraints;
    property Font;
    property Images: TImageList read FImages write SetImages;
    property Indent: TSize read FIndent write SetIndent;
    property ItemIndex: Integer read FItemIndex write SetItemIndex;
    property ParentColor;
    property ParentFont;
    property SelectedFont: TFont read FSelectedFont write SetSelectedFont;
    property TabHeight: Integer read FTabHeight write SetTabHeight default 23;
    property OnButtonClick: TNxPaletteButtonClickEvent read FOnButtonClick write FOnButtonClick;
  end;

  { TNxCSVDocument6 }

  TNxGetValueEvent = procedure (Sender: TObject; X, Y: Integer; var Value: WideString) of object;
  TNxSetValueEvent = procedure (Sender: TObject; X, Y: Integer; Value: WideString) of object;

  TNxCSVDocument6 = class(TComponent)
  private
    FColCount: Integer;
    FMultiLineChar: WideChar;
    FOnAddRow: TNotifyEvent;
    FOnGetValueEvent: TNxGetValueEvent;
    FOnSetValueEvent: TNxSetValueEvent;
    FRowCount: Integer;
    FSeparatorChar: WideChar;
    FStartRow: Integer;
    { Property Accessors }
    function GetValue(X, Y: Integer): WideString;
    procedure SetColCount(const Value: Integer);
    procedure SetMultiLineChar(const Value: WideChar);
    procedure SetRowCount(const Value: Integer);
    procedure SetSeparatorChar(const Value: WideChar);
    procedure SetValue(X, Y: Integer; const Value: WideString);
  protected
    { Event Handlers }
    procedure DoAddRow; dynamic;
    procedure DoGetValueEvent(X, Y: Integer; var Value: WideString); dynamic;
    procedure DoSetValueEvent(X, Y: Integer; Value: WideString); dynamic;
  public
    constructor Create(AOwner: TComponent); override;
    procedure LoadFromCSV(const FileName: WideString; Encoding: TNxEncoding);
    procedure SaveToCSV(const FileName: WideString; Encoding: TNxEncoding);
    { Properties }
    property Value[X, Y: Integer]: WideString read GetValue write SetValue;
  published
    property ColCount: Integer read FColCount write SetColCount default 0;
    property MultiLineChar: WideChar read FMultiLineChar write SetMultiLineChar default #0;
    property RowCount: Integer read FRowCount write SetRowCount default 0;
    property SeparatorChar: WideChar read FSeparatorChar write SetSeparatorChar default ',';
    property StartRow: Integer read FStartRow write FStartRow default 0;
    { Events }
    property OnAddRow: TNotifyEvent read FOnAddRow write FOnAddRow;
    property OnGetValue: TNxGetValueEvent read FOnGetValueEvent write FOnGetValueEvent;
    property OnSetValue: TNxSetValueEvent read FOnSetValueEvent write FOnSetValueEvent;
  end;

  { TNxToolbar6 }

  TNxToolButtonClick = procedure (Sender: TObject; const Index: Integer) of object;

  INxToolbar = interface
    ['{1242B883-CA56-49A4-8176-4DE3DCFB81F0}']
    function GetInstance: TObject;
    procedure ButtonChanged(const Index: Integer);
    procedure DoToolButtonClick(const Index: Integer);
    property Instance: TObject read GetInstance;
  end;

  TNxToolbar6 = class;
  TNxToolButtons6 = class;

  TNxToolButton6 = class(TCollectionItem)
  private
    FAllowAllUp: Boolean;
    FAutoCheck: Boolean;
    FBindValue: WideString;
    FChecked: Boolean;
    FDivider: Boolean;
    FEnabled: Boolean;
    FGroupIndex: Integer;
    FHeight: Integer;
    FHint: WideString;
    FImageIndex: TImageIndex;
    FText: WideString;
    FWidth: Integer;
    { Property Accessors }
    function GetButtons: TNxToolButtons6;
    procedure SetChecked(const Value: Boolean);
    procedure SetDivider(const Value: Boolean);
    procedure SetEnabled(const Value: Boolean);
    procedure SetHeight(const Value: Integer);
    procedure SetImageIndex(const Value: TImageIndex);
    procedure SetText(const Value: WideString);
    procedure SetWidth(const Value: Integer);
  public
    { Overrided Methods }
    procedure Assign(Source: TPersistent); override;
    constructor Create(Collection: TCollection); override;
    { Methods }
    procedure ToggleChecked;
    property Buttons: TNxToolButtons6 read GetButtons;
  published
    property AllowAllUp: Boolean read FAllowAllUp write FAllowAllUp default False;
    property AutoCheck: Boolean read FAutoCheck write FAutoCheck default False;
    property BindValue: WideString read FBindValue write FBindValue;
    property Checked: Boolean read FChecked write SetChecked default False;
    property Divider: Boolean read FDivider write SetDivider default False;
    property Enabled: Boolean read FEnabled write SetEnabled default True;
    property GroupIndex: Integer read FGroupIndex write FGroupIndex default -1;
    property Height: Integer read FHeight write SetHeight default 0;
    property Hint: WideString read FHint write FHint;
    property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default Undefined;
    property Text: WideString read FText write SetText;
    property Width: Integer read FWidth write SetWidth default 0;
  end;

  TNxToolButtons6 = class(TCollection)
  private
    { Property Fields }
    FToolbar: INxToolbar;
    { Property Accessors }
    function GetCheckedCount: Integer;
    function GetItem(Index: Integer): TNxToolButton6;
    procedure SetItem(Index: Integer; const Value: TNxToolButton6);
  protected
    function GetOwner: TPersistent; override;
    procedure Update(Item: TCollectionItem); override;
  public
    constructor Create(AToolbar: INxToolbar);
    function Add: TNxToolButton6;
    function GroupCheckedCount(const GroupIndex: Integer): Integer;
    function IsGrouped: Boolean;
    procedure EnableGroup(const GroupIndex: Integer; const Enabled: Boolean);
    procedure RefreshButton(const Index: Integer);
    procedure UncheckAll;
    procedure UncheckGroup(const GroupIndex, CheckedIndex: Integer);
    { Properties }
    property Items[Index: Integer]: TNxToolButton6 read GetItem write SetItem; default;
    property CheckedCount: Integer read GetCheckedCount;
  end;

  TNxToolbar6 = class(TNxStyledControl6, INxToolbar)
  private
    FBottomLine: Boolean;
    FButtons: TNxToolButtons6;
    FHintDelayTimer: TTimer;
    FHintWindow: THintWindow;
    FHotButton: TNxToolButton6;
    FImages: TImageList;
    FOnButtonClick: TNxToolButtonClick;
    FPressedButton: TNxToolButton6;
    FStylePaint: INxStylePaint;
    FOrientation: TNxOrientation;
    { Property Accessors }
    function GetContentRect: TRect;
    procedure SetBottomLine(const Value: Boolean);
    procedure SetButtons(const Value: TNxToolButtons6);
    procedure SetHotButton(const Value: TNxToolButton6);
    procedure SetImages(const Value: TImageList);
    procedure SetOrientation(const Value: TNxOrientation);
    procedure SetPressedButton(const Value: TNxToolButton6);
  protected
    { INxToolbar }
    function GetInstance: TObject;
    procedure ButtonChanged(const Index: Integer);
    procedure DoPaddingChange(Sender: TObject); override;
    procedure DoToolButtonClick(const Index: Integer); dynamic;
    { Event Handlers }
    procedure DoHintDelayTimer(Sender: TObject);
    { Methods }
    function GetButtonAtPos(X, Y: Integer): TNxToolButton6;
    function GetButtonRect(Button: TNxToolButton6): TRect;
    function GetButtonState(const Index: Integer): TNxButtonState;
    function GetButtonSize(const Index: Integer): TSize;
    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;
    procedure Paint; override;
    procedure ReleaseHint;
    { Paint }
    procedure PaintButton(const Index: Integer; ButtonRect: TRect);
    procedure PaintDivider(DividerRect: TRect);
    procedure RefreshButton(Button: TNxToolButton6);
    { Delphi Messages }
    procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    { Properties }
    property ContentRect: TRect read GetContentRect;
    property HotButton: TNxToolButton6 read FHotButton write SetHotButton;
    property PressedButton: TNxToolButton6 read FPressedButton write SetPressedButton;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Align;
    property BottomLine: Boolean read FBottomLine write SetBottomLine default False;
    property Buttons: TNxToolButtons6 read FButtons write SetButtons;
    property Color;
    property Constraints;
    property Images: TImageList read FImages write SetImages;
    property Orientation: TNxOrientation read FOrientation write SetOrientation default orHorizontal;
    property Padding;
    property ParentColor;
    { Events }
    property OnButtonClick: TNxToolButtonClick read FOnButtonClick write FOnButtonClick;
  end;

  { TNxNetSource }

  TNxNetSource = class(TComponent)
  private
    FAgent: string;
    FNetHandle: HINTERNET;
    FUrl: string;
    { Property Accessors }
    function GetActive: Boolean;
    procedure SetActive(const Value: Boolean);
  public
    function GetAsString: WideString;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Properties }
    property AsString: WideString read GetAsString;
  published
    { Properties }
    property Active: Boolean read GetActive write SetActive default False;
    property Agent: string read FAgent write FAgent;
    property Url: string read FUrl write FUrl;
  end;

implementation

uses
  { Standard }
  SysUtils, Math, TypInfo, Dialogs,
  { Next Standard 6 }
  NxTypInfo6;

{$IF not Declared(UTF8ToString)}
function UTF8ToWideString(const s: UTF8String): WideString;
begin
  Result := UTF8Decode(s);
end;
{$IFEND}

{ TNxPalleteButton }

constructor TNxPaletteButton.Create(AOwner: TNxPalette);
begin
  FCollection := AOwner;
end;

function TNxPaletteButton.GetIndex: Integer;
begin
  Result := FCollection.FButtons.IndexOf(Self);
end;

{ TNxPaletteButtons }

function TNxPalette.AddButton(ReferenceType: TComponentClass;
  ImageIndex: TImageIndex): TNxPaletteButton;
begin
  Result := TNxPaletteButton.Create(Self);

  { Set Prop. }
  Result.ReferenceType := ReferenceType;
  Result.ImageIndex := ImageIndex;

  { Add into List }
  FButtons.Add(Result);
end;

constructor TNxPalette.Create(Caption: TCaption);
begin
  FButtons := TList.Create;
  FCaption := Caption;
end;

destructor TNxPalette.Destroy;
begin
  { Destroy Obj. }
  FButtons.Free;

  inherited;
end;

function TNxPalette.GetButton(Index: Integer): TNxPaletteButton;
begin
  try
    Result := TNxPaletteButton(FButtons[Index]);
  except
    ShowMessage(Format('Access Error to button %d. Total count: %d', [Index, Count]));
    Result := nil;
  end;
end;

function TNxPalette.GetCount: Integer;
begin
  Result := FButtons.Count;
end;

{ TNxToolPalette }

function TNxToolPalette6.Add(Palette: string; ReferenceType: TComponentClass;
  ImageIndex: Integer): TNxPaletteButton;
var
  APalette: TNxPalette;
begin
  APalette := PaletteByText[Palette];

  if not Assigned(APalette) then
  begin
    APalette := TNxPalette.Create(Palette);

    FPalettes.Add(APalette);
  end;

  Result := APalette.AddButton(ReferenceType, ImageIndex);
end;

procedure TNxToolPalette6.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  HoverButton := nil;
  HoverPalette := nil;
end;

constructor TNxToolPalette6.Create(AOwner: TComponent);
begin
  inherited;
  FBottomBorder := True;
  FButtonHeight := szToolButtonHeight;
  FButtonWidth := szToolButtonWidth;
  FHoverButton := nil;
  FHoverPalette := nil;
  FImages := nil;
  FIndent.cx := 0;
  FIndent.cy := 0;
  FItemIndex := 0;
  FPalettes := TList.Create;
  FPressedButton := nil;
  FSelectedFont := TFont.Create;
  FTabHeight := 23;
end;

procedure TNxToolPalette6.CreateParams(var Params: TCreateParams);
begin
  inherited;

  with Params do
  begin
    { Don't redraw on Resize }
		with WindowClass do Style := Style and not(CS_HREDRAW or CS_VREDRAW);
  end;
end;

destructor TNxToolPalette6.Destroy;
begin
  { Destroy Obj. }
  FPalettes.Free;
  FSelectedFont.Free;

  inherited;
end;

procedure TNxToolPalette6.DoButtonClick(Index: Integer);
begin
  if Assigned(FOnButtonClick) then FOnButtonClick(Self, Index);
end;

procedure TNxToolPalette6.DoHintDelayTimer(Sender: TObject);
var
  HintPos: TPoint;
  HintRect: TRect;
begin
  { Stop & Destroy }
  FreeAndNil(FHintDelayTimer);

  if Assigned(FHoverButton) and
    not (FHoverButton = FPressedButton) then
  begin
    { Create Hint Window }
    FButtonHint := HintWindowClass.Create(nil);
    FButtonHint.Color := clInfoBk;

    { Set Position & Activate }
    HintPos := GetButtonRectangle(FHoverButton.Index).TopLeft;
    Inc(HintPos.Y, FButtonHeight);

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

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

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

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

function TNxToolPalette6.GetButtonAtPos(const X, Y: Integer): TNxPaletteButton;
var
  i: Integer;
  ButtonRect: TRect;
begin
  { Default }
  Result := nil;

  ButtonRect := Bounds(4, FIndent.cy + TabHeight + 4, FButtonWidth, FButtonWidth);

  for i := 0 to Pred(Palette[FItemIndex].Count) do
  begin
    { Check? }
    if PtInRect(ButtonRect, Point(X, Y)) then
    begin
      Result := Palette[FItemIndex].Button[i];

      { Done }
      Exit;
    end;

    { Shift right }
    OffsetRect(ButtonRect, FButtonWidth, 0);
  end;
end;

function TNxToolPalette6.GetButtonRectangle(const Index: Integer): TRect;
begin
  Result := Bounds(4, TabHeight + FIndent.cy + 4, FButtonWidth, FButtonWidth);

  { Shift right }
  OffsetRect(Result, FButtonWidth * Index, 0);
end;

function TNxToolPalette6.GetCount: Integer;
begin
  Result := FPalettes.Count;
end;

function TNxToolPalette6.GetPaletteObject(const Index: Integer): TNxPalette;
begin
  Result := TNxPalette(FPalettes[Index]);
end;

function TNxToolPalette6.GetPaletteAtPos(const X, Y: Integer): TNxPalette;
var
  i, TabSize: Integer;
  TabRect: TRect;
  Pos: TPoint;
  Text: WideString;
begin
  { Default }
  Result := nil;

  { Start Pos. }
  Pos := Point(FIndent.cx, FIndent.cy);

  for i := 0 to Pred(FPalettes.Count) do
  begin
    Text := Palette[i].Caption;

    TabSize := GetTextWidth(Canvas, Text, wkNone) + 7 * 2;

    TabRect := Bounds(Pos.X, Pos.Y, TabSize, FTabHeight);

    if PtInRect(TabRect, Point(X, Y)) then
    begin
      Result := Palette[i];

      { Done }
      Exit;
    end;

    { Shift right }
    Inc(Pos.X, TabSize);

    { Add Spacing }
    Inc(Pos.X, pdStandard);
  end;
end;

function TNxToolPalette6.GetPaletteByText(
  const Text: WideString): TNxPalette;
var
  i: Integer;
begin
  { Default }
  Result := nil;

  for i := 0 to Pred(FPalettes.Count) do
    if WideCompareStr(Palette[i].Caption, Text) = 0 then
    begin
      Result := Palette[i];

      { Done. }
      Exit;
    end;
end;

function TNxToolPalette6.GetPaletteRectangle(const Index: Integer): TRect;
var
  i, TabSize: Integer;
  Pos: TPoint;
  Text: WideString;
begin
  { Start Pos. }
  Pos := Point(FIndent.cx, FIndent.cy);

  for i := 0 to Pred(FPalettes.Count) do
  begin
    Text := WideUpperCase(Palette[i].Caption);

    TabSize := GetTextWidth(Canvas, Text, wkNone) + 7 * 2;

    Result := Bounds(Pos.X, Pos.Y, TabSize, FTabHeight);

    if Index = i then
    begin
      { Done }
      Exit;
    end;

    { Shift right }
    Inc(Pos.X, TabSize);

    { Add Spacing }
    Inc(Pos.X, pdStandard);
  end;
end;

function TNxToolPalette6.GetPaletteText(const Index: Integer): WideString;
begin
  Result := Palette[Index].Caption;
end;

procedure TNxToolPalette6.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  inherited;
  if PaletteExist(FItemIndex) then
  begin
    PressedButton := GetButtonAtPos(X, Y);
  end;
  ItemIndex := FPalettes.IndexOf(GetPaletteAtPos(X, Y));
end;

procedure TNxToolPalette6.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if PaletteExist(FItemIndex) then
  begin
    HoverButton := GetButtonAtPos(X, Y);
  end;
  HoverPalette := GetPaletteAtPos(X, Y);
end;

procedure TNxToolPalette6.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
var
  UpButton: TNxPaletteButton;
begin
  inherited;
  if PaletteExist(FItemIndex) then
  begin
    UpButton := GetButtonAtPos(X, Y);
    try
      { Trigger Event }
      if Assigned(PressedButton)
        and (PressedButton = UpButton)
          then DoButtonClick(PressedButton.Index);
    finally
      PressedButton := nil;
    end;
  end;
end;

procedure TNxToolPalette6.Paint;
begin
  inherited;

  { Set Font }
  Canvas.Font.Assign(Font);

  PaintTabs;

  if InRange(FItemIndex, 0, Pred(FPalettes.Count))
    then PaintButtons(FItemIndex);

  if BottomBorder then
    with Canvas do
    begin
      Pen.Color := $00dcdbda;
      Polyline([
        Point(0, ClientHeight - 1),
        Point(ClientWidth, ClientHeight - 1)
      ]);
    end;
end;

procedure TNxToolPalette6.PaintButton(const Tab, Index: Integer;
  ButtonRect: TRect; State: TNxButtonState);
var
  PaintPos: TPoint;
begin
  with Canvas do
  begin

    if bsHover in State then
    begin
      Brush.Color := $00f7efe8;
      Pen.Color := $00f9cea4;

      if bsPressed in State
        then Brush.Color := $00f7e0c9;

      Rectangle(ButtonRect);
    end;

    if Assigned(Images) then
    begin
      PaintPos := ButtonRect.TopLeft;

      Inc(PaintPos.X, (ButtonRect.Right - ButtonRect.Left) div 2 - FImages.Width div 2);
      Inc(PaintPos.Y, (ButtonRect.Bottom - ButtonRect.Top) div 2 - FImages.Height div 2);

      Images.Draw(Canvas, PaintPos.X, PaintPos.Y, Palette[Tab].Button[Index].ImageIndex);
    end;
  end;
end;

procedure TNxToolPalette6.PaintButtons(const Tab: Integer);
var
  i: Integer;
  ButtonRect: TRect;
  State: TNxButtonState;
begin
  ButtonRect := Bounds(4, FIndent.cy + TabHeight + 4, FButtonWidth, FButtonHeight);

  for i := 0 to Pred(Palette[Tab].Count) do
  begin
    State := [];

    if Palette[Tab].Button[i] = HoverButton then
    begin
      if not Assigned(PressedButton)
        or (PressedButton = HoverButton) then Include(State, bsHover);
    end;

    if Palette[Tab].Button[i] = PressedButton then Include(State, bsPressed);

    PaintButton(Tab, i, ButtonRect, State);

    { Shift right }
    OffsetRect(ButtonRect, FButtonWidth, 0);
  end;
end;

procedure TNxToolPalette6.PaintTab(const Tab: Integer; TabRect: TRect);
var
  Text: WideString;
begin
  { Reset }
  Canvas.Font.Assign(Font);

  { Hot Tab }
  if Palette[Tab] = FHoverPalette then
  begin

  end;

  { Selected Tab }
  if Tab = FItemIndex then
    with Canvas do
    begin
      Brush.Color := Color;
      FillRect(TabRect);

      Pen.Color := $00dcdbda;
      Polyline([
        Point(TabRect.Left, TabRect.Bottom),
        TabRect.TopLeft,
        Point(TabRect.Right, TabRect.Top),
        Point(TabRect.Right, TabRect.Bottom + 1)
      ]);
      Font.Assign(SelectedFont);
    end;

  Text := Palette[Tab].Caption;

  DrawTextRect(Canvas, TabRect, Classes.taCenter, taVerticalCenter, Text, wkNone);
end;

procedure TNxToolPalette6.PaintTabs;
var
  i, TabSize: Integer;
  TabRect, ActiveRect: TRect;
  Pos: TPoint;
  Text: WideString;
begin
  with Canvas do
  begin
    Brush.Color := clWhite;
    FillRect(Rect(0, 0, ClientWidth, TabHeight + Indent.cy));
  end;

  Pos := Point(FIndent.cx, FIndent.cy);

  for i := 0 to Pred(FPalettes.Count) do
  begin
    Text := Palette[i].Caption;

    TabSize := GetTextWidth(Canvas, Text, wkNone) + 7 * 2;

    TabRect := Bounds(Pos.X, Pos.Y, TabSize, FTabHeight);

    if i = FItemIndex then ActiveRect := TabRect;

    PaintTab(i, TabRect);

    { Shift right }
    Inc(Pos.X, TabSize);

    { Add Spacing }
    Inc(Pos.X, pdStandard);
  end;

  if PaletteExist(FItemIndex) then
    with Canvas do
    begin
      Pen.Color := clWordTabBorder;
      Polygon([
        Point(0, ActiveRect.Bottom),
        Point(ActiveRect.Left, ActiveRect.Bottom)
      ]);
      Polygon([
        Point(ActiveRect.Right, ActiveRect.Bottom),
        Point(ClipRect.Right, ActiveRect.Bottom)
      ]);
    end;
end;

function TNxToolPalette6.PaletteExist(const Index: Integer): Boolean;
begin
  Result := InRange(Index, 0, Pred(FPalettes.Count));
end;

procedure TNxToolPalette6.RefreshButton(Button: TNxPaletteButton);
var
  R: TRect;
begin
  R := GetButtonRectangle(Button.Index);
{$IFDEF UNSAFE}
  InvalidateRect(Handle, @R, True);
{$ELSE}
  InvalidateRect(Handle, R, True);
{$ENDIF}
end;

procedure TNxToolPalette6.RefreshPalette(Palette: TNxPalette);
var
  R: TRect;
begin
  R := GetPaletteRectangle(FPalettes.IndexOf(Palette));
{$IFDEF UNSAFE}
  InvalidateRect(Handle, @R, True);
{$ELSE}
  InvalidateRect(Handle, R, True);
{$ENDIF}
end;

procedure TNxToolPalette6.SetBottomBorder(const Value: Boolean);
begin
  if Value <> FBottomBorder then
  begin
    FBottomBorder := Value;
    Invalidate;
  end;
end;

procedure TNxToolPalette6.SetButtonHeight(const Value: Integer);
begin
  if Value <> FButtonHeight then
  begin
    FButtonHeight := Value;
    Invalidate;
  end;
end;

procedure TNxToolPalette6.SetButtonWidth(const Value: Integer);
begin
  if Value <> FButtonWidth then
  begin
    FButtonWidth := Value;
    Invalidate;
  end;
end;

procedure TNxToolPalette6.SetHoverButton(const Value: TNxPaletteButton);
var
  PrevHover: TNxPaletteButton;
begin
  if Value <> FHoverButton then
  begin
    { Destroy old Hint, if exists }
    if Assigned(FButtonHint) then
    begin
      { Required }
      FButtonHint.ReleaseHandle;
      { Destroy }
      FreeAndNil(FButtonHint);
    end;

    FreeAndNil(FHintDelayTimer);

    PrevHover := FHoverButton;

    { Set }
    FHoverButton := Value;

    { Old }
    if Assigned(PrevHover) then RefreshButton(PrevHover);

    { Current }
    if Assigned(FHoverButton) then
    begin

      RefreshButton(FHoverButton);

      { Start TTimer }
      if FHoverButton.Hint <> EmptyStr then
      begin
        FHintDelayTimer := TTimer.Create(Self);
        FHintDelayTimer.Interval := Application.HintPause;
        FHintDelayTimer.OnTimer := DoHintDelayTimer;
      end;

    end;

  end;

end;

procedure TNxToolPalette6.SetHoverPalette(const Value: TNxPalette);
var
  PrevHover: TNxPalette;
begin

  if Value <> FHoverPalette then
  begin
    PrevHover := FHoverPalette;

    { Set }
    FHoverPalette := Value;

    { Old }
    if Assigned(PrevHover)
      then RefreshPalette(PrevHover);

    { Current }
    if Assigned(FHoverPalette)
      then RefreshPalette(FHoverPalette);

  end;
end;

procedure TNxToolPalette6.SetImages(const Value: TImageList);
begin
  FImages := Value;

  Invalidate;
end;

procedure TNxToolPalette6.SetIndent(const Value: TSize);
begin
  FIndent := Value;

  Invalidate;
end;

procedure TNxToolPalette6.SetItemIndex(const Value: Integer);
begin
  if Value <> FItemIndex then
  begin
    if PaletteExist(Value) then
    begin
      FItemIndex := Value;

      Invalidate;
    end;
  end;
end;

procedure TNxToolPalette6.SetPressedButton(const Value: TNxPaletteButton);
var
  PrevPressed: TNxPaletteButton;
begin
  if Value <> FPressedButton then
  begin
    PrevPressed := FPressedButton;
    FPressedButton := Value;

    if Assigned(PrevPressed)
      then RefreshButton(PrevPressed);

    if Assigned(FPressedButton)
      then RefreshButton(FPressedButton);
  end;
end;

procedure TNxToolPalette6.SetSelectedFont(const Value: TFont);
begin
  FSelectedFont.Assign(Value);

  Invalidate;
end;

procedure TNxToolPalette6.SetTabHeight(const Value: Integer);
begin
  FTabHeight := Value;

  Invalidate;
end;

{ TNxCSVDocument6 }

constructor TNxCSVDocument6.Create(AOwner: TComponent);
begin
  inherited;
  FColCount := 0;
  FMultiLineChar := EmptyChr;
  FRowCount := 0;
  FSeparatorChar := ',';
  FStartRow := 0;
end;

procedure TNxCSVDocument6.DoAddRow;
begin
  if Assigned(FOnAddRow) then FOnAddRow(Self);
end;

procedure TNxCSVDocument6.DoGetValueEvent(X, Y: Integer; var Value: WideString);
begin
  if Assigned(FOnGetValueEvent) then FOnGetValueEvent(Self, X, Y, Value);  
end;

procedure TNxCSVDocument6.DoSetValueEvent(X, Y: Integer; Value: WideString);
begin
  if Assigned(FOnSetValueEvent) then FOnSetValueEvent(Self, X, Y, Value);
end;

function TNxCSVDocument6.GetValue(X, Y: Integer): WideString;
begin
  Result := EmptyStr;
  DoGetValueEvent(X, Y, Result);
end;

procedure TNxCSVDocument6.LoadFromCSV(const FileName: WideString;
  Encoding: TNxEncoding);
var
  b: Byte;
  w: Word;
  fw: File of WideChar;
  f: File of Byte;
  fa: File of Char;
  QChar, wc: WideChar;
  Ch: Char;
  QNo: Integer;
  Value: WideString;
  UnicodeFile, UnicodeBigEndian, AddValue, NewRow, EofF: Boolean;
  ColumnIndex, RowIndex, ReadIndex: Integer;
begin
  QChar := '"';
  ColumnIndex := -1;
  RowIndex := -1;
  ReadIndex := 0;
  UnicodeFile := False;
  UnicodeBigEndian := False;

  { check file encoding }
  AssignFile(f, FileName);
  FileMode := 0;
  Reset(F);
  if FileSize(F) >= 2 then
  begin
    Read(f, b); { read first byte }
    if b = 255 then
    begin
      Read(f, b);
      if b = 254 then UnicodeFile := True;
    end else if b = 254 then
    begin
      Read(f, b);
      if b = 255 then
      begin
        UnicodeBigEndian := True;
        UnicodeFile := True;
      end;
    end;
  end;
  Close(F);

  { open file for reading }
  if UnicodeFile then
  begin
    AssignFile(fw, Filename);
    FileMode := 0;
    Reset(fw);
    { ignore first widechar }
    Read(fw, wc);
    EofF := Eof(fw);
  end else
  begin
    AssignFile(fa, Filename);
    FileMode := 0;
    Reset(fa);
    EofF := Eof(fa);
  end;

  AddValue := False;
  NewRow := True;
  QNo := 0;
  while not EofF do
  begin
    if UnicodeFile then
    begin
      Read(fw, wc);
      if UnicodeBigEndian then
      begin
        w := Ord(wc);
        wc := Widechar(((w and $255) shl 8) + (w  shr 8)); { rotate bytes }
      end;
      EofF := Eof(fw);
    end else
    begin
      Read(fa, ch);
      wc := WideChar(ch);
      EofF := Eof(fa);
    end;

    if wc = SeparatorChar then
    begin
      if (QNo mod 2 = 0) then AddValue := True else Value := Value + wc;
    end else if wc = MultiLineChar then
    begin
      Value := Value + #13#10;
    end else if wc = QChar then
    begin
      Inc(QNo);
      if (QNo mod 2 = 1) and ((Value <> '') or (QNo = 3)) then Value := Value + QChar;
    end else if wc = #13 then
    begin

    end else if wc = #10 then AddValue := True else Value := Value + wc;

    if AddValue then
    begin
      QNo := 0;
      AddValue := False;
      if NewRow then
      begin
        NewRow := False;
        if ReadIndex >= StartRow then
        begin
          DoAddRow;
          Inc(RowIndex);
        end;
        Inc(ReadIndex); { Row in file }
        ColumnIndex := -1;
      end;

      Inc(ColumnIndex);

      if ReadIndex > StartRow then
      begin
        { Assign Value to Cell }
        Self.Value[ColumnIndex, RowIndex] := Value;

        { Event }
//        if Assigned(FOnLoadProgress) then FOnLoadProgress(Self, ColumnIndex, RowIndex);
      end;

      Value := '';

      if wc = #10 then NewRow := true;
    end;
  end; { while }

  if Value <> '' then
  begin
    Inc(ColumnIndex);
    Self.Value[ColumnIndex,RowIndex] := Value;
  end;
  if UnicodeFile then Close(fw) else Close(fa);
end;

procedure TNxCSVDocument6.SaveToCSV(const FileName: WideString;
  Encoding: TNxEncoding);

  function Proc(s: WideString): WideString;
	var
    i: Integer;
  begin
  	Result := '';

		for i := 1 to Length(s) do
      if s[i] = '"'	then
      begin
        if (i = 1) or (i = Length(s)) then Result := Result + '"' + s[i] else Result := Result + s[i] + '"';
      end else
      begin
        if s[i] = #13 then Result := Result + MultiLineChar else
          if s[i] <> #10 then Result := Result + s[i];
      end;
    if (Pos(SeparatorChar, s) <> 0) or (Pos('"', s) <> 0) then Result := '"' + Result + '"';
  end;

  procedure WriteUnicode;
  var
    y, x, z: Integer;
    f: file of WideChar;
    q: WideString;
    wchr: WideChar;
  begin
    AssignFile(f, FileName);
    Rewrite(f);

    { Header }
    case Encoding of
      ekUnicode: wchr := #$FEFF;
      ekUnicodeBigEndian: wchr := #$FFFE;
    end;

    Write(f, wchr);

    for y := 0 to Pred(RowCount) do
      for x := 0 to Pred(ColCount) do
      begin
        q := Proc(Value[x, y]);

        for z := 1 to Length(q) do Write(f, q[z]);

        if not (x = Pred(ColCount))
          then Write(f, SeparatorChar);

        { WriteLn }
        if x = Pred(ColCount) then
        begin
          wchr := #13;
          Write(f, wchr);
          wchr := #10;
          Write(f, wchr);
        end;
      end;

    CloseFile(f);
  end;

  procedure WriteANSI;
  var
    f: System.Text;
    x, y: Integer;
  begin
    { Open file }
    AssignFile(f, FileName);
    Rewrite(f);

    for y := 0 to Pred(RowCount) do
    begin
      for x := 0 to Pred(ColCount) do
      begin
        //TODO: Conditional write
        Write(f, Proc(Value[x, y]));

        { Don't write at end of line }
        if x <> Pred(ColCount)
          then Write(f, SeparatorChar)
      end;

      { New row }
      Writeln(f);
    end;

    CloseFile(f);
  end;

begin
  case Encoding of
    ekAnsi: WriteANSI;
    ekUnicode, ekUnicodeBigEndian: WriteUnicode;
  end;
end;

procedure TNxCSVDocument6.SetColCount(const Value: Integer);
begin
  FColCount := Value;
end;

procedure TNxCSVDocument6.SetMultiLineChar(const Value: WideChar);
begin
  FMultiLineChar := Value;
end;

procedure TNxCSVDocument6.SetRowCount(const Value: Integer);
begin
  FRowCount := Value;
end;

procedure TNxCSVDocument6.SetSeparatorChar(const Value: WideChar);
begin
  FSeparatorChar := Value;
end;

procedure TNxCSVDocument6.SetValue(X, Y: Integer; const Value: WideString);
begin
  DoSetValueEvent(X, Y, Value);
end;

{ TNxToolbarButton6 }

procedure TNxToolButton6.Assign(Source: TPersistent);
begin
  inherited;
  if Source is TNxToolButton6 then
  begin
    AllowAllUp := TNxToolButton6(Source).AllowAllUp;
  end;
end;

constructor TNxToolButton6.Create(Collection: TCollection);
begin
  inherited;
  FAllowAllUp := False;
  FAutoCheck := False;
  FChecked := False;
  FDivider := False;
  FEnabled := True;
  FGroupIndex := -1;
  FHeight := 0;
  FHint := EmptyStr;
  FImageIndex := -1;
  FText := EmptyStr;
  FWidth := 0;
end;

function TNxToolButton6.GetButtons: TNxToolButtons6;
begin
  Result := TNxToolButtons6(Collection);
end;

procedure TNxToolButton6.SetChecked(const Value: Boolean);
begin
  if Value <> FChecked then
  begin
    if not Value then
    begin
      { Can Uncheck? }
      if (FGroupIndex <> -1) and not FAllowAllUp
        and (GetButtons.GroupCheckedCount(FGroupIndex) <= 1) then
      begin
        Exit;
      end;
      FChecked := Value;
    end else
    begin
      FChecked := Value;
      if FChecked and (FGroupIndex <> -1) then
      begin
        GetButtons.UncheckGroup(FGroupIndex, Index);
      end;
    end;
    Changed(False);
  end;
end;

procedure TNxToolButton6.SetDivider(const Value: Boolean);
begin
  if Value <> FDivider then
  begin
    FDivider := Value;
    Changed(False);
  end;
end;

procedure TNxToolButton6.SetEnabled(const Value: Boolean);
begin
  if Value <> FEnabled then
  begin
    FEnabled := Value;
    Changed(False);
  end;
end;

procedure TNxToolButton6.SetHeight(const Value: Integer);
begin
  if Value <> FHeight then
  begin
    FHeight := Value;
    Changed(True);
  end;
end;

procedure TNxToolButton6.SetImageIndex(const Value: TImageIndex);
begin
  if Value <> FImageIndex then
  begin
    FImageIndex := Value;
    Changed(False);
  end;
end;

procedure TNxToolButton6.SetText(const Value: WideString);
begin
  if Value <> FText then
  begin
    FText := Value;
    Changed(True);
  end;
end;

procedure TNxToolButton6.SetWidth(const Value: Integer);
begin
  if Value <> FWidth then
  begin
    FWidth := Value;
    Changed(True);
  end;
end;

procedure TNxToolButton6.ToggleChecked;
begin
  Checked := not Checked;
end;

{ TNxToolbarButtons6 }

function TNxToolButtons6.Add: TNxToolButton6;
begin
  Result := TNxToolButton6(inherited Add);
  FToolbar.ButtonChanged(Result.Index);
end;

function TNxToolButtons6.GroupCheckedCount(const GroupIndex: Integer): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to Pred(Count) do
  begin
    if Items[i].GroupIndex = GroupIndex then
    begin
      if Items[i].Checked then Inc(Result);
    end;
  end;
end;

constructor TNxToolButtons6.Create(AToolbar: INxToolbar);
begin
  inherited Create(TNxToolButton6);
  FToolbar := AToolbar;
end;

procedure TNxToolButtons6.EnableGroup(const GroupIndex: Integer; const Enabled: Boolean);
var
  i: Integer;
begin
  for i := 0 to Pred(Count) do
  begin
    if Items[i].GroupIndex = GroupIndex then
    begin
      Items[i].Enabled := Enabled;
    end;
  end;
end;

function TNxToolButtons6.GetCheckedCount: Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to Count - 1 do if Items[i].Checked then Inc(Result);
end;

function TNxToolButtons6.GetItem(Index: Integer): TNxToolButton6;
begin
  Result := TNxToolButton6(inherited GetItem(Index));
end;

function TNxToolButtons6.GetOwner: TPersistent;
begin
  if Assigned(FToolbar) then
  begin
    Result := TPersistent(FToolbar.Instance);
  end
  else Result := nil;
end;

function TNxToolButtons6.IsGrouped: Boolean;
begin
  Result := False;
end;

procedure TNxToolButtons6.RefreshButton(const Index: Integer);
begin

end;

procedure TNxToolButtons6.SetItem(Index: Integer;
  const Value: TNxToolButton6);
begin
  inherited SetItem(Index, Value);
end;

procedure TNxToolButtons6.UncheckAll;
var
  i: integer;
begin
  for i := 0 to Pred(Count) do Items[i].Checked := False;
end;

procedure TNxToolButtons6.UncheckGroup(const GroupIndex, CheckedIndex: Integer);
var
  i: integer;
begin
  for i := 0 to Pred(Count) do
    if Items[i].GroupIndex = GroupIndex then
      if i <> CheckedIndex then
      begin
        Items[i].Checked := False;
      end;
end;

procedure TNxToolButtons6.Update(Item: TCollectionItem);
begin
  inherited;
  if Assigned(Item) then
  begin
    FToolbar.ButtonChanged(Item.Index);
  end else
  begin
    FToolbar.ButtonChanged(-1);
  end;
end;

{ TNxToolbar6 }

procedure TNxToolbar6.ButtonChanged(const Index: Integer);
begin
  if Index <> -1 then RefreshButton(Buttons[Index])
  else Invalidate;
end;

procedure TNxToolbar6.CMDialogChar(var Message: TCMDialogChar);
var
  i: Integer;
begin
  with Message do
  begin
    for i := 0 to Pred(Buttons.Count) do
      if IsAccel(CharCode, Buttons[i].Text) then
      begin
        OnButtonClick(Self, i);
        Result := 1;
      end;
  end;
  inherited;
end;

procedure TNxToolbar6.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  HotButton := nil;
end;

constructor TNxToolbar6.Create(AOwner: TComponent);
begin
  inherited;
  FBottomLine := False;
  FButtons := TNxToolButtons6.Create(Self);
  FHintDelayTimer := nil;
  FHintWindow := nil;
  FHotButton := nil;
  FImages := nil;
  FOrientation := orHorizontal;
  FPressedButton := nil;

  { Style }
  FStylePaint := TNxNativeStylePaint.Create(Self);
end;

destructor TNxToolbar6.Destroy;
begin
  { Destroy Hint & Timer }
  ReleaseHint;

  { Destroy obj. }
  FreeAndNil(FButtons);
  FStylePaint := nil;

  inherited;
end;

procedure TNxToolbar6.DoHintDelayTimer(Sender: TObject);
var
  HintRect: TRect;
begin
  { Stop & Destroy }
  FreeAndNil(FHintDelayTimer);

  if Assigned(HotButton) and
    not (HotButton = PressedButton) then
  begin
    { Create Hint Window }
    FHintWindow := HintWindowClass.Create(nil);
    FHintWindow.Color := clInfoBk;

    { Set Position & Activate }

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

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

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

    case Orientation of
      orHorizontal: OffsetRect(HintRect, 0, ClientHeight - Padding.Bottom - Padding.Top);
      orVertical: OffsetRect(HintRect, ClientWidth, 0);
    end;

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

procedure TNxToolbar6.DoPaddingChange(Sender: TObject);
begin
  inherited;
  Invalidate;
end;

procedure TNxToolbar6.DoToolButtonClick(const Index: Integer);
begin
  if Assigned(FOnButtonClick) then FOnButtonClick(Self, Index);
end;

function TNxToolbar6.GetButtonAtPos(X, Y: Integer): TNxToolButton6;
var
  i: Integer;
  Pos: TPoint;
  ButtonRect: TRect;
begin
  Result := nil;

  { Start paint pos }
  Pos := Point(Padding.Left, Padding.Top);

  { Paint buttons }
  for i := 0 to Pred(Buttons.Count) do
  begin

    with GetButtonSize(i) do
    begin
      ButtonRect := Bounds(Pos.X, Pos.Y, cx, cy);

      if PtInRect(ButtonRect, Point(X, Y)) then
      begin
        Result := Buttons[i];

        { Done }
        Exit;
      end;

      case Orientation of
        orHorizontal: OffsetPoint(Pos, cx, 0);
        orVertical: OffsetPoint(Pos, 0, cy);
      end;
    end;

  end;

end;

function TNxToolbar6.GetButtonRect(Button: TNxToolButton6): TRect;
var
  i: Integer;
  Pos: TPoint;
begin
  { Start paint pos }
  Pos := Point(Padding.Left, Padding.Top);

  { Paint buttons }
  for i := 0 to Pred(Buttons.Count) do
  begin

    with GetButtonSize(i) do
    begin
      Result := Bounds(Pos.X, Pos.Y, cx, cy);

      { Done }
      if Buttons[i] = Button
        then Exit;

      case Orientation of
        orHorizontal: OffsetPoint(Pos, cx, 0);
        orVertical: OffsetPoint(Pos, 0, cy);
      end;
    end;

  end;

  { None }
  Unset(Result);
end;

function TNxToolbar6.GetButtonSize(const Index: Integer): TSize;
begin
  with Result, Buttons[Index] do
  begin

    if Divider then
    begin
      case Orientation of
        orHorizontal: cx := pdStandard * 2 + 1;
        orVertical: cy := pdStandard * 2 + 1;
      end;
    end else
    begin
      Result := GetTextSize(Canvas, Text, False);

      if Assigned(Images)
        and (ImageIndex <> Undefined) then
      begin
        Inc(cx, Images.Width);
        Inc(cy, Images.Height);

        { Icon to Text Spacing }
        if Text <> '' then Inc(cx, pdStandard);
      end;

      { Add Padding }
      Inc(cx, pdDouble * 2);
      Inc(cy, pdDouble * 2);
    end;

    { Fit to other Size & Min Height/Width }
    case Orientation of
      orHorizontal:
      begin
        cx := Max(cx, Width);
        cy := RectHeight(ContentRect) - Padding.Top - Padding.Bottom;
      end;
      orVertical:
      begin
        cx := RectWidth(ContentRect) - Padding.Left - Padding.Right;
        cy := Max(cy, Height);
      end;
    end;
  end;

end;

function TNxToolbar6.GetButtonState(const Index: Integer): TNxButtonState;
begin
  { Default }
  Result := [];
  if Buttons[Index].Checked then Include(Result, bsChecked);
  if Buttons[Index] = FHotButton then Include(Result, bsHover);
  if Buttons[Index] = FPressedButton then Include(Result, bsPressed);
  if not Buttons[Index].Enabled then Include(Result, bsDisabled);
end;

function TNxToolbar6.GetContentRect: TRect;
begin
  Result := ClientRect;
  if BottomLine then Dec(Result.Bottom);
end;

function TNxToolbar6.GetInstance: TObject;
begin
  Result := Self;
end;

procedure TNxToolbar6.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  inherited;
  PressedButton := GetButtonAtPos(X, Y);
end;

procedure TNxToolbar6.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  HotButton := GetButtonAtPos(X, Y);
end;

procedure TNxToolbar6.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  inherited;
  if Assigned(PressedButton) and not PressedButton.Divider
      and (GetButtonAtPos(X, Y) = PressedButton) then
  begin
    if PressedButton.Enabled then
    begin
      if PressedButton.AutoCheck then PressedButton.ToggleChecked;
      DoToolButtonClick(PressedButton.Index);
    end;
  end;

  PressedButton := nil;
end;

procedure TNxToolbar6.Paint;
var
  i: Integer;
  Pos: TPoint;
  ButtonRect: TRect;
  PaddingColor: TColor;
begin
  inherited;
  PaddingColor := Color;

  if csDesigning in ComponentState then
  begin
    PaddingColor := GetDesigningPaddingColor(Color);
  end;

  { TODO: Move }
  Canvas.Font.Assign(Font);
  FStylePaint.Canvas := Canvas;
  FStylePaint.Control := Self;

  { Erase padding }
  FillPadding(Canvas, PaddingColor, ContentRect, Padding);

  { Start }
  Pos := Point(Padding.Left, Padding.Top);

  { Paint buttons }
  for i := 0 to Pred(Buttons.Count) do
  begin

    with GetButtonSize(i) do
    begin
      ButtonRect := Bounds(Pos.X, Pos.Y, cx, cy);

      PaintButton(i, ButtonRect);

      case Orientation of
        orHorizontal:
        begin
          OffsetPoint(Pos, cx, 0);
          if Pos.X > ClientWidth
            then Exit;
        end;
        orVertical:
        begin
          OffsetPoint(Pos, 0, cy);
          if Pos.Y > ClientHeight
            then Exit;
        end;
      end;

    end;

  end;

  { Fill space }
  with Canvas do
  begin
    Brush.Color := Color;
    FillRect(Rect(Pos.X, Pos.Y, ContentRect.Right - Padding.Right, ContentRect.Bottom - Padding.Bottom));
  end;

  if BottomLine then
    with Canvas do
    begin
      Pen.Color := $00dcdbda;
      Polyline([
        Point(0, ClientHeight - 1),
        Point(ClientWidth, ClientHeight - 1)
      ]);
    end;
end;

procedure TNxToolbar6.PaintButton(const Index: Integer; ButtonRect: TRect);
var
  Button: TNxToolButton6;
  ButtonState: TNxButtonState;
  ImagePos: TPoint;
  TextRect: TRect;
begin

  { Get object }
  Button := Buttons[Index];

  { Divider or button? }
  if Button.Divider then PaintDivider(ButtonRect) else
  begin
    ButtonState := GetButtonState(Index);

    with Canvas do
    begin
      Brush.Color := Color;
      FillRect(ButtonRect);
    end;

    { Paint Btn. Background }
    FStylePaint.PaintToolButton(ButtonRect, ButtonState);

    TextRect := ButtonRect;
    InflateRect(TextRect, -pdDouble, 0);

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

      Inc(ImagePos.Y, RectHeight(ButtonRect) div 2 - Images.Height div 2);

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

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

    { Draw Text }
    Canvas.Font.Assign(Self.Font);
    DrawTextRect(Canvas, TextRect, taLeftJustify, taVerticalCenter, Button.Text, wkNone, True);
  end;
end;

procedure TNxToolbar6.PaintDivider(DividerRect: TRect);
var
  Middle: Integer;
begin

  with Canvas do
  begin
    Brush.Color := Color;
    FillRect(DividerRect);
    Pen.Color := clSilver;
    case Orientation of
      orHorizontal:
      begin
        Middle := DividerRect.Left + (DividerRect.Right - DividerRect.Left) div 2;
        Polyline([
          Point(Middle, DividerRect.Top + pdStandard),
          Point(Middle, DividerRect.Bottom - pdStandard)
        ]);
      end;
      orVertical:
      begin
        Middle := DividerRect.Top + (DividerRect.Bottom - DividerRect.Top) div 2;
        Polyline([
          Point(DividerRect.Left + pdStandard, Middle),
          Point(DividerRect.Right - pdStandard, Middle)
        ]);
      end;
    end;
  end;
end;

procedure TNxToolbar6.RefreshButton(Button: TNxToolButton6);
begin
  if Assigned(Button) then InvalidateRect(GetButtonRect(Button));
end;

procedure TNxToolbar6.ReleaseHint;
begin
  if Assigned(FHintDelayTimer) then
  begin
    { Destroy }
    FreeAndNil(FHintDelayTimer);
  end;

  { Destroy old Hint, if exists }
  if Assigned(FHintWindow) then
  begin
    { Required }
    FHintWindow.ReleaseHandle;
    { Destroy }
    FreeAndNil(FHintWindow);
  end;
end;

procedure TNxToolbar6.SetBottomLine(const Value: Boolean);
begin
  if Value <> FBottomLine then
  begin
    FBottomLine := Value;
    Invalidate;
  end;
end;

procedure TNxToolbar6.SetButtons(const Value: TNxToolButtons6);
begin
  FButtons.Assign(Value);
end;

procedure TNxToolbar6.SetHotButton(const Value: TNxToolButton6);
begin
  if Value <> FHotButton then
  begin
    RefreshButton(FHotButton);

    ReleaseHint;

    { Set value }
    FHotButton := Value;

    if Assigned(FHotButton)
      and (FHotButton.Hint <> '') then
    begin
      FHintDelayTimer := TTimer.Create(Self);
      FHintDelayTimer.Interval := Application.HintPause;
      FHintDelayTimer.OnTimer := DoHintDelayTimer;
    end;

    RefreshButton(FHotButton);
  end;
end;

procedure TNxToolbar6.SetImages(const Value: TImageList);
begin
  FImages := Value;
end;

procedure TNxToolbar6.SetOrientation(const Value: TNxOrientation);
begin
  if Value <> FOrientation then
  begin
    FOrientation := Value;
    Invalidate;
  end;
end;

procedure TNxToolbar6.SetPressedButton(const Value: TNxToolButton6);
begin
  if Value <> FPressedButton then
  begin
    RefreshButton(FPressedButton);
    FPressedButton := Value;
    if Assigned(FPressedButton) and Value.Enabled then
    begin
      RefreshButton(FPressedButton);
    end;
  end;
end;

{ TNxNetSource }

constructor TNxNetSource.Create(AOwner: TComponent);
begin
  inherited;
  FAgent := 'Delphi';
  FNetHandle := nil;
  FUrl := '';
end;

destructor TNxNetSource.Destroy;
begin
  { Release FNetHandle }
  Active := False;
  inherited;
end;

function TNxNetSource.GetActive: Boolean;
begin
  Result := Assigned(FNetHandle);
end;

function TNxNetSource.GetAsString: WideString;
{$IFDEF WININET}
var
  UrlHandle: HINTERNET;
  Buffer: array[0..1023] of Byte;
  BytesRead: DWord;
  Content, StrBuffer: UTF8String;
{$ENDIF}
begin
  { Default }
  Result := EmptyStr;
  Content := '';

{$IFDEF WININET}
  if Active then
  begin
    UrlHandle := InternetOpenUrl(FNetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);

    { Start }
    if Assigned(UrlHandle) then
      try

        { Read content }
        repeat
          InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
          SetString(StrBuffer, PAnsiChar(@Buffer[0]), BytesRead);

          { Fill }
          Content := Content + StrBuffer;
        until BytesRead = 0;

      finally
        InternetCloseHandle(UrlHandle);
      end
    else
      raise Exception.CreateFmt('Cannot open URL %s', [Url]);

    Result := UTF8ToWideString(Content);
  end;
{$ENDIF}
end;

procedure TNxNetSource.SetActive(const Value: Boolean);
begin
{$IFDEF WININET}
  { Is connected? }
  if Value <> Active then
  begin
    if Value then
      FNetHandle := InternetOpen(PChar(FAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0)
    else
      InternetCloseHandle(FNetHandle);
  end;
{$ENDIF}
end;

end.
