{ ------------------------------------------------------------- }
{ Purpose : NextGrid 6                                          }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2015. All Rights Reserved.         }
{ ------------------------------------------------------------- }
unit NxSlidesDesigner6;

interface

uses
  { Standard }
  Types, Classes, Controls, Messages, Windows, Graphics,
  { Next Standard 6 }
  NxTypes6, NxControls6, NxClasses6,
  { NextGrid 6 }
  NxColumns6, NxGridView6, NxStylePaint6;

const
  SizingHandleSize = 8;
  ToolTipOffset = 16;

type
  { TNxSlidesDesigner6 }

  TNxToolTip = class(THintWindow)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure NCPaint(DC: HDC); override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TNxResizingHandleKind = (rhNone, rhSizeNS, rhSizeNWSE, rhSizeWE);

  TNxSlidesDesigner6 = class(TNxStyledControl6)
  private
    FAssociate: TNxSlidesGridView6;
    FBoundLines: Boolean;
    FDragging: Boolean;
    FDraggingDelta: TPoint;
    FGridColor: TColor;
    FGridLines: Boolean;
    FGridXSize: Integer;
    FGridYSize: Integer;
    FMouseDown: Boolean;
    FMouseDownLocation: TPoint;
    FOnSelectedChange: TNotifyEvent;
    FResizingHandle: TNxResizingHandleKind;
    FResizingSlide: Boolean;
    FSelected: TNxSlideBound6;
    FSnapToGrid: Boolean;
    FStylePaint: INxStylePaint;
    FToolTip: TNxToolTip;
    { Property Accessors }
    function GetButtonState(Kind: TNxResizingHandleKind): TNxButtonState;
    function GetGridSize: TSize;
    function GetHandleRect(Kind: TNxResizingHandleKind): TRect;
    function GetResizing: Boolean;
    function GetSlideHeight: Integer;
    function GetSlideRect: TRect;
    procedure SetAssociate(const Value: TNxSlidesGridView6);
    procedure SetBoundLines(const Value: Boolean);
    procedure SetDragging(const Value: Boolean);
    procedure SetGridColor(const Value: TColor);
    procedure SetGridLines(const Value: Boolean);
    procedure SetGridXSize(const Value: Integer);
    procedure SetGridYSize(const Value: Integer);
    procedure SetResizingSlide(const Value: Boolean);
    procedure SetSelected(const Value: TNxSlideBound6);
    procedure SetSlideHeight(const Value: Integer);
    procedure SetSnapToGrid(const Value: Boolean);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    { Event Handlers }
    procedure DoSelectedChange; dynamic;
    { Mouse & Keyboard }
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseButtonUp;
    { Notifications }
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    { Painting }
    procedure Paint; override;
    procedure PaintBackground; virtual;
    procedure PaintGrid; virtual;
    procedure PaintResizingHandle(HandleRect: TRect; State: TNxButtonState);
    procedure PaintSelection; virtual;
    procedure PaintSlideBound(SlideBound: TNxSlideBound6);
    procedure PaintSlideBounds; virtual;
    procedure PaintSlideGridLine; virtual;
    { Tool-Tip }
    procedure HideToolTip;
    procedure ShowToolTip(Location: TPoint; Text: string);
    { Delphi Messages }
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    { Windows Messages }
    procedure WMActivateApp(var Message: TWMActivateApp); message WM_ACTIVATEAPP;
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Public Methods }
    function GetCaptionRect(SlideBound: TNxSlideBound6): TRect;
    function GetResizingHandleAtPos(X, Y: Integer): TNxResizingHandleKind;
    function GetSlideBoundAtPos(X, Y: Integer): TNxSlideBound6;
    function GetSlideHandleRect: TRect;
    procedure AlignToGrid(var X, Y: Integer);
    procedure OffsetSlide(SlideBound: TNxSlideBound6; dx, dy: Integer);
    { Refresh }
    procedure RefreshSlideBound(SlideBound: TNxSlideBound6);
    procedure RefreshSlideBoundCaption(SlideBound: TNxSlideBound6);
    { Changing Size & Position }
    procedure MoveSlideBoundTo(SlideBound: TNxSlideBound6; X, Y: Integer);
    procedure ResizeSlideBoundTo(Bound: TNxSlideBound6; X, Y: Integer);
    { Properties }
    property ButtonState[Kind: TNxResizingHandleKind]: TNxButtonState read GetButtonState;
    property GridSize: TSize read GetGridSize;
    property HandleRect[Kind: TNxResizingHandleKind]: TRect read GetHandleRect;
    property Resizing: Boolean read GetResizing;
    property ResizingSlide: Boolean read FResizingSlide write SetResizingSlide;
    property Selected: TNxSlideBound6 read FSelected write SetSelected;
    property SlideDragging: Boolean read FDragging write SetDragging;
    property StylePaint: INxStylePaint read FStylePaint write FStylePaint;
    property SlideHeight: Integer read GetSlideHeight write SetSlideHeight;
    property SlideRect: TRect read GetSlideRect;
    property SlideResizingHandleRect: TRect read GetSlideHandleRect;
  published
    property Associate: TNxSlidesGridView6 read FAssociate write SetAssociate;
    property BoundLines: Boolean read FBoundLines write SetBoundLines default True;
    property GridColor: TColor read FGridColor write SetGridColor default clGrayText;
    property GridLines: Boolean read FGridLines write SetGridLines default True;
    property GridXSize: Integer read FGridXSize write SetGridXSize default 8;
    property GridYSize: Integer read FGridYSize write SetGridYSize default 8;
    property SnapToGrid: Boolean read FSnapToGrid write SetSnapToGrid;
    property OnSelectedChange: TNotifyEvent read FOnSelectedChange write FOnSelectedChange;
  end;

implementation

uses
  { Standard }
  SysUtils, Forms, Math,
  NxGraphics6,
  NxCells6, NxColumnViewClasses6;

{ TNxToolTip }

constructor TNxToolTip.Create(AOwner: TComponent);
begin
  inherited;
  Color := clWhite;
end;

procedure TNxToolTip.CreateParams(var Params: TCreateParams);
begin
  inherited;
  with Params do
  begin
    WindowClass.Style := WindowClass.style and not CS_DROPSHADOW;
  end;
end;

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

{ TNxSlidesDesigner6 }

procedure TNxSlidesDesigner6.AlignToGrid(var X, Y: Integer);
begin
  X := X div GridXSize * GridXSize;
  Y := Y div GridXSize * GridYSize;
end;

procedure TNxSlidesDesigner6.CMMouseLeave(var Message: TMessage);
begin
  inherited;
  Screen.Cursor := crDefault;
end;

constructor TNxSlidesDesigner6.Create(AOwner: TComponent);
begin
  inherited;
  FAssociate := nil;
  FDragging := False;
  FGridColor := clGrayText;
  FGridXSize := 8;
  FGridYSize := 8;
  FToolTip := nil;
  FMouseDown := False;
  FResizingSlide := False;
  FSelected := nil;
  FBoundLines := True;
  FGridLines := True;
  FSnapToGrid := True;
end;

procedure TNxSlidesDesigner6.CreateParams(var Params: TCreateParams);
begin
  inherited;
  with Params.WindowClass do Style := Style and not(CS_HREDRAW or CS_VREDRAW);
end;

destructor TNxSlidesDesigner6.Destroy;
begin
  if FToolTip <> nil then
  begin
    FToolTip.ReleaseHandle;
    FreeAndNil(FToolTip);
  end;
  inherited;
end;

procedure TNxSlidesDesigner6.DoSelectedChange;
begin
  if Assigned(FOnSelectedChange) then FOnSelectedChange(Self);
end;

function TNxSlidesDesigner6.GetResizing: Boolean;
begin
  Result := ResizingSlide or (FResizingHandle <> rhNone);
end;

function TNxSlidesDesigner6.GetResizingHandleAtPos(X,
  Y: Integer): TNxResizingHandleKind;
begin
  Result := rhNone;
  if Assigned(Selected) then
  begin
    if PtInRect(HandleRect[rhSizeNS], Point(X, Y)) then Result := rhSizeNS;
    if PtInRect(HandleRect[rhSizeNWSE], Point(X, Y)) then Result := rhSizeNWSE;
    if PtInRect(HandleRect[rhSizeWE], Point(X, Y)) then Result := rhSizeWE;
  end;
end;

function TNxSlidesDesigner6.GetHandleRect(Kind: TNxResizingHandleKind): TRect;
begin
  if Assigned(Selected) then
    case Kind of
      rhSizeNS:
      begin
        Result := Selected.CellRect;
        Result := Bounds(Result.Left, Result.Bottom, SizingHandleSize, SizingHandleSize);
        OffsetRect(Result, RectWidth(Selected.CellRect) div 2 - (SizingHandleSize div 2), -SizingHandleSize div 2);
      end;
      rhSizeNWSE:
      begin
        Result := Selected.CellRect;
        Result := Bounds(Result.Right, Result.Bottom, SizingHandleSize, SizingHandleSize);
        OffsetRect(Result, -SizingHandleSize div 2, -SizingHandleSize div 2);
      end;
      rhSizeWE:
      begin
        Result := Selected.CellRect;
        Result := Bounds(Result.Right, Result.Top, SizingHandleSize, SizingHandleSize);
        OffsetRect(Result, -SizingHandleSize div 2, RectHeight(Selected.CellRect) div 2 - (SizingHandleSize div 2));

      end;
    end;
end;

function TNxSlidesDesigner6.GetSlideBoundAtPos(X, Y: Integer): TNxSlideBound6;
var
  i: Integer;
begin
  for i := Pred(Associate.Bounds.Count) downto 0 do
  begin
    if PtInRect(Associate.Bounds[i].CellRect, Point(X, Y)) then
    begin
      Result := Associate.Bounds[i];
      Exit;
    end;
  end;
  Result := nil;
end;

function TNxSlidesDesigner6.GetButtonState(Kind: TNxResizingHandleKind): TNxButtonState;
begin
  Result := [];
  if Kind = FResizingHandle then Include(Result, bsPressed);
end;

function TNxSlidesDesigner6.GetCaptionRect(
  SlideBound: TNxSlideBound6): TRect;
begin
  Canvas.Font.Assign(SlideBound.CaptionFont);
  Result := TNxSlidesGridView6.GetCaptionRect(Canvas, SlideBound, SlideRect);
end;

function TNxSlidesDesigner6.GetGridSize: TSize;
begin
  Result := Size(GridXSize, GridYSize);
end;

function TNxSlidesDesigner6.GetSlideHeight: Integer;
begin
  if Assigned(Associate) then Result := Associate.SlideHeight else Result := 64;
end;

function TNxSlidesDesigner6.GetSlideRect: TRect;
begin
  Result := Bounds(0, 0, ClientWidth, SlideHeight);
end;

procedure TNxSlidesDesigner6.HideToolTip;
begin
  if Assigned(FToolTip) then
  begin
    FToolTip.ReleaseHandle;
    FreeAndNil(FToolTip);
  end;
end;

procedure TNxSlidesDesigner6.KeyDown(var Key: Word; Shift: TShiftState);
var
  Offset: TSize;
begin
  inherited;
  if Assigned(Selected) then
  begin
    if ssCtrl in Shift
      then Offset := GridSize
      else Offset := Size(1, 1);

    case Key of
      VK_UP: OffsetSlide(Selected, 0, -Offset.cy);
      VK_DOWN: OffsetSlide(Selected, 0, Offset.cy);
      VK_LEFT: OffsetSlide(Selected, -Offset.cx, 0);
      VK_RIGHT: OffsetSlide(Selected, Offset.cx, 0);
    end;
//    if ssShift in Shift then
//    begin
//      case Key of
//        VK_UP: ResizeSlide(Selected, 0, -1);
//        VK_DOWN: ResizeSlide(Selected, 0, 1);
//        VK_LEFT: ResizeSlide(Selected, -1, 0);
//        VK_RIGHT: ResizeSlide(Selected, 1, 0);
//      end;
//    end;
  end;

end;

function TNxSlidesDesigner6.GetSlideHandleRect: TRect;
begin
  Result := Bounds(0, SlideHeight, SizingHandleSize, SizingHandleSize);
  OffsetRect(Result, ClientWidth div 2 - 4, -4);
end;

procedure TNxSlidesDesigner6.MouseButtonUp;
begin
  FDragging := False;
  FMouseDown := False;
  FMouseDownLocation := Point(-1, -1);
  if FResizingHandle <> rhNone then InvalidateRect(HandleRect[FResizingHandle]);
  FResizingHandle := rhNone;
  FResizingSlide := False;
  HideToolTip;
end;

procedure TNxSlidesDesigner6.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited;
  if not Focused and CanFocus
    then SetFocus;

  if Assigned(Associate) then
  begin
    if Button = mbLeft then
    begin
      FMouseDown := True;
      FMouseDownLocation := Point(X, Y);

      if Assigned(Selected) then
      begin
        FResizingHandle := GetResizingHandleAtPos(X, Y);
        if FResizingHandle <> rhNone then
        begin
          InvalidateRect(GetHandleRect(FResizingHandle));
          Exit;
        end;

      end else
      begin
        if PtInRect(SlideResizingHandleRect, Point(X, Y)) then
        begin
          FResizingSlide := True;
          Exit;
        end;
      end;
    end;

    Selected := GetSlideBoundAtPos(X, Y);
  end;
end;

procedure TNxSlidesDesigner6.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  if Assigned(Associate) then
  begin

    { `crSizeAll` cursor }
    if not FDragging and not Resizing then
      Screen.Cursor := IfThen(GetSlideBoundAtPos(X, Y) <> nil, crSizeAll, crDefault);

    { Actions on `Selected` only }
    if Assigned(Selected) then
    begin

      { Already dragging? }
      if FDragging then
      begin

        MoveSlideBoundTo(Selected, X - FDraggingDelta.X, Y - FDraggingDelta.Y);

        ShowToolTip(Point(X + ToolTipOffset, Y + ToolTipOffset),
          IntToStr(Selected.Left) + ', ' + IntToStr(Selected.Top));

        //TODO: Add event OnMoveSlideBound

      end else
      begin

        { Is resizing? }
        case FResizingHandle of
          rhSizeNWSE: ResizeSlideBoundTo(Selected, X, Y);
          rhSizeNS:   ResizeSlideBoundTo(Selected, Selected.Right, Y);
          rhSizeWE:   ResizeSlideBoundTo(Selected, X, Selected.Bottom);
          else
            { Or not Resizing at all? }
            if FMouseDown then
            begin
              SlideDragging := True;
              FDraggingDelta := Point(X - Selected.Left, Y - Selected.Top);
            end else
              case GetResizingHandleAtPos(X, Y) of
                rhSizeNS: Screen.Cursor := crSizeNS;
                rhSizeNWSE: Screen.Cursor := crSizeNWSE;
                rhSizeWE: Screen.Cursor := crSizeWE;
              end;
        end;

      end;

    end else
    begin
      { Not Selected }

      if ResizingSlide then
      begin
        SlideHeight := Y;

        Exit;
      end;

      if PtInRect(SlideResizingHandleRect, Point(X, Y)) then
        Screen.Cursor := crSizeNS;

    end;

  end;
end;

procedure TNxSlidesDesigner6.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited;
  SlideDragging := False;
  MouseButtonUp;
end;

procedure TNxSlidesDesigner6.MoveSlideBoundTo(SlideBound: TNxSlideBound6; X,
  Y: Integer);
var
  OldRect, OldCaptionRect: TRect;
begin
  with SlideBound do
  begin
    OldRect := SlideBound.CellRect;
    if SlideBound = Selected then InflateRect(OldRect, 1, 1);
    OldCaptionRect := GetCaptionRect(SlideBound);

    if SnapToGrid then AlignToGrid(X, Y);

    if (X <> Selected.Left) or
      (Y <> Selected.Top) then
    begin
      Left := X;
      Top := Y;
      InvalidateRect(OldRect);
      InvalidateRect(OldCaptionRect);
      RefreshSlideBound(SlideBound);
    end;
  end;
end;

procedure TNxSlidesDesigner6.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if AComponent = FAssociate then Associate := nil;
end;

procedure TNxSlidesDesigner6.OffsetSlide(SlideBound: TNxSlideBound6; dx, dy: Integer);
var
  OldRect, OldCaptionRect: TRect;
begin
  with SlideBound do
  begin
    OldRect := SlideBound.CellRect;
    if SlideBound = Selected then InflateRect(OldRect, 4, 4);
    OldCaptionRect := GetCaptionRect(SlideBound);

    Left := Left + dx;
    Top := Top + dy;
    InvalidateRect(OldRect);
    InvalidateRect(OldCaptionRect);
    RefreshSlideBound(SlideBound);
  end;
end;

procedure TNxSlidesDesigner6.Paint;
begin
  inherited;
  PaintBackground;
  if GridLines then PaintGrid;
  PaintSlideGridLine;
  if Assigned(Associate) then PaintSlideBounds;
  if Selected = nil then PaintResizingHandle(SlideResizingHandleRect, []) else
  begin
    PaintSelection;
  end;
end;

procedure TNxSlidesDesigner6.PaintBackground;
var
  SlideRect: TRect;
begin
  with Canvas do
  begin
    if Assigned(Associate) then Brush.Color := Associate.Color
      else Brush.Color := Color;
    SlideRect := Bounds(0, 0, ClientWidth, SlideHeight);
    FillRect(SlideRect);
    Brush.Color := Color;
    FillRect(Bounds(0, SlideHeight + 1, ClientWidth, ClientHeight));
  end;
end;

procedure TNxSlidesDesigner6.PaintGrid;
var
  X, Y: Integer;
begin
  X := 0;
  Y := 0;
  while (Y < ClientHeight) and (Y <= SlideHeight) do
  begin
    while X <= ClientWidth do
    begin
      if not Assigned(Selected)
        or not PtInRect(Selected.CellRect, Point(X, Y)) then
          Canvas.Pixels[X, Y] := FGridColor;
      Inc(X, FGridXSize);
    end;
    Inc(Y, FGridYSize);
    X := 0;
  end;
end;

procedure TNxSlidesDesigner6.PaintResizingHandle(HandleRect: TRect; State: TNxButtonState);
begin
  with Canvas do
  begin
    Brush.Color := clWhite;
    if bsHover in State then Brush.Color := clGray;
    if bsPressed in State then Brush.Color := clGray;
    Pen.Color := clGray;
    Rectangle(HandleRect);
  end;
end;

procedure TNxSlidesDesigner6.PaintSelection;
var
  BorderRect: TRect;
begin
  BorderRect := Selected.CellRect;

  with Canvas do
  begin
    Brush.Style := bsClear;
    Pen.Color := clHighlight;
    InflateRect(BorderRect, 1, 1);
    Rectangle(BorderRect);
    Brush.Style := bsSolid;
    if not FDragging then
    begin
      PaintResizingHandle(HandleRect[rhSizeNWSE], ButtonState[rhSizeNWSE]);
      PaintResizingHandle(HandleRect[rhSizeNS], ButtonState[rhSizeNS]);
      PaintResizingHandle(HandleRect[rhSizeWE], ButtonState[rhSizeWE]);
    end;
  end;
end;

procedure TNxSlidesDesigner6.PaintSlideBound(SlideBound: TNxSlideBound6);
var
  CellRect, BorderRect, CaptionRect: TRect;
  PreviewCell: INxCell;
  RowView: INxRowView;
  View: INxColumnView;
  OldCanvas: TCanvas;
begin
  CellRect := SlideBound.CellRect;
  with Canvas do
  begin
    BorderRect := CellRect;
//    InflateRect(BorderRect, 1, 1);

    Brush.Color := clSilver;
    Pen.Color := clGray;

    if SlideBound = Selected then
    begin
      Brush.Color := clHighlight;
      Pen.Color := clHighlight;
      if Dragging then
      begin
      end;
    end;

    if Assigned(SlideBound.Column) then
    begin
      View := SlideBound.Column.View.Clone;

      with View do
      begin
        State := [];
        Selected := False;

        Control := Self;
        Canvas := Self.Canvas;
        Style := Self.StylePaint;

        ClientRect := CellRect;

        PreviewCell := CreateCell(SlideBound.Column.DataType);
        Cell := PreviewCell;

        PreviewCell.AsString := SlideBound.Column.PlaceholderText;
        PreviewCell.Color := Associate.Color;

        Parent := Associate;

        DrawingOptions := SlideBound.Column.DrawingOptions;

        if Supports(SlideBound.Column.View, INxRowView, RowView) then
        begin
          RowView.Row := nil;
        end;

        OldCanvas := StylePaint.Canvas;
        try
          StylePaint.Canvas := Self.Canvas;
          SetClipRect(Canvas, CellRect);
          Paint;
        finally
          SetClipRect(Canvas, ClientRect);
          StylePaint.Canvas := OldCanvas;
        end;

      end;
    end else
    begin
      Brush.Color := Color;
      FillRect(CellRect);
    end;

    SetClipRect(Canvas, ClientRect);

    { `Caption` & `CaptionLocation` }
    if SlideBound.Caption <> '' then
    begin
      Canvas.Font.Assign(SlideBound.CaptionFont);

      CaptionRect := TNxSlidesGridView6.GetCaptionRect(Canvas, SlideBound, SlideRect);

      { `CaptionLocation` }
      Canvas.Brush.Color := Associate.Color;
      Canvas.FillRect(CaptionRect);

      TNxSlidesGridView6.PaintSlideBoundCaption(Canvas, SlideBound, CaptionRect);
    end;

    { Boundaries }
    if BoundLines then
      with Canvas do
      begin
        Brush.Style := bsClear;
        Pen.Color := clSilver;
        if not Assigned(SlideBound.Column) or not SlideBound.Visible then
        begin
          Pen.Style := psDot;
        end;
        Rectangle(BorderRect);
        Brush.Style := bsSolid;
        Pen.Style := psSolid;
      end;

  end;
end;

procedure TNxSlidesDesigner6.PaintSlideBounds;
var
  i: Integer;
begin
  for i := 0 to Pred(Associate.Bounds.Count) do
  begin
    PaintSlideBound(Associate.Bounds[i]);
  end;
end;

procedure TNxSlidesDesigner6.PaintSlideGridLine;
begin
  with Canvas do
  begin
    Pen.Color := clSilver;
    Polyline([
      Point(0, SlideHeight),
      Point(ClientWidth, SlideHeight)
    ]);

    { Margin on the right }
    if Assigned(Associate) then
    begin
      Polyline([
        Point(RectWidth(Associate.ClientRect), 0),
        Point(RectWidth(Associate.ClientRect), SlideHeight)
      ]);
    end;
  end;
end;

procedure TNxSlidesDesigner6.RefreshSlideBound(SlideBound: TNxSlideBound6);
var
  InvalidationRect: TRect;
begin
  InvalidationRect := SlideBound.CellRect;
  if SlideBound = Selected then
  begin
    InflateRect(InvalidationRect, 4, 4);
  end;
  InvalidateRect(InvalidationRect);

  InvalidationRect := GetCaptionRect(SlideBound);
  InvalidateRect(InvalidationRect);

  RefreshSlideBoundCaption(SlideBound);
end;

procedure TNxSlidesDesigner6.RefreshSlideBoundCaption(SlideBound: TNxSlideBound6);
begin
  if SlideBound.Caption <> '' then InvalidateRect(GetCaptionRect(SlideBound));
end;

procedure TNxSlidesDesigner6.ResizeSlideBoundTo(Bound: TNxSlideBound6; X,
  Y: Integer);
var
  OldWidth, OldHeight: Integer;
  OldRect: TRect;
begin
  with Bound do
  begin
    OldRect := Bound.CellRect;
    if Bound = Selected then InflateRect(OldRect, 4, 4);

    AlignToGrid(X, Y);

    if (X <> Selected.Right) or
      (Y <> Selected.Bottom) then
    begin
      OldWidth := Width;
      OldHeight := Height;

      Width := X - Left;
      Height := Y - Top;

      if (Width <> OldWidth) or (Height <> OldHeight) then
      begin
        InvalidateRect(OldRect);
        RefreshSlideBound(Bound);
      end;
      ShowToolTip(Point(X, Y + ToolTipOffset), IntToStr(Width) + ' x ' + IntToStr(Height));
    end;
  end;
end;

procedure TNxSlidesDesigner6.SetAssociate(const Value: TNxSlidesGridView6);
begin
  if Value <> FAssociate then
  begin
    if Assigned(FAssociate) then FAssociate.RemoveFreeNotification(Self);
    FAssociate := Value;
    if Assigned(FAssociate) then
    begin
      FAssociate.FreeNotification(Self);
      FStylePaint := FAssociate.Control.StylePaint;
    end;
    Invalidate;
  end;
end;

procedure TNxSlidesDesigner6.SetDragging(const Value: Boolean);
begin
  if Value <> FDragging then
  begin
    FDragging := Value;
    if FDragging then Screen.Cursor := crDrag else
    begin
      Screen.Cursor := crDefault;
    end;

    if Assigned(FSelected) then
    begin
      RefreshSlideBound(Selected);
    end
    else Assert(True, 'SetDragging AV');
  end;
end;

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

procedure TNxSlidesDesigner6.SetGridXSize(const Value: Integer);
begin
  if Value <> FGridXSize then
  begin
    FGridXSize := Value;
    Invalidate;
  end;
end;

procedure TNxSlidesDesigner6.SetGridYSize(const Value: Integer);
begin
  if Value <> FGridYSize then
  begin
    FGridYSize := Value;
    Invalidate;
  end;
end;

procedure TNxSlidesDesigner6.SetResizingSlide(const Value: Boolean);
begin
  FResizingSlide := Value;
end;

procedure TNxSlidesDesigner6.SetSelected(const Value: TNxSlideBound6);
var
  OldSelected: TNxSlideBound6;
begin
  if Value <> FSelected then
  begin
    if (Value = nil) or (FSelected = nil) then
      InvalidateRect(SlideResizingHandleRect);

    OldSelected := FSelected;
    if Assigned(OldSelected) then
    begin
      RefreshSlideBound(OldSelected);
      InvalidateRect(HandleRect[rhSizeNWSE]);
    end;
    FSelected := Value;
    if Assigned(Selected) then
    begin
      RefreshSlideBound(Selected);
      InvalidateRect(HandleRect[rhSizeNWSE]);
    end;
    DoSelectedChange;
  end;
end;

procedure TNxSlidesDesigner6.SetBoundLines(const Value: Boolean);
begin
  if Value <> FBoundLines then
  begin
    FBoundLines := Value;
    Invalidate;
  end;
end;

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

procedure TNxSlidesDesigner6.SetSlideHeight(const Value: Integer);
var
  AValue: Integer;
begin
  AValue := Value;
  if SnapToGrid then AValue := Value div GridYSize * GridYSize;
  if AValue <> FAssociate.SlideHeight then
  begin
    FAssociate.SlideHeight := AValue;
    Invalidate;
  end;
end;

procedure TNxSlidesDesigner6.SetSnapToGrid(const Value: Boolean);
begin
  FSnapToGrid := Value;
end;

procedure TNxSlidesDesigner6.ShowToolTip(Location: TPoint; Text: string);
var
  HintRect: TRect;
begin
  if FToolTip = nil then
  begin
    FToolTip := TNxToolTip.Create(Self);
    FToolTip.Font.Assign(Font);
  end;

  HintRect := FToolTip.CalcHintRect(Screen.Width, Text, nil);

  { Cordinates as `Screen` }
  HintRect.TopLeft := ClientToScreen(Location);

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

  { Show Hint }
  if Text <> FToolTip.Text then FToolTip.ActivateHint(HintRect, Text);
end;

procedure TNxSlidesDesigner6.WMActivateApp(var Message: TWMActivateApp);
begin
  inherited;
	if not Message.Active then
  begin
    MouseButtonUp;
    Screen.Cursor := crDefault;
  end;
end;

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

end.
