{==============================================================================

  CustomForm Control
  Copyright (C) 2000-2001 by Evgeny Kryukov
  All rights reserved

  All contents of this file and all other files included in this archive
  are Copyright (C) 2001 Evgeny Kryukov. Use and/or distribution of
  them requires acceptance of the License Agreement.

  See License.txt for licence information

===============================================================================}

unit KsForms;

{$I KsCommonVer.Inc}
{$T-,W-,X+,P+}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Buttons,
  Menus, ShellAPI, ImgList, Dialogs, KsHooks, KsBitmap, KsUtils, KsControls,
  KsItems, KsMenus, KsToolBars, KsStatusBars, KsEffects;

const

  WM_TRAYICONMESSAGE    = WM_USER + 364;
  WM_UPDATENCAREA       = WM_USER + 365;
  WM_MAXIMIZE           = WM_USER + 366;

  ShadowColor           = $903F3F3F;
  OutlineShadowColor    = $307F7F7F;

  ResizeStep            = 50;

type

  TKsHitTest = (khtClient, khtCaption, khtLeft, khtTop, khtRight,
    khtBottom, khtBorder, khtTopLeft, khtTopRight, khtBottomLeft,
    khtBottomRight, khtCloseButton, khtHelpButton, khtMinButton,
    khtMaxButton, khtRollButton, khtTrayButton, khtSysMenuButton,
    khtNonClient);

  { TKsBorderStyle is the type of the BorderStyle property for forms.
    <TABLE>
    Value                        Meaning
    -----                        -------
    kbsNone		        No visible border.
    kbsStandard	                Standard resizeable border.
    kbsToolWindow	        Single-line border with a smaller caption.
    </TABLE>
  }
  TKsBorderStyle = (kbsNone, kbsStandard, kbsToolWindow);

  { Specifies which icons appear on the title bar of the form.
    <TABLE>
    Value                       Meaning
    -----                       -------
    kbiClose                    The form has a Close button
    kbiSystemMenu               The form has a System menu
    kbiMinimize                 The form has a Minimize button
    kbiMaximize                 The form has a Maximize button
    kbiHelp                     The form has a Help button
    kbiRollup                   The form has a Rollup/Rolldown button
    kbiTray                     The form has a MinToTray button
    </TABLE>
  }
  TKsBorderIcon = (kbiClose, kbiSystemMenu, kbiMinimize, kbiMaximize,
    kbiHelp, kbiRollup, kbiTray);
  { Specifies which icons appear on the title bar of the form.

    See Also:
      TKsBorderIcon
  }
  TKsBorderIcons = set of TKsBorderIcon;

  { TKsWindowState describes the state of a form window. The following table lists the possible values:
    <TABLE>
    Value                       Meaning
    -----                       -------
    kwsNormal                   The form is in its normal state
    kwsMaximized                The form is in its maximized state
    kwsMinimized                The form is in its minimized state
    kwsRollup                   The form is in its rollup state
    kwsTray                     The form is in its minimized to tray state
    </TABLE>
  }
  TKsWindowState = (kwsNormal, kwsMaximized, kwsMinimized, kwsRollup, kwsTray);

  TPaintEvent = procedure (Canvas: TCanvas) of object;

  { TKsPerfomance set a buffer kind of a form. The following table lists the possible values:
    <TABLE>
    Value                       Meaning
    -----                       -------
    kspPersistentBuffer_High    Persistent buffer. High perfomance, but max memory usage
    kspTemporaryBuffer_Low      Persistent buffer. Low perfomance, but min memory usage
    </TABLE>
  }
  TKsPerfomance = (kspPersistentBuffer_High, kspTemporaryBuffer_Low);

{ TKsCustomForm class }

{ TKsCustomForm is a inheritance of TForm with advanced features }
  TKsCustomForm = class(TKsHookComponent)
  private
    FBorderStyle: TKsBorderStyle;
    FCanResize: boolean;
    FCanMove: boolean;
    FBorderIcons: TKsBorderIcons;
    FBuffer: TKsBitmap;
    FScreen: TKsBitmap;
    FBlend: boolean;
    FBlendValue: integer;
    FConstraints: TSizeConstraints;
    FDownPoint: TPoint;
    FMovePoint: TPoint;
    FRestoreRect: TRect;
    FOldState: TKsWindowState;
    FWindowState: TKsWindowState;
    FLoadedState: TKsWindowState;
    FIconData: TNotifyIconData;
    FTrayMsgWindow: HWnd;
    FMagnetic: boolean;
    FMagneticValue: integer;
    FMenuBar: TKsCustomMenuBar;
    FSysMenu: TKsCustomItem;
    FSysMenuBlend: boolean;
    FSysMenuBlendValue: integer;
    FOldApplicationRestore: TNotifyEvent;
    FOldApplicationMinimize: TNotifyEvent;
    FOldPlacment: HWnd;
    FOldCaption: string;
    FChanging: boolean;
    FSetAppEvent: boolean;
    FMinimizeApplication: boolean;
    FStayOnTop: boolean;
    FShowAnimationTime: integer;
    FShowAnimation: TKsAnimation;
    FAnimating: boolean;
    FSysMenuPoint: TPoint;
    { MDI }
    FOldClientProc: TFarProc;
    FClientInstance: TFarProc;
    FDropShadow: boolean;
    FShadowWidth: integer;
    FSysMenuDropShadow: boolean;
    FSysMenuShadowWidth: integer;
    FSysMenuShowAnimationTime: integer;
    FSysMenuShowAnimation: TKsAnimation;
    FOnPaint: TPaintEvent;
    FUseXPFeatures: boolean;
    FUse2KFeatures: boolean;
    FNeedUpdate: boolean;
    FOldRect: TRect;
    FNoCopyScreen: boolean;
    FAniBuffer: TKsBitmap;
    FDragging: boolean;
    FSimpleDrag: boolean;
    FSimpleDrawRect: TRect;
    FSimpleDrawEnabled: boolean;
    FSavedClentSize: TPoint;
    FSavedOnShortCut: TShortCutEvent;
    FTrayMenu: TPopupMenu;
    procedure ClientWndProc(var Message: TMessage);
    procedure RestoreMDIChildren;
    { }
    procedure CopyScreen(NewRect: TRect);
    procedure PaintCaption(Buffer: TKsBitmap);
    function ApplyMagnetic(R: TRect): TRect;
    { Anmation }
    procedure AnimationShow;
    { Events }
    procedure DoApplicationMinimize(Sender: TObject);
    procedure DoApplicationRestore(Sender: TObject);
    procedure DoSysMenu(Sender: TObject);
    procedure DoFormShortCut(var Msg: TWMKey; var Handled: Boolean);
    { Tray }
    procedure MsgTrayWndProc(var Msg: TMessage);
    procedure EndSession;
    procedure UpdateTrayIcon;
    { Drag }
    procedure DrawDragFrame(R: TRect);
    { Propeties }
    procedure SetBorderStyle(const Value: TKsBorderStyle);
    procedure SetCanMove(const Value: boolean);
    procedure SetCanResize(const Value: boolean);
    procedure SetBorderIcons(const Value: TKsBorderIcons);
    procedure SetBlend(const Value: boolean);
    procedure SetBlendValue(const Value: integer);
    procedure SetMenuBar(const Value: TKsCustomMenuBar);
    procedure SetSysMenuBlend(const Value: boolean);
    procedure SetSysMenuBlendValue(const Value: integer);
    procedure SetWindowState(const Value: TKsWindowState);
    procedure SetConstraints(const Value: TSizeConstraints);
    procedure SetStayOnTop(const Value: boolean);
    procedure SetUseXPFeatures(const Value: boolean);
    procedure SetUse2KFeatures(const Value: boolean);
    function GetBuffer: TKsBitmap;
    procedure SetTrayMenu(const Value: TPopupMenu);
  protected
    FAllowDraw: boolean;
    FDownHitTest: TKsHitTest;
    FEraseBkgnd: boolean; { WM_ERASEBKGND }
    FFormActive: boolean;
    FMoveHitTest: TKsHitTest;
    FWidth, FHeight: integer;
    FRealWidth, FRealHeight: integer;
    FShift: TShiftState;
    { Windows Message Handlers }
    procedure WMMDIActivate(var Message: TWMMDIActivate); message WM_MDIACTIVATE;
    procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCACTIVATE;
    procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST;
    procedure WMNCCalcSize(var Msg: TWMNCCalcSize); message WM_NCCALCSIZE;
    procedure WMNCPaint(var Msg: TMessage); message WM_NCPAINT;
    procedure WMNCLButtonDown(var Msg: TWMNCHitMessage); message WM_NCLBUTTONDOWN;
    procedure WMNCLButtonUp(var Msg: TWMNCHitMessage); message WM_NCLBUTTONUP;
    procedure WMNCLButtonDblClk(var Msg: TWMNCHitMessage); message WM_NCLBUTTONDBLCLK;
    procedure WMNCRButtonDown(var Msg: TWMNCHitMessage); message WM_NCRBUTTONDOWN;
    procedure WMNCRButtonUp(var Msg: TWMNCHitMessage); message WM_NCRBUTTONUP;
    procedure WMNCMouseMove(var Msg: TWMNCHitMessage); message WM_NCMOUSEMOVE;
    procedure WMLButtonUp(var Msg: TWMMouse); message WM_LBUTTONUP;
    procedure WMMouseMove(var Msg: TWMMouse); message WM_MOUSEMOVE;
    procedure WMActivate(var Msg: TWMActivate); message WM_ACTIVATE;
    procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND;
    procedure WMPaint(var Msg: TMessage); message WM_PAINT;
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
    procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;
    { Internal Event }
    procedure WMFormIsBlend(var Msg: TMessage); message WM_FORMISBLEND;
    procedure WMUpdateNCArea(var Msg: TMessage); message WM_UPDATENCAREA;
    procedure WMMaximize(var Msg: TMessage); message WM_MAXIMIZE;
    { After form message }
    procedure AfterControl(Sender: TObject; var Msg: TMessage; var Handled: Boolean); override;
    { Internal }
    function GetCaptionHeight: integer;
    function GetBorderWidth: integer;
    function GetNCRect: TRect;
    function GetParentRect: TRect;
    function NormalizePoint(P: TPoint): TPoint;
    procedure MoveWindow(OffsetX, OffsetY: integer); overload;
    procedure MoveWindow(Left, Top, Width, Height: integer); overload;
    procedure MoveWindow(Bounds: TRect); overload;
    procedure UpdateNCArea(ClipRgn: HRgn);
    procedure ChangeSize; virtual;
    function GetRegion: HRgn; virtual;
    { MDI }
    function IsMDIChildMaximized: boolean;
    function GetMDIParentForm: TCustomForm;
    function GetMDIParentKsForm: TKsCustomForm;
    procedure ChangeMDIParentCaption;
    { MenuBar and Sysmenu }
    procedure CreateSysMenu;
    function CreateMenuItem(AOwner: TComponent): TKsCustomItem; virtual;
    { Protected Routines }
    function GetButtonRect(Button: TKsBorderIcon): TRect; virtual;
    function GetClientRect: TRect; virtual;
    { WindowState's Rect }
    function GetRollupRect: TRect; virtual;
    function GetMinimizedRect: TRect; virtual;
    function GetMaximizedRect: TRect; virtual;
    { Tracking Size }
    function GetMaxTrackSize: TPoint; virtual;
    function GetMinTrackSize: TPoint; virtual;
    { Painting }
    procedure PaintNCArea(Buffer: TKsBitmap); virtual;
    procedure PaintClientArea(Buffer: TKsBitmap); virtual;
    { Mouse Routines }
    procedure NCMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer); dynamic;
    procedure NCMouseMove(Shift: TShiftState; X, Y: integer); dynamic;
    procedure NCMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: integer); dynamic;

    function GetHitTest(X, Y: integer): TKsHitTest; virtual;
    { VCL protected  }
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    { Protected Properties }
    property Buffer: TKsBitmap read GetBuffer;
    property Dragging: boolean read FDragging; 
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Loaded; override;
    procedure Update;
    { Internal }
    procedure DisableDrawing;
    procedure EnableDrawing;

    procedure Tile;
    procedure Cascade;

    { Call close form or close application if it's mainform }
    procedure Close;
    { Call Maximize to maximize the form. When the user mizimizes the form, the Maximize method is automatically called. }
    procedure Maximize;
    { Call Minimize to minimize the form. When the user minimizes the form, the Minimize method is automatically called. }
    procedure Minimize;
    { Call Restore to restore form from maximized, minimized, minimized to tray, rollup state. }
    procedure Restore;
    { Call Help to start context help system. }
    procedure Help;
    { Call Rollup tp rollup form. }
    procedure Rollup;
    { Call MinToTray to minimize the form to system tray. }
    procedure MinToTray;
    { Call SysMenu to popup window system menu. }
    procedure SysMenu;
    { WindowState describes the state of a form window.

      See Also:
        TKsWindowState
    }
    property WindowState: TKsWindowState read FWindowState write SetWindowState;
    { Public }
    property Width: integer read FWidth;
    property Height: integer read FHeight;
    { !!! not yet }
    { Win2K Blend }
    property Use2KFeatures: boolean read FUse2KFeatures write SetUse2KFeatures;
    { ThemeAPI in XP}
    property UseXPFeatures: boolean read FUseXPFeatures write SetUseXPFeatures;
  published
    property Active;
    { Check of the enable alpha-blending features. }
    property Blend: boolean read FBlend write SetBlend;
    { Value of the alpha channel, must between 0..255 }
    property BlendValue: integer read FBlendValue write SetBlendValue;
    { Specifies which icons appear on the title bar of the form
      See Also:
        TKsBorderIcons
    }
    property BorderIcons: TKsBorderIcons read FBorderIcons write SetBorderIcons;
    { Specifies the appearance and behavior of the form border.
      See Also:
        TKsBorderStyle
    }
    property BorderStyle: TKsBorderStyle read FBorderStyle write SetBorderStyle;
    { If this property is true then form can change size. }
    property CanResize: boolean read FCanResize write SetCanResize;
    { If this property is true then form can change move. }
    property CanMove: boolean read FCanMove write SetCanMove;
    { Shadow routines }
    property DropShadow: boolean read FDropShadow write FDropShadow;
    property ShadowWidth: integer read FShadowWidth write FShadowWidth;
    { Check it if your want to using Margnetic features. }
    property Magnetic: boolean read FMagnetic write FMagnetic;
    { Magnetic value  }
    property MagneticValue: integer read FMagneticValue write FMagneticValue;
    property Constraints: TSizeConstraints read FConstraints write SetConstraints;
    { Link to MenuBar component. Need for handle Menu key or Alt key for start menu loop. }
    property MenuBar: TKsCustomMenuBar read FMenuBar write SetMenuBar;
    { If this property is true then minimizes method minimize Apllication, and else minimize only form. }
    property MinimizeApplication: boolean read FMinimizeApplication write FMinimizeApplication default true;
    { Drag only frame }
    property SimpleDrag: boolean read FSimpleDrag write FSimpleDrag;
    { Set animation show kind }
    property ShowAnimation: TKsAnimation read FShowAnimation write FShowAnimation;
    { Set animation show kind }
    property ShowAnimationTime: integer read FShowAnimationTime write FShowAnimationTime;
    { This form remains on top of the desktop and of other forms in the project, except any others that also have StayOnTop as true. }
    property StayOnTop: boolean read FStayOnTop write SetStayOnTop;
    { Link to Blend property for System menu }
    property SysMenuBlend: boolean read FSysMenuBlend write SetSysMenuBlend;
    { Link to BlendValue property for System menu }
    property SysMenuBlendValue: integer read FSysMenuBlendValue write FSysMenuBlendValue;
    property SysMenuDropShadow: boolean read FSysMenuDropShadow write FSysMenuDropShadow;
    property SysMenuShadowWidth: integer read FSysMenuShadowWidth write FSysMenuShadowWidth;
    property SysMenuShowAnimation: TKsAnimation read FSysMenuShowAnimation write FSysMenuShowAnimation;
    property SysMenuShowAnimationTime: integer read FSysMenuShowAnimationTime write FSysMenuShowAnimationTime;
    { TrayMenu items }
    property TrayMenu: TPopupMenu read FTrayMenu write SetTrayMenu;
    { Events }
    property OnPaint: TPaintEvent read FOnPaint write FOnPaint;
  end;

var
  CaptionFont: TFont;
  SmCaptionFont: TFont;

implementation {===============================================================}

uses
  KsCommonConst, KsWin2k, KsWinXP;

{$R *.res}

const

  CaptionHeight         = 19;
  SmCaptionHeight       = 15;
  BorderWidth           = 3;

  BufferWidth: integer  = 2000;
  BufferHeight: integer = 2000;
  BufferBorder: integer = 500;
  SharedBuffer: TKsBitmap    = nil; { Shared buffer }

type
  THackControl = class(TWinControl);
  THackForm = class(TCustomForm);

var
  SysMenuImages: TCustomImageList;

procedure InitCaptionFont;
var
  NonClientMetrics: TNonClientMetrics;
begin
  CaptionFont := TFont.Create;
  NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
    CaptionFont.Handle := CreateFontIndirect(NonClientMetrics.lfCaptionFont);

  SmCaptionFont := TFont.Create;
  NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
    SmCaptionFont.Handle := CreateFontIndirect(NonClientMetrics.lfSmCaptionFont);
end;

procedure InitSysMenuImages;
var
  B: TBitmap;
begin
  SysMenuImages := TImageList.Create(nil);
  SysMenuImages.DrawingStyle := dsNormal;
  SysMenuImages.Masked := true;
  { Add images }
  B := TBitmap.Create;
  try
    B.Handle := LoadBitmap(HInstance, PChar('KSE_CLOSE'));
    SysMenuImages.AddMasked(B, RGB(255,0,255));
    B.Handle := LoadBitmap(HInstance, PChar('KSE_MINIMIZE'));
    SysMenuImages.AddMasked(B, RGB(255,0,255));
    B.Handle := LoadBitmap(HInstance, PChar('KSE_MAXIMIZE'));
    SysMenuImages.AddMasked(B, RGB(255,0,255));
    B.Handle := LoadBitmap(HInstance, PChar('KSE_RESTORE'));
    SysMenuImages.AddMasked(B, RGB(255,0,255));
    B.Handle := LoadBitmap(HInstance, PChar('KSE_ROLLUP'));
    SysMenuImages.AddMasked(B, RGB(255,0,255));
    B.Handle := LoadBitmap(HInstance, PChar('KSE_TRAY'));
    SysMenuImages.AddMasked(B, RGB(255,0,255));
  finally
    B.Free;
  end;
end;

procedure RemoveMDIClientEdge(ClientHandle: THandle);
var
  Style: Longint;
begin
  if ClientHandle <> 0 then
  begin
    Style := GetWindowLong(ClientHandle, GWL_EXSTYLE);
    Style := Style and not WS_EX_CLIENTEDGE;
     
    SetWindowLong(ClientHandle, GWL_EXSTYLE, Style);
    SetWindowPos(ClientHandle, 0, 0,0,0,0, SWP_FRAMECHANGED or SWP_NOACTIVATE or
      SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER);
  end;
end;

{ TKsCustomForm ===============================================================}

constructor TKsCustomForm.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FMinimizeApplication := true;
  FBorderStyle := kbsStandard;
  FBorderIcons := [kbiClose, kbiSystemMenu, kbiMinimize, kbiMaximize,
    kbiRollup, kbiTray];
  FConstraints := TSizeConstraints.Create(nil);

  FBlend := false;
  FBlendValue := 150;
  FEraseBkgnd := false;
  FDropShadow := false;
  FShadowWidth := 4;

  FUseXPFeatures := false;
  FUse2KFeatures := false;

  FSysMenuBlend := false;
  FSysMenuBlendValue := 150;
  FSysMenuDropShadow := false;
  FSysMenuShadowWidth := 4;
  FSysMenuShowAnimation := kaNone;
  FSysMenuShowAnimationTime := 300;

  FSimpleDrag := false;

  FCanResize := true;
  FCanMove := true;

  FMagnetic := true;
  FMagneticValue := 20;

  FShowAnimation := kaNone; 
  FShowAnimationTime := 400;

  FScreen := TKsBitmap.Create;

  if Owner is TForm then
  begin
    (Owner as TForm).BorderStyle := bsSizeable;
    (Owner as TForm).BorderIcons := [];
    Control := (Owner as TForm);
    (Owner as TForm).AutoScroll := false;

    if Application.MainForm = Owner as TForm then
      FMinimizeApplication := true;

    if (Owner as TForm).FormStyle = fsMDIChild then
    begin
      FMinimizeApplication := false;
      FMagneticValue := 9;
      FBorderIcons := [kbiClose, kbiSystemMenu, kbiMinimize, kbiMaximize, kbiRollup];
    end;
  end
  else
    if Owner is TWinControl then
    begin
      Control := (Owner as TWinControl);
    end;

  FTrayMsgWindow := AllocateHWnd(MsgTrayWndProc);

  if not (csDesigning in ComponentState) then
  begin
    FillChar(FIConData, 0, SizeOf(TNotifyIconData));
    with FIconData do
    begin
      cbSize := SizeOf(TNotifyIconData);
      Wnd := FTrayMsgWindow;
      uID := Integer(Self);
      uFlags := nif_Message or nif_Icon or nif_Tip;
      uCallBackMessage := wm_TrayIconMessage;
      szTip := '';
    end;
  end;

  FAllowDraw := true;
end;

destructor TKsCustomForm.Destroy;
begin
  FConstraints.Free;

  if Assigned(FOldApplicationRestore) then
  begin
    Application.OnRestore := FOldApplicationRestore;
    Application.OnMinimize := FOldApplicationMinimize;
  end;

  if not (csDesigning in ComponentState) then
    Shell_NotifyIcon(nim_Delete, @FIconData);

  DeallocateHWnd(FTrayMsgWindow);

  if FBuffer <> nil then FBuffer.Free;
  FScreen.Free;
  inherited Destroy;
end;

procedure TKsCustomForm.Loaded;
var
  ExStyle: longint;
begin
  if FBlend then
  begin
    FBuffer := TKsBitmap.Create;
    FBuffer.SetSize(FRealWidth, FRealHeight);
  end;

  inherited Loaded;

  if csDesigning in ComponentState then Exit;

  if FBlend or FDropShadow then
    FScreen.SetSize(FRealWidth, FRealHeight);

  if not (csDesigning in ComponentState) and (Form <> nil) and (Form.FormStyle = fsMDIForm) and (THackForm(Form).ClientHandle <> 0) then
  begin
    FClientInstance := MakeObjectInstance(ClientWndProc);
    FOldClientProc := Pointer(GetWindowLong(THackForm(Form).ClientHandle, GWL_WNDPROC));
    SetWindowLong(THackForm(Form).ClientHandle, GWL_WNDPROC, Longint(FClientInstance));
  end;

  if FUse2KFeatures and IsWin2k and (Handle <> 0) then
  begin
    ExStyle := GetWindowLong(Handle, GWL_EXSTYLE);
    ExStyle := ExStyle or WS_EX_LAYERED;
    SetWindowLong(Handle, GWL_EXSTYLE, ExStyle);
  end;

  if not (csDesigning in ComponentState) and (Form <> nil) then
  begin
    FSavedOnShortCut := (Owner as TForm).OnShortCut;
    (Owner as TForm).OnShortCut := DoFormShortCut;
  end;

  Update;

  UpdateTrayIcon;

  if FLoadedState <> kwsNormal then
  begin
    case FLoadedState of
      kwsMaximized: Maximize;
      kwsMinimized: Minimize;
      kwsRollup: Rollup;
      kwsTray: MinToTray;
    end;
  end;
end;

{ System menu }

function TKsCustomForm.CreateMenuItem(AOwner: TComponent): TKsCustomItem;
begin
  Result := TKsCustomItem.Create(AOwner);
end;

procedure TKsCustomForm.CreateSysMenu;
  function AddItem(ACaption: string; AShortCut: TShortCut): TKsCustomItem;
  begin
    Result := CreateMenuItem(Self);
    with Result do
    begin
      Caption := ACaption;
      ShortCut := AShortCut;
      Images := SysMenuImages;
      OnClick := DoSysMenu;
    end;
    FSysMenu.Add(Result);
  end;

var
  Item: TKsCustomItem;
begin
  if FSysMenu <> nil then
    FSysMenu.Free;

  FSysMenu := CreateMenuItem(Self);

  { Add items }
  if (FWindowState = kwsMaximized) or (FWindowState = kwsMinimized) or
     (FWindowState = kwsRollup) or (FWindowState = kwsTray) then
  begin
    Item := AddItem(SKsRestore, 0);
    Item.ImageIndex := 3;
  end;

  if (kbiMinimize in FBorderIcons) and ((FWindowState = kwsNormal) or
     (FWindowState = kwsMaximized) or (FWindowState = kwsRollup)) then
  begin
    Item := AddItem(SKsMinimize, 0);
    Item.ImageIndex := 1;
  end;

  if (kbiMaximize in FBorderIcons) and ((FWindowState = kwsNormal)
     or (FWindowState = kwsRollup)) then
  begin
    Item := AddItem(SKsMaximize, 0);
    Item.ImageIndex := 2;
  end;

  if (kbiRollup in FBorderIcons) and ((FWindowState = kwsNormal) or
     (FWindowState = kwsMaximized)) then
  begin
    Item := AddItem(SKsRollup, 0);
    Item.ImageIndex := 4;
  end;

  if (kbiTray in FBorderIcons) and ((FWindowState = kwsNormal) or
     (FWindowState = kwsMaximized) or (FWindowState = kwsRollup)) then
  begin
    Item := AddItem(SKsMinToTray, 0);
    Item.ImageIndex := 5;
  end;

  if kbiClose in FBorderIcons then
  begin
    if FSysMenu.Count > 0 then AddItem('-', 0);

    Item := AddItem(SKsClose, TextToShortCut('Alt+F4'));
    Item.ImageIndex := 0;
  end;

  { Set Properties }
  FSysMenu.PopupBlend := FSysMenuBlend;
  FSysMenu.PopupBlendValue := FSysMenuBlendValue;
  FSysMenu.DropShadow := FSysMenuDropShadow;
  FSysMenu.ShadowWidth := FSysMenuShadowWidth;
  FSysMenu.ShowAnimation := FSysMenuShowAnimation;
  FSysMenu.ShowAnimationTime := FSysMenuShowAnimationTime;
end;

procedure TKsCustomForm.DoSysMenu(Sender: TObject);
var
  Item: TKsCustomItem;
begin
  Item := TKsCustomItem(Sender);
  if Item.Caption = SKsClose then
    Close;
  if Item.Caption = SKsMaximize then
    Maximize;
  if Item.Caption = SKsMinimize then
    Minimize;
  if Item.Caption = SKsRollup then
    Rollup;
  if Item.Caption = SKsMinToTray then
    MinToTray;
  if Item.Caption = SKsRestore then
    Restore;
end;

procedure TKsCustomForm.DoFormShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
  if FMenuBar <> nil then
    Handled := FMenuBar.IsShortCut(Msg);

  if not Handled and Assigned(FSavedOnShortCut) then
    FSavedOnShortCut(Msg, Handled);
end;

{ Sys commands }

procedure TKsCustomForm.UpdateTrayIcon;
begin
  if (Form <> nil) and (Form.Icon.Handle <> 0) then
    FIconData.hIcon := Form.Icon.Handle
  else
    FIconData.hIcon := Application.Icon.Handle;
  Shell_NotifyIcon(nim_Modify, @FIconData);
end;

procedure TKsCustomForm.Update;
var
  R: TRect;
  Delta: TPoint;
begin
  if not Active then Exit;
  if Form = nil then Exit;
  if csDesigning in ComponentState then Exit;

  if Active then
  begin
    Delta := Point(0, 0);
    { Update }
    FNoCopyScreen := true;
    try
      { Save current size }
      with GetClientRect do
      begin
        FSavedClentSize.X := Form.ClientWidth;
        FSavedClentSize.Y := Form.ClientHeight;
      end;
      { Update client rect }
//      SetWindowPos(Handle, 0, Form.Left, Form.Top, FWidth, FHeight, SWP_FRAMECHANGED or SWP_NOSENDCHANGING);
      Form.Height := Form.Height + 1;
      Form.Height := Form.Height - 1;
      { Check for border changing }
      if FSavedClentSize.X <> 0 then
        if (FSavedClentSize.X <> Form.ClientWidth) or
           (FSavedClentSize.Y <> Form.ClientHeight)
        then
        begin
          Delta := Point(FSavedClentSize.X - Form.ClientWidth, FSavedClentSize.Y - Form.ClientHeight);
          if (Delta.X <> 0) or (Delta.Y <> 0) then
          begin
            Form.Width := Form.Width + Delta.X;
            Form.Height := Form.Height + Delta.Y;
          end;
        end;
    finally
      FNoCopyScreen := false;
    end;
    R := Rect(0, 0, FWidth, FHeight);
    InvalidateRect(Form.Handle, @R, true);
  end;
end;

procedure TKsCustomForm.Close;
var
  CloseAction: TCloseAction;
begin
  if Form <> nil then
  begin
    if fsModal in Form.FormState then
      Form.ModalResult := mrCancel
    else
      if Form.CloseQuery then
      begin
        if Form.FormStyle = fsMDIChild then
          if kbiMinimize in BorderIcons then
            CloseAction := caMinimize
          else
            CloseAction := caNone
        else
          CloseAction := caHide;

        THackForm(Form).DoClose(CloseAction);

        if CloseAction <> caNone then
          if Application.MainForm = Form then
            Application.Terminate
          else
            if CloseAction = caHide then
            begin
              { Hide }
              if (GetMDIParentKsForm <> nil) and (GetMDIParentKsForm.MenuBar <> nil) then
                GetMDIParentKsForm.MenuBar.HideMDIItems;
              RestoreMDIChildren;

              Form.Hide;
            end
            else
              if CloseAction = caMinimize then
              begin
                { Minimize }
                Minimize;
              end
              else
              begin
                if (GetMDIParentKsForm <> nil) and (GetMDIParentKsForm.MenuBar <> nil) then
                   GetMDIParentKsForm.MenuBar.HideMDIItems;
                RestoreMDIChildren;
                { Free }
                Form.Release;
              end;
      end;
  end;
end;

procedure TKsCustomForm.Restore;
var
  SaveState: TKsWindowState;
  SaveRect: TRect;
begin
  case FWindowState of
    kwsNormal: begin
      FWindowState := kwsNormal;
      MoveWindow(FRestoreRect);
    end;
    kwsMaximized: begin
      case FOldState of
        kwsRollup: begin
          FWindowState := kwsNormal;
          MoveWindow(FRestoreRect);
          Rollup;
        end;
        kwsMinimized: if not FMinimizeApplication then { no MainForm form }
        begin
          FWindowState := kwsNormal;
          MoveWindow(FRestoreRect);
          Maximize;
        end;
      else
        FOldState := FWindowState;
        FWindowState := kwsNormal;
        MoveWindow(FRestoreRect);
        if FDropShadow then
        begin
          if Form <> nil then
            Form.Invalidate;
          UpdateNCArea(0);
        end;

        { For MDIChild }
        if (Form <> nil) and (Form.FormStyle = fsMDIChild) then
        begin
          Update;
          if Owner is TWinControl then
            PostMessage((Owner as TWinControl).Handle, WM_SIZE, 0, 0);

          if (GetMDIParentKsForm <> nil) and (GetMDIParentKsForm.MenuBar <> nil) then
            GetMDIParentKsForm.MenuBar.HideMDIItems;

          RestoreMDIChildren;
        end;
      end;
    end;
    kwsMinimized: begin
      case FOldState of
        kwsMaximized: begin
          Maximize;
          FOldState := kwsNormal;
        end;
        kwsRollup: begin
          Rollup;
          FOldState := kwsNormal;
        end;
      else
        FWindowState := kwsNormal;
        MoveWindow(FRestoreRect);
      end;
    end;
    kwsRollup: begin
      if (FWidth = RectWidth(GetMaximizedRect)) or (FOldState = kwsMaximized) then
      begin
        Maximize;
        FOldState := kwsNormal;
      end
      else
      begin
        FWindowState := kwsNormal;
        OffsetRect(FRestoreRect, -FRestoreRect.Left, -FRestoreRect.Top);
        OffsetRect(FRestoreRect, Form.Left, Form.Top);
        MoveWindow(FRestoreRect);
      end;
    end;
    kwsTray: begin
      { Restore window }
      SaveRect := FRestoreRect;
      SaveState := FOldState;
      if FMinimizeApplication then { MainForm }
      begin
        Application.Restore;
        ShowWindow(Application.Handle, sw_Restore);
        SetForegroundWindow(Application.Handle);
      end
      else
      begin
        ShowWindow(Handle, sw_Restore);
        SetForegroundWindow(Handle);
      end;
      Shell_NotifyIcon(nim_Delete, @FIconData);
      FOldState := SaveState;
      FRestoreRect := SaveRect;
      { Restore state }
      case FOldState of
        kwsMaximized: begin
          Maximize;
          FRestoreRect := SaveRect;
          FOldState := kwsNormal;
        end;
        kwsRollup: begin
          Rollup;
          FRestoreRect := SaveRect;
          FOldState := kwsNormal;
        end;
      else
        FWindowState := kwsNormal;
        MoveWindow(FRestoreRect);
      end;
    end;
  end;
end;

procedure TKsCustomForm.Maximize;
begin
  if FWindowState <> kwsMaximized then
  begin
    FOldState := FWindowState;
    if (FOldState <> kwsRollup) and (FOldState <> kwsMinimized) then
      FRestoreRect := Form.BoundsRect;
    FWindowState := kwsMaximized;
    MoveWindow(GetMaximizedRect);
    if Form <> nil then
      Form.Invalidate;
    UpdateNCArea(0);

    { For MDIChild }
    if (Form <> nil) and (Form.FormStyle = fsMDIChild) then
    begin
      Update;

      if (GetMDIParentKsForm <> nil) and (GetMDIParentKsForm.MenuBar <> nil) then
        GetMDIParentKsForm.MenuBar.ShowMDIItems(Self);
      ChangeMDIParentCaption;
    end;
  end
  else
    Restore;
end;

procedure TKsCustomForm.Minimize;
begin
  if Form = nil then Exit;
  if fsModal in Form.FormState then Exit;

  if FWindowState <> kwsMinimized then
  begin
    FOldState := FWindowState;
    FWindowState := kwsMinimized;

    if (FOldState <> kwsRollup) and (FOldState <> kwsMaximized) then
      FRestoreRect := Form.BoundsRect;

    { For MDIChild }
    if (Form <> nil) and (Form.FormStyle = fsMDIChild) then
    begin
      Update;
      if Owner is TWinControl then
        PostMessage((Owner as TWinControl).Handle, WM_SIZE, 0, 0);

      if (GetMDIParentKsForm <> nil) and (GetMDIParentKsForm.MenuBar <> nil) then
        GetMDIParentKsForm.MenuBar.HideMDIItems;

      RestoreMDIChildren;
    end;

    if FMinimizeApplication then { MainForm }
      Application.Minimize
    else
      MoveWindow(GetMinimizedRect);
  end
  else
    Restore;
end;

procedure TKsCustomForm.Rollup;
begin
  if FWindowState <> kwsRollup then
  begin
    FOldState := FWindowState;
    FWindowState := kwsRollup;

    if (FOldState <> kwsMinimized) and (FOldState <> kwsMaximized) then
      FRestoreRect := Form.BoundsRect;

    MoveWindow(GetRollupRect);
  end
  else
    Restore;
end;

procedure TKsCustomForm.MinToTray;
begin
  if Form = nil then Exit;
  if fsModal in Form.FormState then Exit;

  if FWindowState <> kwsTray then
  begin
    FOldState := FWindowState;
    FWindowState := kwsTray;

    if (FOldState <> kwsMinimized) and (FOldState <> kwsMaximized) and (FOldState <> kwsRollup) then
      FRestoreRect := Form.BoundsRect;

    if FMinimizeApplication then { MainForm }
    begin
      { Set Hint }
      StrPLCopy(FIconData.szTip, Application.Title, SizeOf(FIconData.szTip ) - 1);
      { Min }
      Application.Minimize;
      ShowWindow(Application.Handle, SW_HIDE);
    end
    else
    begin
      { Set Hint }
      if Form <> nil then
        StrPLCopy(FIconData.szTip, Form.Caption, SizeOf(FIconData.szTip ) - 1);
      { Min }
      ShowWindow(Handle, SW_HIDE);
    end;

    Shell_NotifyIcon(nim_Add, @FIconData);
  end
  else
    Restore;
end;

procedure TKsCustomForm.Help;
begin
  SendMessage(Handle, WM_SYSCOMMAND, SC_CONTEXTHELP, 0)
end;

procedure TKsCustomForm.SysMenu;
var
  P: TPoint;
begin
  if FWindowState = kwsTray then
    GetCursorPos(P)
  else
    if (FSysMenuPoint.X = 0) and (FSysMenuPoint.Y = 0) then
      P := Form.ClientToScreen(Point(0, 0))
    else
      P := FSysMenuPoint;

  if (FWindowState = kwsTray) and (FTrayMenu <> nil) then
  begin
    { Tray menu }
    if FTrayMenu is TKsCustomPopupMenu then
      (FTrayMenu as TKsCustomPopupMenu).Items.Tray := true;

    FTrayMenu.Popup(P.X, P.Y);
  end
  else
  begin
    { System menu }
    CreateSysMenu;
    if FSysMenu <> nil then
    begin
      FSysMenu.Tray := FWindowState = kwsTray;
      FSysMenu.Popup(nil, P.X, P.Y, Rect(0, 0, 0, 0));
    end;
  end;
end;

{ Internal Routines }

procedure TKsCustomForm.UpdateNCArea(ClipRgn: HRgn);
var
  R, R1: TRect;
  DC: HDC;
begin
  if (Form <> nil) and (Form.FormStyle = fsMDIChild) and (FWindowState = kwsMaximized) then Exit;

  if Buffer = nil then Exit;

  if FAllowDraw then
  begin
    { Paint NC area }
    if FAnimating then
      DC := FAniBuffer.DC
    else
      DC := GetWindowDC(Handle);
    if DC = 0 then Exit;

    try
      { Draw Shadow }
      if FDropShadow then
        with GetClientRect do
        begin
          { Drop top shadow }
          Buffer.FillRect(Rect(FShadowWidth, FShadowWidth, FWidth, Top), ShadowColor);
          Buffer.MergeDraw(FScreen, FShadowWidth, FShadowWidth, Rect(FShadowWidth, FShadowWidth, FWidth, Top));
          { Drop left shadow }
          Buffer.FillRect(Rect(FShadowWidth, FShadowWidth, Left, FHeight), ShadowColor);
          Buffer.MergeDraw(FScreen, FShadowWidth, FShadowWidth, Rect(FShadowWidth, FShadowWidth, Left, FHeight));
          { Drop right shadow }
          Buffer.FillRect(Rect(Right, 0, FRealWidth, FShadowWidth), ckTransparent);
          Buffer.FillRect(Rect(Right, FShadowWidth, FRealWidth, FRealHeight), ShadowColor);
          Buffer.MergeDraw(FScreen, Right, 0, Rect(Right, 0, FRealWidth, FRealHeight));
          { Drop bottom shadow }
          Buffer.FillRect(Rect(0, Bottom, FShadowWidth, FRealHeight), ckTransparent);
          Buffer.FillRect(Rect(FShadowWidth, Bottom, FWidth, FRealHeight), ShadowColor);
          Buffer.MergeDraw(FScreen, 0, Bottom, Rect(0, Bottom, FWidth, FRealHeight));
        end;

      { Paint NC Area to Buffer }
      if not FUse2KFeatures and FBlend then
      begin
        { Blending }
        Buffer.Blend := Blend;
        { Paint To Buffer }
        PaintNCArea(TKsBitmap(Buffer));
        { Blend }
        with GetClientRect do
        begin
          { Blend }
          Buffer.SetAlpha(FBlendValue, Rect(0, 0, FWidth, Top));
          Buffer.SetAlpha(FBlendValue, Rect(0, Bottom, FWidth, FHeight));
          Buffer.SetAlpha(FBlendValue, Rect(0, Top, Left, FHeight));
          Buffer.SetAlpha(FBlendValue, Rect(Right, Top, FWidth, FHeight));

          R := GetNCRect;
          { Top }
          R1 := R;
          R1.Bottom := R1.Top + Top;
          Buffer.MergeDraw(FScreen, 0, 0, R1);
          { Bottom }
          R1 := R;
          R1.Top := R1.Bottom - (FHeight - Bottom);
          Buffer.MergeDraw(FScreen, 0, Bottom, R1);
          { Left }
          R1 := R;
          R1.Right := R1.Left + Left;
          R1.Top := R1.Top + Top;
          R1.Bottom := R1.Bottom - (FHeight - Bottom);
          Buffer.MergeDraw(FScreen, 0, Top, R1);
          { Right }
          R1 := R;
          R1.Left := R1.Right - (FWidth - Right);
          R1.Top := R1.Top + Top;
          R1.Bottom := R1.Bottom - (FHeight - Bottom);
          Buffer.MergeDraw(FScreen, Right, Top, R1);
        end;
      end
      else
      begin
        { Paint To Buffer }
        PaintNCArea(TKsBitmap(Buffer));
      end; 

      { Paint Buffer }
      with GetClientRect do
      begin
        { Top Buffer }
        R := Rect(0, 0, FRealWidth, Top);
        Buffer.Draw(DC, 0, 0, R);
        { Bottom Buffer }
        R := Rect(Left, Bottom, Right, FRealHeight);
        Buffer.Draw(DC, Left, Bottom, R);
        { Left Buffer }
        R := Rect(0, Top, Left, FRealHeight);
        Buffer.Draw(DC, 0, Top, R);
        { Right Buffer }
        R := Rect(Right, Top, FRealWidth, FRealHeight);
        Buffer.Draw(DC, Right, Top, R);
      end;
    finally
      if not FAnimating then
        ReleaseDC(Handle, DC);
    end;
  end;
end;

function TKsCustomForm.GetRegion: HRgn;
begin
  Result := 0;
end;

procedure TKsCustomForm.ChangeSize;
var
  OldRgn, Rgn, ShadowRgn: HRgn;
  Msg: TWMSize;
  i: integer;
begin
  if FBuffer <> nil then
  begin
    { Blending }
    FBuffer.SetSize(FRealWidth, FRealHeight);
    FBuffer.Clear(ckTransparent);
  end;

  if Buffer <> nil then
    Buffer.FillRect(Rect(0, 0, FRealWidth, FRealHeight), ckTransparent);
    
  { Set Region }
  FAllowDraw := false;
  try
    OldRgn := 0;
    GetWindowRgn(Handle, OldRgn);
    Rgn := GetRegion;

    if FDropShadow and (FWindowState <> kwsMaximized) then
    begin
      ShadowRgn := GetRegion;
      OffsetRgn(ShadowRgn, FShadowWidth, FShadowWidth);
      CombineRgn(Rgn, Rgn, ShadowRgn, RGN_OR);
      DeleteObject(ShadowRgn);
    end;

    SetWindowRgn(Handle, Rgn, true);
  finally
    DeleteObject(OldRgn);
    FAllowDraw := true;
  end;
end;

function TKsCustomForm.GetNCRect: TRect;
begin
  Result := Rect(0, 0, FWidth, FHeight);
end;

function TKsCustomForm.GetParentRect: TRect;
var
  R: TRect;
  Parent: HWnd;
begin
  Parent := GetParent(Handle);
  if Parent <> 0 then
  begin
    Windows.GetClientRect(Parent, R);
    Result := R; 
  end
  else
  begin
    { Use Desktop }
    SystemParametersInfo(SPI_GETWORKAREA, 0, @R, 0);
    Result := R;
  end;
end;

function TKsCustomForm.NormalizePoint(P: TPoint): TPoint;
var
  WindowPos, ClientPos: TPoint;
  HandleParent: HWnd;
begin
  HandleParent := GetParent(Handle);
  { Calc windowpos - screen }
  WindowPos := Point(Control.Left, Control.Top);
  if HandleParent <> 0 then
    ClientToScreen(HandleParent, WindowPos);
  { Calc clientpos - screen }
  ClientPos := Point(0, 0);
  ClientToScreen(Handle, ClientPos);
  { Calc local pos }
  Result := P;
  ScreenToClient(Handle, Result);
  { Offset to client offset }
  Inc(Result.X, ClientPos.X-WindowPos.X);
  Inc(Result.Y, ClientPos.Y-WindowPos.Y);
end;

function TKsCustomForm.ApplyMagnetic(R: TRect): TRect;
var
  ParentR: TRect;
begin
  Result := R;

  if FMagnetic then
  begin
    if FDropShadow then
    begin
      Dec(R.Right, FShadowWidth); 
      Dec(R.Bottom, FShadowWidth); 
    end;

    ParentR := GetParentRect;
    with ParentR do
    begin
      if Abs(R.Left - Left) < FMagneticValue then
        OffsetRect(Result, - (R.Left - Left), 0)
      else
        if Abs(R.Right - Right) < FMagneticValue then
          OffsetRect(Result, -(R.Right - Right), 0);

      if Abs(R.Top - Top) < FMagneticValue then
        OffsetRect(Result, 0, - (R.Top - Top))
      else
        if Abs(R.Bottom - Bottom) < FMagneticValue then
          OffsetRect(Result, 0, -(R.Bottom - Bottom));
    end;
  end;
end;

procedure TKsCustomForm.CopyScreen(NewRect: TRect);
var
  DesktopDC: HDC;
  OldScreen: TKsBitmap;
  IntRect: TRect;
  OldCopyRect, NewCopyRect: TRect;
begin
  if Control = nil then Exit;
  if CompareRect(NewRect, FOldRect) then Exit;
  if FNoCopyScreen then Exit;
  if not FBlend and ((FWindowState = kwsMaximized) or (FOldState = kwsMaximized)) then
  begin
    if FOldState = kwsMaximized then FOldState := kwsNormal;
    Exit;
  end;

  { Copy Screen Rect }
  DesktopDC := GetDC(0);
  if (DesktopDC <> 0) then
  begin
    { Set old states }
    OldScreen := FScreen;
    { Set new screen }
    FScreen := TKsBitmap.Create;
    FScreen.SetSize(RectWidth(NewRect), RectHeight(NewRect));
    { Calc intersection }
    IntersectRect(IntRect, FOldRect, NewRect);
    OldCopyRect := IntRect;
    OffsetRect(OldCopyRect, -FOldRect.Left, -FOldRect.Top);
    NewCopyRect := IntRect;
    OffsetRect(NewCopyRect, -NewRect.Left, -NewRect.Top);
    { Copy from screen }
    if IsRectEmpty(IntRect) then
      { Full Size Copy }
      BitBlt(FScreen.DC, 0, 0, FScreen.Width, FScreen.Height, DesktopDC,
        NewRect.Left, NewRect.Top, SRCCOPY)
    else
    begin
      { Left Part Copy }
      if NewCopyRect.Left > 0 then
        BitBlt(FScreen.DC, 0, 0, NewCopyRect.Left, FScreen.Height, DesktopDC,
          NewRect.Left, NewRect.Top, SRCCOPY);
      { Top Part Copy }
      if NewCopyRect.Top > 0 then
        BitBlt(FScreen.DC, 0, 0, FScreen.Width, NewCopyRect.Top, DesktopDC,
          NewRect.Left, NewRect.Top, SRCCOPY);
      { Right Part Copy }
      if NewCopyRect.Right < FScreen.Width then
        BitBlt(FScreen.DC, NewCopyRect.Right, 0, FScreen.Width - NewCopyRect.Right,
          FScreen.Height, DesktopDC, NewRect.Left + NewCopyRect.Right, NewRect.Top, SRCCOPY);
      { Bottom Part Copy }
      if NewCopyRect.Bottom < FScreen.Height then
        BitBlt(FScreen.DC, 0, NewCopyRect.Bottom, FScreen.Width,
        FScreen.Height - NewCopyRect.Bottom, DesktopDC, NewRect.Left, NewRect.Top + NewCopyRect.Bottom, SRCCOPY);
    end;
    { Copy part from Old to New }
    if not IsRectEmpty(FOldRect) then
      OldScreen.Draw(FScreen, NewCopyRect, OldCopyRect);

    { Delete old screen image }
    OldScreen.Free;
  end;
  ReleaseDC(0, DesktopDC);

  FOldRect := NewRect;
end;

procedure TKsCustomForm.AnimationShow;
var
  DesktopDC: HDC;
  R: TRect;
  OldActive: boolean;
  i: integer;
  Animation: TKsAnimation;
  RectRgn, ShadowRgn, Rgn: HRgn;
begin
  if FBlend then Exit;
  if FShowAnimation = kaNone then Exit;
  if Control = nil then Exit;
  if Buffer = nil then Exit;

  { Show Animation }
  FAnimating := true;
  FAniBuffer := TKsBitmap.Create;
  try
    FAniBuffer.SetSize(FRealWidth, FRealHeight);

    { CopyScreen }
    GetWindowRect(Handle, R);
    FOldRect := Rect(0, 0, 0, 0);
    CopyScreen(R);

    { Make form image }
    OldActive := FFormActive;
    FFormActive := true;

    UpdateNCArea(0);
    PaintClientArea(FAniBuffer);
    FFormActive := OldActive;

    { Make transparent region }
    Rgn := GetRegion;
    if FDropShadow and (FWindowState <> kwsMaximized) then
    begin
      ShadowRgn := GetRegion;
      OffsetRgn(ShadowRgn, FShadowWidth, FShadowWidth);
      CombineRgn(Rgn, Rgn, ShadowRgn, RGN_OR);
      DeleteObject(ShadowRgn);
    end;
    RectRgn := CreateRectRgn(0, 0, FRealWidth, FRealHeight);
    try
      CombineRgn(Rgn, RectRgn, Rgn, RGN_DIFF);
      SelectClipRgn(FAniBuffer.DC, Rgn);

      FAniBuffer.SetAlpha(255);
//      FAniBuffer.FillRoundRect(Rect(0, 0, FRealWidth, FRealHeight), 0, ckTransparent);
      FAniBuffer.PerformTransparent(ckTransparent);

      SelectClipRgn(FAniBuffer.DC, 0);
    finally
      DeleteObject(RectRgn);
      DeleteObject(Rgn);
    end;

    R := GetClientRect;
    { Paint Graph Controls }
    if Form <> nil then
    begin
      MoveWindowOrg(FAniBuffer.DC, R.Left, R.Top);
      try
        THackControl(Form).PaintControls(FAniBuffer.DC, nil);
      finally
        MoveWindowOrg(FAniBuffer.DC, -R.Left, -R.Top);
      end;
    end;
    { Paint Win controls }
    for i := 0 to Control.ControlCount - 1 do
      if (Control.Controls[i] <> Control) and
         (Control.Controls[i] is TWinControl) then
      with Control.Controls[i] as TWinControl do
        if Visible then PaintTo(FAniBuffer.DC, R.Left + Left, R.Top + Top);

    { Animation }
    DesktopDC := GetDC(0);
    try
      { Animation }
      Animation := FShowAnimation;
      if Animation = kaRandom then
        while (Animation = kaRandom) do
          Animation := TKsAnimation(1 + Random(4));

      R := Rect(0, 0, FRealWidth, FRealHeight);

      with GetClientRect do
      begin
        R.TopLeft := Control.ClientToScreen(Point(-Left, -Top));
        R.BottomRight := Control.ClientToScreen(Point(FRealWidth - Left, FRealHeight - Top));
      end;

      case Animation of
        kaFade: FadeAnimation(DesktopDC, FScreen, Rect(0, 0, FRealWidth, FRealHeight),
          FAniBuffer, R, FShowAnimationTime);
        kaSlide: SlideAnimation(DesktopDC, FScreen, Rect(0, 0, FRealWidth, FRealHeight),
          FAniBuffer, R, Random(8), FShowAnimationTime);
        kaRect: RectAnimation(DesktopDC, FScreen, Rect(0, 0, FRealWidth, FRealHeight),
          FAniBuffer, R, Random(5), FShowAnimationTime);
      end; 
    finally
      ReleaseDC(0, DesktopDC);
    end;
  finally
    FAnimating := false;
    FAniBuffer.Free;
  end;
end;

{ Protected Painting Routines =================================================}

function TKsCustomForm.GetButtonRect(Button: TKsBorderIcon): TRect;
var
  CaptionRect, BtnRect, R: TRect;
  Pos: integer;
begin
  Result := Rect(0, 0, 0, 0);

  if not (Button in FBorderIcons) then Exit;

  CaptionRect := GetClientRect;

  CaptionRect.Bottom := CaptionRect.Top + GetCaptionHeight - 1;
  OffsetRect(CaptionRect, 0, -GetCaptionHeight);

  if Button = kbiSystemMenu then
  begin
    Result := CaptionRect;
    Result.Right := Result.Left + RectHeight(CaptionRect);
    Exit;
  end;

  { Calc Button size }
  if IsThemeAvailable then
  begin
    BtnRect := Rect(0, 0, GetCaptionHeight - 1, GetCaptionHeight - 4);
  end
  else
  begin
    if FBorderStyle = kbsToolWindow then
      BtnRect := Rect(0, 0, GetSystemMetrics(SM_CXSMSIZE), GetSystemMetrics(SM_CYSMSIZE))
    else
      BtnRect := Rect(0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE));
    Dec(BtnRect.Right, 2);
    Dec(BtnRect.Bottom, 4);
  end;

  { Start button position }
  Pos := CaptionRect.Right - 2 - RectWidth(BtnRect);

  { Close Button }
  if kbiClose in FBorderIcons then
  begin
    R := BtnRect;
    OffsetRect(R, Pos, CaptionRect.Top + 2);

    if Button = kbiClose then
    begin
      Result := R;
      Exit;
    end;

    Dec(Pos, RectWidth(BtnRect)+2);
  end;

  if FBorderStyle = kbsToolWindow then Exit;

  { Max Button }
  if (FWindowState <> kwsMinimized) and (kbiMaximize in FBorderIcons) then
  begin
    R := BtnRect;
    OffsetRect(R, Pos, CaptionRect.Top + 2);

    if Button = kbiMaximize then
    begin
      Result := R;
      Exit;
    end;

    Dec(Pos, RectWidth(BtnRect));
  end;

  { Min Button }
  if kbiMinimize in FBorderIcons then
  begin
    R := BtnRect;
    OffsetRect(R, Pos, CaptionRect.Top + 2);

    if Button = kbiMinimize then
    begin
      Result := R;
      Exit;
    end;

    Dec(Pos, RectWidth(BtnRect));
  end;

  { Roll Button }
  if (FWindowState <> kwsMinimized) and (kbiRollup in FBorderIcons) then
  begin
    Dec(Pos, 2);

    R := BtnRect;
    OffsetRect(R, Pos, CaptionRect.Top + 2);

    if Button = kbiRollup then
    begin
      Result := R;
      Exit;
    end;

    Dec(Pos, RectWidth(BtnRect));
  end;

  { Tray Button }
  if (FWindowState <> kwsMinimized) and (kbiTray in FBorderIcons) then
  begin
    R := BtnRect;
    OffsetRect(R, Pos, CaptionRect.Top + 2);

    if Button = kbiTray then
    begin
      Result := R;
      Exit;
    end;

    Dec(Pos, RectWidth(BtnRect));
  end;

  { Help Button }
  if (FWindowState <> kwsRollup) and (FWindowState <> kwsMinimized) and (kbiHelp in FBorderIcons) then
  begin
    Dec(Pos, 2);

    R := BtnRect;
    OffsetRect(R, Pos, CaptionRect.Top + 2);

    if Button = kbiHelp then
    begin
      Result := R;
      Exit;
    end;
  end;
end;

procedure TKsCustomForm.PaintCaption(Buffer: TKsBitmap);
 procedure DrawButton(R: TRect; Kind: TKsBorderIcon; Down: boolean);
 var
   Flag: integer;
   IHandle, IHandle2 : HICON;
   IconX, IconY : integer;
   ID: boolean;
   Canvas: TCanvas;
 begin
   if Form = nil then Exit;
   if RectWidth(R) = 0 then Exit;
   if (Form.FormStyle = fsMDIChild) and (FWindowState = kwsMaximized) then Exit;  

   Flag := 0;
   if Down then
     Flag := Flag or DFCS_PUSHED;

   case Kind of
     kbiSystemMenu: begin
       ID := false;
       if Form.Icon.Handle <> 0 then
         IHandle := Form.Icon.Handle
       else
         if Application.Icon.Handle <> 0 then
           IHandle := Application.Icon.Handle
         else
         begin
           IHandle := LoadIcon(0, IDI_APPLICATION);
           ID := true;
         end;
       IconX := GetSystemMetrics(SM_CXSMICON);
       if IconX = 0 then IconX := GetSystemMetrics(SM_CXSIZE);
       IconY := GetSystemMetrics(SM_CYSMICON);
       if IconY = 0 then IconY := GetSystemMetrics(SM_CYSIZE);
       IHandle2 := CopyImage(IHandle, IMAGE_ICON, IconX, IconY, LR_COPYFROMRESOURCE);
       DrawIconEx(Buffer.DC, R.Left, R.Top, IHandle2, 0, 0, 0, 0, DI_NORMAL);
       DestroyIcon(IHandle2);
       if ID then DestroyIcon(IHandle);
     end;
     kbiClose: DrawFrameControl(Buffer.DC, R, DFC_CAPTION, DFCS_CAPTIONCLOSE or Flag);
     kbiHelp: DrawFrameControl(Buffer.DC, R, DFC_CAPTION, DFCS_CAPTIONHELP or Flag);
     kbiMaximize: begin
       if FWindowState = kwsMaximized then
         DrawFrameControl(Buffer.DC, R, DFC_CAPTION, DFCS_CAPTIONRESTORE or Flag)
       else
         DrawFrameControl(Buffer.DC, R, DFC_CAPTION, DFCS_CAPTIONMAX or Flag);
     end;
     kbiMinimize: begin
       if FWindowState = kwsMinimized then
         DrawFrameControl(Buffer.DC, R, DFC_CAPTION, DFCS_CAPTIONRESTORE or Flag)
       else
         DrawFrameControl(Buffer.DC, R, DFC_CAPTION, DFCS_CAPTIONMIN or Flag);
     end;
     kbiRollup: begin
       DrawFrameControl(Buffer.DC, R, DFC_BUTTON, DFCS_BUTTONPUSH or Flag);
       { Draw Glyph }
       InflateRect(R, -4, -5);
       if not Down then
         OffsetRect(R, -1, -1);
       Canvas := TCanvas.Create;
       try
         Canvas.Handle := Buffer.DC;
         {}
         Canvas.Brush.Color := clBlack;
         Canvas.Pen.Color := clBlack;
         if FWindowState = kwsRollup then
         begin
           Canvas.Polygon([Point(R.Left, R.Top), Point(R.Right, R.Top),
             Point((R.Left + R.Right) div 2, R.Bottom)])
         end
         else
         begin
           Canvas.Polygon([Point(R.Left, R.Bottom), Point(R.Right, R.Bottom),
             Point((R.Left + R.Right) div 2, R.Top)])
         end;
       finally
         Canvas.Handle := 0;
         Canvas.Free;
       end;
     end;
     kbiTray: begin
       DrawFrameControl(Buffer.DC, R, DFC_BUTTON, DFCS_BUTTONPUSH or Flag);
       { Draw Glyph }
       InflateRect(R, -5, -5);
       OffsetRect(R, 0, 2);
       if Down then
         OffsetRect(R, 1, 1);
         
       Canvas := TCanvas.Create;
       try
         Canvas.Handle := Buffer.DC;
         {}
         Canvas.Brush.Color := clBlack;
         Canvas.Pen.Color := clBlack;

         Canvas.Ellipse(R.Left, R.Top, R.Right, R.Bottom);
       finally
         Canvas.Handle := 0;
         Canvas.Free;
       end;
     end;
   end;
 end;
var
  CaptionRect, R: TRect;
  LeftColor, RightColor: TKColor;
  Down: boolean;
  S: string;
  Count: integer;
begin
  { Paint Caption }
  CaptionRect := GetClientRect;

  CaptionRect.Bottom := CaptionRect.Top + GetCaptionHeight - 1;
  OffsetRect(CaptionRect, 0, -GetCaptionHeight);

  if FFormActive then
  begin
    LeftColor := KColor(GetSysColor(COLOR_ACTIVECAPTION));
    RightColor := KColor(GetSysColor(COLOR_GRADIENTACTIVECAPTION));
  end
  else
  begin
    LeftColor := KColor(GetSysColor(COLOR_INACTIVECAPTION));
    RightColor := KColor(GetSysColor(COLOR_GRADIENTINACTIVECAPTION));
  end;
  Buffer.FillGradientRect(CaptionRect, LeftColor, RightColor, false);

  { Draw Text }
  if Form <> nil then
  begin
    if FBorderStyle = kbsToolWindow then
      Buffer.Font := SmCaptionFont
    else
      Buffer.Font := CaptionFont;

    if FFormActive then Buffer.Font.Color := clCaptionText
    else Buffer.Font.Color := clInactiveCaptionText;

    R := CaptionRect;

    if (FBorderStyle <> kbsToolWindow) and (kbiSystemMenu in FBorderIcons) then
      inc(R.Left, RectHeight(CaptionRect));

    { Calc Caption width }
    if (BorderStyle = kbsToolWindow) then
      S := FormatStr(Buffer, Form.Caption, FWidth - R.Left - GetCaptionHeight*2)
    else
    begin
      Count := 0;
      if kbiClose in BorderIcons then Inc(Count);
      if kbiMaximize in BorderIcons then Inc(Count);
      if kbiMinimize in BorderIcons then Inc(Count);
      if kbiHelp in BorderIcons then Inc(Count);
      if kbiRollup in BorderIcons then Inc(Count);
      if kbiTray in BorderIcons then Inc(Count);

      S := FormatStr(Buffer, Form.Caption, FWidth - R.Left - GetCaptionHeight*Count)
    end;

    { Draw text }
    InflateRect(R, -2, 0);
    Buffer.DrawText(S, R, Form.DrawTextBiDiModeFlags(DT_LEFT or DT_VCENTER or DT_SINGLELINE));
  end;

  { Paint Buttons }

  { Close Button }
  if kbiClose in FBorderIcons then
  begin
    R := GetButtonRect(kbiClose);

    Down := (ssLeft in FShift) and (FDownHitTest = khtCloseButton) and (FMoveHitTest = khtCloseButton);
    DrawButton(R, kbiClose, Down);
    { Draw Glyph }
  end;

  if FBorderStyle = kbsToolWindow then Exit;

  { Icon Button }
  if kbiSystemMenu in FBorderIcons then
  begin
    R := GetButtonRect(kbiSystemMenu);
    DrawButton(R, kbiSystemMenu, false);
    { Draw Glyph }
  end;

  { Min Button }
  if kbiMinimize in FBorderIcons then
  begin
    R := GetButtonRect(kbiMinimize);

    Down := (ssLeft in FShift) and (FDownHitTest = khtMinButton) and (FMoveHitTest = khtMinButton);
    DrawButton(R, kbiMinimize, Down);
  end;

  { Max Button }
  if kbiMaximize in FBorderIcons then
  begin
    R := GetButtonRect(kbiMaximize);

    Down := (ssLeft in FShift) and (FDownHitTest = khtMaxButton) and (FMoveHitTest = khtMaxButton);
    DrawButton(R, kbiMaximize, Down);
  end;

  { Roll Button }
  if kbiRollup in FBorderIcons then
  begin
    R := GetButtonRect(kbiRollup);

    Down := (ssLeft in FShift) and (FDownHitTest = khtRollButton) and (FMoveHitTest = khtRollButton);
    DrawButton(R, kbiRollup, Down);
  end;

  { Tray Button }
  if kbiTray in FBorderIcons then
  begin
    R := GetButtonRect(kbiTray);

    Down := (ssLeft in FShift) and (FDownHitTest = khtTrayButton) and (FMoveHitTest = khtTrayButton);
    DrawButton(R, kbiTray, Down);
  end;

  { Help Button }
  if kbiHelp in FBorderIcons then
  begin
    R := GetButtonRect(kbiHelp);

    Down := (ssLeft in FShift) and (FDownHitTest = khtHelpButton) and (FMoveHitTest = khtHelpButton);
    DrawButton(R, kbiHelp, Down);
  end;
end;

procedure TKsCustomForm.PaintNCArea(Buffer: TKsBitmap);
var
  R: TRect;
begin
  if FBorderStyle = kbsNone then Exit;
//  if not FNeedUpdate and not FBlend then Exit;

  { Paint NC Area }
  R := GetNCRect;

  { Paint Border }
  DrawEdge(Buffer.DC, R, EDGE_RAISED, BF_RECT);
  InflateRect(R, -2, -2);
  with GetClientRect do
  begin
    { Top }
    Buffer.FillRect(Rect(R.Left, R.Top, R.Right, Top), ckBtnFace);
    { Left }
    Buffer.FillRect(Rect(R.Left, Top, Left, R.Bottom), ckBtnFace);
    { Right }
    Buffer.FillRect(Rect(Right, Top, R.Right, R.Bottom), ckBtnFace);
    { Bottom }
    Buffer.FillRect(Rect(R.Left, Bottom, R.Right, R.Bottom), ckBtnFace);
  end;

  PaintCaption(Buffer);
end;

procedure TKsCustomForm.PaintClientArea(Buffer: TKsBitmap);
var
  R: TRect;
  Color: TKColor;
begin
  { Paint Client Area }
  R := GetClientRect;
  Color := KColor(Form.Color);

  Buffer.FillRect(R, Color);
end;

procedure TKsCustomForm.MoveWindow(OffsetX, OffsetY: integer);
var
  R: TRect;
begin
  if ((OffsetX <> 0) or (OffsetY <> 0)) and (Form <> nil) then
  begin
    R := Form.BoundsRect;
    with R do
      MoveWindow(Left + OffsetX, Top + OffsetY, FWidth, FHeight);
  end;
end;

procedure TKsCustomForm.MoveWindow(Left, Top, Width, Height: integer);
 procedure ProcessPaintMessages;
 { Dispatches all pending WM_PAINT messages. In effect, this is like an
   'UpdateWindow' on all visible windows }
 var
   Msg: TMsg;
 begin
   while PeekMessage(Msg, 0, WM_PAINT, WM_PAINT, PM_NOREMOVE) do
   begin
     case Integer(GetMessage(Msg, 0, WM_PAINT, WM_PAINT)) of
       -1: Break; { if GetMessage failed }
       0: begin
            { Repost WM_QUIT messages }
            PostQuitMessage(Msg.WParam);
            Break;
          end;
     end;
     DispatchMessage(Msg);
   end;
 end;

begin
  if Form <> nil then
  begin
    Form.BoundsRect := Rect(Left, Top, Left+Width, Top+Height);
    ProcessPaintMessages;
  end;
end;

procedure TKsCustomForm.MoveWindow(Bounds: TRect);
begin
  with Bounds do
    MoveWindow(Left, Top, Right-Left, Bottom-Top);
end;

procedure TKsCustomForm.DrawDragFrame(R: TRect);
var
  DesktopDC: HDC;
  Canvas: TCanvas;
  ParentHandle: HWnd;
begin
  if FWindowState = kwsMaximized then Exit;
  { Copy Screen Rect }
  if Form = nil then Exit;

  if Form.FormStyle = fsMDIChild then
    if GetMDIParentForm <> nil then
      ParentHandle := THackForm(GetMDIParentForm).ClientHandle
    else
      ParentHandle := 0
  else
    ParentHandle := 0;

  DesktopDC := GetWindowDC(ParentHandle);
  if (DesktopDC <> 0) then
  begin
    Canvas := TCanvas.Create;
    try
      Canvas.Handle := DesktopDC;
      
      Canvas.Brush.Style := bsClear;
      Canvas.Pen.Style := psInsideFrame;
      Canvas.Pen.Color := clWhite;
      Canvas.Pen.Width := 2;
      Canvas.Pen.Mode := pmXor;

      { Erase old }
      if not IsRectEmpty(FSimpleDrawRect) then
        with FSimpleDrawRect do
          Canvas.Rectangle(Left, Top, Right, Bottom);
      { Draw new }
      if not IsRectEmpty(R) then
        with R do
          Canvas.Rectangle(Left, Top, Right, Bottom);
      { Save old }
      FSimpleDrawRect := R;
    finally
      Canvas.Handle := 0;
      Canvas.Free;
    end;
  end;
  ReleaseDC(ParentHandle, DesktopDC);
end;

{ Protected Routines ==========================================================}

function TKsCustomForm.GetBorderWidth: integer;
begin
  if IsThemeAvailable then
    Result := BorderWidth
  else
    Result := GetSystemMetrics(SM_CXSIZEFRAME);
end;

function TKsCustomForm.GetCaptionHeight: integer;
begin
  case FBorderStyle of
    kbsNone: Result := 0;
    kbsStandard:
      if IsThemeAvailable then
        Result := CaptionHeight
      else
        Result := GetSystemMetrics(SM_CYCAPTION);
    kbsToolWindow:
      if IsThemeAvailable then
        Result := SmCaptionHeight
      else
        Result := GetSystemMetrics(SM_CYSMCAPTION);
  else
    Result := 0;
  end;
end;

function TKsCustomForm.GetClientRect: TRect;
begin
  if (Form <> nil) and (Form.FormStyle = fsMDIChild) and (FWindowState = kwsMaximized) then
  begin
    { MDI Child Maximized }
    Result := GetNCRect;
    Exit;
  end;

  case FBorderStyle of
    kbsNone: begin
      Result := GetNCRect;
    end;
    kbsStandard, kbsToolWindow: begin
      Result := GetNCRect;
      { Frame }
      Inc(Result.Left, GetBorderWidth);
      Dec(Result.Right, GetBorderWidth);
      Inc(Result.Top, GetBorderWidth);
      Dec(Result.Bottom, GetBorderWidth);
      { Caption }
      Inc(Result.Top, GetCaptionHeight);
    end;
  end;
end;

{ Tracking }

function TKsCustomForm.GetMaxTrackSize: TPoint;
var
  R: TRect;
begin
  R := GetMaximizedRect;
  Result := Point(RectWidth(R), RectHeight(R));

  if (FConstraints.MaxWidth <> 0) then
    Result.X := FConstraints.MaxWidth;
  if (FConstraints.MaxHeight <> 0) then
    Result.Y := FConstraints.MaxHeight;
end;

function TKsCustomForm.GetMinTrackSize: TPoint;
begin
  Result := Point(150, 40);

  if (FConstraints.MinWidth <> 0) then
    Result.X := FConstraints.MinWidth;
  if (FConstraints.MinHeight <> 0) then
    Result.Y := FConstraints.MinHeight;
end;

{ State's rect }

function TKsCustomForm.GetMaximizedRect: TRect;
var
  R: TRect;
begin
  if (Form <> nil) and (Form.FormStyle <> fsMDIChild) then
  begin
    SystemParametersInfo(SPI_GETWORKAREA, 0, @R, 0);
    { for DualMonotors }
    IntersectRect(R, R, Rect(Screen.DesktopLeft, Screen.DesktopTop,
      Screen.DesktopLeft + Screen.DesktopWidth, Screen.DesktopTop + Screen.DesktopHeight));

    with GetClientRect do
      InflateRect(R, Left, Left);
  end
  else
  begin
    if (Form <> nil) and (Form.FormStyle = fsMDIChild) and (Form.Owner is TCustomForm) and
       (THackForm(Form.Owner).ClientHandle <> 0)
    then
      Windows.GetClientRect(THackForm(Form.Owner).ClientHandle, R)
    else
      R := Rect(0, 0, Screen.Width, Screen.Height);

    { Unknown }
    R.Right := R.Right + 1;
    R.Bottom := R.Bottom + 1;
  end;

  Result := R;
end;

function TKsCustomForm.GetMinimizedRect: TRect;
begin
  with GetClientRect do
    Result := Rect(0, 0, GetSystemMetrics(SM_CXMINIMIZED), Top + FHeight-Bottom);

  with GetParentRect do
    OffsetRect(Result, 0, Bottom-Result.Bottom);
end;

function TKsCustomForm.GetRollupRect: TRect;
begin
  with GetClientRect do
    Result := Rect(0, 0, FWidth, Top + FHeight-Bottom);
  if Form <> nil then
    OffsetRect(Result, Form.Left, Form.Top);
end;

function TKsCustomForm.GetHitTest(X, Y: integer): TKsHitTest;
const
  SizeWidth = 20;
var
  R: TRect;
  CaptionHeight: integer;
begin
  Result := khtClient;

  R := GetClientRect;

  { Caption }
  if Y < R.Top then
    Result := khtCaption;

  CaptionHeight := GetCaptionHeight;

  { System Buttons }

  if PtInRect(GetButtonRect(kbiSystemMenu), Point(X, Y)) then
    Result := khtSysMenuButton;
  if PtInRect(GetButtonRect(kbiClose), Point(X, Y)) then
    Result := khtCloseButton;
  if PtInRect(GetButtonRect(kbiMinimize), Point(X, Y)) then
    Result := khtMinButton;
  if PtInRect(GetButtonRect(kbiMaximize), Point(X, Y)) then
    Result := khtMaxButton;
  if PtInRect(GetButtonRect(kbiHelp), Point(X, Y)) then
    Result := khtHelpButton;
  if PtInRect(GetButtonRect(kbiRollup), Point(X, Y)) then
    Result := khtRollButton;
  if PtInRect(GetButtonRect(kbiTray), Point(X, Y)) then
    Result := khtTrayButton;

  if (FWindowState = kwsMaximized) or
     (FWindowState = kwsMinimized) or
     (FWindowState = kwsRollup) then Exit;
  
  { Top }
  if Y < R.Top - CaptionHeight then
    if X < SizeWidth then Result := khtTopLeft
    else
      if X > FWidth - SizeWidth then Result := khtTopRight
      else
        Result := khtTop;

  { Bottom }
  if (Y > R.Bottom) and (Y < FHeight) then
    if X < SizeWidth then Result := khtBottomLeft
    else
      if X > FWidth - SizeWidth then Result := khtBottomRight
      else
        Result := khtBottom;

  { Left }
  if X < R.Left then
    if Y < SizeWidth then Result := khtTopLeft
    else
      if Y > FHeight - SizeWidth then Result := khtBottomLeft
      else
        Result := khtLeft;

  { Right }
  if (X > R.Right) and (X < FWidth) then
    if Y < SizeWidth then Result := khtTopRight
    else
      if Y > FHeight - SizeWidth then Result := khtBottomRight
      else
        Result := khtRight;

end;

procedure TKsCustomForm.NCMouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: integer);
begin
  { Handle HitTest }
  if Button = mbLeft then
  begin
    FDownHitTest := GetHitTest(X, Y);
    FMoveHitTest := FDownHitTest;
    FDownPoint := Point(X, Y);
    FMovePoint := Point(X, Y);

    UpdateNCArea(0);

    if FDownHitTest in [khtCaption..khtBottomRight] then
    begin
      FDragging := true;
      FSimpleDrawRect := Rect(0, 0, 0, 0);
      if FSimpleDrag then
      begin
        FAllowDraw := false;
        FSimpleDrawEnabled := true;
      end;
    end;
    SetCapture(Handle);
  end;
end;

procedure TKsCustomForm.NCMouseMove(Shift: TShiftState; X, Y: integer);
var
  NewRect: TRect;
begin
  if Form = nil then Exit;
  { Handle HitTest }
  if (ssLeft in Shift) and FDragging then
  begin
    NewRect := Form.BoundsRect;
    { Handle DownHiTest }
    case FDownHitTest of
      khtCaption: if (FCanMove) and (FWindowState <> kwsMaximized) then
      begin
        OffsetRect(NewRect, X - FDownPoint.X, Y - FDownPoint.Y);
        NewRect := ApplyMagnetic(NewRect);
      end;
      khtLeft: if (FCanResize) and (FWindowState <> kwsMaximized) and (FWindowState <> kwsMinimized) and (FWindowState <> kwsRollup) then
      begin
        Inc(NewRect.Left, X - FDownPoint.X);
        if NewRect.Left > NewRect.Right - GetMinTrackSize.X then
          NewRect.Left := NewRect.Right - GetMinTrackSize.X;
      end;
      khtRight: if (FCanResize) and (FWindowState <> kwsMaximized) and (FWindowState <> kwsMinimized) and (FWindowState <> kwsRollup) then
      begin
        if FSimpleDrawEnabled then
          Inc(NewRect.Right, X - FDownPoint.X)
        else
          Inc(NewRect.Right, X - FMovePoint.X);
      end;
      khtTop: if (FCanResize) and (FWindowState <> kwsMaximized) and (FWindowState <> kwsMinimized) and (FWindowState <> kwsRollup) then
      begin
        Inc(NewRect.Top, Y - FDownPoint.Y);
        if NewRect.Top > NewRect.Bottom - GetMinTrackSize.Y then
          NewRect.Top := NewRect.Bottom - GetMinTrackSize.Y;
      end;
      khtBottom: if (FCanResize) and (FWindowState <> kwsMaximized) and (FWindowState <> kwsMinimized) and (FWindowState <> kwsRollup) then
      begin
        if FSimpleDrawEnabled then
          Inc(NewRect.Bottom, Y - FDownPoint.Y)
        else
          Inc(NewRect.Bottom, Y - FMovePoint.Y)
      end;
      khtTopLeft: if (FCanResize) and (FWindowState <> kwsMaximized) and (FWindowState <> kwsMinimized) and (FWindowState <> kwsRollup) then
      begin
        Inc(NewRect.Top, Y - FDownPoint.Y);
        if NewRect.Top > NewRect.Bottom - GetMinTrackSize.Y then
          NewRect.Top := NewRect.Bottom - GetMinTrackSize.Y;
        Inc(NewRect.Left, X - FDownPoint.X);
        if NewRect.Left > NewRect.Right - GetMinTrackSize.X then
          NewRect.Left := NewRect.Right - GetMinTrackSize.X;
      end;
      khtTopRight: if (FCanResize) and (FWindowState <> kwsMaximized) and (FWindowState <> kwsMinimized) and (FWindowState <> kwsRollup) then
      begin
        Inc(NewRect.Top, Y - FDownPoint.Y);
        if NewRect.Top > NewRect.Bottom - GetMinTrackSize.Y then
          NewRect.Top := NewRect.Bottom - GetMinTrackSize.Y;
        if FSimpleDrawEnabled then
          Inc(NewRect.Right, X - FDownPoint.X)
        else
          Inc(NewRect.Right, X - FMovePoint.X);
      end;
      khtBottomLeft: if (FCanResize) and (FWindowState <> kwsMaximized) and (FWindowState <> kwsMinimized) and (FWindowState <> kwsRollup) then
      begin
        if FSimpleDrawEnabled then
          Inc(NewRect.Bottom, Y - FDownPoint.Y)
        else
          Inc(NewRect.Bottom, Y - FMovePoint.Y);
        Inc(NewRect.Left, X - FDownPoint.X);
        if NewRect.Left > NewRect.Right - GetMinTrackSize.X then
          NewRect.Left := NewRect.Right - GetMinTrackSize.X;
      end;
      khtBottomRight: if (FCanResize) and (FWindowState <> kwsMaximized) and (FWindowState <> kwsMinimized) and (FWindowState <> kwsRollup) then
      begin
        if FSimpleDrawEnabled then
        begin
          Inc(NewRect.Bottom, Y - FDownPoint.Y);
          Inc(NewRect.Right, X - FDownPoint.X);
        end
        else
        begin
          Inc(NewRect.Bottom, Y - FMovePoint.Y);
          Inc(NewRect.Right, X - FMovePoint.X);
        end;
      end;
    end;

    if FSimpleDrag then
    begin
      DrawDragFrame(NewRect);
    end
    else
    begin
      MoveWindow(NewRect);

      { Handle MoveHitTest}
      FMoveHitTest := GetHitTest(X, Y);
      { Repaint NonClient }
      UpdateNCArea(0);
    end;
    { Set MovePoint }
    FMovePoint := Point(X, Y);
  end
  else
  begin
    { Handle MoveHitTest}
    FMoveHitTest := GetHitTest(X, Y);
    if FMoveHitTest = khtSysMenuButton then
      Form.Hint := 'Sytem Menu';

    FMoveHitTest := khtClient;
  end;
end;

procedure TKsCustomForm.NCMouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: integer);
var
  HT: TKsHitTest;
begin
  HT := GetHitTest(X, Y);

  if Button = mbLeft then
  begin
    ReleaseCapture;

    { Action }
    if (ssDouble in Shift) then
    begin
      if (kbiMaximize in BorderIcons) {and (FCanResize) }then
      begin
        { Maximize }
        if (FDownHitTest = khtCaption) then
        begin
          if FWindowState <> kwsMinimized then Maximize
          else Restore;
          Exit;
        end;
      end;
      { Sys Icon }
      if FDownHitTest = khtSysMenuButton then
      begin
        Close;
        Exit;
      end;
    end;

    if FDownHitTest = HT then
      case GetHitTest(X, Y) of
        khtSysMenuButton: SysMenu;
        khtCloseButton: Close;
        khtMinButton: Minimize;
        khtMaxButton: Maximize;
        khtHelpButton: Help;
        khtRollButton: Rollup;
        khtTrayButton: MinToTray;
     end;

    if FSimpleDrag then
    begin
      { Erase last rect }
      DrawDragFrame(Rect(0, 0, 0, 0));
      { Finish simple drag }
      FSimpleDrag := false;
      try
        NCMouseMove([ssLeft], X, Y);
      finally
        FSimpleDrag := true;
      end;
      if FSimpleDrag then
      begin
        FSimpleDrawEnabled := false;
        FAllowDraw := true;
      end;
      UpdateNCArea(0);
      Form.Invalidate;
    end
    else
      UpdateNCArea(0);

    FDownHitTest := khtClient;

    FDragging := false;
  end;

  if (Button = mbRight) and (HT = khtCaption) and (Form <> nil) then
  begin
    with GetClientRect do
      FSysMenuPoint := Form.ClientToScreen(Point(X - Left, Y - Top));
    SysMenu;
    FSysMenuPoint := Point(0, 0);
  end;

  FDownHitTest := khtClient;
end;

function TKsCustomForm.GetMDIParentForm: TCustomForm;
begin
  Result := nil;
  if (Form <> nil) and (Form.FormStyle = fsMDIChild) and (Form.Owner is TCustomForm) then
    Result := (Form.Owner as TCustomForm);
end;

function TKsCustomForm.GetMDIParentKsForm: TKsCustomForm;
var
  i: integer;
  ParentForm: TCustomForm;
begin
  Result := nil;

  ParentForm := GetMDIParentForm;
  if ParentForm <> nil then
    with ParentForm do
      for i := 0 to ComponentCount - 1 do
        if (Components[i] is TKsCustomForm) and (Components[i] as TKsCustomForm).Active then
        begin
          Result := Components[i] as TKsCustomForm;
          Exit;
        end;
end;

function TKsCustomForm.IsMDIChildMaximized: boolean;
var
  i, c: integer;
begin
  Result := false;

  if Form = nil then Exit;

  if Form.FormStyle = fsMDIChild then
  begin
    { From MDIChild }
    if (GetMDIParentForm <> nil) and (THackForm(GetMDIParentForm).MDIChildCount > 0) then
    begin
      for c := 0 to THackForm(GetMDIParentForm).MDIChildCount - 1 do
        with THackForm(GetMDIParentForm) do
        begin
          Result := MDIChildren[c].WindowState = wsMaximized;
          if Result then Exit;

          { Check KSForms }
          with MDIChildren[c] do
            for i := 0 to ComponentCount - 1 do
              if (Components[i] is TKsCustomForm) and (Components[i] as TKsCustomForm).Active then
              begin
                Result := (Components[i] as TKsCustomForm).WindowState = kwsMaximized;
                if Result then Exit;
              end;
        end;
    end;
  end;

  if Form.FormStyle = fsMDIForm then
  begin
    { From MDIChild }
    if (Form.MDIChildCount > 0) then
    begin
       for c := 0 to Form.MDIChildCount - 1 do
         with Form.MDIChildren[c] do
         begin
            { Check KSForms }
            for i := 0 to ComponentCount - 1 do
              if (Components[i] is TKsCustomForm) and (Components[i] as TKsCustomForm).Active then
              begin
                Result := (Components[i] as TKsCustomForm).WindowState = kwsMaximized;
                If Result then Exit;
              end;
         end;
    end;
  end;
end;

procedure TKsCustomForm.ChangeMDIParentCaption;
var
  S: string;
  ParentForm: TCustomForm;
begin
  ParentForm := GetMDIParentForm;

  if ParentForm <> nil then
  begin
    { Change caption }
    S := ParentForm.Caption;
    if Pos('[', S) > 0 then Delete(S, Pos('[', S)-3, Length(S));

    if IsMDIChildMaximized then
    begin
      ParentForm.Caption := S + ' - ['+Form.Caption+']';
      PostMessage(ParentForm.Handle, WM_UPDATENCAREA, 0, 0);
    end
    else
      ParentForm.Caption := S;
  end;
end;

procedure TKsCustomForm.RestoreMDIChildren;
var
  c, i: integer;
begin
  { Restore All MDI Children }
  if Form.FormStyle = fsMDIChild then
  begin
    { From MDIChild }
    if (GetMDIParentForm <> nil) and (THackForm(GetMDIParentForm).MDIChildCount > 0) then
    begin
      for c := 0 to THackForm(GetMDIParentForm).MDIChildCount - 1 do
        with THackForm(GetMDIParentForm) do
        begin
          { Check KSForms }
          with MDIChildren[c] do
            for i := 0 to ComponentCount - 1 do
              if (Components[i] is TKsCustomForm) and (Components[i] as TKsCustomForm).Active then
              begin
                if (Components[i] as TKsCustomForm).WindowState = kwsMaximized then
                  (Components[i] as TKsCustomForm).Restore;
              end;
        end;
    end;
    ChangeMDIParentCaption;
  end;

  if Form.FormStyle = fsMDIForm then
  begin
    for c := 0 to THackForm(Form).MDIChildCount - 1 do
      with THackForm(Form) do
      begin
        { Check KSForms }
        with MDIChildren[c] do
          for i := 0 to ComponentCount - 1 do
            if (Components[i] is TKsCustomForm) and (Components[i] as TKsCustomForm).Active then
            begin
              if (Components[i] as TKsCustomForm).WindowState = kwsMaximized then
                (Components[i] as TKsCustomForm).Restore;
            end;
      end;
  end;
end;

procedure TKsCustomForm.ClientWndProc(var Message: TMessage);

  procedure Default;
  begin
    with Message do
      Result := CallWindowProc(FOldClientProc, THackForm(Form).ClientHandle, Msg, wParam, lParam);
  end;

var
  DC: HDC;
  PS: TPaintStruct;
begin
  if THackForm(Form).ClientHandle = 0 then Exit;

  with Message do
    case Msg of
      WM_NCHITTEST:
        begin
          Default;
          if Result = HTCLIENT then Result := HTTRANSPARENT;
        end;
      WM_ERASEBKGND:
        begin
          if (Form <> nil) and (Form.FormStyle = fsMDIForm) and (IsMDIChildMaximized) then
          else
            SendMessage(Handle, WM_ERASEBKGND, TWMEraseBkGnd(Message).DC, BufferFlag);
          Result := 0;
        end;
      $3F://!
        begin
          if (Form.FormStyle = fsMDIForm) then
            RemoveMDIClientEdge(THackForm(Form).ClientHandle);
        end;
      WM_PAINT:
        begin
          DC := TWMPaint(Message).DC;
          if DC = 0 then TWMPaint(Message).DC := BeginPaint(THackForm(Form).ClientHandle, PS);
          try
            THackForm(Form).PaintHandler(TWMPaint(Message));
          finally
            if DC = 0 then EndPaint(THackForm(Form).ClientHandle, PS);
          end;
          Message.Result := 0;
        end;
    else
      Default;
    end;
end;

procedure TKsCustomForm.DisableDrawing;
begin
  FAllowDraw := false;
end;

procedure TKsCustomForm.EnableDrawing;
begin
  FAllowDraw := true;
end;

{ VCL Routines ================================================================}

procedure TKsCustomForm.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent = FMenuBar) then
    FMenuBar := nil;
  if (Operation = opRemove) and (AComponent = FTrayMenu) then
    FTrayMenu := nil;
end;

{ Internal Windows messages ===================================================}

procedure TKsCustomForm.WMMaximize(var Msg: TMessage);
begin
  Maximize;

  MessageHandled;
end;

procedure TKsCustomForm.WMFormIsBlend(var Msg: TMessage);
begin
  if FBlend then
    Msg.Result := 1
  else
    Msg.Result := 0;

  MessageHandled;
end;

procedure TKsCustomForm.WMUpdateNCArea(var Msg: TMessage);
begin
  if Msg.wParam = WM_GETTEXT then
  begin
    { Posted from WM_GETTEXT }
    { Enabled caption }
    SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle, GWL_STYLE ) or WS_CAPTION);
  end;
  { Update }
  UpdateNCArea(0);
  { Set Handled }
  MessageHandled;
end;

{ Windows Messages ============================================================}

procedure TKsCustomForm.AfterControl(Sender: TObject; var Msg: TMessage;
  var Handled: Boolean);
var
  i: integer;
  ChildForm: TKsCustomForm;
  R: TRect;
  NewCaption: string;
begin
  { After form handled }
  with Msg do
    case Msg of
      WM_GETTEXT: if (FOldCaption <> string(PChar(lParam))) then
      begin
        { Disabled caption }
        SetWindowLong(Handle, GWL_STYLE, GetWindowLong(Handle, GWL_STYLE ) and not WS_CAPTION);
        { Post for update }
        PostMessage(Handle, WM_UPDATENCAREA, WM_GETTEXT, 0);
        { Save Result }
        FOldCaption := PChar(lParam);

        { For MDIChild }
        if (Form <> nil) and (Form.FormStyle = fsMDIChild) and
           IsMDIChildMaximized
        then
          ChangeMDIParentCaption; 

        { Set handled flag }
        Result := 0;
      end;
      WM_SIZE: if (Form <> nil) and (Form.FormStyle = fsMDIForm) and
                  (Form.MDIChildCount > 0) and (IsMDIChildMaximized)
      then
      begin
        for i := 0 to Form.MDIChildCount - 1 do
        begin
          if THackForm(Form).ClientHandle <> 0 then
            Windows.GetClientRect(THackForm(Form).ClientHandle, R)
          else
            Windows.GetClientRect(Handle, R);

          if Form.MDIChildren[i] = Form.ActiveMDIChild then
            Form.MDIChildren[i].BoundsRect := R;
        end;
      end;
    end;
end;

procedure TKsCustomForm.WMNCCalcSize(var Msg: TWMNCCalcSize);
var
  rgrc: PNCCalcSizeParams;
  WP: PWindowPos;
  m_lLeft, m_lRight, m_lTop, m_lBottom: integer;
begin
  if (Msg.CalcValidRects) then
  begin
    rgrc := Msg.CalcSize_Params;

    with GetClientRect do
    begin
      m_lLeft := left;
      m_lRight := FRealWidth-right;
      m_lTop := top;
      m_lBottom := FRealHeight-bottom;
    end;

    WP := rgrc.lppos;
    with rgrc^.rgrc[0] do
    begin
      left := WP^.x;
      top := WP^.y;
      right := WP^.x + WP^.cx;
      bottom := WP^.y + WP^.cy;
      left := left + m_lLeft;
      top := top + m_lTop;
      right := right - m_lRight;
      bottom := bottom - m_lBottom;
    end;
    rgrc^.rgrc[1] := rgrc^.rgrc[0];
    Msg.CalcSize_Params := rgrc;
    Msg.Result := WVR_VALIDRECTS;
  end;

  { Set handled flag }
  Msg.Result := 0;
  MessageHandled;
end;

procedure TKsCustomForm.WMNCPaint(var Msg: TMessage);
begin
  UpdateNCArea(0);
  { Set handled flag }
  Msg.Result := 0;
  MessageHandled;
end;

procedure TKsCustomForm.WMNCActivate(var Msg: TWMNCActivate);
begin
  FFormActive := Msg.Active;

  if (not FSetAppEvent) and (Form <> nil) and (FMinimizeApplication) then
  begin
    FOldApplicationMinimize := Application.OnMinimize;
    Application.OnMinimize := DoApplicationMinimize;
    FOldApplicationRestore := Application.OnRestore;
    Application.OnRestore := DoApplicationRestore;
    FSetAppEvent := true
  end;

  { Set handled flag }
  if not Msg.Active then
  begin
    UpdateNCArea(0);
    Msg.Result := 1;
    MessageHandled;
  end
  else
  begin
    if not ((Form <> nil) and (Form.FormStyle = fsMDIChild)) then
    begin
      UpdateNCArea(0);
      Msg.Result := 0;
      MessageHandled;
    end
    else
      Msg.Result := 0;
  end;
end;

procedure TKsCustomForm.WMNCHitTest(var Msg: TWMNCHitTest);
var
  P: TPoint;
begin
  P := NormalizePoint(Point(Msg.XPos, Msg.YPos));

  Msg.Result := HTCLIENT;

  case GetHitTest(P.X, P.Y) of
    khtCaption: if FCanMove then Msg.Result := HTCAPTION;
    khtLeft: if FCanResize then Msg.Result := HTLEFT;
    khtTop: if FCanResize then Msg.Result := HTTOP;
    khtRight: if FCanResize then Msg.Result := HTRIGHT;
    khtBottom: if FCanResize then Msg.Result := HTBOTTOM;
    khtBorder: Msg.Result := HTBORDER;
    khtTopLeft: if FCanResize then Msg.Result := HTTOPLEFT;
    khtTopRight: if FCanResize then Msg.Result := HTTOPRIGHT;
    khtBottomLeft: if FCanResize then Msg.Result := HTBOTTOMLEFT;
    khtBottomRight: if FCanResize then Msg.Result := HTBOTTOMRIGHT;
    khtCloseButton:Msg.Result := HTCLOSE;
    khtHelpButton: Msg.Result := HTHELP;
    khtMinButton: Msg.Result := HTMINBUTTON;
    khtMaxButton: Msg.Result := HTMAXBUTTON;
    khtRollButton: Msg.Result := HTREDUCE;
    khtTrayButton: Msg.Result := HTREDUCE;
    khtSysMenuButton: Msg.Result := HTREDUCE;
    khtNonClient: Msg.Result := HTREDUCE;
  end;

  { Set handled flag }
  MessageHandled;
end;

procedure TKsCustomForm.WMNCLButtonDblClk(var Msg: TWMNCHitMessage);
var
  P: TPoint;
begin
  P := NormalizePoint(Point(Msg.XCursor, Msg.YCursor));
  NCMouseDown(mbLeft, [ssLeft, ssDouble], P.X, P.Y);
  NCMouseUp(mbLeft, [ssLeft, ssDouble], P.X, P.Y);
  FShift := [];
  { Set handled flag }
  Msg.Result := 1;
  MessageHandled;
end;

procedure TKsCustomForm.WMNCLButtonDown(var Msg: TWMNCHitMessage);
var
  P: TPoint;
begin
  if FFormActive = false then
    SetFocus(Handle);

  P := NormalizePoint(Point(Msg.XCursor, Msg.YCursor));

  FShift := [ssLeft];

  NCMouseDown(mbLeft, FShift, P.X, P.Y);
  { Set handled flag }
  Msg.Result := 0;
  Msg.Msg := WM_NULL;
  MessageHandled;
end;

procedure TKsCustomForm.WMNCLButtonUp(var Msg: TWMNCHitMessage);
var
  P: TPoint;
begin
  P := NormalizePoint(Point(Msg.XCursor, Msg.YCursor));

  FShift := [];

  NCMouseUp(mbLeft, FShift, P.X, P.Y);
  { Set handled flag }
  Msg.Result := 0;
  Msg.Msg := WM_NULL;
  MessageHandled;
end;

procedure TKsCustomForm.WMNCMouseMove(var Msg: TWMNCHitMessage);
var
  P: TPoint;
begin
  P := NormalizePoint(Point(Msg.XCursor, Msg.YCursor));

  NCMouseMove(FShift, P.X, P.Y);
  { Set handled flag }
  Msg.Result := 0;
  Msg.Msg := WM_NULL;
  MessageHandled;
end;

procedure TKsCustomForm.WMNCRButtonDown(var Msg: TWMNCHitMessage);
var
  P: TPoint;
begin
  if FFormActive = false then
    SetFocus(Handle);

  P := NormalizePoint(Point(Msg.XCursor, Msg.YCursor));

  FShift := [ssRight];

  NCMouseDown(mbRight, FShift, P.X, P.Y);
  { Set handled flag }
  Msg.Result := 0;
  Msg.Msg := WM_NULL;
  MessageHandled;
end;

procedure TKsCustomForm.WMNCRButtonUp(var Msg: TWMNCHitMessage);
var
  P: TPoint;
begin
  P := NormalizePoint(Point(Msg.XCursor, Msg.YCursor));

  FShift := [];

  NCMouseUp(mbRight, FShift, P.X, P.Y);
  { Set handled flag }
  Msg.Result := 0;
  Msg.Msg := WM_NULL;
  MessageHandled;
end;

procedure TKsCustomForm.WMLButtonUp(var Msg: TWMMouse);
var
  P: TPoint;
begin
  if Form = nil then Exit;
  if FShift = [] then Exit;

  P := Point(Msg.XPos, Msg.YPos);
  P := Form.ClientToScreen(P);
  P := NormalizePoint(P);

  FShift := [];

  NCMouseUp(mbLeft, FShift, P.X, P.Y);
end;

procedure TKsCustomForm.WMMouseMove(var Msg: TWMMouse);
var
  P: TPoint;
begin
  { Set  Application Event }
  if Form = nil then Exit;

  if FShift = [] then Exit;

  P := Point(Msg.XPos, Msg.YPos);
  P := Form.ClientToScreen(P);
  P := NormalizePoint(P);

  NCMouseMove(FShift, P.X, P.Y);
end;

procedure TKsCustomForm.WMActivate(var Msg: TWMActivate);
begin
  if not FFormActive and Msg.Minimized then
    Minimize;
end;

procedure TKsCustomForm.WMPaint(var Msg: TMessage);
var
  DC: HDC;
  PS: TPaintStruct;
begin
  if Form = nil then Exit;

  DC := TWMPaint(Msg).DC;
  if DC = 0 then TWMPaint(Msg).DC := BeginPaint(Handle, PS);
  try
    THackForm(Form).PaintHandler(TWMPaint(Msg));
  finally
    if DC = 0 then EndPaint(Handle, PS);
  end;

  Msg.Result := 0;
  MessageHandled;
end;

procedure TKsCustomForm.WMEraseBkgnd(var Msg: TWMEraseBkgnd);
var
  DC: HDC;
  R: TRect;
  Canvas: TCanvas;
begin
  if (Form <> nil) and (Form.FormStyle = fsMDIForm) and (Msg.Unused <> BufferFlag) then
  begin
    Msg.Result := 1;
    MessageHandled;
    Exit;
  end;

  if FAllowDraw then
  begin
    { Paint to buffer }
    if (Msg.Unused and BufferFlag <> 0) and (Msg.DC <> 0) then
    begin
      PaintClientArea(Buffer);
      with GetClientRect do
        Buffer.Draw(Msg.DC, -Left, -Top);

      Msg.Result := 1;
      MessageHandled;

      Exit;
    end;

    { Paint Client }
    if FEraseBkgnd then
      DC := GetDC(Handle)
    else
      DC := Msg.DC;

    if DC = 0 then
    begin
      Msg.Result := 1;
      MessageHandled;

      Exit;
    end;

    try
      { Paint Buffer}
      with GetClientRect do
      begin
        { Paint }
        PaintClientArea(Buffer);

        { Blend }
        if not FUse2KFeatures and FBlend then
        begin
          Buffer.SetAlpha(FBlendValue, GetClientRect);
          Buffer.MergeDraw(FScreen, Left, Top, Rect(Left, Top, Right, Bottom));
        end;

        { Form Paint }
        if Assigned(FOnPaint) and not (csLoading in ComponentState) then
        begin
          Canvas := TCanvas.Create;
          try
            Canvas.Handle := Buffer.DC;
            FOnPaint(Canvas);
          finally
            Canvas.Handle := 0;
            Canvas.Free;
          end;
        end;

        { Buffer Paint }
        Buffer.Draw(DC, -Left, -Top);
      end;
    finally
      if (FEraseBkgnd and (Msg.Unused and BufferFlag = 0)) or (Msg.DC = 0) then
        ReleaseDC(Handle, DC);
    end;
  end;

  { Set handled flag }
  Msg.Result := 1;
  MessageHandled;
end;

procedure TKsCustomForm.WMWindowPosChanging(var Msg: TWMWindowPosChanging);
var
  OldRgn, Rgn: HRgn;
  NewRect: TRect;
  Changed: boolean;
begin
  { Set internal properties }
  if (Msg.WindowPos^.flags and SWP_NOSIZE = 0) or
     (Msg.WindowPos^.flags and SWP_NOMOVE = 0) then
  begin
    with GetMaxTrackSize do
    begin
      if Msg.WindowPos^.cx > X then Msg.WindowPos^.cx := X;
      if Msg.WindowPos^.cy > Y then Msg.WindowPos^.cy := Y;
    end;

    if (FWindowState <> kwsRollup) and (FWindowState <> kwsMinimized) then
      with GetMinTrackSize do
      begin
        if Msg.WindowPos^.cx < X then Msg.WindowPos^.cx := X;
        if Msg.WindowPos^.cy < Y then Msg.WindowPos^.cy := Y;
      end;

    { Cahnge Size }
    if (Msg.WindowPos^.cx <> FWidth) or (Msg.WindowPos^.cy <> FHeight) then
    begin
      FRealWidth := Msg.WindowPos^.cx;
      FRealHeight := Msg.WindowPos^.cy;
      FWidth := FRealWidth;
      FHeight := FRealHeight;

      if FDropShadow and (FWindowState <> kwsMaximized) then
      begin
        Dec(FWidth, FShadowWidth);
        Dec(FHeight, FShadowWidth);
      end;

      Changed := true;
    end
    else
      Changed := false;

    { Copy screen image }
    if FBlend or FDropShadow then
    begin
      with Msg.WindowPos^ do
        if flags and SWP_NOMOVE <> 0 then
        begin
          GetWindowRect(Handle, NewRect);
          NewRect := Rect(NewRect.Left, NewRect.Top, NewRect.Left + cx, NewRect.Top + cy) // no move
        end
        else
          NewRect := Rect(x, y, x + cx, y + cy); // moving

      CopyScreen(NewRect);
    end;

    if Changed then
      ChangeSize;
  end;

  if (Msg.WindowPos^.flags and SWP_SHOWWINDOW <> 0) then
  begin
    if (FBlend) or (FDropShadow) then
    begin
      { Change ZOrder }
      FOldRect := Rect(0, 0, 0, 0);
      GetWindowRect(Handle, NewRect);
      CopyScreen(NewRect);
    end;
    { Animation }
    AnimationShow;
  end;

  if FBlend or FDropShadow then
    Msg.WindowPos^.flags := Msg.WindowPos^.flags or SWP_NOCOPYBITS;

  { Set handled flag }
  Msg.Result := 0;
  MessageHandled;
end;

procedure TKsCustomForm.WMSysCommand(var Msg: TWMSysCommand);
begin
  with Msg do
  begin
    { MainMenu }
    if (CmdType and $FFF0 = SC_KEYMENU) and (Key <> VK_SPACE) and
       (Key <> Ord('-')) and (GetCapture = 0) and (FMenuBar <> nil) then
    begin
      PostMessage(Handle, WM_CHAR, Key, 0);
      FMenuBar.EnterLoop;
      Result := 1;
    end;
    { System Menu }
    if ((CmdType and $FFF0 = SC_KEYMENU) and (Key = VK_SPACE)) or
        (CmdType and $FFF0 = SC_MOUSEMENU) then
    begin
      SysMenu;
    end;
    { Close }
    if (CmdType and $FFF0 = SC_CLOSE) then
      Close;
  end;
end;

procedure TKsCustomForm.WMMDIActivate(var Message: TWMMDIActivate);
var
  ParentForm: TCustomForm;
begin
  { MDI Child }
  ParentForm := GetMDIParentForm;
  if ParentForm <> nil then
  begin
    if IsMDIChildMaximized then
    begin
      { Change Caption }
      ChangeMDIParentCaption;
      { Maximize if need }
      if (Message.ChildWnd <> Handle) then
      begin
        if FWindowState <> kwsMaximized then
          PostMessage(Handle, WM_MAXIMIZE, 0, 0);
      end
      else
        if (GetMDIParentKsForm <> nil) and (GetMDIParentKsForm.MenuBar <> nil) then
          GetMDIParentKsForm.MenuBar.ShowMDIItems(Self);
    end;
  end;
end;

{ Internal Tray Event }

procedure TKsCustomForm.DoApplicationMinimize(Sender: TObject);
begin
  if FWindowState <> kwsMinimized then
    if FMinimizeApplication then
      if FWindowState <> kwsTray then
        Minimize;
  if Assigned(FOldApplicationMinimize) then
    FOldApplicationMinimize(Sender);
end;

procedure TKsCustomForm.DoApplicationRestore;
begin
  if FWindowState = kwsMinimized then
    if FMinimizeApplication then
    begin
      Restore;
      UpdateNCArea(0);
    end;
  if Assigned(FOldApplicationRestore) then
    FOldApplicationRestore(Sender);
end;

procedure TKsCustomForm.EndSession;
begin
  Shell_NotifyIcon(nim_Delete, @FIconData);
end;

procedure TKsCustomForm.MsgTrayWndProc(var Msg: TMessage);
begin
  with Msg do
  begin
    case Msg of
      WM_QUERYENDSESSION:
        Result := 1;     // Allow Windows to shut down
      WM_ENDSESSION:
        EndSession;      // Be sure to clean up tray icon
      WM_TRAYICONMESSAGE:
      begin
        case LParam of
          WM_LBUTTONDOWN: ;
          WM_LBUTTONUP: ;
          WM_LBUTTONDBLCLK:
          begin
            Restore;
          end;
          WM_RBUTTONDOWN: ;
          WM_RBUTTONUP: 
          begin
            SysMenu;
          end;
          WM_RBUTTONDBLCLK: ;
        end;
      end;
    end;
  end;
  Dispatch(Msg);
end;

{ Properties ==================================================================}

procedure TKsCustomForm.SetBorderStyle(const Value: TKsBorderStyle);
begin
  FBorderStyle := Value;
  if not (csLoading in ComponentState) then
    Update;
end;

procedure TKsCustomForm.SetCanMove(const Value: boolean);
begin
  FCanMove := Value;
  if not (csLoading in ComponentState) then
    Update;
end;

procedure TKsCustomForm.SetCanResize(const Value: boolean);
begin
  FCanResize := Value;
  if not (csLoading in ComponentState) then
    Update;
end;

procedure TKsCustomForm.SetBorderIcons(const Value: TKsBorderIcons);
begin
  FBorderIcons := Value;
  if not (csLoading in ComponentState) then
    Update;
end;

procedure TKsCustomForm.SetBlend(const Value: boolean);
begin
  FBlend := Value;
  if not (csLoading in ComponentState) then
    Update;
end;

procedure TKsCustomForm.SetBlendValue(const Value: integer);
begin
  FBlendValue := Value;
  if not (csLoading in ComponentState) then
    Update;
end;

procedure TKsCustomForm.SetConstraints(const Value: TSizeConstraints);
begin
  FConstraints.Assign(Value);
  if Form <> nil then
  begin
    if (FConstraints.MinWidth <> 0) and (FConstraints.MinWidth > Form.Width) then
      Form.Width := FConstraints.MinWidth;
    if (FConstraints.MinHeight <> 0) and (FConstraints.MinHeight > Form.Height) then
      Form.Height := FConstraints.MinHeight;
  end;

  if not (csLoading in ComponentState) then
    Update;
end;

procedure TKsCustomForm.SetWindowState(const Value: TKsWindowState);
begin
  if FWindowState <> Value then
  begin
    if not (csLoading in ComponentState) then
    begin
      case Value of
        kwsNormal: Restore;
        kwsMaximized: Maximize;
        kwsMinimized: Minimize;
        kwsRollup: Rollup;
        kwsTray: MinToTray;
      else
        FWindowState := Value;
      end;
    end
    else
      FLoadedState := Value;
  end;
end;

procedure TKsCustomForm.SetMenuBar(const Value: TKsCustomMenuBar);
begin
  FMenuBar := Value;
end;

procedure TKsCustomForm.SetSysMenuBlend(const Value: boolean);
begin
  FSysMenuBlend := Value;
end;

procedure TKsCustomForm.SetSysMenuBlendValue(const Value: integer);
begin
  FSysMenuBlendValue := Value;
end;

procedure TKsCustomForm.SetStayOnTop(const Value: boolean);
var
  WndParam: HWnd;
begin
  if FStayOnTop <> Value then
  begin
    FStayOnTop := Value;

    if not (csDesigning in ComponentState) and (Form <> nil) then
    begin
      if FStayOnTop then
        WndParam := HWND_TOPMOST
      else
        WndParam := HWND_NOTOPMOST;

      SetWindowPos(Handle, WndParam, Form.Left, Form.Top, FWidth, FHeight,
        SWP_NOACTIVATE or SWP_NOCOPYBITS or SWP_NOMOVE or SWP_NOSIZE);
    end;
  end;
end;

procedure TKsCustomForm.SetUseXPFeatures(const Value: boolean);
begin
  FUseXPFeatures := Value;
end;

procedure TKsCustomForm.SetUse2KFeatures(const Value: boolean);
begin
  FUse2KFeatures := Value;
end;

procedure TKsCustomForm.Cascade;
begin
  if IsMDIChildMaximized then
    RestoreMDIChildren;

  Application.ProcessMessages;

  if Form <> nil then
    Form.Cascade;
end;

procedure TKsCustomForm.Tile;
begin
  if IsMDIChildMaximized then
    RestoreMDIChildren;

  Application.ProcessMessages;

  if Form <> nil then
    Form.Tile;
end;

function TKsCustomForm.GetBuffer: TKsBitmap;
begin
  if FBuffer <> nil then
    Result := FBuffer
  else
    Result := SharedBuffer;
end;

procedure TKsCustomForm.SetTrayMenu(const Value: TPopupMenu);
begin
  FTrayMenu := Value;
end;

initialization
  InitCaptionFont;
  InitSysMenuImages;

  BufferWidth := Screen.Width + BufferBorder;
  BufferHeight := Screen.Height + BufferBorder;

  SharedBuffer := TKsBitmap.Create;
  SharedBuffer.SetSize(BufferWidth, BufferHeight);
finalization
  SharedBuffer.Free;
  SharedBuffer := nil;

  SysMenuImages.Free;
  SmCaptionFont.Free;
  SmCaptionFont := nil;
  CaptionFont.Free;
  CaptionFont := nil;
end.
