unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, ComCtrls, Buttons, Menus, ImgList, KsBitmap, KsSkinObjects,
  KsSkinSource, ActnList, ZPropLst, StdCtrls, ToolWin, ExtDlgs,
  KsSkinObjects2, KsSkinVersion;

type

  TPanelView = (vWork, vImage);
  TGripKind = (gkMove, gkTopLeft, gkTopRight, gkBottomLeft, gkBottomRight);

  TfrmMain = class(TForm)
    LeftPanel: TPanel;
    TopPanel: TPanel;
    Panel1: TPanel;
    Panel2: TPanel;
    Panel3: TPanel;
    SkinTree: TTreeView;
    Panel4: TPanel;
    Panel5: TPanel;
    Panel6: TPanel;
    Splitter1: TSplitter;
    Splitter2: TSplitter;
    PanelWork: TPanel;
    btnNew: TSpeedButton;
    btnOpen: TSpeedButton;
    btnSave: TSpeedButton;
    MainMenu: TMainMenu;
    Glyphs: TImageList;
    File1: TMenuItem;
    New1: TMenuItem;
    Open1: TMenuItem;
    Save1: TMenuItem;
    N1: TMenuItem;
    Exit1: TMenuItem;
    Actions: TActionList;
    actnFileNew: TAction;
    actnFileOpen: TAction;
    actnFileSave: TAction;
    actnFileSaveAs: TAction;
    Saveas1: TMenuItem;
    Help1: TMenuItem;
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;
    Panel7: TPanel;
    Inspector: TZPropList;
    Tabs: TTabControl;
    PageOld: TPageScroller;
    Panel10: TPanel;
    btnSelect: TSpeedButton;
    cbScale: TComboBox;
    btnDelete: TSpeedButton;
    Label1: TLabel;
    SpeedButton1: TSpeedButton;
    ScrollBox1: TScrollBox;
    WorkArea: TPaintBox;
    PanelImages: TPanel;
    Panel8: TPanel;
    Panel9: TPanel;
    Panel11: TPanel;
    btnAddImage: TButton;
    ImageView: TImage;
    btnDelImage: TButton;
    actnTestEmpty: TAction;
    btnFront: TSpeedButton;
    PageStandard: TPageScroller;
    SpeedButton11: TSpeedButton;
    actnCopy: TAction;
    actnPaste: TAction;
    Edit1: TMenuItem;
    Copyto1: TMenuItem;
    Pas1: TMenuItem;
    SpeedButton12: TSpeedButton;
    SkinToolBar: TToolBar;
    ToolButton2: TToolButton;
    btnBitmapSkinObject: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    SpeedButton4: TSpeedButton;
    SpeedButton5: TSpeedButton;
    SpeedButton6: TSpeedButton;
    SpeedButton7: TSpeedButton;
    SpeedButton8: TSpeedButton;
    SpeedButton9: TSpeedButton;
    SpeedButton10: TSpeedButton;
    ToolBar2: TToolBar;
    btnSkinObject: TSpeedButton;
    ToolButton1: TToolButton;
    btnBitmapObject: TSpeedButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    btnActiveObject: TSpeedButton;
    btnActiveBitmap: TSpeedButton;
    ToolButton5: TToolButton;
    btnSystemButton: TSpeedButton;
    ToolButton6: TToolButton;
    btnButtonObject: TSpeedButton;
    SpeedButton13: TSpeedButton;
    Panel14: TPanel;
    Button1: TButton;
    cbCharset: TComboBox;
    KSDevelopmantSite1: TMenuItem;
    N2: TMenuItem;
    AboutSkinBuilder1: TMenuItem;
    actnTest: TAction;
    Panel15: TPanel;
    Label6: TLabel;
    Label7: TLabel;
    ToolButton7: TToolButton;
    btnTextObject: TSpeedButton;
    Panel12: TPanel;
    actnUndo: TAction;
    N3: TMenuItem;
    Undo1: TMenuItem;
    btnReplaceImage: TBitBtn;
    procedure Exit1Click(Sender: TObject);
    procedure actnFileNewExecute(Sender: TObject);
    procedure actnFileOpenExecute(Sender: TObject);
    procedure actnFileSaveExecute(Sender: TObject);
    procedure actnFileSaveAsExecute(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure WorkAreaPaint(Sender: TObject);
    procedure cbScaleChange(Sender: TObject);
    procedure SkinTreeChange(Sender: TObject; Node: TTreeNode);
    procedure WorkAreaMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure btnDeleteClick(Sender: TObject);
    procedure btnSkinObjectClick(Sender: TObject);
    procedure btnAddImageClick(Sender: TObject);
    procedure btnBitmapSkinObjectClick(Sender: TObject);
    procedure btnDelImageClick(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
    procedure actnTestEmptyExecute(Sender: TObject);
    procedure SpeedButton4Click(Sender: TObject);
    procedure SpeedButton5Click(Sender: TObject);
    procedure SpeedButton6Click(Sender: TObject);
    procedure SpeedButton7Click(Sender: TObject);
    procedure SpeedButton8Click(Sender: TObject);
    procedure SpeedButton9Click(Sender: TObject);
    procedure SpeedButton10Click(Sender: TObject);
    procedure btnBackClick(Sender: TObject);
    procedure TabsChange(Sender: TObject);
    procedure actnCopyExecute(Sender: TObject);
    procedure actnPasteExecute(Sender: TObject);
    procedure btnBitmapObjectClick(Sender: TObject);
    procedure btnActiveObjectClick(Sender: TObject);
    procedure btnActiveBitmapClick(Sender: TObject);
    procedure btnSystemButtonClick(Sender: TObject);
    procedure btnButtonObjectClick(Sender: TObject);
    procedure SpeedButton13Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure KSDevelopmantSite1Click(Sender: TObject);
    procedure AboutSkinBuilder1Click(Sender: TObject);
    procedure InspectorChange(Sender: TObject);
    procedure actnTestExecute(Sender: TObject);
    procedure btnTextObjectClick(Sender: TObject);
    procedure WorkAreaMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure WorkAreaMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FormDestroy(Sender: TObject);
    procedure InspectorChanging(Sender: TZPropList; var CanChange: Boolean);
    procedure actnUndoExecute(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure btnReplaceImageClick(Sender: TObject);
  private
    { Private declarations }
    FSkinSource: TSeSkinSource;
    FRoot: TSeSkinObject;
    FSelected: TSeSkinObject;
    FSelectedBitmap: TKsBitmap;
    FModified: boolean;
    FFileName: string;
    FScale: single;
    FCount: integer;
    FBuilding: boolean;
    FMyClipboard: string;
    FDragging: boolean;
    FDragGrip: TGripKind;
    FDragPoint: TPoint;
    FStateList: TList;
    FStateNum: integer;
    procedure Clear;
    procedure CreateSkin;
    procedure OpenSkin;
    procedure SaveSkin;
    procedure SaveSkinAs;
    function HasModified: boolean;
    function GetOwner: TSeSkinObject;
    procedure UpdateSkin;
    procedure BuildTree;
    procedure CreateSkinObject(ObjectClass: TSeSkinObjectClass);
    procedure DeleteSkinObject;
    procedure SelectObject(SkinObject: TSeSkinObject);
    procedure SelectBitmap(Bitmap: TKsBitmap);
    procedure ShowPanel(View: TPanelView);
    procedure AddCharsetItem(const S: string);
    function GetGripRect(GripKind: TGripKind): TRect;
    { Undo/Redo }
    procedure SaveState;
    procedure RestoreState;
  public
    { Public declarations }
  end;

const

  sEmptySkin = 'skin.kskn';

var
  frmMain: TfrmMain;

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

uses OpenBitmap, TestForm, Clipbrd, EmptyForm, ShellAPI, about;

{$R *.DFM}

const
  GripSize = 3;

var
  TempSkinFile: string;

type
  TCompAccess = class(TComponent);

  TStateClass = class(TPersistent)
  private
    FRoot: string;
    FSelected: string;
    FStream: TStream;
  public
    constructor Create;
    destructor Destroy; override;
    property Stream: TStream read FStream write FStream;
    property Root: string read FRoot write FRoot;
    property Selected: string read FSelected write FSelected;
  end;

{ TStateClass }

constructor TStateClass.Create;
begin
  inherited Create;
  FStream := TMemoryStream.Create;
end;

destructor TStateClass.Destroy;
begin
  if FStream <> nil then FStream.Free;
  inherited;
end;

{ TfrmMain class }

procedure TfrmMain.Clear;
begin
  FRoot := nil;
  FSelected := nil;
  FSelectedBitmap := nil;
  FModified := false;

  FScale := 1;
  cbScale.ItemIndex := 0;
end;

procedure TfrmMain.CreateSkin;
var
  SkinObject: TSeSkinObject;
begin
  { Create }
  if HasModified then
  begin
    if FSkinSource <> nil then
      FSkinSource.Free;

    { Create }
    FSkinSource := TSeSkinSource.Create(nil);
    { Add standard object }
    SkinObject := TSeSkinObject.Create(nil);
    SkinObject.Name := 'Form';
    SkinObject.Kind := skForm;
    SkinObject.BoundsRect := Rect(0, 0, 300, 250);
    FSkinSource.Add(SkinObject);
    { Set property }
    FFileName := sEmptySkin;
    Clear;
    UpdateSkin;
  end;
end;

procedure TfrmMain.OpenSkin;
begin
  { Open }
  if HasModified then
  begin
    if OpenDialog.Execute then
    begin
      if FSkinSource <> nil then
        FSkinSource.Free;
      FSkinSource := TSeSkinSource.Create(nil);
      FSkinSource.LoadFromFile(OpenDialog.FileName);

      Clear;
      FFileName := OpenDialog.FileName;
      UpdateSkin;
    end;
  end;
end;

procedure TfrmMain.SaveSkin;
begin
  { Save }
  if FModified then
  begin
    if FFileName = sEmptySkin then
      SaveSkinAs
    else
      FSkinSource.SaveToFile(FFileName);

    FModified := false;
    UpdateSkin;
  end;
end;

procedure TfrmMain.SaveSkinAs;
begin
  { Save }
  if SaveDialog.Execute then
  begin
    FSkinSource.SaveToFile(SaveDialog.FileName);

    FFileName := SaveDialog.FileName;
    FModified := false;
    UpdateSkin;
  end;
end;

function TfrmMain.HasModified: boolean;
begin
  Result := true;

  if (FSkinSource <> nil) then
  begin
    if FModified then
      case MessageDlg('Skin has modified. Save changes?', mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
        mrYes: SaveSkin;
        mrNo: Result := true;
        mrCancel: Result := false;
      end;
  end;
end;

procedure TfrmMain.UpdateSkin;
var
  SkinObject: TSeSkinObject;
begin
  Caption := 'SkinBuilder ' +sSeSkinVersion+ ' - ['+FFileName+']';

  BuildTree;

  if FRoot <> nil then
  begin
    { Set Title }
    SkinObject := FRoot.FindObjectByKind(skTitle);
    if SkinObject <> nil then
      SkinObject.Text := 'Skin Builder Title';
    { Set MenuBar and PopupMenu Item Text  }
    SkinObject := FRoot.FindObjectByName('Item');
    if SkinObject <> nil then
      SkinObject.Text := 'Item Text';
    { Set Text Object text  }
    SkinObject := FRoot.FindObjectByName('Text');
    if SkinObject <> nil then
      SkinObject.Text := 'Sample';
    { Set Text Object text  }
    SkinObject := FRoot.FindObjectByName('ToolbarItem');
    if SkinObject <> nil then
      SkinObject.Text := 'Item Text';
    { Set Caption Object text  }
    SkinObject := FRoot.FindObjectByName('Caption');
    if SkinObject <> nil then
      SkinObject.Text := 'Caption';

    WorkArea.Width := Round(FRoot.Width * FScale);
    WorkArea.Height := Round(FRoot.Height * FScale);

    FRoot.Aligning;
  end;

  WorkAreaPaint(Self);
end;

{ Inspector and Tree }

procedure TfrmMain.AddCharsetItem(const S: string);
begin
  cbCharset.Items.Add(S);
end;

procedure TfrmMain.BuildTree;

 procedure AddObject(Node: TTreeNode; SkinObject: TSeSkinObject); overload;
 var
   i: integer;
   AddNode: TTreeNode;
   Child: TTreeNode;
 begin
   Child := SkinTree.Items.AddChild(Node, SkinObject.Name);
   Child.Data := SkinObject;

   if SkinObject = FSelected then
      Child.Selected := true;

   for i := 0 to SkinObject.Count-1 do
   begin
     if SkinObject[i].Count > 0 then
       AddObject(Child, SkinObject[i])
     else
     begin
       AddNode := SkinTree.Items.AddChild(Child, SkinObject[i].Name);
       AddNode.Data := SkinObject[i];

       if SkinObject[i] = FSelected then
         AddNode.Selected := true;
     end;
   end;
 end;

 procedure AddObject(Node: TTreeNode; Bitmap: TKsBitmap); overload;
 var
   Child: TTreeNode;
 begin
   Child := SkinTree.Items.AddChild(Node, Bitmap.Name);
   Child.Data := Bitmap;

   if Bitmap = FSelectedBitmap then
     Child.Selected := true;
 end;

var
  i: integer;
  SkinObject: TSeSkinObject;
  Bitmap: TKsBitmap;
  Node: TTreeNode;
  S: string;
begin
  { Make charsert List }
  cbCharset.Items.Clear;
  GetCharsetValues(AddCharsetItem);
  CharsetToIdent(DEFAULT_CHARSET, S);
  cbCharset.ItemIndex := cbCharset.Items.IndexOf(S);

  if FBuilding then Exit;

  FBuilding := true;

  SkinTree.Items.BeginUpdate;
  try
    SkinTree.Items.Clear;

    { Make Root }
    Node := SkinTree.Items.AddChild(nil, 'Skin Source');
    Node.Data := FSkinSource;
    if FSelected = nil then
    begin
      Node.Selected := true;
      Inspector.CurObj := FskinSource;
    end;
    { Build Objects Tree }
    for i := 0 to FSkinSource.Count-1 do
    begin
      SkinObject := FSkinSource[i];
      AddObject(Node, SkinObject);
    end;
    { Build Images Tree }
    Node := SkinTree.Items.AddChild(nil, 'Images');
    Node.Data := PanelImages;
    for i := 0 to FSkinSource.Bitmaps.Count-1 do
    begin
      Bitmap := FSkinSource.Bitmaps[i];
      AddObject(Node, Bitmap);
    end;
  finally
    SkinTree.Items.EndUpdate;
    FBuilding := false;
  end;
end;

procedure TfrmMain.SelectObject(SkinObject: TSeSkinObject);
var
  Par: TSeSkinObject; 
begin
  ShowPanel(vWork);

  if SkinObject = nil then
  begin
    { Is a root }
    Inspector.CurObj := FSkinSource;
    FSelected := nil;
    FRoot := nil;
    Panel1.Caption := ' Objects Tree - Skin Source';
    UpdateSkin;
    Exit;
  end;

  if SkinObject.Owner = nil then
  begin
    FRoot := SkinObject;
    FSelected := SkinObject;
    Inspector.CurObj := SkinObject;
    UpdateSkin;
  end
  else
  begin
    Par := SkinObject;
    while Par.Owner <> nil do
      Par := (Par.Owner as TSeSkinObject);

    FRoot := Par;
    FSelected := SkinObject;
    Inspector.CurObj := SkinObject;
    UpdateSkin;
  end;

  Label6.Caption := SkinObject.Name + ': ';
  Label7.Caption := SkinObject.ClassName + ';';

  if FSelected <> nil then
    Panel1.Caption := ' Objects Tree - '+FSelected.Name;
  if FRoot = nil then
    Panel1.Caption := ' Objects Tree - Root';
end;

procedure TfrmMain.SelectBitmap(Bitmap: TKsBitmap);
var
  B: TBitmap;
begin
  ShowPanel(vImage);

  FSelectedBitmap := Bitmap;
  if Bitmap <> nil then
  begin
    B := TBitmap.Create;
    try
      B.Width := Bitmap.Width;
      B.Height := Bitmap.Height;

      Bitmap.Draw(B.Canvas.Handle, 0, 0);

      ImageView.Picture.Assign(B);

      Panel1.Caption := ' Objects Tree - '+Bitmap.Name;
    finally
      B.Free;
    end;
    Inspector.CurObj := nil;
  end;
end;

function TfrmMain.GetGripRect(GripKind: TGripKind): TRect;
begin
  Result := Rect(0, 0, 0, 0);

  if FSelected <> nil then
    with FSelected.BoundsRect do
    begin
      case GripKind of
        gkMove: Result := Rect((Left + Right) div 2 - GripSize, (Top + Bottom) div 2 - GripSize, (Left + Right) div 2 + GripSize, (Top + Bottom) div 2 + GripSize);
        gkTopLeft: Result := Rect(Left - GripSize, Top - GripSize, Left + GripSize, Top + GripSize);
        gkTopRight: Result := Rect(Right - GripSize, Top - GripSize, Right + GripSize, Top + GripSize);
        gkBottomLeft: Result := Rect(Left - GripSize, Bottom - GripSize, Left + GripSize, Bottom + GripSize);
        gkBottomRight: Result := Rect(Right - GripSize, Bottom - GripSize, Right + GripSize, Bottom + GripSize);
      end;
    end;
end;

procedure TfrmMain.WorkAreaMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  Grip: TGripKind;
  R: TRect;
  SkinObject: TSeSkinObject;
begin
  { Mouse down }
  X := Round(X / FScale);
  Y := Round(Y / FScale);

  if Button = mbLeft then
  begin
    { Start Dragging }
    if FSelected = nil then Exit;

    for Grip := Low(Grip) to High(Grip) do
    begin
      R := GetGripRect(Grip);
      if (not IsRectEmpty(R)) and PtInRect(R, Point(X, Y)) then
      begin
        FDragPoint := Point(X, Y);
        FDragging := true;
        FDragGrip := Grip;

        Exit;
      end
      else
        WorkArea.Cursor := crDefault;
    end;

    { Select object}
    if FRoot <> nil then
    begin
      SkinObject := FRoot.FindObjectByPoint(Point(X, Y));
      if SkinObject <> nil then
        SelectObject(SkinObject);
    end;
  end;
end;

procedure TfrmMain.WorkAreaMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
var
  Grip: TGripKind;
  R: TRect;
begin
  { Mouse move }
  X := Round(X / FScale);
  Y := Round(Y / FScale);

  if Shift = [] then
  begin
    for Grip := Low(Grip) to High(Grip) do
    begin
      R := GetGripRect(Grip);
      if (not IsRectEmpty(R)) and PtInRect(R, Point(X, Y)) then
      begin
        case Grip of
          gkMove: WorkArea.Cursor := crSizeAll;
          gkTopLeft: WorkArea.Cursor := crSizeNWSE;
          gkTopRight: WorkArea.Cursor := crSizeNESW;
          gkBottomLeft: WorkArea.Cursor := crSizeNESW;
          gkBottomRight: WorkArea.Cursor := crSizeNWSE;
        end;
        Break;
      end
      else
        WorkArea.Cursor := crDefault;
    end;
  end;

  if (Shift = [ssLeft]) and FDragging and (FSelected <> nil) and
     ((FDragPoint.X <> X) or (FDragPoint.Y <> Y)) then
  begin
    R := FSelected.BoundsRect;
    case FDragGrip of
      gkMove: begin
        { Moving }
        R := FSelected.BoundsRect;
        OffsetRect(R, X - FDragPoint.X, Y - FDragPoint.Y);
      end;
      gkTopLeft: begin
        { Resize - TopLeft grip }
        R.Left := R.Left + (X - FDragPoint.X);
        R.Top := R.Top + (Y - FDragPoint.Y);
      end;
      gkTopRight: begin
        { Resize - TopRight grip }
        R.Right := R.Right + (X - FDragPoint.X);
        R.Top := R.Top + (Y - FDragPoint.Y);
      end;
      gkBottomLeft: begin
        { Resize - BottomLeft grip }
        R.Left := R.Left + (X - FDragPoint.X);
        R.Bottom := R.Bottom + (Y - FDragPoint.Y);
      end;
      gkBottomRight: begin
        { Resize - BottomRight grip }
        R.Right := R.Right + (X - FDragPoint.X);
        R.Bottom := R.Bottom + (Y - FDragPoint.Y);
      end;
    end;
    FSelected.BoundsRect := R;
    FDragPoint := Point(X, Y);
    { Update }
    if (FSelected.Owner <> nil) and (FSelected.Owner is TSeSkinObject) then
      TSeSkinObject(FSelected.Owner).Aligning; 
    FRoot.Aligning;
    WorkAreaPaint(Self);
    Inspector.CurObj := nil;
    Inspector.CurObj := FSelected;

    FModified := true;
  end;
end;

procedure TfrmMain.WorkAreaMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  { Mouse Up }
  FDragging := false;
end;

procedure TfrmMain.WorkAreaPaint(Sender: TObject);
var
  Buffer: TKsBitmap;
  SrcRect, DstRect: TRect;
  R: TRect;
  BufferCanvas: TCanvas;
  Grip: TGripKind;
begin
  { Paint }
  if FRoot <> nil then
  begin
    if FRoot.Width * FRoot.Height = 0 then Exit;

    Buffer := TKsBitmap.Create;
    try
      Buffer.SetSize(FRoot.Width, FRoot.Height);
      Buffer.Blend := true;

      FRoot.State := ssDesign;
      FRoot.BoundsRect := Rect(0, 0, Buffer.Width, Buffer.Height);
      FRoot.Draw(Buffer);

      { Draw Selected Rect }
      if FSelected <> nil then
      begin
        BufferCanvas := TCanvas.Create;
        try
          BufferCanvas.Handle := Buffer.DC;
          with BufferCanvas do
          begin
            { Draw rect }
            Brush.Style := bsClear;
            Pen.Mode := pmXor;
            Pen.Color := clSilver;
            Pen.Style := psDot;
	    with FSelected.BoundsRect do
              Rectangle(Left, Top, Right, Bottom);

            { Dra Grips }
            for Grip := Low(Grip) to high(Grip) do
            begin
              R := GetGripRect(Grip);
              Buffer.FillRect(R, ckRed);
              Buffer.DrawRect(R, ckYellow);
            end;
          end;
        finally
          BufferCanvas.Handle := 0;
          BufferCanvas.Free;
        end;
      end;

      { Draw }
      SrcRect := Rect(0, 0, Buffer.Width, Buffer.Height);
      DstRect := Rect(0, 0, Round(Buffer.Width * FScale), Round(Buffer.Height * FScale));
      Buffer.Draw(WorkArea.Canvas.Handle, DstRect, SrcRect);
    finally
      Buffer.Free;
    end;
  end
  else
  begin
    { Empty }
    WorkArea.Canvas.Pen.Style := psClear;
    WorkArea.Canvas.Brush.Color := clBtnFace;
    WorkArea.Canvas.Rectangle(0, 0, WorkArea.Width+1, WorkArea.Height+1);
  end;
end;

procedure TfrmMain.cbScaleChange(Sender: TObject);
begin
  case cbScale.ItemIndex of
    0: FScale := 1;
    1: FScale := 2;
    2: FScale := 4;
    3: FScale := 8;
  end;
  UpdateSkin;
end;

function TfrmMain.GetOwner: TSeSkinObject;
begin
  if (FSelected <> nil) then
    Result := FSelected
  else
    if (FRoot <> nil) then
      Result := FRoot
    else
      Result := nil;
end;

procedure TfrmMain.CreateSkinObject(ObjectClass: TSeSkinObjectClass);
var
  SkinObject: TSeSkinObject;
  S: string;
begin
  if FSkinSource = nil then Exit;

  { Save state }
  SaveState;

  { Add TSeSkinObject }
  Inc(FCount);
  if GetOwner <> nil then
  begin
    SkinObject := ObjectClass.Create(GetOwner);
    SkinObject.BoundsRect := Rect(GetOwner.Left, GetOwner.Top, GetOwner.Left +
      30, GetOwner.Top + 20);
    S := SkinObject.ClassName;
    Delete(S, 1, 3);
    SkinObject.Name := S + IntToStr(FCount);
    SkinObject.Bitmaps := FSkinSource.Bitmaps;

    FSelected := SkinObject;
    UpdateSkin;
    SelectObject(SkinObject);
  end
  else
  begin
    SkinObject := ObjectClass.Create(nil);
    SkinObject.BoundsRect := Rect(0, 0, 200, 100);
    S := SkinObject.ClassName;
    Delete(S, 1, 3);
    SkinObject.Name := S + IntToStr(FCount);
    SkinObject.Bitmaps := FSkinSource.Bitmaps;

    FSkinSource.Add(SkinObject);

    FRoot := SkinObject;
    FSelected := SkinObject;

    UpdateSkin;
    SelectObject(SkinObject);
  end;
  FModified := true;
end;

procedure TfrmMain.DeleteSkinObject;
var
  Owner: TSeSkinObject;
begin
  { Delete }
  if (FSelected <> nil) and (FSelected.Owner <> nil) and
     (FSelected.Owner is TSeSkinObject)
  then
  begin
    Owner := FSelected.Owner as TSeSkinObject;
    Owner.Remove(FSelected);
    FSelected.Free;

    FSelected := Owner;
    UpdateSkin;
    FModified := true;
    Exit;
  end;

  if (FSelected <> nil) and (FSelected = FRoot) then
  begin
    FSkinSource.Remove(FSelected);

    FSelected.Free;
    FSelected := nil;
    if FSkinSource.Form <> nil then
      FRoot := FSkinSource.Form
    else
      FRoot := nil;
    UpdateSkin;
    FModified := true;
  end;
end;

{ Enents ======================================================================}

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  TempSkinFile := ExtractFilePath(ParamStr(0))+'temp_skin.kskn';

  OpenDialog.DefaultExt := DefaultFilter.GetFileExtension;
  OpenDialog.Filter := GetDialogFilter;
  SaveDialog.DefaultExt := DefaultFilter.GetFileExtension;
  SaveDialog.Filter := GetDialogFilter;

  FStateList := TList.Create;

  DesignMode := true;
  CreateSkin;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
var
  i: integer;
begin
  DeleteFile(TempSkinFile);

  for i := 0 to FStateList.Count - 1 do
    TMemoryStream(FStateList[i]).Free;
  FStateList.Free;

  Clear;
end;

procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  {}
  if FModified then
  begin
    case MessageDlg('Skin''s was changed. Save?', mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
      mrYes: SaveSkin;
      mrCancel: CanClose := false;
    end;
  end;
end;

procedure TfrmMain.Exit1Click(Sender: TObject);
begin
  { Exit }
  Close;
end;

procedure TfrmMain.actnFileNewExecute(Sender: TObject);
begin
  CreateSkin;
end;

procedure TfrmMain.actnFileOpenExecute(Sender: TObject);
begin
  OpenSkin;
end;

procedure TfrmMain.actnFileSaveExecute(Sender: TObject);
begin
  SaveSkin;
end;

procedure TfrmMain.actnFileSaveAsExecute(Sender: TObject);
begin
  SaveSkinAs;
end;

procedure TfrmMain.SkinTreeChange(Sender: TObject; Node: TTreeNode);
begin
  if Node = nil then Exit;
  if Node.Data = nil then Exit;
  if FBuilding then Exit;
  { Select Root }
  if Node.Data = FSkinSource then
  begin
    Panel1.Caption := ' Objects Root';
    SelectObject(nil);
    Exit;
  end;
  { Select "Images" }
  if Node.Data = PanelImages then
  begin
    Panel1.Caption := ' Objects Tree - Images';
    ShowPanel(vImage);
    Exit;
  end;
  { Select SkinObject }
  if TObject(Node.Data) is TSeSkinObject then
  begin
    SelectObject(TseSkinObject(Node.Data));
    Exit;
  end;
  { Select Bitmap }
  if TObject(Node.Data) is TKsBitmap then
  begin
    SelectBitmap(TKsBitmap(Node.Data));
    Exit;
  end;
end;

procedure TfrmMain.btnDeleteClick(Sender: TObject);
begin
  DeleteSkinObject;
  FModified := true;
end;

procedure TfrmMain.ShowPanel(View: TPanelView);
begin
  case View of
    vWork: begin
      PanelImages.Hide;
      FSelectedBitmap := nil;
      PanelWork.Show;
    end;
    vImage: begin
      PanelWork.Hide;
      FSelected := nil;
      FRoot := nil;
      PanelImages.Show;
    end;
  end;
end;

procedure TfrmMain.btnAddImageClick(Sender: TObject);
var
  Bmp: TKsBitmap;
begin
  if FSkinSource = nil then Exit;

  { Add Image }
  FModified := true;

  Bmp := OpenSkinBitmap;
  if Bmp <> nil then
  begin
    { Add Image }
    FSkinSource.Bitmaps.Add(Bmp);
    FSelectedBitmap := Bmp;
    UpdateSkin;
    SelectBitmap(FSelectedBitmap);
  end;
end;

procedure TfrmMain.btnReplaceImageClick(Sender: TObject);
var
  Bmp: TKsBitmap;
begin
  if FSkinSource = nil then Exit;
  if FSelectedBitmap = nil then Exit;

  { Replace Image }
  FModified := true;

  Bmp := OpenSkinBitmap;
  if Bmp <> nil then
  begin
    { Add Image }
    FSkinSource.ReplaceBitmap(FSelectedBitmap, Bmp);

    FSelectedBitmap := Bmp;
    UpdateSkin;
    SelectBitmap(FSelectedBitmap);
  end;
end;

procedure TfrmMain.btnDelImageClick(Sender: TObject);
begin
  if FSelectedBitmap = nil then Exit;
  FSkinSource.Bitmaps.Remove(FSelectedBitmap);

  FModified := true;

  FSelectedBitmap.Free;
  FSelectedBitmap := nil;
  ImageView.Picture.Bitmap := nil;
  FRoot := nil;

  UpdateSkin;
end;

{ Object creation }

procedure TfrmMain.btnSkinObjectClick(Sender: TObject);
begin
  CreateSkinObject(TSeSkinObject);
end;

procedure TfrmMain.btnBitmapSkinObjectClick(Sender: TObject);
begin
  CreateSkinObject(TSeBitmapSkinObject);
end;

procedure TfrmMain.SpeedButton2Click(Sender: TObject);
begin
  CreateSkinObject(TSeFormBitmapSkinObject);
end;

procedure TfrmMain.SpeedButton3Click(Sender: TObject);
begin
  CreateSkinObject(TSeSysButtonSkinObject);
end;

procedure TfrmMain.SpeedButton4Click(Sender: TObject);
begin
  CreateSkinObject(TSeLinkSkinObject);
end;

procedure TfrmMain.SpeedButton5Click(Sender: TObject);
begin
  CreateSkinObject(TSeActiveSkinObject);
end;

procedure TfrmMain.SpeedButton6Click(Sender: TObject);
begin
  CreateSkinObject(TSeColorButtonObject);
end;

procedure TfrmMain.SpeedButton7Click(Sender: TObject);
begin
  CreateSkinObject(TSeBitmapBoxObject);
end;

procedure TfrmMain.SpeedButton8Click(Sender: TObject);
begin
  CreateSkinObject(TSeActiveBitmapBoxObject);
end;

procedure TfrmMain.SpeedButton9Click(Sender: TObject);
begin
  CreateSkinObject(TSeButtonBitmapBoxObject);
end;

procedure TfrmMain.SpeedButton10Click(Sender: TObject);
begin
  CreateSkinObject(TSeRollPanelObject);
end;

procedure TfrmMain.btnBitmapObjectClick(Sender: TObject);
begin
  { BitmapObject }
  CreateSkinObject(TSeBitmapObject);
end;

procedure TfrmMain.btnActiveObjectClick(Sender: TObject);
begin
  { Active Object }
  CreateSkinObject(TSeActiveObject);
end;

procedure TfrmMain.btnActiveBitmapClick(Sender: TObject);
begin
  { ActiveBitmap }
  CreateSkinObject(TSeActiveBitmap);
end;

procedure TfrmMain.btnSystemButtonClick(Sender: TObject);
begin
  { System Button }
  CreateSkinObject(TSeSystemButton);
end;

procedure TfrmMain.btnButtonObjectClick(Sender: TObject);
begin
  { Button Object }
  CreateSkinObject(TSeButtonObject);
end;

procedure TfrmMain.btnTextObjectClick(Sender: TObject);
begin
  { Button Object }
  CreateSkinObject(TSeTextObject);
end;

procedure TfrmMain.actnTestEmptyExecute(Sender: TObject);
begin
  { Save skin to temp file }
  FSkinSource.SaveToFile(TempSkinFile);

  { Test }
  frmTestEmpty := TfrmTestEmpty.Create(Self);
  try
    DesignMode := false;
    frmTestEmpty.SE.SkinFile := TempSkinFile;

    frmTestEmpty.ShowModal;
  finally
    DesignMode := true;
    frmTestEmpty.Free;
  end;
end;

procedure TfrmMain.actnTestExecute(Sender: TObject);
begin
  { Save skin to temp file }
  FSkinSource.SaveToFile(TempSkinFile);

  { Test }
  frmTest := TfrmTest.Create(Self);
  try
    DesignMode := false;
    frmTest.SE.SkinFile := TempSkinFile;
    frmTest.ShowModal;
  finally
    frmTest.Free;
    DesignMode := true;
  end;
end;

procedure TfrmMain.SpeedButton13Click(Sender: TObject);
begin
  SaveSkin;
  { Test }
  frmTest := TfrmTest.Create(Self);
  try
    frmTest.SE.SkinFile := FFileName;
    frmTest.ShowModal;
  finally
    frmTest.Free;
  end;
end;

procedure TfrmMain.btnBackClick(Sender: TObject);
var
  Owner: TSeSkinObject;
begin
  { Back }
  if (FSelected <> nil) and (FSelected.Owner <> nil) and
     (FSelected.Owner is TSeSkinObject)
  then
  begin
    Owner := FSelected.Owner as TSeSkinObject;

    TCompAccess(Owner).SetChildOrder(FSelected, 0); 

    UpdateSkin;

    FModified := true;
    Exit;
  end;
end;

procedure TfrmMain.TabsChange(Sender: TObject);
begin
  case Tabs.TabIndex of
    0: PageStandard.BringToFront;
    1: PageOld.BringToFront;
  end;
end;

procedure TfrmMain.actnCopyExecute(Sender: TObject);
var
  S: TStringStream;
  SkinObject: TSeSkinObject;
begin
  if FSkinSource = nil then Exit;

  { Copy }
  if (FSelected <> nil) then
  begin
    SkinObject := FSelected.CreateCopy(nil);
    SkinObject.Name := 'CopyOf'+SkinObject.Name;

    S := TStringStream.Create('');
    try
      SaveSkinObject(S, SkinObject);

      FMyClipboard := S.DataString;
    finally
      S.Free;
      SkinObject.Free;
    end;
  end;
end;

procedure TfrmMain.actnPasteExecute(Sender: TObject);
var
  SkinObject: TSeSkinObject;
  S: TStringStream;
begin
  if FSkinSource = nil then Exit;
  if FMyClipboard = '' then Exit;

  { Paste }
  if (FSelected <> nil) then
  begin
    S := TStringStream.Create(FMyClipboard);
    try
      try
        SkinObject := LoadSkinObject(S, FSelected);
        SkinObject.Bitmaps := FSkinSource.Bitmaps;
        SkinObject.AfterLoad;
      except
        raise Exception.Create('Invalid clipboard format');
      end;
    finally
      S.Free;
    end;

    if SkinObject = nil then
    begin
      ShowMessage('Invalid clipboard format');
    end;

    FSelected.Aligning;

    { Add TSeSkinObject }
    FSelected := SkinObject;

    UpdateSkin;
    SelectObject(SkinObject);

    FModified := true;
  end
  else
    if FRoot <> nil then
    begin
      { To root }
      S := TStringStream.Create(FMyClipboard);
      try
        try
          SkinObject := LoadSkinObject(S, FRoot);
          SkinObject.Bitmaps := FSkinSource.Bitmaps;
          SkinObject.AfterLoad;
        except
          raise Exception.Create('Invalid clipboard format');
        end;
      finally
        S.Free;
      end;

      if SkinObject = nil then
      begin
        ShowMessage('Invalid clipboard format');
      end;

      FRoot.Aligning;

      { Add TSeSkinObject }
      FSelected := SkinObject;

      UpdateSkin;
      SelectObject(SkinObject);

      FModified := true;
    end
    else
    begin
      { To nil root }
      S := TStringStream.Create(FMyClipboard);
      try
        try
          SkinObject := LoadSkinObject(S, nil);
          SkinObject.Bitmaps := FSkinSource.Bitmaps;
          SkinObject.AfterLoad;
        except
          raise Exception.Create('Invalid clipboard format');
        end;
      finally
        S.Free;
      end;

      if SkinObject = nil then
      begin
        ShowMessage('Invalid clipboard format');
      end;

      FSkinSource.Add(SkinObject);
      { Add TSeSkinObject }
      FRoot := SkinObject;
      FSelected := SkinObject;

      UpdateSkin;
      SelectObject(SkinObject);

      FModified := true;
    end;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
var
  i: integer;
  CharSet: Longint;
begin
  if FSkinSource = nil then Exit;
  { Set Charset }
  if IdentToCharset(cbCharset.Text, CharSet) then
  begin
    for i := 0 to FSkinSource.Count - 1 do
      FSkinSource.Objects[i].SetCharset(CharSet)
  end;
end;

procedure TfrmMain.KSDevelopmantSite1Click(Sender: TObject);
begin
  { Homepage }
  ShellExecute(0, 'open', 'http://www.ksdev.com', nil, nil, SW_SHOWNORMAL);
end;

procedure TfrmMain.AboutSkinBuilder1Click(Sender: TObject);
begin
  { About }
  frmAbout.ShowModal;
end;

procedure TfrmMain.InspectorChanging(Sender: TZPropList; var CanChange: Boolean);
begin
  { Save state }
  SaveState;
end;

procedure TfrmMain.InspectorChange(Sender: TObject);
begin
  { Inspector change }
  UpdateSkin;
  FModified := true;
end;

procedure TfrmMain.RestoreState;
begin
  { Restore state for Undo }
  if FStateNum = 0 then Exit;
  if FStateList.Count = 0 then Exit;

  { Restore }
  FSkinSource.Clear;
  TStateClass(FStateList[FStateNum-1]).Stream.Position := 0;
  FSkinSource.LoadFromStream(TStateClass(FStateList[FStateNum-1]).Stream);
  FRoot := FSkinSource.GetObjectByName(TStateClass(FStateList[FStateNum-1]).Root);
  FSelected := FSkinSource.GetObjectByName(TStateClass(FStateList[FStateNum-1]).Selected);

  { Free }
  TStateClass(FStateList[FStateNum-1]).Free;
  FStateList.Delete(FStateNum-1);
  Dec(FStateNum);

  { Update }
  UpdateSkin;
end;

procedure TfrmMain.SaveState;
var
  State: TStateClass;
begin
  { Save state for Undo }
  State := TStateClass.Create;
  FSkinSource.SaveToStream(State.Stream);
  if FRoot <> nil then
    State.Root := FRoot.Name;
  if FSelected <> nil then
    State.Selected := FSelected.Name;

  { Add to list }
  FStateList.Add(State);

  Inc(FStateNum);

  if FStateNum > 2 then
  begin
    TStateClass(FStateList[0]).Free;
    FStateList.Delete(0);
    Dec(FStateNum);
  end;
end;

procedure TfrmMain.actnUndoExecute(Sender: TObject);
begin
  RestoreState;
end;

end.

