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

  SkinEngine
  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 KsSkinEngine;

{$I KsSkinVer.Inc}

interface

uses Windows, Messages, Forms, Sysutils, Classes, Graphics, Controls, ImgList,
  KsBitmap, KsSkinVersion, KsSkinObjects, KsSkinSource;

const

  WM_SKINCHANGE         = WM_USER + 678;
  WM_BEFORECHANGE       = WM_USER + 679;

type

{ TSeSkinEngine class }

{ Base skins's support component. Use this component for link controls or forms with specifies skin source. }
  TSeSkinEngine = class(TComponent)
  private
    FSkinSource: TSeSkinSource;
    FSkinFile: string;
    FDeltaHue: integer;
    FSkinStoreCompression: boolean;
    FOnAfterChange: TNotifyEvent;
    FOnBeforeChange: TNotifyEvent;
    function GetVersion: TSeSkinVersion;
    procedure SetVersion(const Value: TSeSkinVersion);
    procedure SetSkinFile(const Value: string);
    function GetDeltaHue: integer;
    procedure SetDeltaHue(const Value: integer);
    function GetSkinStore: string;
    procedure SetSkinStore(const Value: string);
    procedure SetSkinStoreCompression(const Value: boolean);
    procedure SetOnAfterChange(const Value: TNotifyEvent);
    procedure SetOnBeforeChange(const Value: TNotifyEvent);
  protected
    procedure BeforeUpdate;
    procedure RepaintForms;
    { Skin Store }
    procedure ReadData(Stream: TStream);
    procedure WriteData(Stream: TStream);
    procedure DefineProperties(Filer: TFiler); override;
    function IsSkinLoaded: boolean;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Loaded; override;
    procedure Update;
    property SkinSource: TSeSkinSource read FSkinSource;
  published
    property DeltaHue: integer read FDeltaHue write SetDeltaHue;
    { This string value located file with skin sources.

      Syntax:
      <CODE>
        <disk>:\<path>\<filename.kskn> - "C:\Skins\MySkin\MySkin.kskn"
        <Folder>\<filename.kskn> - "MySkins\myskin.kskn"
      </CODE>

      SkinEngine use executable file path at prefix

      Typically skin sources located in program path.

        "C:\Program Files\MySkinProgram\" - path to executable program

        "C:\Program Files\MySkinProgram\MySkins" - path to skin source


      If you use such kind of skin locate, you must be set SkinFile property at "MySkins\MySkin.kskn".
      You can use this method in run-time for load skin source.

      <CODE>
        SkinEngineObject.SkinFile := 'MySkin\Myskin.kskn';
      </CODE>

      After call this method's SkinEngine load skin source and update form and all skin-object.
    }
    property SkinFile: string read FSkinFile write SetSkinFile;
    property SkinStore: string read GetSkinStore write SetSkinStore;
    property SkinStoreCompression: boolean read FSkinStoreCompression
      write SetSkinStoreCompression default false;
    property Version: TSeSkinVersion read GetVersion write SetVersion;
    { events }
    property OnBeforeChange: TNotifyEvent read FOnBeforeChange write SetOnBeforeChange;
    property OnAfterChange: TNotifyEvent read FOnAfterChange write SetOnAfterChange;
  end;

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

uses KsSkinForms, ZKsStream;

{ TSeSkinEngine }

constructor TSeSkinEngine.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FSkinSource := TSeSkinSource.Create(Self);
  FSkinStoreCompression := false;
end;

destructor TSeSkinEngine.Destroy;
begin
  FSkinSource.Free;
  inherited Destroy;
end;

procedure TSeSkinEngine.Loaded;
begin
  inherited;

  if Assigned(FOnAfterChange) and (SkinSource.Count > 0) then
    FOnAfterChange(Self);
end;

{ Internal }

procedure TSeSkinEngine.BeforeUpdate;
var
  i, j: integer;
begin
  if Assigned(FOnBeforeChange) then
    FOnBeforeChange(Self);

  for i := 0 to Screen.FormCount-1 do
  begin
    SendMessage(Screen.Forms[i].Handle, WM_BEFORECHANGE, Integer(Self), Integer(Self));

    with Screen.Forms[i] do
      for j := 0 to ComponentCount - 1 do
      begin
        if Components[j] is TWinControl then
          SendMessage((Components[j] as TWinControl).Handle, WM_BEFORECHANGE, Integer(Self), Integer(Self));
        if Components[j] is TGraphicControl then
          (Components[j] as TGraphicControl).Perform(WM_BEFORECHANGE, Integer(Self), Integer(Self));
      end;
  end;

  Application.ProcessMessages;
end;

procedure TSeSkinEngine.Update;
var
  i, j: integer;
begin
  for i := 0 to Screen.FormCount-1 do
  begin
    SendMessage(Screen.Forms[i].Handle, WM_SKINCHANGE, Integer(Self), Integer(Self));

    with Screen.Forms[i] do
      for j := 0 to ComponentCount - 1 do
      begin
        if Components[j] is TWinControl then
          SendMessage((Components[j] as TWinControl).Handle, WM_SKINCHANGE, Integer(Self), Integer(Self));
        if Components[j] is TGraphicControl then
          (Components[j] as TGraphicControl).Perform(WM_SKINCHANGE, Integer(Self), Integer(Self));
      end;

    Application.ProcessMessages;
  end;

  if Assigned(FOnAfterChange) then
    FOnAfterChange(Self);
end;

procedure TSeSkinEngine.RepaintForms;
var
  i, j: integer;
begin
  for i := 0 to Screen.FormCount - 1 do
  begin
    if not Screen.Forms[i].Visible then Continue;

    for j := 0 to Screen.Forms[i].ComponentCount - 1 do
      if Screen.Forms[i].Components[j] is TSeSkinForm then
      begin
        (Screen.Forms[i].Components[j] as TSeSkinForm).UpdateForm;
        (Screen.Forms[i].Components[j] as TSeSkinForm).UpdateControls;
      end;
  end;
end;

{ SkinStore property }

function TSeSkinEngine.IsSkinLoaded: boolean;
begin
  Result := true;
end;

procedure TSeSkinEngine.ReadData(Stream: TStream);
var
  IsStore: byte;
begin
  Stream.Read(IsStore, SizeOf(IsStore));

  if (IsStore > 0) and (FSkinSource <> nil) then
  begin
    FSkinSource.LoadFromStream(Stream);
    FSkinFile := '';
  end;
end;

procedure TSeSkinEngine.WriteData(Stream: TStream);
var
  IsStore: byte;
begin
  if (FSkinSource <> nil) and (FSkinSource.Count > 0) then
    IsStore := 1
  else
    IsStore := 0;

  if IsStore = 1 then IsStore := 2; // Set compression flag
  { Store Flags }
  Stream.Write(IsStore, SizeOf(IsStore));

  { Store skin in DFM }
  if (FSkinSource <> nil) and (FSkinSource.Count > 0) then
    FSkinSource.SaveToStream(Stream);
end;

procedure TSeSkinEngine.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineBinaryProperty('SkinStream', ReadData, WriteData, IsSkinLoaded);
end;

{ Properties }

function TSeSkinEngine.GetSkinStore: string;
begin
  Result := '(none)';

  if csDesigning in ComponentState then
  begin
    if (FSkinSource <> nil) and (FSkinSource.Count > 0) then
      Result := ExtractFileName(FSkinSource.SkinName);
  end;
end;

procedure TSeSkinEngine.SetSkinStore(const Value: string);
begin
end;

procedure TSeSkinEngine.SetSkinFile(const Value: string);
begin
  FSkinFile := Value;

  if csDesigning in ComponentState then
  begin
    if (FSkinFile <> '') and (FSkinSource.Count > 0) then
      FSkinSource.Clear;
    Exit;
  end;

  if FSkinSource.Count > 0 then
  begin
    { Set clear state }
    FSkinSource.IsChanging := true;
    { Send broadcast }
    BeforeUpdate;
    { Clear }
    FSkinSource.Clear;
    { Load new }
    FSkinSource.LoadFromFile(FSkinFile);
    { Reset Clear }
    FSkinSource.IsChanging := false;
    { Set Hue }
    FSkinSource.DeltaHue := FDeltaHue;
    { Update }
    Update;
  end
  else
  begin
    { Load }
    FSkinSource.LoadFromFile(FSkinFile);
    { Set Hue }
    FSkinSource.DeltaHue := FDeltaHue;
    { Update }
    Update;
  end;
end;

function TSeSkinEngine.GetVersion: TSeSkinVersion;
begin
  Result := sSeSkinVersion;
end;

procedure TSeSkinEngine.SetVersion(const Value: TSeSkinVersion);
begin
end;

function TSeSkinEngine.GetDeltaHue: integer;
begin
  Result := FDeltaHue;
end;

procedure TSeSkinEngine.SetDeltaHue(const Value: integer);
begin
  if (FDeltaHue <> Value) or (FSkinSource.DeltaHue <> Value) then
  begin
    FDeltaHue := Value;
    if FSkinSource.Count > 0 then
    begin
      { Send broadcast }
      BeforeUpdate;
      { Set Hue }
      FSkinSource.DeltaHue := FDeltaHue;
      { Update }
      Update;
    end;
  end;
end;

procedure TSeSkinEngine.SetSkinStoreCompression(const Value: boolean);
begin
  FSkinStoreCompression := Value;
end;

procedure TSeSkinEngine.SetOnAfterChange(const Value: TNotifyEvent);
begin
  FOnAfterChange := Value;
end;

procedure TSeSkinEngine.SetOnBeforeChange(const Value: TNotifyEvent);
begin
  FOnBeforeChange := Value;
end;

end.




