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

  SkinEngine VCL by Evgeny Kryukov
  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 KsSkinObjects;

{$I KsSkinVer.Inc}

interface

uses Windows, Classes, Sysutils, Messages, Controls, Graphics, Forms, KsBitmap;

const

  WM_INVALIDATESKINOBJECT       = WM_USER + 1097;
  WM_GETSKINFORM                = WM_USER + 1098;

  DesignMode: boolean           = false;

type

  { Sets the object's alignment. The following table lists the possible values:
    <TABLE>
    Value                       Meaning
    -----                       -------
    saNone                      No aligment use
    saLeft, saMostLeft          Aligment to left side
    saTop, saMostTop            Aligment to top side
    saRight, saMostRight        Aligment to right side
    saBottom, saMostBottom      Aligment to bottom side
    saClient                    Aligment by owner client rect
    saTopLeft                   Aligment by topleft
    saTopRight                  Aligment by topright
    saBottomLeft,               Aligment by bottomleft
    saBottomRight               Aligment by bottomright
    saCenter                    Aligment in owner center
    saText                      Aligment by text width
    </TABLE>
  }
  TSeAlign = (saNone, saLeft, saMostLeft, saTop, saMostTop, saRight, saMostRight,
    saBottom, saMostBottom, saClient, saTopLeft, saTopRight, saBottomLeft,
    saBottomRight, saCenter, saText);

  { Sets then SkinObject kind}
  TSeKind = (
    skNone,
    { Forms }
    skForm, skCaption, skTitle, skTop, skLeft, skRight, skBottom, skTopLeft,
    skTopRight, skBottomLeft, skBottomRight, skSysButton, skClient,
    { Menus }
    skMenuBar, skMenuBarItem, skMenuBarItemText,
    skPopupMenu, skPopupMenuItem, skPopupMenuItemText,
    skItemText,
    skTranparent
  );

  { Sets SkinObject state. The following table lists the possible values:
    <TABLE>
    Value                       Meaning
    -----                       -------
    ssNormal                    Normal state
    ssDesign                    Design mode (in SkinBuilder)
    ssMaximized                 Parent form is maximized
    ssMinimized                 Parent form is minimized
    ssRollup                    Parent form is rollup

    ssHot                       Object is rollover
    ssPressed                   Object is pressed
    ssFocused                   Object is focused
    ssDisabled                  Object is disabled
    </TABLE>
  }
  TSeState = (
    ssNormal,
    { Builder}
    ssDesign,
    { Forms }
    ssMaximized, ssMinimized, ssRollup,
    { Menus and Controls }
    ssHot, ssPressed, ssFocused, ssDisabled
  );

  { Sets the text aligment. }
  TSeTextAlign = (taTopLeft, taTopCenter, taTopRight, taLeft, taCenter,
    taRight, taBottomLeft, taBottomCenter, taBottomRight);

  { Sets the text showing effect. The following table lists the possible values:
    <TABLE>
    Value                       Meaning
    -----                       -------
    teNone                      No effect
    teShadow                    Draw with black shadow
    </TABLE>
  }
  TSeTextEffect = (teNone, teShadow);

  { The following table lists the possible values:
    <TABLE>
    Value                       Meaning
    -----                       -------
    tsTile                      Bitmap tile on the object
    tsStretch                   Bitmap stretch by object size
    tsCenter                    Bitmap centered
    </TABLE>
  }
  TscTileStyle = (tsTile, tsStretch, tsCenter);

  { The following table lists the possible values:
    <TABLE>
    Value                       Meaning
    -----                       -------
    sbaNone                     No actions
    sbaClose                    Close parent form
    sbaHelp                     Help
    sbaMinimize                 Minimize parent form
    sbaMaximize                 Maximize parent form
    sbaRestore                  Restore parent form
    sbaRollUp                   Rollup parent form
    sbaRollDown                 Rolldown parent form
    sbaTray                     Minimize to tray
    sbaSysMenu                  Popup system menu
    sbaRollPanel                Roolup (or Rolldown) RollPanel
    </TABLE>
  }
  TSeAction = (sbaNone, sbaClose, sbaHelp, sbaMinimize, sbaMaximize,
    sbaRestore, sbaRollUp, sbaRollDown, sbaTray, sbaSysMenu, sbaRollPanel,
    sbaCustom);

  TSeButtonState = (bsNormal, bsHover, bsDown);

  { The following table lists the possible values:
    <TABLE>
    Value                       Meaning
    -----                       -------
    rkLeft                      Roll to left
    rkTop                       Roll to top
    rkRight                     Roll to right
    rkBottom                    Roll to bottom
    </TABLE>
  }
  TSeRollKind = (rkLeft, rkTop, rkRight, rkBottom);

{ TSeSkinObject class }

  TSeSkinObject = class(TComponent)
  private
    FAlign: TSeAlign;
    FKind: TSeKind;
    FLeft: integer;
    FTop: integer;
    FWidth: integer;
    FHeight: integer;
    FVisible: boolean;
    FBitmaps: TKsBitmapList;
    FActive: boolean;
    FState: TSeState;
    FParentControl: TWinControl;
    FOldWidth, FOldHeight: integer;
    FText: string;
    FTextAlign: TSeTextAlign;
    FTextMarginRight: integer;
    FTextMarginLeft: integer;
    FTextMarginTop: integer;
    FTextEffect: TSeTextEffect;
    FDrawIfOwner: boolean;
    FRightText: string;
    FEnabled: boolean;
    FMarginLeft: integer;
    FMarginTop: integer;
    FMarginBottom: integer;
    FMarginRight: integer;
    FBiDiMode: TBiDiMode;
    procedure ReadData(Stream: TStream);
    procedure WriteData(Stream: TStream);

    function GetCount: integer;
    function GetObject(index: integer): TSeSkinObject;
    function GetBoundsRect: TRect;
    procedure SetHeight(const Value: integer);
    procedure SetWidth(const Value: integer);
    procedure SetLeft(const Value: integer);
    procedure SetTop(const Value: integer);
    procedure SetBoundsRect(const Value: TRect);
    procedure SetBitmaps(const Value: TKsBitmapList);
    procedure SetFont(const Value: TFont);
    procedure SetActive(const Value: boolean);
    procedure SetParentControl(const Value: TWinControl);
    procedure SetMarginBottom(const Value: integer);
    procedure SetMarginLeft(const Value: integer);
    procedure SetMarginRight(const Value: integer);
    procedure SetMarginTop(const Value: integer);
    procedure SetBiDiMode(const Value: TBiDiMode);
  protected
    FColor: TColor;
    FFont: TFont;
    procedure DefineProperties(Filer: TFiler); override;
    function GetSkinForm: TComponent;
    procedure DrawText(Buffer: TKsBitmap);

    function UseRightToLeftAlignment: Boolean;
    function UseRightToLeftReading: Boolean;
    function DrawTextBiDiModeFlags(Flags: Integer): Longint;
    function DrawTextBiDiModeFlagsReadingOnly: Longint;

    function GetFont: TFont; virtual;
    procedure SetState(const Value: TSeState); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterLoad; virtual;
    { Assign, Copy }
    procedure Assign(Source: TPersistent); override;
    function CreateCopy(AOwner: TSeSkinObject): TSeSkinObject;
    { Aligning }
    procedure Aligning;
    procedure Invalidate;
    { Drawing }
    procedure Draw(Buffer: TKsBitmap); virtual;
    procedure DrawChild(Buffer: TKsBitmap); 
    { Region }
    function GetRegion: HRgn;
    function CreateRegion: HRgn; virtual;
    { Colors }
    procedure ChangeHue(DeltaHue: integer); virtual;
    { Events }
    procedure MouseHover; virtual;
    procedure MouseLeave; virtual;
    procedure MouseDown(Button: TMouseButton; X, Y: integer); virtual;
    procedure MouseMove(Shift: TShiftState; X, Y: integer); virtual;
    procedure MouseUp(Button: TMouseButton; X, Y: integer); virtual;
    procedure MouseDouble(Button: TMouseButton; X, Y: integer); virtual;
    { Children }
    procedure Add(SkinObject: TSeSkinObject);
    procedure Remove(SkinObject: TSeSkinObject);
    function FindObjectByKind(AKind: TSeKind): TSeSkinObject;
    function FindObjectByName(AName: string): TSeSkinObject;
    function FindObjectByPoint(Point: TPoint): TSeSkinObject;
    procedure SetCharset(CharSet: TFontCharset); virtual;
    { properties }
    property Active: boolean read FActive write SetActive;
    property BiDiMode: TBiDiMode read FBiDiMode write SetBiDiMode;
    property Bitmaps: TKsBitmapList read FBitmaps write SetBitmaps;
    property BoundsRect: TRect read GetBoundsRect write SetBoundsRect;
    property Count: integer read GetCount;
    property Enabled: boolean read FEnabled write FEnabled;
    property ParentControl: TWinControl read FParentControl write SetParentControl;
    property Objects[index: integer]: TSeSkinObject read GetObject; default;
    property State: TSeState read FState write SetState;
    property Text: string read FText write FText;
    property RightText: string read FRightText write FRightText; 
    property Visible: boolean read FVisible write FVisible;
  published
    { Specifies the object aligment
      See Also:
        TSeAlign
    }
    property Align: TSeAlign read FAlign write FAlign;
    { Specifies the object color }
    property Color: TColor read FColor write FColor;
    { If true then object drawing if have children }
    property DrawIfOwner: boolean read FDrawIfOwner write FDrawIfOwner;
    { Specifies the object font }
    property Font: TFont read GetFont write SetFont;
    { Specifies the object kind }
    property Kind: TSeKind read FKind write FKind;
    { Specifies the object left position }
    property Left: integer read FLeft write SetLeft;
    { Specifies the object top position }
    property Top: integer read FTop write SetTop;
    { Specifies the object height }
    property Height: integer read FHeight write SetHeight;
    { Specifies the object width }
    property Width: integer read FWidth write SetWidth;
    { Specifies the left margin }
    property MarginLeft: integer read FMarginLeft write SetMarginLeft;
    { Specifies the right margin }
    property MarginRight: integer read FMarginRight write SetMarginRight;
    { Specifies the top margin }
    property MarginTop: integer read FMarginTop write SetMarginTop;
    { Specifies the bottom margin }
    property MarginBottom: integer read FMarginBottom write SetMarginBottom;
    { Specifies the text aligment }
    property TextAlign: TSeTextAlign read FTextAlign write FTextAlign;
    { Specifies the text effect }
    property TextEffect: TSeTextEffect read FTextEffect write FTextEffect;
    { Specifies the left text margin }
    property TextMarginLeft: integer read FTextMarginLeft write FTextMarginLeft;
    { Specifies the right text margin }
    property TextMarginRight: integer read FTextMarginRight write FTextMarginRight;
    { Specifies the top text margin }
    property TextMarginTop: integer read FTextMarginTop write FTextMarginTop;
  end;

  TSeSkinObjectClass = class of TSeSkinObject;

{ TSeActiveObject class }

  TSeActiveObject = class(TSeSkinObject)
  private
    FActiveColor: TColor;
    FActiveFont: TFont;
    procedure SetActiveFont(const Value: TFont);
  protected
     function GetFont: TFont; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Assign, Copy }
    procedure Assign(Source: TPersistent); override;
    { Drawing }
    procedure Draw(Buffer: TKsBitmap); override;
    { Colors }
    procedure ChangeHue(DeltaHue: integer); override;
  published
    { Specifies the color if Active = true }
    property ActiveColor: TColor read FActiveColor write FActiveColor;
    { Specifies the font if Active = true }
    property ActiveFont: TFont read FActiveFont write SetActiveFont;
  end;

{ TSeBitmapObject class }

  TSeBitmapObject = class(TSeSkinObject)
  private
    FBitmap: TKsBitmapLink;
    FTileStyle: TscTileStyle;
    FBorderTileStyle: TscTileStyle;
    procedure SetBitmap(const Value: TKsBitmapLink);
    procedure SetBorderTileStyle(const Value: TscTileStyle);
  protected
    procedure DrawNormal(Buffer: TKsBitmap);
    procedure DrawRect(Buffer: TKsBitmap; MarginRect, MarginDstRect: TRect;
      ATileStyle: TscTileStyle);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterLoad; override;
    { Assign, Copy }
    procedure Assign(Source: TPersistent); override;
    { Drawing }
    procedure Draw(Buffer: TKsBitmap); override;
    { Region }
    function CreateRegion: HRgn; override;
  published
    { Specifies the object's bitmap }
    property Bitmap: TKsBitmapLink read FBitmap write SetBitmap;
    { Specifies the dysplay tile style }
    property TileStyle: TscTileStyle read FTileStyle write FTileStyle;
    { Specifies the border tile style }
    property BorderTileStyle: TscTileStyle read FBorderTileStyle write SetBorderTileStyle;
  end;

{ TSeActiveBitmap class }

  TSeActiveBitmap = class(TSeBitmapObject)
  private
    FActiveBitmap: TKsBitmapLink;
    FActiveFont: TFont;
    procedure SetActiveFont(const Value: TFont);
    procedure SetActiveBitmap(const Value: TKsBitmapLink);
  protected
    function GetFont: TFont; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterLoad; override;
    { Assign, Copy }
    procedure Assign(Source: TPersistent); override;
    { Drawing }
    procedure Draw(Buffer: TKsBitmap); override;
  published
    { Specifies the bitmap if Active = true }
    property ActiveBitmap: TKsBitmapLink read FActiveBitmap write SetActiveBitmap;
    { Specifies the font if Active = true }
    property ActiveFont: TFont read FActiveFont write SetActiveFont;
  end;

{ TSeSystemButton class }

  TSeSystemButton = class(TSeActiveBitmap)
  private
    FAction: TSeAction;
    FBitmapPressed: TKsBitmapLink;
    FBitmapHot: TKsBitmapLink;
    { Internal }
    FButtonState: TSeButtonState;
    FShift: TShiftState;
    procedure DoAction;

    procedure SetAction(const Value: TSeAction);
    procedure SetBitmapPressed(const Value: TKsBitmapLink);
    procedure SetBitmapHot(const Value: TKsBitmapLink);
  protected
    procedure SetState(const Value: TSeState); override;
    procedure SetChildState(const Value: TSeState);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterLoad; override;
    { Assign, Copy }
    procedure Assign(Source: TPersistent); override;
    { Drawing }
    procedure Draw(Buffer: TKsBitmap); override;
    { Events }
    procedure MouseHover; override;
    procedure MouseLeave; override;
    procedure MouseDown(Button: TMouseButton; X, Y: integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: integer); override;
    procedure MouseUp(Button: TMouseButton; X, Y: integer); override;
    procedure MouseDouble(Button: TMouseButton; X, Y: integer); override;
  published
    { Specifies the click action }
    property Action: TSeAction read FAction write SetAction;
    { Specifies bitmap in ssHot state }
    property BitmapHot: TKsBitmapLink read FBitmapHot write SetBitmapHot;
    { Specifies bitmap in ssPressed state }
    property BitmapPressed: TKsBitmapLink read FBitmapPressed write SetBitmapPressed;
  end;

{ TSeButtonObject class }

  TSeButtonObject = class(TSeBitmapObject)
  private
    FBitmapFocused: TKsBitmapLink;
    FBitmapHot: TKsBitmapLink;
    FBitmapPressed: TKsBitmapLink;
    FBitmapDisabled: TKsBitmapLink;
    procedure SetBitmapFocused(const Value: TKsBitmapLink);
    procedure SetBitmapDisabled(const Value: TKsBitmapLink);
    procedure SetBitmapHot(const Value: TKsBitmapLink);
    procedure SetBitmapPressed(const Value: TKsBitmapLink);
  protected
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterLoad; override;
    { Assign, Copy }
    procedure Assign(Source: TPersistent); override;
    { Drawing }
    procedure Draw(Buffer: TKsBitmap); override;
  published
    { Specifies the bitmap in ssHot state }
    property BitmapHot: TKsBitmapLink read FBitmapHot write SetBitmapHot;
    { Specifies the bitmap in ssFocused state }
    property BitmapFocused: TKsBitmapLink read FBitmapFocused write SetBitmapFocused;
    { Specifies the bitmap in ssPressed state }
    property BitmapPressed: TKsBitmapLink read FBitmapPressed write SetBitmapPressed;
    { Specifies the bitmap in ssDisabled state }
    property BitmapDisabled: TKsBitmapLink read FBitmapDisabled write SetBitmapDisabled;
  end;

{ TSeTextObject class }

  TSeTextObject = class(TSeSkinObject)
  private
    FFontHot: TFont;
    FFontFocused: TFont;
    FFontPressed: TFont;
    FFontDisabled: TFont;
    procedure SetFontHot(const Value: TFont);
    procedure SetFontDisabled(const Value: TFont);
    procedure SetFontFocused(const Value: TFont);
    procedure SetFontPressed(const Value: TFont);
  protected
    function GetFont: TFont; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { Assign, Copy }
    procedure Assign(Source: TPersistent); override;
    { Drawing }
    procedure Draw(Buffer: TKsBitmap); override;
    { Colors }
    procedure ChangeHue(DeltaHue: integer); override;
    { Font }
    procedure SetCharset(CharSet: TFontCharset); override;
  published
    property FontHot: TFont read FFontHot write SetFontHot;
    property FontFocused: TFont read FFontFocused write SetFontFocused;
    property FontPressed: TFont read FFontPressed write SetFontPressed;
    property FontDisabled: TFont read FFontDisabled write SetFontDisabled;
  end;

{ Registration }

var
  RegObjects: TList; { registered class }

procedure RegisterSkinObject(ObjectClass: TSeSkinObjectClass);
function GetSkinObjectClass(ClassName: string): TSeSkinObjectClass;

procedure SaveSkinObject(Stream: TStream; SkinObject: TSeSkinObject);
function LoadSkinObject(Stream: TStream; Owner: TSeSkinObject): TSeSkinObject;

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

uses KsUtils, KsForms, KsSkinForms;

procedure RegisterSkinObject(ObjectClass: TSeSkinObjectClass);
begin
  if RegObjects = nil then
    RegObjects := TList.Create;
  if RegObjects <> nil then
    RegObjects.Add(ObjectClass);
end;

function GetSkinObjectClass(ClassName: string): TSeSkinObjectClass;
var
  i: integer;
begin
  for i := 0 to RegObjects.Count-1 do
    if TSeSkinObjectClass(RegObjects[i]).ClassName = ClassName then
    begin
      Result := TSeSkinObjectClass(RegObjects[i]);
      Exit;;
    end;

  Result := nil;
end;

{ I/O Routines }

procedure SaveSkinObject(Stream: TStream; SkinObject: TSeSkinObject);
var
  BinStream:TMemoryStream;
  StrStream: TStringStream;
  s: string;
begin
  if SkinObject = nil then Exit;

  BinStream := TMemoryStream.Create;
  try
    StrStream := TStringStream.Create(S);
    try
      BinStream.WriteComponent(SkinObject);
      BinStream.Seek(0, soFromBeginning);
      ObjectBinaryToText(BinStream, StrStream);
      StrStream.Seek(0, soFromBeginning);
      S := StrStream.DataString;
    finally
      StrStream.Free;
    end;
  finally
    BinStream.Free
  end;

  WriteString(Stream, SkinObject.ClassName);
  WriteString(Stream, S);
end;

function LoadSkinObject(Stream: TStream; Owner: TSeSkinObject): TSeSkinObject;
var
  SkinObjectClass: TSeSkinObjectClass;
  S, ClassName: string;
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  Result := nil;

  ClassName := ReadString(Stream);

  { make object }
  SkinObjectClass := GetSkinObjectClass(ClassName);
  if SkinObjectClass <> nil then
    Result := SkinObjectClass.Create(Owner);

  { Load object }
  if Result <> nil then
  begin
    S := ReadString(Stream);
    StrStream := TStringStream.Create(S);
    try
      BinStream := TMemoryStream.Create;
      try
        ObjectTextToBinary(StrStream, BinStream);
        BinStream.Seek(0, soFromBeginning);

        BinStream.ReadComponent(Result);

        { Set Charset }
        Result.SetCharset(DEFAULT_CHARSET);
      finally
        BinStream.Free;
      end;
    finally
      StrStream.Free;
    end;
  end;
end;

{ TSeSkinObject ===============================================================}

constructor TSeSkinObject.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEnabled := true;
  FVisible := true;
  FColor := clWhite;
  FFont := TFont.Create;
  FTextAlign := taLeft;
end;

destructor TSeSkinObject.Destroy;
begin
  FFont.Free;
  inherited Destroy;
end;

procedure TSeSkinObject.AfterLoad;
var
  i: integer;
begin
  if Count = 0 then Exit;

  for i := 0 to Count-1 do
    Objects[i].AfterLoad;
end;

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

procedure TSeSkinObject.Assign(Source: TPersistent);
var
  i: integer;
  Child: TSeSkinObject;
begin
  if Source is TSeSkinObject then
  begin
    { Assign }
    Name := (Source as TSeSkinObject).Name;

    FAlign := (Source as TSeSkinObject).FAlign;
    FKind := (Source as TSeSkinObject).FKind;
    FAlign := (Source as TSeSkinObject).FAlign;
    FBitmaps := (Source as TSeSkinObject).FBitmaps;
    FColor := (Source as TSeSkinObject).FColor;
    FDrawIfOwner := (Source as TSeSkinObject).FDrawIfOwner;
    FFont.Assign((Source as TSeSkinObject).FFont);
    FKind := (Source as TSeSkinObject).FKind;
    FTextAlign := (Source as TSeSkinObject).FTextAlign;
    FTextEffect := (Source as TSeSkinObject).FTextEffect;
    FTextMarginLeft := (Source as TSeSkinObject).FTextMarginLeft;
    FTextMarginRight := (Source as TSeSkinObject).FTextMarginRight;
    FTextMarginTop := (Source as TSeSkinObject).FTextMarginTop;

    FMarginLeft := (Source as TSeSkinObject).FMarginLeft;
    FMarginTop := (Source as TSeSkinObject).FMarginTop;
    FMarginRight := (Source as TSeSkinObject).FMarginRight;
    FMarginBottom := (Source as TSeSkinObject).FMarginBottom;

    { Copy Objects }
    for i := 0 to (Source as TSeSkinObject).Count - 1 do
    begin
      Child := (Source as TSeSkinObject).Objects[i].CreateCopy(Self);
    end;

    { Bounds }
    FLeft := (Source as TSeSkinObject).Left;
    FTop := (Source as TSeSkinObject).Top;
    FWidth := (Source as TSeSkinObject).Width;
    FHeight := (Source as TSeSkinObject).Height;
//    BoundsRect := (Source as TSeSkinObject).BoundsRect;
  end
  else
    inherited Assign(Source);
end;

function TSeSkinObject.CreateCopy(AOwner: TSeSkinObject): TSeSkinObject;
var
  ClassType: TSeSkinObjectClass;
begin
  ClassType := GetSkinObjectClass(ClassName);
  if ClassType <> nil then
  begin
    Result := ClassType.Create(AOwner);
    Result.Assign(Self);
  end
  else
    Result := nil;
end;

function TSeSkinObject.GetSkinForm: TComponent;
var
  R: Integer;
begin
  Result := nil;

  if FParentControl <> nil then
  begin
    R := SendMessage(FParentControl.Handle, WM_GETSKINFORM, 0, 0);
    if R <> 0 then
      Result := TSeSkinForm(R);
  end
end;

{ BiDi Mode }

function TSeSkinObject.DrawTextBiDiModeFlags(Flags: Integer): Longint;
begin
  Result := Flags;
  { do not change center alignment }
  if UseRightToLeftAlignment then
    if Result and DT_RIGHT = DT_RIGHT then
      Result := Result and not DT_RIGHT { removing DT_RIGHT, makes it DT_LEFT }
    else if not (Result and DT_CENTER = DT_CENTER) then
      Result := Result or DT_RIGHT;
  Result := Result or DrawTextBiDiModeFlagsReadingOnly;
end;

function TSeSkinObject.DrawTextBiDiModeFlagsReadingOnly: Longint;
begin
  if UseRightToLeftReading then
    Result := DT_RTLREADING
  else
    Result := 0;
end;

function TSeSkinObject.UseRightToLeftAlignment: Boolean;
begin
  Result := SysLocale.MiddleEast and (BiDiMode <> bdLeftToRight);
end;

function TSeSkinObject.UseRightToLeftReading: Boolean;
begin
  Result := SysLocale.MiddleEast and (BiDiMode = bdRightToLeft);
end;

{ Aligning ====================================================================}

procedure TSeSkinObject.Aligning;
var
  i: integer;
  R: TRect;
begin
  R := BoundsRect;
  { most top & most bottom align }
  for i := 0 to Count-1 do
  begin
    if (Objects[i].Align = saMostTop) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Left, R.Top, R.Right, R.Top+Height);
      Inc(R.Top, Height);
    end;
    if (Objects[i].Align = saMostBottom) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Left, R.Bottom-Height, R.Right, R.Bottom);
      Dec(R.Bottom, Height);
    end;
  end;
  { most left & right align }
  for i := 0 to Count-1 do
  begin
    if (Objects[i].Align = saMostLeft) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Left, R.Top, R.Left+Width, R.Bottom);
      Inc(R.Left, Width);
    end;
    if (Objects[i].Align = saMostRight) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Right-Width, R.Top, R.Right, R.Bottom);
      Dec(R.Right, Width);
    end;
  end;
  { top & bottom align }
  for i := 0 to Count-1 do
  begin
    if (Objects[i].Align = saTop) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Left, R.Top, R.Right, R.Top+Height);
      Inc(R.Top, Height);
    end;
    if (Objects[i].Align = saBottom) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Left, R.Bottom-Height, R.Right, R.Bottom);
      Dec(R.Bottom, Height);
    end;
  end;
  { left & right align }
  for i := 0 to Count-1 do
  begin
    if (Objects[i].Align = saLeft) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Left, R.Top, R.Left+Width, R.Bottom);
      Inc(R.Left, Width);
    end;
    if (Objects[i].Align = saRight) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Right-Width, R.Top, R.Right, R.Bottom);
      Dec(R.Right, Width);
    end;
  end;
  { text align }
{  for i := Count-1 Downto 0 do
    if (Objects[i].FVisible) and (Objects[i].Align = saText) then
    with Objects[i] do
    begin
      if not FMakeTextAlign then
      begin
        FMakeTextAlign := true;
        try
          Self.Width := R.left + (Self.FWidth - R.Right) + Width;
        finally
          FMakeTextAlign := false;
        end;
      end
      else
      begin
        BoundsRect := Rect(R.Left, R.Top, R.Right, R.Bottom);
      end;
    end;}
  { center align }
  for i := Count-1 Downto 0 do
    if (Objects[i].Align = saCenter) and (Objects[i].FVisible) then
    with Objects[i] do
    begin
      BoundsRect := Rect(R.Left + (R.Right-R.left-Width) div 2,
        R.Top + (R.Bottom-R.Top-Height) div 2,
        R.Left + (R.Right-R.Left-Width) div 2 + FWidth,
        R.Top + (R.Bottom-R.Top-Height) div 2 + FHeight);
    end;
  { client align }
  for i := 0 to Count-1 do
    if (Objects[i].Align = saClient) and (Objects[i].FVisible) then
      Objects[i].BoundsRect := Rect(R.Left, R.Top, R.Right, R.Bottom);
  { other align }
  for i := 0 to Count-1 do
  begin
    if Objects[i].Align = saTopRight then
      Objects[i].FLeft := FWidth - (FOldWidth - Objects[i].FLeft);
    if Objects[i].Align = saBottomLeft then
      Objects[i].FTop := FHeight - (FOldHeight - Objects[i].FTop);
    if Objects[i].Align = saBottomRight then
    begin
      Objects[i].FLeft := FWidth - Objects[i].FLeft;
      Objects[i].FTop := FHeight - Objects[i].FTop;
    end;
  end;
end;

procedure TSeSkinObject.Invalidate;
begin
  if not Visible then Exit;

  if FParentControl <> nil then
    SendMessage(FParentControl.Handle, WM_INVALIDATESKINOBJECT, 0, Integer(Self));
end;

{ Drawing =====================================================================}

procedure TSeSkinObject.Draw(Buffer: TKsBitmap);
var
  i: integer;
  Child: TSeSkinObject;
begin
  if not Visible then Exit;
  if FWidth <= 0 then Exit;
  if FHeight <= 0 then Exit;

  if (Count = 0) or ((Count > 0) and (FDrawIfOwner)) then
  begin
    Buffer.FillRect(BoundsRect, KColor(FColor));
    DrawText(Buffer);
  end;

  DrawChild(Buffer);
end;

procedure TSeSkinObject.DrawChild(Buffer: TKsBitmap);
var
  Child: TSeSkinObject;
  i: integer;
begin
  if Count > 0 then
  begin
    { Draw childs }
    for i := 0 to Count-1 do
    begin
      Child := Objects[i];
      if (Child.Visible) and (Child.Width > 0) and (Child.Height > 0) then
      begin
        Child.Draw(Buffer);
      end;
    end;
  end;
end;

procedure TSeSkinObject.DrawText(Buffer: TKsBitmap);
var
  SaveColor: TColor;
  Flags: integer;
  R: TRect;
  S: string;
begin
  if FText = '-' then
  begin
    { Separator }
    R := BoundsRect;
    InflateRect(R, -3, -(RectHeight(R) div 2 - 1));
    R.Bottom := R.Top + 1;
    Buffer.FillRect(R, KColor(GetFont.Color));

    Exit;
  end;
  { Draw text }
  if FText <> '' then
  begin
    case FTextAlign of
      taTopLeft: Flags := DT_SINGLELINE or DT_TOP or DT_LEFT;
      taTopCenter: Flags := DT_SINGLELINE or DT_TOP or DT_CENTER;
      taTopRight: Flags := DT_SINGLELINE or DT_TOP or DT_RIGHT;
      taLeft: Flags := DT_SINGLELINE or DT_VCENTER or DT_LEFT;
      taCenter: Flags := DT_SINGLELINE or DT_VCENTER or DT_CENTER;
      taRight: Flags := DT_SINGLELINE or DT_VCENTER or DT_RIGHT;
      taBottomLeft: Flags := DT_SINGLELINE or DT_BOTTOM or DT_LEFT;
      taBottomCenter: Flags := DT_SINGLELINE or DT_BOTTOM or DT_CENTER;
      taBottomRight: Flags := DT_SINGLELINE or DT_BOTTOM or DT_RIGHT;
    else
      Flags := DT_SINGLELINE or DT_CENTER or DT_VCENTER;
    end;
    R := BoundsRect;
    Inc(R.Left, FTextMarginLeft);
    Dec(R.Right, FTextMarginRight);
    Inc(R.Top, FTextMarginTop);
    { Set font }
    Buffer.Font := FFont;
    if not FEnabled then
      Buffer.Font.Color := clGray;
    { Set text }
    if FKind = skTitle then
      S := FormatStr(Buffer, FText, FWidth - FTextMarginLeft - FTextMarginRight)
    else
      S := FText;

    Flags := DrawTextBiDiModeFlags(Flags);

    { Draw text }
    case FTextEffect of
      teNone: Buffer.DrawText(S, R, Flags);
      teShadow: begin
        { Draw shadow }
        SaveColor := Buffer.Font.Color;
        Buffer.Font.Color := clBlack;
        OffsetRect(R, 1, 1);
        Buffer.DrawText(S, R, Flags);
        { Draw text }
        Buffer.Font.Color := SaveColor;
        OffsetRect(R, -1, -1);
        Buffer.DrawText(S, R, Flags);
      end;
    end;
  end;

  if FRightText <> '' then
  begin
    { Draw FRightText }
    case FTextAlign of
      taTopLeft: Flags := DT_SINGLELINE or DT_TOP or DT_RIGHT;
      taTopCenter: Flags := DT_SINGLELINE or DT_TOP or DT_CENTER;
      taTopRight: Flags := DT_SINGLELINE or DT_TOP or DT_LEFT;
      taLeft: Flags := DT_SINGLELINE or DT_VCENTER or DT_RIGHT;
      taCenter: Flags := DT_SINGLELINE or DT_VCENTER or DT_CENTER;
      taRight: Flags := DT_SINGLELINE or DT_VCENTER or DT_LEFT;
      taBottomLeft: Flags := DT_SINGLELINE or DT_BOTTOM or DT_RIGHT;
      taBottomCenter: Flags := DT_SINGLELINE or DT_BOTTOM or DT_CENTER;
      taBottomRight: Flags := DT_SINGLELINE or DT_BOTTOM or DT_LEFT;
    else
      Flags := DT_SINGLELINE or DT_CENTER or DT_VCENTER;
    end;
    R := BoundsRect;
    Inc(R.Left, FTextMarginLeft);
    Dec(R.Right, FTextMarginRight);
    Inc(R.Top, FTextMarginTop);

    if FParentControl <> nil then
      Flags := FParentControl.DrawTextBiDiModeFlags(Flags);

    Buffer.DrawText(FRightText, R, Flags)
  end;
end;

{ Font }

function TSeSkinObject.GetFont: TFont;
begin
  Result := FFont;
end;

{ Colors }

procedure TSeSkinObject.ChangeHue(DeltaHue: integer);
var
  i: integer;
begin
  FColor := KColorToColor(KsBitmap.ChangeHue(KColor(FColor), DeltaHue));
  FFont.Color := KColorToColor(KsBitmap.ChangeHue(KColor(FFont.Color), DeltaHue));

  if Count > 0 then
    for i := 0 to Count - 1 do
      Objects[i].ChangeHue(DeltaHue);
end;

{ Region ======================================================================}

function TSeSkinObject.GetRegion: HRgn;
var
  i: integer;
  ChildMask: HRgn;
  AllChildMask: HRgn;
begin
  if (FWidth <= 0) or (FHeight <= 0) then
  begin
    Result := 0;
    Exit;
  end;

  { Add child mask }
  if Count > 0 then
  begin
    if DrawIfOwner then
    begin
      Result := CreateRegion;
      Exit;
    end;

    Result := CreateRectRgn(0, 0, 0, 0);

    for i := 0 to Count-1 do
    begin
      if not Objects[i].Visible then Continue;

      ChildMask := Objects[i].GetRegion;
      if ChildMask <> 0 then
      begin
        CombineRgn(Result, Result, ChildMask, RGN_OR);
        DeleteObject(ChildMask);
      end;
    end;
  end
  else
    Result := CreateRegion;
end;

function TSeSkinObject.CreateRegion: HRgn;
begin
  Result := CreateRectRgn(FLeft, FTop, FLeft + FWidth, FTop + FHeight);
end;

{ Events ======================================================================}

procedure TSeSkinObject.MouseHover;
begin
end;

procedure TSeSkinObject.MouseLeave;
begin
end;

procedure TSeSkinObject.MouseDouble(Button: TMouseButton; X, Y: integer);
begin

end;

procedure TSeSkinObject.MouseDown(Button: TMouseButton; X, Y: integer);
begin

end;

procedure TSeSkinObject.MouseMove(Shift: TShiftState; X, Y: integer);
begin

end;

procedure TSeSkinObject.MouseUp(Button: TMouseButton; X, Y: integer);
begin

end;

{ Children ====================================================================}

procedure TSeSkinObject.SetCharset(CharSet: TFontCharset);
var
  i: integer;
begin
  Font.Charset := CharSet;

  if Count > 0 then
    for i := 0 to Count - 1 do
      Objects[i].SetCharSet(CharSet);
end;

procedure TSeSkinObject.Add(SkinObject: TSeSkinObject);
begin
  InsertComponent(SkinObject);
end;

procedure TSeSkinObject.Remove(SkinObject: TSeSkinObject);
begin
  RemoveComponent(SkinObject);
end;

function TSeSkinObject.FindObjectByKind(AKind: TSeKind): TSeSkinObject;
var
  i: integer;
begin
  Result := nil;
  if FKind = AKind then
  begin
    Result := Self;
    Exit;
  end;
  if Count = 0 then Exit;
  for i := 0 to Count-1 do
  begin
    Result := Objects[i].FindObjectByKind(AKind);
    if Result <> nil then
      Break;
  end;
end;

function TSeSkinObject.FindObjectByName(AName: string): TSeSkinObject;
var
  i: integer;
begin
  Result := nil;
  if LowerCase(Name) = LowerCase(AName) then
  begin
    Result := Self;
    Exit;
  end;
  if Count = 0 then Exit;
  for i := 0 to Count-1 do
  begin
    Result := Objects[i].FindObjectByName(AName);
    if Result <> nil then
      Break;
  end;
end;

function TSeSkinObject.FindObjectByPoint(Point: TPoint): TSeSkinObject;
var
  i: integer;
  SkinObject: TSeSkinObject;
begin
  Result := nil;
  if not FVisible then
    Exit;
  if not PtInRect(BoundsRect, Point) then
    Exit;
  if (FKind = skTranparent) and not DesignMode then
    Exit;

  if Count = 0 then
  begin
    if PtInRect(BoundsRect, Point) then
      Result := Self;
  end
  else
  begin
    for i := 0 to Count-1 do
    begin
      SkinObject := Objects[i].FindObjectByPoint(Point);
      if SkinObject <> nil then
      begin
        Result := SkinObject;
      end;
    end;
    if Result = nil then
      Result := Self;
  end;
end;

{ Custom property =============================================================}

procedure TSeSkinObject.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineBinaryProperty('Objects', ReadData, WriteData, true);
end;

procedure TSeSkinObject.ReadData(Stream: TStream);
var
  i, Count: integer;
begin
  { Load Count }
  Stream.Read(Count, SizeOf(Integer));
  { Load Objects }
  for i := 0 to Count-1 do
    LoadSkinObject(Stream, Self);
end;

procedure TSeSkinObject.WriteData(Stream: TStream);
var
  i, Count: integer;
begin
  Count := GetCount;
  { Save Count }
  Stream.Write(Count, SizeOf(Integer));
  { Load Objects }
  for i := 0 to Count-1 do
    SaveSkinObject(Stream, Objects[i]);
end;

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

procedure TSeSkinObject.SetBoundsRect(const Value: TRect);
begin
  if (not DesignMode) and (Value.Left = FLeft) and (Value.Top = FTop) and (Value.Right = FLeft + FWidth) and
     (Value.Bottom = FTop + FHeight)
  then
  begin
    FOldWidth := FOldWidth;
    Exit;
  end;

  FOldWidth := FWidth;
  FOldHeight := FHeight;

  FLeft := Value.Left;
  FTop := Value.Top;
  FWidth := Value.Right - Value.Left;
  FHeight := Value.Bottom - Value.Top;
  if FWidth < 0 then FWidth := 0;
  if FHeight < 0 then FHeight := 0;

  Aligning;
end;

function TSeSkinObject.GetCount: integer;
begin
  Result := ComponentCount;
end;

function TSeSkinObject.GetObject(index: integer): TSeSkinObject;
begin
  if (index >= 0) and (index < Count) then
    Result := TSeSkinObject(Components[index])
  else
    Result := nil;
end;

function TSeSkinObject.GetBoundsRect: TRect;
begin
  Result := Rect(FLeft, FTop, FLeft + FWidth, FTop + FHeight);
end;

procedure TSeSkinObject.SetHeight(const Value: integer);
begin
  BoundsRect := Rect(FLeft, FTop, FLeft + FWidth, FTop + Value);
end;

procedure TSeSkinObject.SetWidth(const Value: integer);
begin
  BoundsRect := Rect(FLeft, FTop, FLeft + Value, FTop + FHeight);
end;

procedure TSeSkinObject.SetLeft(const Value: integer);
begin
  BoundsRect := Rect(Value, FTop, Value + FWidth, FTop + FHeight);
end;

procedure TSeSkinObject.SetTop(const Value: integer);
begin
  BoundsRect := Rect(FLeft, Value, FLeft + FWidth, Value + FHeight);
end;

procedure TSeSkinObject.SetBitmaps(const Value: TKsBitmapList);
var
  i: integer;
begin
  FBitmaps := Value;

  if Count = 0 then Exit;

  for i := 0 to Count-1 do
    Objects[i].Bitmaps := Value;
end;

procedure TSeSkinObject.SetBiDiMode(const Value: TBiDiMode);
var
  i: integer;
begin
  FBiDiMode := Value;

  if Count = 0 then Exit;

  for i := 0 to Count-1 do
    Objects[i].BiDiMode := Value;
end;

procedure TSeSkinObject.SetFont(const Value: TFont);
begin
  FFont.Assign(Value);
end;

procedure TSeSkinObject.SetActive(const Value: boolean);
var
  i: integer;
begin
  if FActive <> Value then
  begin
    FActive := Value;

    if Count = 0 then Exit;

    for i := 0 to Count-1 do
      Objects[i].Active := Value;
  end;
end;

procedure TSeSkinObject.SetParentControl(const Value: TWinControl);
var
  i: integer;
begin
  FParentControl := Value;

  if FParentControl <> nil then
    BiDiMode := FParentControl.BiDiMode;
    
  if Count = 0 then Exit;

  for i := 0 to Count-1 do
    Objects[i].ParentControl := Value;
end;

procedure TSeSkinObject.SetState(const Value: TSeState);
var
  i: integer;
begin
  begin
    FState := Value;

    if Count = 0 then Exit;

    for i := 0 to Count-1 do
      Objects[i].State := Value;
  end;
end;

procedure TSeSkinObject.SetMarginBottom(const Value: integer);
begin
  FMarginBottom := Value;
end;

procedure TSeSkinObject.SetMarginLeft(const Value: integer);
begin
  FMarginLeft := Value;
end;

procedure TSeSkinObject.SetMarginRight(const Value: integer);
begin
  FMarginRight := Value;
end;

procedure TSeSkinObject.SetMarginTop(const Value: integer);
begin
  FMarginTop := Value;
end;



{ TSeActiveObject =========================================================}



constructor TSeActiveObject.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FActiveFont := TFont.Create;
end;

destructor TSeActiveObject.Destroy;
begin
  FActiveFont.Free;
  inherited Destroy;
end;

procedure TSeActiveObject.Assign(Source: TPersistent);
begin
  if Source is TSeActiveObject then
  begin
    inherited Assign(Source);
    ActiveColor := (Source as TSeActiveObject).ActiveColor;
    ActiveFont := (Source as TSeActiveObject).ActiveFont;
  end
  else
    inherited;
end;

procedure TSeActiveObject.ChangeHue(DeltaHue: integer);
begin
  inherited;
  FActiveColor := KColorToColor(KsBitmap.ChangeHue(KColor(FActiveColor), DeltaHue));
  FActiveFont.Color := KColorToColor(KsBitmap.ChangeHue(KColor(FActiveFont.Color), DeltaHue));
end;

function TSeActiveObject.GetFont: TFont;
begin
  case FState of
    ssHot, ssFocused: Result := FActiveFont;
  else
    Result := inherited GetFont;
  end;
end;

procedure TSeActiveObject.Draw(Buffer: TKsBitmap);
var
  SaveColor: TColor;
  SaveFont: TFont;
begin
  if FWidth <= 0 then Exit;
  if FHeight <= 0 then Exit;

  if (FActive) or (FState in [ssFocused, ssHot]) then
  begin
    SaveFont := FFont;
    SaveColor := FColor;
    try
      FColor := FActiveColor;
      FFont := FActiveFont;
      inherited Draw(Buffer);
    finally
      FFont := SaveFont;
      FColor := SaveColor;
    end;
  end
  else
    inherited Draw(Buffer);
end;

procedure TSeActiveObject.SetActiveFont(const Value: TFont);
begin
  FActiveFont.Assign(Value);
end;



{ TSeBitmapObject =========================================================}



constructor TSeBitmapObject.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FBitmap := TKsBitmapLink.Create;
end;

destructor TSeBitmapObject.Destroy;
begin
  FBitmap.Free;
  inherited Destroy;
end;

procedure TSeBitmapObject.AfterLoad;
var
  NewLink: TKsBitmapLink;
begin
  inherited AfterLoad;
  if FBitmaps <> nil then
  begin
    NewLink := FBitmaps.GetBitmapLink(FBitmap.Name, FBitmap.Rect);
    if NewLink <> nil then
    begin
      FBitmap.Free;
      FBitmap := NewLink;
    end;
  end;
end;

procedure TSeBitmapObject.Assign(Source: TPersistent);
begin
  if Source is TSeBitmapObject then
  begin
    inherited Assign(Source);
    Bitmap := (Source as TSeBitmapObject).Bitmap;
    FTileStyle := (Source as TSeBitmapObject).FTileStyle;
    BorderTileStyle := (Source as TSeBitmapObject).FBorderTileStyle;
  end
  else
    inherited;
end;

procedure TSeBitmapObject.DrawRect(Buffer: TKsBitmap; MarginRect, MarginDstRect: TRect;
  ATileStyle: TscTileStyle);
var
  DstRect, R: TRect;
  i, j: integer;
  W, H: integer;
  DW, DH: integer;
begin
  W := RectWidth(MarginRect);
  H := RectHeight(MarginRect);
  if W * H = 0 then Exit;

  FBitmap.Bitmap.Blend := true;

  case ATileStyle of
    tsTile: begin
      OffsetRect(MarginDstRect, Left, Top);
      FBitmap.Bitmap.DrawTile(Buffer, MarginDstRect, MarginRect);
    end;
    tsStretch: begin
      OffsetRect(MarginDstRect, Left, Top);
      FBitmap.Bitmap.DrawStretch(Buffer, MarginDstRect, MarginRect);
    end;
    tsCenter: begin
      R := BoundsRect;
      FBitmap.Draw(Buffer, Left + (Width - RectWidth(R)) div 2,
        Top + (Height - RectHeight(R)) div 2);
    end;
  end;
  FBitmap.Bitmap.Blend := false;
end;

procedure TSeBitmapObject.DrawNormal(Buffer: TKsBitmap);
var
  DstRect, R: TRect;
  i, j: integer;
  W, H: integer;
  DW, DH: integer;
begin
  if not FBitmap.Assigned then
  begin
    inherited Draw(Buffer);
    Exit;
  end;

  if FWidth <= 0 then Exit;
  if FHeight <= 0 then Exit;

  case FTileStyle of
    tsTile: begin
      FBitmap.Bitmap.Blend := true;
      FBitmap.Bitmap.DrawTile(Buffer, BoundsRect, FBitmap.Rect);
      FBitmap.Bitmap.Blend := false;
    end;
    tsStretch: begin
      FBitmap.Bitmap.Blend := true;
      FBitmap.Bitmap.Draw(Buffer, BoundsRect, FBitmap.Rect);
      FBitmap.Bitmap.Blend := false;
    end;
    tsCenter: begin
      R := FBitmap.Rect;
      FBitmap.Bitmap.Blend := true;
      FBitmap.Draw(Buffer, Left + (Width - RectWidth(R)) div 2,
        Top + (Height - RectHeight(R)) div 2);
      FBitmap.Bitmap.Blend := false;
    end;
  end;

  DrawText(Buffer);
  Aligning;
  DrawChild(Buffer);
end;

procedure TSeBitmapObject.Draw(Buffer: TKsBitmap);
var
  SrcRect, DstRect: TRect;
begin
  if not FBitmap.Assigned then Exit;
  if FWidth <= 0 then Exit;
  if FHeight <= 0 then Exit;

  if (MarginLeft = 0) and (MarginTop = 0) and
     (MarginRight = 0) and (MarginBottom = 0) then
  begin
    DrawNormal(Buffer);
    Exit;
  end;

  { Draw Face }

  FBitmap.Bitmap.Blend := true;

  { Draw Center Rect }
  with FBitmap.Rect do
    SrcRect := Rect(Left + FMarginLeft, Top + FMarginTop, Right - FMarginRight,
      Bottom - FMarginBottom);
  DstRect := Rect(FMarginLeft, FMarginTop, FWidth - FMarginRight, FHeight - FMarginBottom);
  DrawRect(Buffer, SrcRect, DstRect, FTileStyle);

  { Draw Top Border }
  with FBitmap.Rect do
    SrcRect := Rect(Left + FMarginLeft, Top, Right - FMarginRight,
      Top + FMarginTop);
  DstRect := Rect(FMarginLeft, 0, FWidth - FMarginRight, FMarginTop);
  DrawRect(Buffer, SrcRect, DstRect, FBorderTileStyle);

  { Draw Bottom Border }
  with FBitmap.Rect do
    SrcRect := Rect(Left + FMarginLeft, Bottom - FMarginBottom, Right - FMarginRight,
      Bottom);
  DstRect := Rect(FMarginLeft, FHeight - FMarginBottom, FWidth - FMarginRight, FHeight);
  DrawRect(Buffer, SrcRect, DstRect, FBorderTileStyle);

  { Draw Left Border }
  with FBitmap.Rect do
    SrcRect := Rect(Left, Top + FMarginTop, Left + FMarginLeft,
      Bottom - FMarginBottom);
  DstRect := Rect(0, FMarginTop, FMarginLeft, FHeight - FMarginBottom);
  DrawRect(Buffer, SrcRect, DstRect, FBorderTileStyle);

  { Draw Right Border }
  with FBitmap.Rect do
    SrcRect := Rect(Right - FMarginRight, Top + FMarginTop, Right,
      Bottom - FMarginBottom);
  DstRect := Rect(FWidth - FMarginRight, FMarginTop, FWidth,
    FHeight - FMarginBottom);
  DrawRect(Buffer, SrcRect, DstRect, FBorderTileStyle);

  FBitmap.Bitmap.Blend := true;
  
  { Draw Angles }
  with FBitmap.Rect do
    SrcRect := Rect(Left, Top, Left + FMarginLeft, Top + FMarginTop);
  FBitmap.Bitmap.Draw(Buffer, Left, Top, SrcRect);
  with FBitmap.Rect do
    SrcRect := Rect(Right - FMarginRight, Top, Right, Top + FMarginTop);
  FBitmap.Bitmap.Draw(Buffer, Left + FWidth - FMarginRight, Top, SrcRect);

  with FBitmap.Rect do
    SrcRect := Rect(Left, Bottom - FMarginBottom, Left + FMarginLeft, Bottom);
  FBitmap.Bitmap.Draw(Buffer, Left, Top + FHeight - FMarginBottom, SrcRect);

  with FBitmap.Rect do
    SrcRect := Rect(Right - FMarginRight, Bottom - FMarginBottom, Right, Bottom);
  FBitmap.Bitmap.Draw(Buffer, Left + FWidth - FMarginRight,
    Top + FHeight - FMarginBottom, SrcRect);

  FBitmap.Bitmap.Blend := false;

  { Draw Text }
  DrawText(Buffer);
  Aligning;
  DrawChild(Buffer);
end;

function TSeBitmapObject.CreateRegion: HRgn;
var
  TempImage: TKsBitmap;
  SaveRect: TRect;
begin
  if not FBitmap.Assigned then Exit;
  if FWidth <= 0 then Exit;
  if FHeight <= 0 then Exit;
  if FKind = skClient then
  begin
    Result := CreateRectRgn(FLeft, FTop, FLeft + FWidth, FTop + FHeight);
    Exit;
  end;

  { Make mask from bitmap }
  SaveRect := BoundsRect;
  try
    { Set new rect }
    BoundsRect := Rect(0, 0, FWidth, FHeight);
    { Draw to TempImage }
    TempImage := TKsBitmap.Create;
    try
      TempImage.SetSize(FWidth, FHeight);
      TempImage.Clear(ckTransparent);
      { Draw to Temp }
      Draw(TempImage);
      { Create region }
      Result := CreateRegionFromBitmap(TempImage, SaveRect.Left, SaveRect.Top);
    finally
      TempImage.Free;
    end;
  finally
    BoundsRect := SaveRect;
  end;
end;

procedure TSeBitmapObject.SetBitmap(const Value: TKsBitmapLink);
begin
  FBitmap.Assign(Value);
end;

procedure TSeBitmapObject.SetBorderTileStyle(const Value: TscTileStyle);
begin
  FBorderTileStyle := Value;
end;



{ TSeActiveBitmap =============================================================}



constructor TSeActiveBitmap.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FActiveBitmap := TKsBitmapLink.Create;
  FActiveFont := TFont.Create;
end;

destructor TSeActiveBitmap.Destroy;
begin
  FActiveFont.Free;
  FActiveBitmap.Free;
  inherited Destroy;
end;

procedure TSeActiveBitmap.AfterLoad;
var
  NewLink: TKsBitmapLink;
begin
  inherited AfterLoad;
  if FBitmaps <> nil then
  begin
    NewLink := FBitmaps.GetBitmapLink(FActiveBitmap.Name, FActiveBitmap.Rect);
    if NewLink <> nil then
    begin
      FActiveBitmap.Free;
      FActiveBitmap := NewLink;
    end;
  end;
end;

procedure TSeActiveBitmap.Assign(Source: TPersistent);
begin
  if Source is TSeActiveBitmap then
  begin
    inherited Assign(Source);
    ActiveBitmap := (Source as TSeActiveBitmap).ActiveBitmap;
    ActiveFont := (Source as TSeActiveBitmap).ActiveFont;
  end
  else
    inherited;
end;

function TSeActiveBitmap.GetFont: TFont;
begin
  case FState of
    ssHot, ssFocused: Result := FActiveFont;
  else
    Result := inherited GetFont;
  end;
end;

procedure TSeActiveBitmap.Draw(Buffer: TKsBitmap);
var
  SaveBitmap: TKsBitmapLink;
begin
  if ((FActive) or (FState in [ssFocused, ssHot])) and FActiveBitmap.Assigned then
  begin
    SaveBitmap := FBitmap;
    try
      FBitmap := FActiveBitmap;
      inherited Draw(Buffer);
    finally
      FBitmap := SaveBitmap;
    end;
  end
  else
    inherited Draw(Buffer);
end;

procedure TSeActiveBitmap.SetActiveBitmap(const Value: TKsBitmapLink);
begin
  FActiveBitmap.Assign(Value);
end;

procedure TSeActiveBitmap.SetActiveFont(const Value: TFont);
begin
  FActiveFont.Assign(Value);
end;



{ TSeSystemButton }



constructor TSeSystemButton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FKind := skSysButton;

  FBitmapHot := TKsBitmapLink.Create;
  FBitmapPressed := TKsBitmapLink.Create;
end;

destructor TSeSystemButton.Destroy;
begin
  FBitmapHot.Free;
  FBitmapPressed.Free;
  inherited Destroy;
end;

procedure TSeSystemButton.AfterLoad;
var
  NewLink: TKsBitmapLink;
begin
  inherited AfterLoad;
  if FBitmaps <> nil then
  begin
    { Hover }
    NewLink := FBitmaps.GetBitmapLink(FBitmapHot.Name, FBitmapHot.Rect);
    if NewLink <> nil then
    begin
      FBitmapHot.Free;
      FBitmapHot := NewLink;
    end;
    { Down }
    NewLink := FBitmaps.GetBitmapLink(FBitmapPressed.Name, FBitmapPressed.Rect);
    if NewLink <> nil then
    begin
      FBitmapPressed.Free;
      FBitmapPressed := NewLink;
    end;
  end;
end;

procedure TSeSystemButton.Assign(Source: TPersistent);
begin
  if Source is TSeSystemButton then
  begin
    inherited Assign(Source);
    BitmapHot := (Source as TSeSystemButton).BitmapHot;
    BitmapPressed := (Source as TSeSystemButton).BitmapPressed;
    FAction := (Source as TSeSystemButton).FAction;
  end
  else
    inherited;
end;

procedure TSeSystemButton.SetState(const Value: TSeState);
var
  SkinForm: TSeSkinForm;
begin
  inherited SetState(Value);

  SkinForm := TSeSkinForm(GetSkinForm);

  if SkinForm = nil then Exit;

  case FState of
    ssNormal: begin
      Visible := true;
      if FAction = sbaRestore then Visible := false;
      if FAction = sbaRollDown then Visible := false;
    end;
    ssMaximized: begin
      Visible := true;
      if FAction = sbaMaximize then Visible := false;
      if FAction = sbaRollDown then Visible := false;
    end;
    ssMinimized: begin
      Visible := true;
      if FAction = sbaMinimize then Visible := false;
      if FAction = sbaMaximize then Visible := false;
      if FAction = sbaRollUp then Visible := false;
      if FAction = sbaRollDown then Visible := false;
    end;
    ssRollup: begin
      Visible := true;
      if FAction = sbaRestore then Visible := false;
      if FAction = sbaRollUp then Visible := false;
    end;
  end;
  
  case FAction of
    sbaNone: ;
    sbaClose: if not (kbiClose in SkinForm.BorderIcons) then Visible := false;
    sbaHelp: if not (kbiHelp in SkinForm.BorderIcons) then Visible := False;
    sbaMinimize: if not (kbiMinimize in SkinForm.BorderIcons) then Visible := False;
    sbaMaximize: if not (kbiMaximize in SkinForm.BorderIcons) then Visible := False;
    sbaRestore: if not (kbiMaximize in SkinForm.BorderIcons) then Visible := False;
    sbaRollUp: if not (kbiRollUp in SkinForm.BorderIcons) then Visible := False;
    sbaRollDown: if not (kbiRollUp in SkinForm.BorderIcons) then Visible := False;
    sbaTray: if not (kbiTray in SkinForm.BorderIcons) then Visible := False;
    sbaSysMenu: if not (kbiSystemMenu in SkinForm.BorderIcons) then Visible := False;
    sbaRollPanel: ;
  end;
end;

procedure TSeSystemButton.DoAction;
var
  SkinForm: TSeSkinForm;
  SkinObject: TSeSkinObject;
  OwnerObject: TSeSkinObject;
begin
  SkinForm := TSeSkinForm(GetSkinForm);
  case FAction of
    sbaClose: if SkinForm <> nil then begin
      SkinForm.Close;
    end;
    sbaHelp: if SkinForm <> nil then begin
      SkinForm.Help;
    end;
    sbaMinimize: if SkinForm <> nil then begin
      SkinForm.Minimize;
    end;
    sbaMaximize: if SkinForm <> nil then begin
      SkinForm.Maximize;
    end;
    sbaRestore: if SkinForm <> nil then begin
      SkinForm.Restore;
    end;
    sbaRollup: if SkinForm <> nil then begin
      SkinForm.Rollup;
    end;
    sbaRollDown: if SkinForm <> nil then begin
      SkinForm.Restore;
    end;
    sbaTray: if SkinForm <> nil then begin
      SkinForm.MinToTray;
    end;
    sbaSysMenu: if SkinForm <> nil then begin
      SkinForm.SysMenu;
    end;
    sbaCustom: if SkinForm <> nil then
      if Assigned(SkinForm.OnCustomAction) then SkinForm.OnCustomAction(Self);
  end;
end;

procedure TSeSystemButton.Draw(Buffer: TKsBitmap);
var
  SaveBitmap: TKsBitmapLink;
  IHandle, IHandle2 : HICON;
  IconX, IconY : integer;
  ID: boolean;
  R: TRect;
  SaveActive: boolean;
begin
  if FWidth <= 0 then Exit;
  if FHeight <= 0 then Exit;

  if FAction = sbaSysMenu then
  begin
    if FParentControl = nil then Exit;
    if not (FParentControl is TForm) then Exit;

    R := BoundsRect;

    ID := false;
    if TForm(FParentControl).Icon.Handle <> 0 then
      IHandle := TForm(FParentControl).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);
    Exit;
  end;

  if (not Active) then
  begin
    try
      SetChildState(ssDisabled);
      inherited Draw(Buffer);
    finally
      SetChildState(ssNormal);
    end;
  end
  else
    case FButtonState of
      bsNormal: begin
        inherited Draw(Buffer);
      end;
      bsHover: begin
        SaveBitmap := FBitmap;
        try
          if FBitmapHot.Assigned then
            FBitmap := FBitmapHot;
          SaveActive := FActive;
          try
            Active := false;
            SetChildState(ssHot);
            inherited Draw(Buffer);
          finally
            Active := SaveActive;
            SetChildState(ssNormal);
          end;
        finally
          FBitmap := SaveBitmap;
        end;
      end;
      bsDown: begin
        SaveBitmap := FBitmap;
        try
          if FBitmapPressed.Assigned then
            FBitmap := FBitmapPressed;

          SaveActive := FActive;
          try
            Active := false;
            SetChildState(ssPressed);
            inherited Draw(Buffer);
          finally
            SetChildState(ssNormal);
            Active := SaveActive;
          end;
        finally
          FBitmap := SaveBitmap;
        end;
      end;
    end;
end;

procedure TSeSystemButton.SetChildState(const Value: TSeState);
var
  i: integer;
begin
  if Count = 0 then Exit;

  for i := 0 to Count-1 do
    Objects[i].State := Value;
end;

procedure TSeSystemButton.MouseHover;
begin
  if ssLeft in FShift then
    FButtonState := bsDown
  else
    FButtonState := bsHover;
  Invalidate;
end;

procedure TSeSystemButton.MouseLeave;
begin
  FButtonState := bsNormal;
  Invalidate;
end;

procedure TSeSystemButton.MouseDouble(Button: TMouseButton; X, Y: integer);
var
  SkinForm: TSeSkinForm;
  SkinObject: TSeSkinObject;
  OwnerObject: TSeSkinObject;
begin
  if (Button = mbLeft) and (FAction = sbaSysMenu) then
  begin
    SkinForm := TSeSkinForm(GetSkinForm);
    if SkinForm <> nil then
      SkinForm.Close;
  end;
end;

procedure TSeSystemButton.MouseDown(Button: TMouseButton; X,
  Y: integer);
begin
  if Button = mbLeft then
  begin
    FButtonState := bsDown;
    Invalidate;
  end;
end;

procedure TSeSystemButton.MouseMove(Shift: TShiftState; X,
  Y: integer);
var
  Pos: TPoint;
begin
  if (FButtonState = bsDown) and (ssLeft in Shift) then
  begin
    FButtonState := bsDown;
    Invalidate;
  end
  else
  begin
    { Show Hint }
    Pos := Point(Left, Top);
    if FParentControl <> nil then
      Pos := FParentControl.ClientToScreen(Pos);
    {$IFDEF SE_COMPILER5}
    Application.ActivateHint(Pos);
    {$ENDIF}
  end;
  FShift := Shift;
end;

procedure TSeSystemButton.MouseUp(Button: TMouseButton; X,
  Y: integer);
begin
  if (FButtonState = bsDown) and (Button = mbLeft) then
  begin
    FButtonState := bsHover;
    Invalidate;

    DoAction;
  end;
  FShift := [];
end;

procedure TSeSystemButton.SetAction(const Value: TSeAction);
begin
  FAction := Value;
end;

procedure TSeSystemButton.SetBitmapPressed(const Value: TKsBitmapLink);
begin
  FBitmapPressed.Assign(Value);
end;

procedure TSeSystemButton.SetBitmapHot(const Value: TKsBitmapLink);
begin
  FBitmapHot.Assign(Value);
end;



{ TSeButtonObject =============================================================}



constructor TSeButtonObject.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FBitmapFocused := TKsBitmapLink.Create;
  FBitmapHot := TKsBitmapLink.Create;
  FBitmapPressed := TKsBitmapLink.Create;
  FBitmapDisabled := TKsBitmapLink.Create;
end;

destructor TSeButtonObject.Destroy;
begin
  FBitmapFocused.Free;
  FBitmapHot.Free;
  FBitmapPressed.Free;
  FBitmapDisabled.Free;
  inherited Destroy;
end;

procedure TSeButtonObject.AfterLoad;
var
  NewLink: TKsBitmapLink;
begin
  inherited AfterLoad;
  if FBitmaps <> nil then
  begin
    { Focused }
    NewLink := FBitmaps.GetBitmapLink(FBitmapFocused.Name, FBitmapFocused.Rect);
    if NewLink <> nil then
    begin
      FBitmapFocused.Free;
      FBitmapFocused := NewLink;
    end;
    { Hot }
    NewLink := FBitmaps.GetBitmapLink(FBitmapHot.Name, FBitmapHot.Rect);
    if NewLink <> nil then
    begin
      FBitmapHot.Free;
      FBitmapHot := NewLink;
    end;
    { Pressed }
    NewLink := FBitmaps.GetBitmapLink(FBitmapPressed.Name, FBitmapPressed.Rect);
    if NewLink <> nil then
    begin
      FBitmapPressed.Free;
      FBitmapPressed := NewLink;
    end;
    { Disabled }
    NewLink := FBitmaps.GetBitmapLink(FBitmapDisabled.Name, FBitmapDisabled.Rect);
    if NewLink <> nil then
    begin
      FBitmapDisabled.Free;
      FBitmapDisabled := NewLink;
    end;
  end;
end;

procedure TSeButtonObject.Assign(Source: TPersistent);
begin
  if Source is TSeButtonObject then
  begin
    inherited Assign(Source);
    BitmapFocused := (Source as TSeButtonObject).BitmapFocused;
    BitmapHot := (Source as TSeButtonObject).BitmapHot;
    BitmapDisabled := (Source as TSeButtonObject).BitmapDisabled;
    BitmapPressed := (Source as TSeButtonObject).BitmapPressed;
  end
  else
    inherited;
end;

procedure TSeButtonObject.Draw(Buffer: TKsBitmap);
var
  SaveBitmap: TKsBitmapLink;
begin
  if not FBitmap.Assigned then Exit;
  if FWidth <= 0 then Exit;
  if FHeight <= 0 then Exit;

  if (FState = ssFocused) and (FBitmapFocused.Assigned) then
  begin
    SaveBitmap := FBitmap;
    try
      FBitmap := FBitmapFocused;

      inherited Draw(Buffer);
    finally
      FBitmap := SaveBitmap;
    end;
    Exit;
  end;

  if (FState = ssHot) and (FBitmapHot.Assigned) then
  begin
    SaveBitmap := FBitmap;
    try
      FBitmap := FBitmapHot;

      inherited Draw(Buffer);
    finally
      FBitmap := SaveBitmap;
    end;
    Exit;
  end;

  if (FState = ssDisabled) and (FBitmapDisabled.Assigned) then
  begin
    SaveBitmap := FBitmap;
    try
      FBitmap := FBitmapDisabled;

      inherited Draw(Buffer);
    finally
      FBitmap := SaveBitmap;
    end;
    Exit;
  end;

  if (FState = ssPressed) and (FBitmapPressed.Assigned) then
  begin
    SaveBitmap := FBitmap;
    try
      FBitmap := FBitmapPressed;

      inherited Draw(Buffer);
    finally
      FBitmap := SaveBitmap;
    end;
    Exit;
  end;

  inherited Draw(Buffer);
end;

procedure TSeButtonObject.SetBitmapDisabled(const Value: TKsBitmapLink);
begin
  FBitmapDisabled.Assign(Value);
end;

procedure TSeButtonObject.SetBitmapFocused(const Value: TKsBitmapLink);
begin
  FBitmapFocused.Assign(Value);
end;

procedure TSeButtonObject.SetBitmapHot(const Value: TKsBitmapLink);
begin
  FBitmapHot.Assign(Value);
end;

procedure TSeButtonObject.SetBitmapPressed(const Value: TKsBitmapLink);
begin
  FBitmapPressed.Assign(Value);
end;

{ TSeTextObject }

constructor TSeTextObject.Create(AOwner: TComponent);
begin
  inherited;
  FFontHot := TFont.Create;
  FFontFocused := TFont.Create;
  FFontPressed := TFont.Create;
  FFontDisabled := TFont.Create;
end;

destructor TSeTextObject.Destroy;
begin
  FFontHot.Free;
  FFontFocused.Free;
  FFontPressed.Free;
  FFontDisabled.Free;
  inherited;

end;

procedure TSeTextObject.Assign(Source: TPersistent);
begin
  if Source is TSeTextObject then
  begin
    inherited Assign(Source);
    FontHot := (Source as TSeTextObject).FontHot;
    FontFocused := (Source as TSeTextObject).FontFocused;
    FontPressed := (Source as TSeTextObject).FontPressed;
    FontDisabled := (Source as TSeTextObject).FontDisabled;
  end
  else
    inherited;
end;

procedure TSeTextObject.ChangeHue(DeltaHue: integer);
begin
  inherited;
  FFontHot.Color := KColorToColor(KsBitmap.ChangeHue(KColor(FFontHot.Color), DeltaHue));
  FFontFocused.Color := KColorToColor(KsBitmap.ChangeHue(KColor(FFontFocused.Color), DeltaHue));
  FFontPressed.Color := KColorToColor(KsBitmap.ChangeHue(KColor(FFontPressed.Color), DeltaHue));
  FFontDisabled.Color := KColorToColor(KsBitmap.ChangeHue(KColor(FFontDisabled.Color), DeltaHue));
end;

procedure TSeTextObject.Draw(Buffer: TKsBitmap);
var
  SaveFont: TFont;
begin
  if FWidth <= 0 then Exit;
  if FHeight <= 0 then Exit;

  case FState of
    ssHot: begin
      SaveFont := FFont;
      try
        FFont := FFontHot;
        inherited Draw(Buffer);
      finally
        FFont := SaveFont;
      end;
    end;
    ssFocused: begin
      SaveFont := FFont;
      try
        FFont := FFontFocused;
        inherited Draw(Buffer);
      finally
        FFont := SaveFont;
      end;
    end;
    ssPressed: begin
      SaveFont := FFont;
      try
        FFont := FFontPressed;
        inherited Draw(Buffer);
      finally
        FFont := SaveFont;
      end;
    end;
    ssDisabled: begin
      SaveFont := FFont;
      try
        FFont := FFontDisabled;
        inherited Draw(Buffer);
      finally
        FFont := SaveFont;
      end;
    end;
  else
    inherited Draw(Buffer);
  end;
end;

function TSeTextObject.GetFont: TFont;
begin
  case FState of
    ssHot: Result := FFontHot;
    ssFocused: Result := FFontFocused;
    ssPressed: Result := FFontPressed;
    ssdisabled: Result := FFontDisabled;
  else
    Result := inherited GetFont;
  end;
end;

procedure TSeTextObject.SetFontDisabled(const Value: TFont);
begin
  FFontDisabled.Assign(Value);
end;

procedure TSeTextObject.SetFontFocused(const Value: TFont);
begin
  FFontFocused.Assign(Value);
end;

procedure TSeTextObject.SetFontHot(const Value: TFont);
begin
  FFontHot.Assign(Value);
end;

procedure TSeTextObject.SetFontPressed(const Value: TFont);
begin
  FFontPressed.Assign(Value);
end;

procedure TSeTextObject.SetCharset(CharSet: TFontCharset);
begin
  inherited;
  FFontHot.Charset := CharSet;
  FFontFocused.Charset := CharSet;
  FFontPressed.Charset := CharSet;
  FFontDisabled.Charset := CharSet;
end;

initialization
  RegisterSkinObject(TSeSkinObject);
  RegisterSkinObject(TSeActiveObject);
  RegisterSkinObject(TSeBitmapObject);
  RegisterSkinObject(TSeActiveBitmap);
  RegisterSkinObject(TSeSystemButton);
  RegisterSkinObject(TSeButtonObject);
  RegisterSkinObject(TSeTextObject);
finalization
  RegObjects.Free;
end.

