{*************************************************************************
                Copyright (c) PilotLogic Software House

  Package pl_ORCA
  This file is part of CodeTyphon Studio (https://www.pilotlogic.com/)

 ***** BEGIN LICENSE BLOCK *****
 * Version: LGPL-2.1 with link exception (Modified LGPL)
 *
 * The contents of this file are subject to the 
 * GNU LIBRARY GENERAL PUBLIC LICENSE Version 2.1 (the "License")
 * with the following modification:
 * 
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent modules,
 * and to copy and distribute the resulting executable under terms of your choice,
 * provided that you also meet, for each linked independent module, the terms
 * and conditions of the license of that module. An independent module is a
 * module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but
 * you are not obligated to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * ***** END LICENSE BLOCK *****
 
***************************************************************************}

Unit orca_editor_style;

{$mode objfpc}{$H+}

Interface

Uses
   LCLProc, LCLIntf, LCLType, LMessages, LResources, LazUtf8,
   Math, typinfo, IntfGraphics,
   Classes, Variants, SysUtils, Contnrs, Forms, Controls, Dialogs, Graphics,
   StdCtrls, DB, DBCtrls, ExtCtrls, Menus, Clipbrd, ActnList, ImgList,
   orca_scene2d,
   orca_editor_brush;

Type

   TD2StyleDesigner = Class(TForm)
      d2Scene1: TD2Scene;
      Root1: TD2Background;
      ObjectsTree: TD2TreeView;
      d2Scene2: TD2Scene;
      Root2: TD2Background;
      Inspector: TD2Inspector;
      DesignScene: TD2Scene;
      d2Scene3: TD2Scene;
      Root4: TD2Background;
      OpenDialog1: TOpenDialog;
      btnLoadFile: TD2Button;
      SaveDialog1: TSaveDialog;
      btnSaveFile: TD2Button;
      btnClearAll: TD2Button;
      btnLoadDefault: TD2Button;
      btnBackground: TD2Button;
      rectBack: TD2Rectangle;
      d2BrushDialog1: TD2BrushDialog;
      btnApply: TD2Button;
      Button4: TD2Button;
      btnCancel: TD2Button;
      ttClearTimer: TTimer;
      Resources1: TD2Resources;
      textFilter: TD2TextBoxClearBtn;
      Splitter1: TSplitter;
      Splitter2: TSplitter;
      d2Scene4: TD2Scene;
      Root3: TD2Background;
      StatusBar1: TD2StatusBar;
      Label1: TD2Label;
      Procedure ObjectsTreeChange(Sender: TObject);
      Procedure btnLoadFileClick(Sender: TObject);
      Procedure btnSaveFileClick(Sender: TObject);
      Procedure btnApplyClick(Sender: TObject);
      Procedure btnClearAllClick(Sender: TObject);
      Procedure btnLoadDefaultClick(Sender: TObject);
      Procedure FormCreate(Sender: TObject);
      Procedure btnBackgroundClick(Sender: TObject);
      Procedure ObjectsTreeMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
      Procedure ttClearTimerTimer(Sender: TObject);
      Procedure ObjectsTreeDragChange(SourceItem, DestItem: TD2TreeViewItem; Allow: Boolean);
      Procedure InspectorChangeProperty(Sender: TObject; PropertyName: String);
      Procedure ObjectsTreeChangeCheck(Sender: TObject);
      Procedure DesignRootDragOver(Sender: TObject; Const Data: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean);
      Procedure textFilterChangeTracking(Sender: TObject);
   Private
      FResource: TD2Resources;
      Procedure UpdateTree;
      Procedure LoadFromStrings(Str: TStrings);
      Procedure SaveToStrings(Str: TStrings);
      Procedure DoDeleteButton(Sender: TObject);
      Procedure DoVisCheck(Sender: TObject);
      Procedure DeleteObject(AObject: TD2Object; FreeObject: Boolean);
      Procedure TreeItemApplyResource(Sender: TObject);
   Protected
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   End;

   TD2StyleIDEDesigner = Class(TD2Designer)
   Private
   Public
      Procedure SelectObject(ADesignerForm: TComponent; AObject: TD2Object; MultiSelection: Array Of TD2Object); Override;
      Procedure Modified(ADesignerForm: TComponent); Override;
      Function UniqueName(ADesignerForm: TComponent; Const aClassName: String): String; Override;
      Function IsSelected(ADesignerForm: TComponent; AObject: TObject): Boolean; Override;
      Procedure Add2DObject(AObject: TD2Object); Override;
      Procedure Delete2DObject(AObject: TD2Object); Override;
   End;

Function DesignResources(AResource: TD2Resources; Current: String): Boolean;

Var
   GvarD2StyleDesigner: TD2StyleDesigner;

Implementation

{$R *.lfm}

Type
   TD2HackBackground = Class(TD2Background);

   //=====================================================================

Function DesignResources(AResource: TD2Resources; Current: String): Boolean;
Var
   SaveDesigner: TD2Designer;
Begin
   Result := False;
   If AResource <> nil Then
   Begin
      SaveDesigner := GvarD2Designer;
      GvarD2Designer := TD2StyleIDEDesigner.Create(Application);

      GvarD2StyleDesigner := TD2StyleDesigner.Create(Application);

      Application.ProcessMessages;
      GvarD2StyleDesigner.DesignScene.DesignTime := True;
      GvarD2StyleDesigner.FResource := AResource;

      If GvarD2StyleDesigner.DesignScene.Root <> nil Then
         GvarD2StyleDesigner.DesignScene.Root.DeleteChildren;

      AResource.FillStrings;
      GvarD2StyleDesigner.LoadFromStrings(AResource.Resource);

      GvarD2StyleDesigner.ObjectsTree.CollapseAll;

      If (GvarD2StyleDesigner.DesignScene.Root <> nil) And (GvarD2StyleDesigner.DesignScene.Root.FindResource(Current) <> nil) Then
         GvarD2Designer.SelectObject(nil, GvarD2StyleDesigner.DesignScene.Root.FindResource(Current), []);

      If GvarD2StyleDesigner.ShowModal = mrOk Then
      Begin
         GvarD2StyleDesigner.SaveToStrings(AResource.Resource);
         Result := True;
      End;

      GvarD2StyleDesigner.d2Scene1.Free;
      GvarD2StyleDesigner.DesignScene.Free;
      GvarD2StyleDesigner.d2Scene2.Free;
      GvarD2StyleDesigner.d2Scene3.Free;

      GvarD2StyleDesigner.Free;
      GvarD2StyleDesigner := nil;

      GvarD2Designer.Free;

      GvarD2Designer := SaveDesigner;
   End;
End;
//=============== TD2StyleDesigner =====================================

Procedure TD2StyleDesigner.FormCreate(Sender: TObject);
Begin
   OpenDialog1.Filter := 'ORCA2D style|*.orca2dstyle';
   SaveDialog1.Filter := 'ORCA2D style|*.orca2dstyle';
End;

Procedure TD2StyleDesigner.UpdateTree;
//---------------
   Procedure _ProcessObject(Obj: TD2Object; Node: TD2TreeViewItem);
   Var
      i: Integer;
      N: TD2TreeViewItem;
      S: Ansistring;
   Begin
      S := Obj.ResourceName;
      If S = '' Then
         S := Obj.ClassName
      Else
         S := S + ':' + Obj.ClassName;

      If (textFilter.TextW <> '') And (Obj.Parent = DesignScene.Root) Then
      Begin
         If Pos(textFilter.TextW, S) = 0 Then
            Exit;
      End;

      If Obj = DesignScene.Root Then
      Begin
         N := nil;
      End Else
      If Node <> nil Then
      Begin
         N := TD2TreeViewItem.Create(Self);
         N.Parent := Node;
         N.TextW := S;
         N.TagObject := Obj;
         N.Resource := 'objecttreeitemstyle';
         N.OnApplyResource := @TreeItemApplyResource;
         If Obj.IsVisual Then
            N.IsChecked := Not Obj.Visual.Locked;
      End Else Begin
         N := TD2TreeViewItem.Create(Self);
         N.Parent := ObjectsTree;
         N.TextW := S;
         N.TagObject := Obj;
         N.Resource := 'objecttreeitemstyle';
         N.OnApplyResource := @TreeItemApplyResource;
         If Obj.IsVisual Then
            N.IsChecked := Not Obj.Visual.Locked;
      End;
      Obj.TagObject := N;
      If Obj.isVisual And (Obj.Parent = DesignScene.Root) Then
      Begin
         Obj.Visual.DesignHide := True;

         With Obj.Visual Do
         Begin
            SetBounds(Trunc((DesignScene.Width - Width) / 2), Trunc((DesignScene.Height - Height) / 2), Width, Height);
         End;
      End;
      If Obj.ChildrenCount > 0 Then
         For i := 0 To Obj.ChildrenCount - 1 Do
         Begin
            If (DesignScene.Root Is TD2Background) And (Obj.Children[i] = TD2HackBackground(DesignScene.Root).FResourceLink) Then
               Continue;
            _ProcessObject(Obj.Children[i], N);
         End;
   End;
   //---------------
Begin
   If DesignScene.Root <> nil Then
   Begin
      ObjectsTree.Clear;
      ObjectsTree.BeginUpdate;
      _ProcessObject(DesignScene.Root, nil);
      ObjectsTree.EndUpdate;
      DesignScene.AddUpdateRect(d2Rect(0, 0, 1000, 1000));
   End;
End;

Procedure TD2StyleDesigner.textFilterChangeTracking(Sender: TObject);
Begin
   UpdateTree;
End;

Procedure TD2StyleDesigner.DoVisCheck(Sender: TObject);
Begin
   TD2VisualObject(TD2Object(Sender).TagObject).Visible := TD2Object(Sender).Data;
End;

Procedure TD2StyleDesigner.ObjectsTreeChange(Sender: TObject);
Var
   xNew, xOld: TD2Object;
Begin
   If (ObjectsTree.Selected <> nil) And (ObjectsTree.Selected.TagObject <> nil) Then
   Begin
      xOld := TD2Object(Inspector.SelectedObject);
      If (xOld <> nil) And (xOld.Parent <> nil) Then
         While xOld.Parent <> DesignScene.Root Do
            xOld := xOld.Parent;

      xNew := TD2Object(ObjectsTree.Selected.TagObject);

      While xNew.Parent <> DesignScene.Root Do
         xNew := xNew.Parent;

      If xNew.IsVisual Then
      Begin
         xNew.Visual.Position.X := round((DesignScene.Width - xNew.Visual.Width) / 2);
         xNew.Visual.Position.Y := round((DesignScene.Height - xNew.Visual.Height) / 2);

         If (xOld <> nil) And (xOld.IsVisual) Then
         Begin
            If xOld <> DesignScene.Root Then
               xOld.Visual.DesignHide := True;
         End Else
         If (xOld <> nil) And (xOld.Parent <> nil) And (xOld.Parent.IsVisual) Then
         Begin
            If xOld.Parent <> DesignScene.Root Then
               xOld.Parent.Visual.DesignHide := True;
         End;
         If xNew <> DesignScene.Root Then
            xNew.Visual.DesignHide := False;
      End Else Begin
         If (xNew <> nil) And (xNew.Parent <> nil) And (xNew.Parent.IsVisual) Then
         Begin
            xNew.Parent.Visual.DesignHide := False;
         End;
      End;

      Inspector.SelectedObject := TD2Object(ObjectsTree.Selected.TagObject);

      If TD2Object(ObjectsTree.Selected.TagObject).isVisual Then
         DesignScene.Selected := TD2VisualObject(ObjectsTree.Selected.TagObject);
   End;
End;

Procedure TD2StyleDesigner.btnLoadFileClick(Sender: TObject);
Var
   xStr: TStrings;
Begin
   If OpenDialog1.Execute Then
   Begin
      If FResource <> nil Then
      Begin
         FResource.LoadFromFile(OpenDialog1.FileName);
         LoadFromStrings(FResource.Resource);
      End Else Begin
         xStr := TStringList.Create;
         xStr.LoadFromFile(OpenDialog1.FileName);
         LoadFromStrings(xStr);
         xStr.Free;
      End;
   End;
End;

Procedure TD2StyleDesigner.btnSaveFileClick(Sender: TObject);
Var
   xStr: TStrings;
Begin
   If SaveDialog1.Execute Then
   Begin
      If FResource <> nil Then
      Begin
         SaveToStrings(FResource.Resource);
         FResource.SaveToFile(SaveDialog1.FileName);
      End Else Begin
         xStr := TStringList.Create;
         SaveToStrings(xStr);
         xStr.SaveToFile(SaveDialog1.FileName);
         xStr.Free;
      End;
   End;
End;

Procedure TD2StyleDesigner.LoadFromStrings(Str: TStrings);
Var
   i: Integer;
   xMS: TStream;
   xRoot: TD2Object;
Begin
   xMS := TMemoryStream.Create;
   Try
      Inspector.SelectedObject := nil;
      DesignScene.Selected := nil;
      ObjectsTree.Selected := nil;

      Str.SaveToStream(xMS);
      If xMS.Position > 0 Then
      Begin
         xMS.Position := 0;
         If DesignScene.Root <> nil Then
            DesignScene.Root.Free;

         xRoot := CreateD2ObjectFromStream(nil, xMS);
         DesignScene.AddObject(xRoot);

         For i := 0 To xRoot.ChildrenCount - 1 Do
            If xRoot.Children[i].isVisual Then
            Begin
               TD2VisualObject(xRoot.Children[i]).Locked := False;
            End;

         If xRoot.ChildrenCount > 0 Then
            GvarD2Designer.SelectObject(nil, xRoot.Children[0], []);
         UpdateTree;
         DesignScene.RealignRoot;

         If xRoot Is TD2Background Then
         Begin
            btnBackground.Visible := True;
            rectBack.Fill.Assign(TD2Background(xRoot).Fill);
         End Else Begin
            btnBackground.Visible := False;
         End;
      End;
   Finally
      xMS.Free;
   End;
End;

Procedure TD2StyleDesigner.SaveToStrings(Str: TStrings);
Var
   i: Integer;
   xMS: TStream;
   xRoot: TD2Object;
Begin
   xMS := TMemoryStream.Create;
   Try
      xRoot := DesignScene.Root;
      Str.Clear;
      For i := 0 To xRoot.ChildrenCount - 1 Do
         If xRoot.Children[i].isVisual Then
         Begin
            TD2VisualObject(xRoot.Children[i]).Locked := False;
         End;

      xRoot.SaveToStream(xMS);
      xMS.Position := 0;
      Str.LoadFromStream(xMS);
   Finally
      xMS.Free;
   End;
End;

Procedure TD2StyleDesigner.btnApplyClick(Sender: TObject);
Begin
   If FResource <> nil Then
      SaveToStrings(FResource.Resource);
End;

Procedure TD2StyleDesigner.btnClearAllClick(Sender: TObject);
Begin
   btnClearAll.Enabled := False;
   ttClearTimer.Enabled := True;
End;

Procedure TD2StyleDesigner.ttClearTimerTimer(Sender: TObject);
Var
   xR: TD2Background;
Begin
   ttClearTimer.Enabled := False;
   Inspector.SelectedObject := nil;
   DesignScene.Selected := nil;
   ObjectsTree.Selected := nil;

   DesignScene.Root.Free;
   xR := TD2Background.Create(nil);
   DesignScene.AddObject(xR);

   UpdateTree;
   btnClearAll.Enabled := True;
End;

Procedure TD2StyleDesigner.btnLoadDefaultClick(Sender: TObject);
Var
   i: Integer;
   xMS: TStream;
   xRoot: TD2Object;
Begin
   If GvarD2DefaultStyles = nil Then
      Exit;

   Inspector.SelectedObject := nil;
   DesignScene.Selected := nil;
   ObjectsTree.Selected := nil;
   xRoot := nil;


   If System.FindResource(HInstance, PChar('DEFAULTORCASTYLE'), RT_RCDATA) <> 0 Then
   Begin
      xMS := TResourceStream.Create(HInstance, 'DEFAULTORCASTYLE', RT_RCDATA);
      xRoot := CreateD2ObjectFromBinStream(nil, xMS);
      xMS.Free;
   End;
   DesignScene.Root.Free;
   DesignScene.AddObject(xRoot);
   For i := 0 To xRoot.ChildrenCount - 1 Do
      If xRoot.Children[i].isVisual Then
      Begin
         TD2VisualObject(xRoot.Children[i]).Locked := False;
      End;

   If xRoot.ChildrenCount > 0 Then
      GvarD2Designer.SelectObject(nil, xRoot.Children[0], []);

   UpdateTree;

   DesignScene.RealignRoot;

   If xRoot Is TD2Background Then
   Begin
      btnBackground.Visible := True;
      rectBack.Fill.Assign(TD2Background(xRoot).Fill);
   End Else Begin
      btnBackground.Visible := False;
   End;
End;

Procedure TD2StyleDesigner.DeleteObject(AObject: TD2Object; FreeObject: Boolean);
Var
   A, P: TD2Object;
   xSel: TD2TreeViewItem;
Begin
   If AObject <> nil Then
   Begin
      AObject.RemoveFreeNotify(GvarD2StyleDesigner);

      xSel := TD2TreeViewItem(AObject.TagObject);
      A := AObject;
      P := A.Parent;
      If P <> nil Then
      Begin
         Inspector.SelectedObject := P;
         If P.TagObject <> nil Then
            ObjectsTree.Selected := TD2TreeViewItem(P.TagObject);
      End Else Begin
         Inspector.SelectedObject := nil;
         If P.TagObject <> nil Then
            ObjectsTree.Selected := nil;
      End;
      xSel.Release;
      If FreeObject Then
         A.Release
      Else
         A.TagObject := nil;
      ObjectsTree.Realign;
   End;
End;

Procedure TD2StyleDesigner.btnBackgroundClick(Sender: TObject);
Begin
   d2BrushDialog1.Brush.Assign(rectBack.Fill);
   If d2BrushDialog1.Execute Then
   Begin
      rectBack.Fill.Assign(d2BrushDialog1.Brush);
      If (DesignScene.Root <> nil) And (DesignScene.Root Is TD2Background) Then
         TD2Background(DesignScene.Root).Fill.Assign(d2BrushDialog1.Brush);
      TD2Background(DesignScene.Root).Repaint;
   End;
End;

Procedure TD2StyleDesigner.DoDeleteButton(Sender: TObject);
Begin
   If ObjectsTree.Selected = nil Then
      Exit;
   If (Sender Is TD2SpeedButton) Then
   Begin
      TD2SpeedButton(Sender).Enabled := False;
   End;
   DeleteObject(TD2Object(ObjectsTree.Selected.TagObject), True);
End;

Procedure TD2StyleDesigner.ObjectsTreeMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   If (Button = mbRight) Then
   Begin
      DesignScene.OpenDesignPopup;
   End;
End;

Procedure TD2StyleDesigner.ObjectsTreeDragChange(SourceItem, DestItem: TD2TreeViewItem; Allow: Boolean);
Begin
   If DestItem = nil Then
      TD2Object(SourceItem.TagObject).Parent := DesignScene.Root
   Else
      TD2Object(SourceItem.TagObject).Parent := TD2Object(DestItem.TagObject);
End;

Procedure TD2StyleDesigner.InspectorChangeProperty(Sender: TObject; PropertyName: String);
Begin
   If GvarD2StyleDesigner.Inspector.SelectedObject = nil Then
      Exit;
   If TD2TreeViewItem(TD2Object(GvarD2StyleDesigner.Inspector.SelectedObject).TagObject) = nil Then
      Exit;

   If PropertyName = 'ResourceName' Then
   Begin
      TD2TreeViewItem(TD2Object(GvarD2StyleDesigner.Inspector.SelectedObject).TagObject).TextW :=
         GetPropValue(GvarD2StyleDesigner.Inspector.SelectedObject, PropertyName) + ':' + GvarD2StyleDesigner.Inspector.SelectedObject.ClassName;
   End;
End;

Procedure TD2StyleDesigner.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   If (Operation = opRemove) And (AComponent Is TD2Object) And Assigned(TD2Object(AComponent).Scene) And (TD2Object(AComponent).Scene.GetComponent = DesignScene) Then
   Begin
      DeleteObject(TD2Object(AComponent), False);
   End Else Begin
      Inherited;
   End;
End;

Procedure TD2StyleDesigner.ObjectsTreeChangeCheck(Sender: TObject);
Begin
   If (ObjectsTree.Selected <> nil) And (ObjectsTree.Selected.TagObject <> nil) And (TD2Object(ObjectsTree.Selected.TagObject).IsVisual) Then
   Begin
      TD2VisualObject(ObjectsTree.Selected.TagObject).Locked := Not ObjectsTree.Selected.IsChecked;
   End;
End;

Procedure TD2StyleDesigner.DesignRootDragOver(Sender: TObject; Const Data: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean);
Begin
   Accept := True;
End;

Procedure TD2StyleDesigner.TreeItemApplyResource(Sender: TObject);
Begin
   TD2TreeViewItem(Sender).Binding['deletebutton'] := EventToVariant(@DoDeleteButton);
   TD2TreeViewItem(Sender).Binding['vischeck'] := EventToVariant(@DoVisCheck);

   If TD2TreeViewItem(Sender).FindBinding('vischeck') <> nil Then
   Begin
      TD2Object(TD2TreeViewItem(Sender).FindBinding('vischeck')).TagObject := TD2TreeViewItem(Sender).TagObject;
      TD2VisualObject(TD2TreeViewItem(Sender).FindBinding('vischeck')).Visible := TD2TreeViewItem(Sender).TagObject Is TD2VisualObject;
      If TD2TreeViewItem(Sender).TagObject Is TD2VisualObject Then
         TD2TreeViewItem(Sender).Binding['vischeck'] := TD2VisualObject(TD2TreeViewItem(Sender).TagObject).Visible;
   End;

   If Not (TD2TreeViewItem(Sender).TagObject Is TD2VisualObject) Then
      TD2VisualObject(TD2TreeViewItem(Sender).FindResource('check')).Visible := False;
End;

//=============== TD2StyleIDEDesigner ========================================

Procedure TD2StyleIDEDesigner.Modified(ADesignerForm: TComponent);
Begin
End;

Function TD2StyleIDEDesigner.IsSelected(ADesignerForm: TComponent; AObject: TObject): Boolean;
Begin
   Result := False;
End;

Procedure TD2StyleIDEDesigner.SelectObject(ADesignerForm: TComponent; AObject: TD2Object; MultiSelection: Array Of TD2Object);
Begin
   If AObject = GvarD2StyleDesigner.DesignScene.Root Then
      Exit;

   GvarD2StyleDesigner.Inspector.SelectedObject := AObject;
   If GvarD2StyleDesigner.Inspector.SelectedObject Is TD2Object Then
      TD2Object(GvarD2StyleDesigner.Inspector.SelectedObject).AddFreeNotify(GvarD2StyleDesigner);
   If AObject.TagObject <> nil Then
   Begin
      GvarD2StyleDesigner.ObjectsTree.Selected := TD2TreeViewItem(AObject.TagObject);
   End;
End;

Function TD2StyleIDEDesigner.UniqueName(ADesignerForm: TComponent; Const aClassName: String): String;
Begin
   Result := '';
End;

Procedure TD2StyleIDEDesigner.Add2DObject(AObject: TD2Object);
Begin
   Inherited;
   GvarD2StyleDesigner.UpdateTree;
End;

Procedure TD2StyleIDEDesigner.Delete2DObject(AObject: TD2Object);
Begin
   Inherited;
End;


End.
