{*************************************************************************
                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_scene2d;

{$MODE DELPHI}

{$I orca_define.inc}
{$H+}
{$ALIGN ON}
{$MINENUMSIZE 4}

Interface

Uses
   {$IFDEF ORCA2D_OS_WINDOWS}
   Windows, Messages, ShellAPI, ActiveX, CommCtrl, MultiMon,
   GDIPAPI, GDIPOBJ, GDIPUTIL, GDICONST,
   //.....
   {$IFDEF ORCA3D_USE_DIRECTX9}
    wincodec,
    D2D1, DirectWrite, DXGI,
   {$ENDIF}
   {$IFDEF ORCA3D_USE_DIRECTX11}
    wincodec,
    D2D1, DirectWrite, DXGI,
   {$ENDIF}
   {$IFDEF ORCA3D_USE_DIRECTX12}
    DX12.D2D1, DX12.DCommon, DX12.DWrite, DX12.DXGI, DX12.WinCodec,
    DX12.OCIdl,
   {$ENDIF}
   //.....
   {$ENDIF}

   {$IFDEF ORCA2D_USE_CARBON}
   MacOSAll,
   CarbonDef, CarbonPrivate, carboncanvas, CarbonProc, Maps,
   {$ENDIF}
   {$IFDEF ORCA2D_USE_COCOA}
 
   {$ENDIF}
   {$IFDEF ORCA2D_USE_GTK2}
   xlib, x,xutil,
   cairo, cairoXlib,
   gtk2def, gtk2proc, gtk2, gdk2, gdk2x, gdk2pixbuf, glib2,
   pango, pangocairo,
   pangocairo_gtk2_api,
   {$ENDIF}
   {$IFDEF ORCA2D_USE_GTK3}
   libGdk3, libxlib2, libGLib2, libGdkPixbuf2, libGObject2, libGio2, libcairo1, libPango1, libcairo,
   libPangoCairo1, gtk3objects,
   pangocairo_gtk3_api,
   {$ENDIF}
   {$IFDEF ORCA2D_USE_GTK4}
   {$ENDIF}
   {$IFDEF ORCA2D_USE_QT4}
   {$ENDIF}
   {$IFDEF ORCA2D_USE_QT5}
   {$ENDIF}
   {$IFDEF ORCA2D_USE_QT6}
   {$ENDIF}
   Math, typinfo, IntfGraphics,
   LCLProc, LCLIntf, LCLType, LMessages, LResources, LazUtf8,
   Classes, Variants, SysUtils, Contnrs, Forms, Controls, Dialogs, Graphics,
   StdCtrls, DB, DBCtrls, ExtCtrls, Menus, Clipbrd, ActnList, ImgList;

   //=============================================================================
   //=============== GLobal const/Emu Types ======================================
   //=============================================================================

Const
   cnGripSize = 3;
   cnRotSize = 10;
   cnInitRepeatPause = 400;  // pause before repeat timer (ms)
   cnRepeatPause = 100;  // pause before hint window displays (ms)
   cnSpaceSize = 5;   // size of space between special buttons
   cnColorPickSize = 10;
   cnmerge3 = 0;
   cnmerged2d = 0;
   cnmergegdip = 0;
   cnmerge2 = 0;

Const
   WM_ADDUPDATERECT = WM_USER + 123;

   InvalideCanvasState = $FFFFFFFF;

   vcAliceblue = '#FFF0F8FF';
   vcAntiquewhite = '#FFFAEBD7';
   vcAqua = '#FF00FFFF';
   vcAquamarine = '#FF7FFFD4';
   vcAzure = '#FFF0FFFF';
   vcBeige = '#FFF5F5DC';
   vcBisque = '#FFFFE4C4';
   vcBlack = '#FF000000';
   vcBlanchedalmond = '#FFFFEBCD';
   vcBlue = '#FF0000FF';
   vcBlueviolet = '#FF8A2BE2';
   vcBrown = '#FFA52A2A';
   vcBurlywood = '#FFDEB887';
   vcCadetblue = '#FF5F9EA0';
   vcChartreuse = '#FF7FFF00';
   vcChocolate = '#FFD2691E';
   vcCoral = '#FFFF7F50';
   vcCornflowerblue = '#FF6495ED';
   vcCornsilk = '#FFFFF8DC';
   vcCrimson = '#FFDC143C';
   vcCyan = '#FF00FFFF';
   vcDarkblue = '#FF00008B';
   vcDarkcyan = '#FF008B8B';
   vcDarkgoldenrod = '#FFB8860B';
   vcDarkgray = '#FFA9A9A9';
   vcDarkgreen = '#FF006400';
   vcDarkgrey = '#FFA9A9A9';
   vcDarkkhaki = '#FFBDB76B';
   vcDarkmagenta = '#FF8B008B';
   vcDarkolivegreen = '#FF556B2F';
   vcDarkorange = '#FFFF8C00';
   vcDarkorchid = '#FF9932CC';
   vcDarkred = '#FF8B0000';
   vcDarksalmon = '#FFE9967A';
   vcDarkseagreen = '#FF8FBC8F';
   vcDarkslateblue = '#FF483D8B';
   vcDarkslategray = '#FF2F4F4F';
   vcDarkslategrey = '#FF2F4F4F';
   vcDarkturquoise = '#FF00CED1';
   vcDarkviolet = '#FF9400D3';
   vcDeeppink = '#FFFF1493';
   vcDeepskyblue = '#FF00BFFF';
   vcDimgray = '#FF696969';
   vcDimgrey = '#FF696969';
   vcDodgerblue = '#FF1E90FF';
   vcFirebrick = '#FFB22222';
   vcFloralwhite = '#FFFFFAF0';
   vcForestgreen = '#FF228B22';
   vcFuchsia = '#FFFF00FF';
   vcGainsboro = '#FFDCDCDC';
   vcGhostwhite = '#FFF8F8FF';
   vcGold = '#FFFFD700';
   vcGoldenrod = '#FFDAA520';
   vcGray = '#FF808080';
   vcGreen = '#FF008000';
   vcGreenyellow = '#FFADFF2F';
   vcGrey = '#FF808080';
   vcHoneydew = '#FFF0FFF0';
   vcHotpink = '#FFFF69B4';
   vcIndianred = '#FFCD5C5C';
   vcIndigo = '#FF4B0082';
   vcIvory = '#FFFFFFF0';
   vcKhaki = '#FFF0E68C';
   vcLavender = '#FFE6E6FA';
   vcLavenderblush = '#FFFFF0F5';
   vcLawngreen = '#FF7CFC00';
   vcLemonchiffon = '#FFFFFACD';
   vcLightblue = '#FFADD8E6';
   vcLightcoral = '#FFF08080';
   vcLightcyan = '#FFE0FFFF';
   vcLightgoldenrodyellow = '#FFFAFAD2';
   vcLightgray = '#FFD3D3D3';
   vcLightgreen = '#FF90EE90';
   vcLightgrey = '#FFD3D3D3';
   vcLightpink = '#FFFFB6C1';
   vcLightsalmon = '#FFFFA07A';
   vcLightseagreen = '#FF20B2AA';
   vcLightskyblue = '#FF87CEFA';
   vcLightslategray = '#FF778899';
   vcLightslategrey = '#FF778899';
   vcLightsteelblue = '#FFB0C4DE';
   vcLightyellow = '#FFFFFFE0';
   vcLime = '#FF00FF00';
   vcLimegreen = '#FF32CD32';
   vcLinen = '#FFFAF0E6';
   vcMagenta = '#FFFF00FF';
   vcMaroon = '#FF800000';
   vcMediumaquamarine = '#FF66CDAA';
   vcMediumblue = '#FF0000CD';
   vcMediumorchid = '#FFBA55D3';
   vcMediumpurple = '#FF9370DB';
   vcMediumseagreen = '#FF3CB371';
   vcMediumslateblue = '#FF7B68EE';
   vcMediumspringgreen = '#FF00FA9A';
   vcMediumturquoise = '#FF48D1CC';
   vcMediumvioletred = '#FFC71585';
   vcMidnightblue = '#FF191970';
   vcMintcream = '#FFF5FFFA';
   vcMistyrose = '#FFFFE4E1';
   vcMoccasin = '#FFFFE4B5';
   vcNavajowhite = '#FFFFDEAD';
   vcNavy = '#FF000080';
   vcOldlace = '#FFFDF5E6';
   vcOlive = '#FF808000';
   vcOlivedrab = '#FF6B8E23';
   vcOrange = '#FFFFA500';
   vcOrangered = '#FFFF4500';
   vcOrchid = '#FFDA70D6';
   vcPalegoldenrod = '#FFEEE8AA';
   vcPalegreen = '#FF98FB98';
   vcPaleturquoise = '#FFAFEEEE';
   vcPalevioletred = '#FFDB7093';
   vcPapayawhip = '#FFFFEFD5';
   vcPeachpuff = '#FFFFDAB9';
   vcPeru = '#FFCD853F';
   vcPink = '#FFFFC0CB';
   vcPlum = '#FFDDA0DD';
   vcPowderblue = '#FFB0E0E6';
   vcPurple = '#FF800080';
   vcRed = '#FFFF0000';
   vcRosybrown = '#FFBC8F8F';
   vcRoyalblue = '#FF4169E1';
   vcSaddlebrown = '#FF8B4513';
   vcSalmon = '#FFFA8072';
   vcSandybrown = '#FFF4A460';
   vcSeagreen = '#FF2E8B57';
   vcSeashell = '#FFFFF5EE';
   vcSienna = '#FFA0522D';
   vcSilver = '#FFC0C0C0';
   vcSkyblue = '#FF87CEEB';
   vcSlateblue = '#FF6A5ACD';
   vcSlategray = '#FF708090';
   vcSlategrey = '#FF708090';
   vcSnow = '#FFFFFAFA';
   vcSpringgreen = '#FF00FF7F';
   vcSteelblue = '#FF4682B4';
   vcTan = '#FFD2B48C';
   vcTeal = '#FF008080';
   vcThistle = '#FFD8BFD8';
   vcTomato = '#FFFF6347';
   vcTurquoise = '#FF40E0D0';
   vcViolet = '#FFEE82EE';
   vcWheat = '#FFF5DEB3';
   vcWhite = '#FFFFFFFF';
   vcWhitesmoke = '#FFF5F5F5';
   vcYellow = '#FFFFFF00';
   vcYellowgreen = '#FF9ACD32';


Type
   TD2ColorIdent = Record
      Name: String;
      Value: String;
   End;

Const

   d2ColorIdents: Array [0..146] Of TD2ColorIdent = (
      (Name: 'Aliceblue'; Value: '#FFF0F8FF'),
      (Name: 'Antiquewhite'; Value: '#FFFAEBD7'),
      (Name: 'Aqua'; Value: '#FF00FFFF'),
      (Name: 'Aquamarine'; Value: '#FF7FFFD4'),
      (Name: 'Azure'; Value: '#FFF0FFFF'),
      (Name: 'Beige'; Value: '#FFF5F5DC'),
      (Name: 'Bisque'; Value: '#FFFFE4C4'),
      (Name: 'Black'; Value: '#FF000000'),
      (Name: 'Blanchedalmond'; Value: '#FFFFEBCD'),
      (Name: 'Blue'; Value: '#FF0000FF'),
      (Name: 'Blueviolet'; Value: '#FF8A2BE2'),
      (Name: 'Brown'; Value: '#FFA52A2A'),
      (Name: 'Burlywood'; Value: '#FFDEB887'),
      (Name: 'Cadetblue'; Value: '#FF5F9EA0'),
      (Name: 'Chartreuse'; Value: '#FF7FFF00'),
      (Name: 'Chocolate'; Value: '#FFD2691E'),
      (Name: 'Coral'; Value: '#FFFF7F50'),
      (Name: 'Cornflowerblue'; Value: '#FF6495ED'),
      (Name: 'Cornsilk'; Value: '#FFFFF8DC'),
      (Name: 'Crimson'; Value: '#FFDC143C'),
      (Name: 'Cyan'; Value: '#FF00FFFF'),
      (Name: 'Darkblue'; Value: '#FF00008B'),
      (Name: 'Darkcyan'; Value: '#FF008B8B'),
      (Name: 'Darkgoldenrod'; Value: '#FFB8860B'),
      (Name: 'Darkgray'; Value: '#FFA9A9A9'),
      (Name: 'Darkgreen'; Value: '#FF006400'),
      (Name: 'Darkgrey'; Value: '#FFA9A9A9'),
      (Name: 'Darkkhaki'; Value: '#FFBDB76B'),
      (Name: 'Darkmagenta'; Value: '#FF8B008B'),
      (Name: 'Darkolivegreen'; Value: '#FF556B2F'),
      (Name: 'Darkorange'; Value: '#FFFF8C00'),
      (Name: 'Darkorchid'; Value: '#FF9932CC'),
      (Name: 'Darkred'; Value: '#FF8B0000'),
      (Name: 'Darksalmon'; Value: '#FFE9967A'),
      (Name: 'Darkseagreen'; Value: '#FF8FBC8F'),
      (Name: 'Darkslateblue'; Value: '#FF483D8B'),
      (Name: 'Darkslategray'; Value: '#FF2F4F4F'),
      (Name: 'Darkslategrey'; Value: '#FF2F4F4F'),
      (Name: 'Darkturquoise'; Value: '#FF00CED1'),
      (Name: 'Darkviolet'; Value: '#FF9400D3'),
      (Name: 'Deeppink'; Value: '#FFFF1493'),
      (Name: 'Deepskyblue'; Value: '#FF00BFFF'),
      (Name: 'Dimgray'; Value: '#FF696969'),
      (Name: 'Dimgrey'; Value: '#FF696969'),
      (Name: 'Dodgerblue'; Value: '#FF1E90FF'),
      (Name: 'Firebrick'; Value: '#FFB22222'),
      (Name: 'Floralwhite'; Value: '#FFFFFAF0'),
      (Name: 'Forestgreen'; Value: '#FF228B22'),
      (Name: 'Fuchsia'; Value: '#FFFF00FF'),
      (Name: 'Gainsboro'; Value: '#FFDCDCDC'),
      (Name: 'Ghostwhite'; Value: '#FFF8F8FF'),
      (Name: 'Gold'; Value: '#FFFFD700'),
      (Name: 'Goldenrod'; Value: '#FFDAA520'),
      (Name: 'Gray'; Value: '#FF808080'),
      (Name: 'Green'; Value: '#FF008000'),
      (Name: 'Greenyellow'; Value: '#FFADFF2F'),
      (Name: 'Grey'; Value: '#FF808080'),
      (Name: 'Honeydew'; Value: '#FFF0FFF0'),
      (Name: 'Hotpink'; Value: '#FFFF69B4'),
      (Name: 'Indianred'; Value: '#FFCD5C5C'),
      (Name: 'Indigo'; Value: '#FF4B0082'),
      (Name: 'Ivory'; Value: '#FFFFFFF0'),
      (Name: 'Khaki'; Value: '#FFF0E68C'),
      (Name: 'Lavender'; Value: '#FFE6E6FA'),
      (Name: 'Lavenderblush'; Value: '#FFFFF0F5'),
      (Name: 'Lawngreen'; Value: '#FF7CFC00'),
      (Name: 'Lemonchiffon'; Value: '#FFFFFACD'),
      (Name: 'Lightblue'; Value: '#FFADD8E6'),
      (Name: 'Lightcoral'; Value: '#FFF08080'),
      (Name: 'Lightcyan'; Value: '#FFE0FFFF'),
      (Name: 'Lightgoldenrodyellow'; Value: '#FFFAFAD2'),
      (Name: 'Lightgray'; Value: '#FFD3D3D3'),
      (Name: 'Lightgreen'; Value: '#FF90EE90'),
      (Name: 'Lightgrey'; Value: '#FFD3D3D3'),
      (Name: 'Lightpink'; Value: '#FFFFB6C1'),
      (Name: 'Lightsalmon'; Value: '#FFFFA07A'),
      (Name: 'Lightseagreen'; Value: '#FF20B2AA'),
      (Name: 'Lightskyblue'; Value: '#FF87CEFA'),
      (Name: 'Lightslategray'; Value: '#FF778899'),
      (Name: 'Lightslategrey'; Value: '#FF778899'),
      (Name: 'Lightsteelblue'; Value: '#FFB0C4DE'),
      (Name: 'Lightyellow'; Value: '#FFFFFFE0'),
      (Name: 'Lime'; Value: '#FF00FF00'),
      (Name: 'Limegreen'; Value: '#FF32CD32'),
      (Name: 'Linen'; Value: '#FFFAF0E6'),
      (Name: 'Magenta'; Value: '#FFFF00FF'),
      (Name: 'Maroon'; Value: '#FF800000'),
      (Name: 'Mediumaquamarine'; Value: '#FF66CDAA'),
      (Name: 'Mediumblue'; Value: '#FF0000CD'),
      (Name: 'Mediumorchid'; Value: '#FFBA55D3'),
      (Name: 'Mediumpurple'; Value: '#FF9370DB'),
      (Name: 'Mediumseagreen'; Value: '#FF3CB371'),
      (Name: 'Mediumslateblue'; Value: '#FF7B68EE'),
      (Name: 'Mediumspringgreen'; Value: '#FF00FA9A'),
      (Name: 'Mediumturquoise'; Value: '#FF48D1CC'),
      (Name: 'Mediumvioletred'; Value: '#FFC71585'),
      (Name: 'Midnightblue'; Value: '#FF191970'),
      (Name: 'Mintcream'; Value: '#FFF5FFFA'),
      (Name: 'Mistyrose'; Value: '#FFFFE4E1'),
      (Name: 'Moccasin'; Value: '#FFFFE4B5'),
      (Name: 'Navajowhite'; Value: '#FFFFDEAD'),
      (Name: 'Navy'; Value: '#FF000080'),
      (Name: 'Oldlace'; Value: '#FFFDF5E6'),
      (Name: 'Olive'; Value: '#FF808000'),
      (Name: 'Olivedrab'; Value: '#FF6B8E23'),
      (Name: 'Orange'; Value: '#FFFFA500'),
      (Name: 'Orangered'; Value: '#FFFF4500'),
      (Name: 'Orchid'; Value: '#FFDA70D6'),
      (Name: 'Palegoldenrod'; Value: '#FFEEE8AA'),
      (Name: 'Palegreen'; Value: '#FF98FB98'),
      (Name: 'Paleturquoise'; Value: '#FFAFEEEE'),
      (Name: 'Palevioletred'; Value: '#FFDB7093'),
      (Name: 'Papayawhip'; Value: '#FFFFEFD5'),
      (Name: 'Peachpuff'; Value: '#FFFFDAB9'),
      (Name: 'Peru'; Value: '#FFCD853F'),
      (Name: 'Pink'; Value: '#FFFFC0CB'),
      (Name: 'Plum'; Value: '#FFDDA0DD'),
      (Name: 'Powderblue'; Value: '#FFB0E0E6'),
      (Name: 'Purple'; Value: '#FF800080'),
      (Name: 'Red'; Value: '#FFFF0000'),
      (Name: 'Rosybrown'; Value: '#FFBC8F8F'),
      (Name: 'Royalblue'; Value: '#FF4169E1'),
      (Name: 'Saddlebrown'; Value: '#FF8B4513'),
      (Name: 'Salmon'; Value: '#FFFA8072'),
      (Name: 'Sandybrown'; Value: '#FFF4A460'),
      (Name: 'Seagreen'; Value: '#FF2E8B57'),
      (Name: 'Seashell'; Value: '#FFFFF5EE'),
      (Name: 'Sienna'; Value: '#FFA0522D'),
      (Name: 'Silver'; Value: '#FFC0C0C0'),
      (Name: 'Skyblue'; Value: '#FF87CEEB'),
      (Name: 'Slateblue'; Value: '#FF6A5ACD'),
      (Name: 'Slategray'; Value: '#FF708090'),
      (Name: 'Slategrey'; Value: '#FF708090'),
      (Name: 'Snow'; Value: '#FFFFFAFA'),
      (Name: 'Springgreen'; Value: '#FF00FF7F'),
      (Name: 'Steelblue'; Value: '#FF4682B4'),
      (Name: 'Tan'; Value: '#FFD2B48C'),
      (Name: 'Teal'; Value: '#FF008080'),
      (Name: 'Thistle'; Value: '#FFD8BFD8'),
      (Name: 'Tomato'; Value: '#FFFF6347'),
      (Name: 'Turquoise'; Value: '#FF40E0D0'),
      (Name: 'Violet'; Value: '#FFEE82EE'),
      (Name: 'Wheat'; Value: '#FFF5DEB3'),
      (Name: 'White'; Value: '#FFFFFFFF'),
      (Name: 'Whitesmoke'; Value: '#FFF5F5F5'),
      (Name: 'Yellow'; Value: '#FFFFFF00'),
      (Name: 'Yellowgreen'; Value: '#FF9ACD32')
      );

Type

   PIntArray = ^TIntArray;
   TIntArray = Array [0..0] Of Integer;

   TD2Point = Packed Record
      X: Single;
      Y: Single;
   End;

   TD2CubicBezier = Array [0..3] Of TD2Point;

   PD2PointArray = ^TD2PointArray;
   TD2PointArray = Array [0..0] Of TD2Point;

   TD2Rect = Packed Record
      Case Integer Of
         0: (Left, Top, Right, Bottom: Single);
         1: (TopLeft, BottomRight: TD2Point);
   End;
   TSelArea = Array Of TD2Rect;

   TD2Corner = (d2CornerTopLeft, d2CornerTopRight, d2CornerBottomLeft, d2CornerBottomRight);
   TD2Corners = Set Of TD2Corner;

   TD2CornerType = (d2CornerRound, d2CornerBevel, d2CornerInnerRound, d2CornerInnerLine);

   TD2Side = (d2SideTop, d2SideLeft, d2SideBottom, d2SideRight);
   TD2Sides = Set Of TD2Side;

   TD2VectorArray = Array [0..2] Of Single;

   TD2Vector = Packed Record
      Case Integer Of
         0: (V: TD2VectorArray;);
         1: (X: Single;
            Y: Single;
            W: Single;);
   End;

   TD2MatrixArray = Array [0..2] Of TD2Vector;

   TD2Matrix = Packed Record
      Case Integer Of
         0: (M: TD2MatrixArray;);
         1: (m11, m12, m13: Single;
            m21, m22, m23: Single;
            m31, m32, m33: Single);
   End;

   TD2Polygon = Array Of TD2Point;
   PD2Polygon = ^TD2Polygon;
   TMessage = TLMessage;

   PD2Color = ^TD2Color;
   TD2Color = Cardinal;

   PD2Color24 = ^TD2Color24;
   TD2Color24 = Packed Record
      Case Longword Of
         0: (R, G, B: Byte);
   End;

   PD2ColorRec = ^TD2ColorRec;
   TD2ColorRec = Packed Record
      Case Longword Of
         0: (Color: TD2Color);
         2: (HiWord, LoWord: Word);
      {$ifdef  FPC_BIG_ENDIAN}
      3: (A,R,G,B:System.Byte);
         {$else}
         3: (B, G, R, A: System.Byte);
      {$endif}
   End;

   PD2ColorArray = ^TD2ColorArray;
   TD2ColorArray = Array [0..4] Of TD2Color;

   PD2ColorRecArray = ^TD2ColorRecArray;
   TD2ColorRecArray = Array [0..0] Of TD2ColorRec;


   PD2Color24Array = ^TD2Color24Array;
   TD2Color24Array = Array [0..0] Of TD2Color24;

   //.................................

   TD2LineType = (d2LineNormal, d2LineHorizontal, d2LineVertical);
   TD2GradientStyle = (d2LinearGradient, d2RadialGradient);
   TD2AniIndicatorStyle = (d2AniIndicatorLine, d2AniIndicatorCircle);
   TD2CloseAlign = (d2ButtonAlignLeft, d2ButtonAlignRight);
   TD2ListStyle = (d2ListVertical, d2ListHorizontal);
   TD2ValueType = (d2ValueInteger, d2ValueFloat);
   TD2CalDayOfWeek = (d2Monday, d2Tuesday, d2Wednesday, d2Thursday, d2Friday, d2Saturday, d2Sunday, d2LocaleDefault);
   TD2Nad2lyph = (ngEnabled, ngDisabled);
   TD2NavigateBtn = (nbFirst, nbPrior, nbNext, nbLast, nbInsert, nbDelete, nbEdit, nbPost, nbCancel, nbRefresh);
   TD2NavButtonSet = Set Of TD2NavigateBtn;
   TD2NavButtonStyle = Set Of (nsAllowTimer, nsFocusRect);

   TD2WrapMode = (d2WrapTile, d2WrapTileOriginal, d2WrapTileStretch);
   TD2BrushStyle = (d2BrushNone, d2BrushSolid, d2BrushGradient, d2BrushBitmap, d2BrushResource, d2BrushVisual);
   TD2FontStyle = (d2FontRegular, d2FontBold, d2FontItalic, d2FontBoldItalic, d2FontUnderline, d2FontStrikeout);
   TD2TextAlign = (d2TextAlignCenter, d2TextAlignNear, d2TextAlignFar);

   TD2PathPointKind = (d2PathPointMoveTo, d2PathPointLineTo, d2PathPointCurveTo, d2PathPointClose);
   TD2StrokeCap = (d2CapFlat, d2CapRound);
   TD2StrokeJoin = (d2JoinMiter, d2JoinRound, d2JoinBevel);
   TD2StrokeDash = (d2DashSolid, d2DashDash, d2DashDot, d2DashDashDot, d2DashDashDotDot, d2DashCustom);
   TD2AnimationType = (d2AnimationIn, d2AnimationOut, d2AnimationInOut);

   TD2InterpolationType = (d2InterpolationLinear, d2InterpolationQuadratic, d2InterpolationCubic, d2InterpolationQuartic,
      d2InterpolationQuintic, d2InterpolationSinusoidal, d2InterpolationExponential,
      d2InterpolationCircular, d2InterpolationElastic, d2InterpolationBack, d2InterpolationBounce);

   TD2Align = (vaNone, vaTopLeft, vaTopRight, vaBottomLeft, vaBottomRight, vaTop, vaLeft, vaRight, vaBottom,
      vaMostTop, vaMostBottom, vaMostLeft, vaMostRight, vaClient, vaContents, vaCenter, vaVertCenter,
      vaHorzCenter, vaHorizontal, vaVertical, vaScale, vaFit, vaFitLeft, vaFitRight);

   TD2CalloutPosition = (d2CalloutTop, d2CalloutLeft, d2CalloutBottom, d2CalloutRight);
   TD2PathWrap = (d2PathOriginal, d2PathFit, d2PathStretch, d2PathTile);
   TD2ButtonLayout = (d2GlyphLeft, d2GlyphRight, d2GlyphTop, d2GlyphBottom, d2GlyphCenter);

   TEditCharCase = (d2ecNormal, d2ecUpperCase, d2ecLowerCase);
   TD2DragMode = (d2DragManual, d2DragAutomatic);
   TD2Orientation = (d2Horizontal, d2Vertical);

   TD2Placement = (d2PlacementBottom, d2PlacementTop, d2PlacementLeft, d2PlacementRight, d2PlacementCenter,
      d2PlacementBottomCenter, d2PlacementTopCenter, d2PlacementLeftCenter, d2PlacementRightCenter,
      d2PlacementAbsolute, d2PlacementMouse, d2PlacementMouseCenter);

   TD2PlacementScene = (d2PlacementSceneNew, d2PlacementSceneTarget);

   TD2MessageType = (d2MessageWarning, d2MessageError, d2MessageInformation, d2MessageConfirmation, d2MessageCustom);

   TD2MessageButton = (d2ButtonYes, d2ButtonNo, d2ButtonOK, d2ButtonCancel, d2ButtonAbort, d2ButtonRetry,
      d2ButtonIgnore, d2ButtonAll, d2ButtonNoToAll, d2ButtonYesToAll, d2ButtonHelp);
   TD2MessageButtons = Set Of TD2MessageButton;

   TInsertOption = (ioSelected, ioMoveCaret, ioCanUndo, ioUnDoPairedWithPriv);
   TInsertOptions = Set Of TInsertOption;

   TDeleteOption = (doMoveCaret, doCanUndo);
   TDeleteOptions = Set Of TDeleteOption;

   TActionType = (atDelete, atInsert);

   TD2ImageWrap = (d2ImageOriginal, d2ImageFit, d2ImageStretch, d2ImageTile);

   TLinesBegs = Array Of Integer;
   PLinesBegs = ^TLinesBegs;

   TCaretPosition = Record
      Line, Pos: Integer;
   End;

   PEdtAction = ^TEdtAction;

   TEdtAction = Record
      ActionType: TActionType;
      PairedWithPriv: Boolean;
      StartPosition: Integer;
      DeletedFragment: WideString;
      Length: Integer;
   End;

   TD2PathPoint = Packed Record
      Kind: TD2PathPointKind;
      Point: TD2Point;
   End;

   //................................

Const
   Epsilon: Single = 1e-40;
   c180: Single = 180;
   c360: Single = 360;
   cPI: Single = 3.141592654;
   c2PI: Single = 6.283185307;
   cPIdiv2: Single = 1.570796326;
   cPIdiv4: Single = 0.785398163;
   c3PIdiv4: Single = 2.35619449;
   cInv2PI: Single = 1 / 6.283185307;
   cInv360: Single = 1 / 360;
   cOneHalf: Single = 0.5;
   c180divPI: Single = 57.2957795155;
   cPIdiv180: Single = 0.017453292;
   CurveKappa = 0.5522847498;
   CurveKappaInv = 1 - CurveKappa;


   IdentityMatrix: TD2Matrix = (m11: 1.0; m12: 0.0; m13: 0.0;
      m21: 0.0; m22: 1.0; m23: 0.0;
      m31: 0.0; m32: 0.0; m33: 1.0);

   ZeroMatrix: TD2Matrix = (m11: 0.0; m12: 0.0; m13: 0.0;
      m21: 0.0; m22: 0.0; m23: 0.0;
      m31: 0.0; m32: 0.0; m33: 0.0);
   NullRect: TD2Rect = (Left: 0; Top: 0; Right: 0; Bottom: 0);

   AllCorners: TD2Corners = [d2CornerTopLeft, d2CornerTopRight, d2CornerBottomLeft, d2CornerBottomRight];
   AllSides: TD2Sides = [d2SideTop, d2SideLeft, d2SideBottom, d2SideRight];

   ClosePolygon: TD2Point = (X: $FFFF; Y: $FFFF);


   d2WideNull = System.Widechar(#0);
   d2WideTabulator = System.Widechar(#9);
   d2WideSpace = System.Widechar(#32);
   d2WideCarriageReturn = System.Widechar($D);
   d2WideLineFeed = System.Widechar($A);
   d2WideVerticalTab = System.Widechar($B);
   d2WideFormFeed = System.Widechar($C);
   d2WideLineSeparator = System.Widechar($2028);
   d2WideParagraphSeparator = System.Widechar($2029);

   BOM_LSB_FIRST = System.Widechar($FEFF);
   BOM_MSB_FIRST = System.Widechar($FFFE);

   //=============================================================================
   //=============== Classes =====================================================
   //=============================================================================
Type

   TD2Canvas = Class;
   TD2Object = Class;
   TD2CustomScene = Class;
   TD2VisualObject = Class;
   TD2ObjectClass = Class Of TD2Object;
   TD2Resources = Class;
   TD2Control = Class;
   TD2Bitmap = Class;
   TD2Brush = Class;
   TD2BrushObject = Class;
   TD2BitmapObject = Class;
   TD2PathObject = Class;
   TD2Font = Class;
   TD2ControlActionLink = Class;

   TD2KeyEvent = Procedure(Sender: TObject; Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState) Of Object;
   TOnPaintEvent = Procedure(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect) Of Object;
   TD2MouseEvent = Procedure(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single) Of Object;
   TD2MouseMoveEvent = Procedure(Sender: TObject; Shift: TShiftState; X, Y, Dx, Dy: Single) Of Object;
   TD2MouseWheelEvent = Procedure(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TD2Point; Var Handled: Boolean) Of Object;
   TD2ProcessTickEvent = Procedure(Sender: TObject; time, deltaTime: Single) Of Object;
   TD2ObjectSortCompare = Function(item1, item2: TD2Object): Integer;

   TD2DragObject = Record
      Source: TObject;
      Files: Array Of WideString;
      Data: Variant;
   End;

   TD2SaveData = Record
      Index: Cardinal;
      Matrix: TD2Matrix;
      AbsoluteMatrix: TD2Matrix;
      InvertMatrix: TD2Matrix;
      Fill: TD2Brush;
      Stroke: TD2Brush;
      StrokeThickness: Single;
      StrokeCap: TD2StrokeCap;
      StrokeJoin: TD2StrokeJoin;
      StrokeDash: TD2StrokeDash;
      Dash: Array Of Single;
      DashOffset: Single;
      Font: TD2Font;
      Data: Pointer;
   End;
   TD2SaveDataArray = Array Of TD2SaveData;

   TD2Timer = Class(TTimer)
   End;

   TD2AniThread = Class(TD2Timer)
   Private
      FAniList: TList;
      FStartTime: Single;
      FTime: Single;
      FDeltaTime: Single;
      Procedure OneStep;
      Procedure DoSyncTimer(Sender: TObject);
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   End;

   TD2ControlActionLink = Class(TActionLink)
   Protected
      FClient: TD2Control;
      Procedure AssignClient(AClient: TObject); Override;
      Function DoShowHint(Var HintStr: String): Boolean; Virtual;
      Procedure SetCaption(Const Value: String); Override;
      Procedure SetEnabled(Value: Boolean); Override;
      Procedure SetHelpContext(Value: THelpContext); Override;
      Procedure SetHelpKeyword(Const Value: String); Override;
      Procedure SetHelpType(Value: THelpType); Override;
      Procedure SetHint(Const Value: String); Override;
      Procedure SetVisible(Value: Boolean); Override;
      Procedure SetOnExecute(Value: TNotifyEvent); Override;
   Public
      Function IsCaptionLinked: Boolean; Override;
      Function IsEnabledLinked: Boolean; Override;
      Function IsHelpLinked: Boolean; Override;
      Function IsHintLinked: Boolean; Override;
      Function IsVisibleLinked: Boolean; Override;
      Function IsOnExecuteLinked: Boolean; Override;
   End;

   TD2ControlActionLinkClass = Class Of TD2ControlActionLink;

   TD2Font = Class(TPersistent)
   Private
      FSize: Single;
      FStyle: TD2FontStyle;
      FClearType: Boolean;
      FFamily: String;
      FOnChanged: TNotifyEvent;
      Function isSizeStored: Boolean;
      Function isFamilyStored: Boolean;
      Procedure SetSize(Const Value: Single);
      Procedure SetFamily(Const Value: String);
      Procedure SetStyle(Const Value: TD2FontStyle);
      Procedure SetClearType(Const Value: Boolean);
   Public
      Constructor Create;
      Destructor Destroy; Override;
      Procedure Assign(Source: TPersistent); Override;
      Procedure AssignTo(Dest: TPersistent); Override;
      Property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
   Published
      Property ClearType: Boolean read FClearType write SetClearType Default True;
      Property Family: String read FFamily write SetFamily Stored isFamilyStored;
      Property Size: Single read FSize write SetSize Stored isSizeStored;
      Property Style: TD2FontStyle read FStyle write SetStyle Default d2FontRegular;
   End;


   TD2BrushResource = Class(TPersistent)
   Private
      FResource: TD2BrushObject;
      FResourceName: String;
      FOnChanged: TNotifyEvent;
      Function GetBrush: TD2Brush;
      Procedure SetResource(Const Value: TD2BrushObject);
      Function GetResourceName: String;
      Procedure SetResourceName(Const Value: String);
   Public
      Destructor Destroy; Override;
      Property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
      Procedure Assign(Source: TPersistent); Override;
      Property Brush: TD2Brush read GetBrush;
   Published
      Property Resource: TD2BrushObject read FResource write SetResource Stored False;
      Property ResourceName: String read GetResourceName write SetResourceName;
   End;

   TD2BrushBitmap = Class(TPersistent)
   Private
      FOnChanged: TNotifyEvent;
      FBitmap: TD2Bitmap;
      FWrapMode: TD2WrapMode;
      Procedure SetWrapMode(Const Value: TD2WrapMode);
      Procedure SetBitmap(Value: TD2Bitmap);
   Public
      Constructor Create;
      Destructor Destroy; Override;
      Property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
      Procedure Assign(Source: TPersistent); Override;
   Published
      Property Bitmap: TD2Bitmap read FBitmap write SetBitmap;
      Property WrapMode: TD2WrapMode read FWrapMode write SetWrapMode;
   End;


   TD2GradientPoint = Class(TCollectionItem)
   Private
      FColor: TD2Color;
      FOffset: Single;
      Function GetColor: String;
      Procedure SetColor(Const Value: String);
   Public
      Constructor Create(ACollection: TCollection); Override;
      Procedure Assign(Source: TPersistent); Override;
      Property IntColor: TD2Color read FColor write FColor;
   Published
      Property Color: String read GetColor write SetColor;
      Property Offset: Single read FOffset write FOffset;
   End;

   TD2GradientPoints = Class(TCollection)
   Private
      Function GetPoint(Index: Integer): TD2GradientPoint;
   Public
      Property Points[Index: Integer]: TD2GradientPoint read GetPoint; Default;
   End;


   TD2Position = Class(TPersistent)
   Private
      FY: Single;
      FX: Single;
      FDefaultValue: TD2Point;
      FOnChange: TNotifyEvent;
      Procedure SetX(Const Value: Single);
      Procedure SetY(Const Value: Single);
      Procedure SetPoint(Const Value: TD2Point);
      Function GetPoint: TD2Point;
      Procedure SetVector(Const Value: TD2Vector);
      Function GetVector: TD2Vector;
   Protected
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadPoint(Reader: TReader);
      Procedure WritePoint(Writer: TWriter);
   Public
      Constructor Create(Const ADefaultValue: TD2Point); Virtual;
      Procedure Assign(Source: TPersistent); Override;
      Function Empty: Boolean;
      Procedure Reflect(Const Normal: TD2Vector);
      Property Point: TD2Point read GetPoint write SetPoint;
      Property Vector: TD2Vector read GetVector write SetVector;
      Property DefaultValue: TD2Point read FDefaultValue write FDefaultValue;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   Published
      Property X: Single read FX write SetX Stored False;
      Property Y: Single read FY write SetY Stored False;
   End;

   TD2Transform = Class(TPersistent)
   Private
      FMatrix: TD2Matrix;
      FRotateAngle: Single;
      FPosition: TD2Position;
      FScale: TD2Position;
      FSkew: TD2Position;
      FRotateCenter: TD2Position;
      FOnChanged: TNotifyEvent;
      Procedure SetRotateAngle(Const Value: Single);
   Protected
      Procedure MatrixChanged(Sender: TObject);
      Property Skew: TD2Position read FSkew write FSkew;
   Public
      Constructor Create; Virtual;
      Destructor Destroy; Override;
      Procedure Assign(Source: TPersistent); Override;
      Property Matrix: TD2Matrix read FMatrix;
      Property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
   Published
      Property Position: TD2Position read FPosition write FPosition;
      Property Scale: TD2Position read FScale write FScale;
      Property RotateAngle: Single read FRotateAngle write SetRotateAngle;
      Property RotateCenter: TD2Position read FRotateCenter write FRotateCenter;
   End;

   TD2Gradient = Class(TPersistent)
   Private
      FPoints: TD2GradientPoints;
      FOnChanged: TNotifyEvent;
      FStartPosition: TD2Position;
      FStopPosition: TD2Position;
      FStyle: TD2GradientStyle;
      FRadialTransform: TD2Transform;
      Procedure SetStartPosition(Const Value: TD2Position);
      Procedure SetStopPosition(Const Value: TD2Position);
      Procedure PositionChanged(Sender: TObject);
      Procedure SetColor(Const Value: String);
      Procedure SetColor1(Const Value: String);
      Function isLinearStored: Boolean;
      Procedure SetStyle(Const Value: TD2GradientStyle);
      Function isRadialStored: Boolean;
      Procedure SetRadialTransform(Const Value: TD2Transform);
   Public
      Constructor Create;
      Destructor Destroy; Override;
      Procedure Assign(Source: TPersistent); Override;
      Procedure Change;
      Function InterpolateColor(Offset: Single): TD2Color;
      Property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
      Property Color: String write SetColor;
      Property Color1: String write SetColor1;
   Published
      Property Points: TD2GradientPoints read FPoints write FPoints;
      Property Style: TD2GradientStyle read FStyle write SetStyle;
      Property StartPosition: TD2Position read FStartPosition write SetStartPosition Stored isLinearStored;
      Property StopPosition: TD2Position read FStopPosition write SetStopPosition Stored isLinearStored;
      Property RadialTransform: TD2Transform read FRadialTransform write SetRadialTransform Stored isRadialStored;
   End;

   TD2Visual = Class(TPersistent)
   Private
      FOnChanged: TNotifyEvent;
      FVisualObject: TD2VisualObject;
      Procedure SetVisualObject(Const Value: TD2VisualObject);
   Public
      Constructor Create;
      Destructor Destroy; Override;
      Procedure Assign(Source: TPersistent); Override;
      Property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
   Published
      Property VisualObject: TD2VisualObject read FVisualObject write SetVisualObject;
   End;

   TD2Brush = Class(TPersistent)
   Private
      FColor: TD2Color;
      FStyle: TD2BrushStyle;
      FOnChanged: TNotifyEvent;
      FGradient: TD2Gradient;
      FVisual: TD2Visual;
      FDefaultStyle: TD2BrushStyle;
      FDefaultColor: TD2Color;
      FResource: TD2BrushResource;
      FBitmap: TD2BrushBitmap;
      Procedure SetColor(Const Value: String);
      Procedure SetStyle(Const Value: TD2BrushStyle);
      Procedure SetGradient(Const Value: TD2Gradient);
      Procedure SetVisual(Const Value: TD2Visual);
      Function isColorStored: Boolean;
      Function isGradientStored: Boolean;
      Function isVisualStored: Boolean;
      Function GetColor: String;
      Procedure SetSolidColor(Const Value: TD2Color);
      Function isStyleStored: Boolean;
      Procedure SetResource(Const Value: TD2BrushResource);
      Function isResourceStored: Boolean;
      Function isBitmapStored: Boolean;
      Function GetSolidColor: TD2Color;
   Protected
      Procedure GradientChanged(Sender: TObject);
      Procedure VisualChanged(Sender: TObject);
      Procedure ResourceChanged(Sender: TObject);
      Procedure BitmapChanged(Sender: TObject);
   Public
      Constructor Create(Const ADefaultStyle: TD2BrushStyle; Const ADefaultColor: TD2Color);
      Destructor Destroy; Override;
      Procedure Assign(Source: TPersistent); Override;
      Property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
      Property SolidColor: TD2Color read GetSolidColor write SetSolidColor;
      Property DefaultColor: TD2Color read FDefaultColor write FDefaultColor;
      Property DefaultStyle: TD2BrushStyle read FDefaultStyle write FDefaultStyle;
   Published
      Property Color: String read GetColor write SetColor Stored isColorStored;
      Property Bitmap: TD2BrushBitmap read FBitmap write FBitmap Stored isBitmapStored;
      Property Style: TD2BrushStyle read FStyle write SetStyle Stored isStyleStored;
      Property Gradient: TD2Gradient read FGradient write SetGradient Stored isGradientStored;
      Property Resource: TD2BrushResource read FResource write SetResource Stored isResourceStored;
      Property Visual: TD2Visual read FVisual write SetVisual Stored isVisualStored;
   End;

   Id2SizeGrip = Interface
      ['{86960F17-A3D9-4A26-A1A1-E7F4F0016F71}']
   End;

   Id2Scene = Interface
      ['{5F9591A0-DA32-4AF3-B3A1-91283599DE97}']
      Procedure AddObject(AObject: TD2Object);
      Procedure RemoveObject(AObject: TD2Object);
      Procedure BeginDrag;
      Procedure BeginResize;
      Procedure AddUpdateRect(R: TD2Rect);
      Procedure InsertObject(Const ClassName: String);
      Function GetActiveControl: TD2Control;
      Function GetDisableUpdate: Boolean;
      Procedure SetDisableUpdate(Value: Boolean);
      Function GetDesignTime: Boolean;
      Function GetCanvas: TD2Canvas;
      Function GetRoot: TD2Object;
      Function GetOwner: TComponent;
      Function GetComponent: TComponent;
      Function GetStyle: TD2Resources;
      Procedure SetStyle(Const Value: TD2Resources);
      Function GetTransparency: Boolean;
      Procedure UpdateResource;
      Procedure Notification(AComponent: TComponent; Operation: TOperation);
      Function GetSelected: TD2VisualObject;
      Procedure SetSelected(Const Value: TD2VisualObject);
      Function GetDesignPlaceObject: TD2VisualObject;
      Function GetUpdateRectsCount: Integer;
      Function GetUpdateRect(Const Index: Integer): TD2Rect;
      Procedure SetCaptured(Const Value: TD2VisualObject);
      Function GetCaptured: TD2VisualObject;
      Procedure SetFocused(Const Value: TD2VisualObject);
      Function GetFocused: TD2VisualObject;
      Procedure SetDesignRoot(Const Value: TD2VisualObject);
      Function GetMousePos: TD2Point;
      Function LocalToScreen(Const Point: TD2Point): TD2Point;
      Procedure BeginDragObj(Source: TObject; ABitmap: TD2Bitmap);
      Procedure DoDesignSelect(AObject: TObject);
      Function GetAnimatedCaret: Boolean;
      Function ShowKeyboardForControl(AObject: TD2Object): Boolean;
      Function HideKeyboardForControl(AObject: TD2Object): Boolean;
   End;

   TD2WideStrings = Class(TPersistent)
   Private
      FUpdateCount: Integer;
      FCaseSensitive: Boolean;
      FSorted: Boolean;
      Function GetCommaText: WideString;
      Function GetName(Index: Integer): WideString;
      Function GetValue(Const Name: WideString): WideString;
      Procedure ReadData(Reader: TReader);
      Procedure SetCommaText(Const Value: WideString);
      Procedure SetValue(Const Name, Value: WideString);
      Procedure WriteData(Writer: TWriter);
      Function GetValueFromIndex(Index: Integer): WideString;
      Procedure SetValueFromIndex(Index: Integer; Const Value: WideString);
      Procedure SetCaseSensitive(Const Value: Boolean);
      Procedure SetSorted(Const Value: Boolean);
   Protected
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure Error(Const Msg: String; Data: Integer);
      Function CompareStrings(Const S1, S2: WideString): Integer; Virtual;
      Function Get(Index: Integer): WideString; Virtual; Abstract;
      Function GetCapacity: Integer; Virtual;
      Function GetCount: Integer; Virtual; Abstract;
      Function GetObject(Index: Integer): TObject; Virtual;
      Function GetTextStr: WideString; Virtual;
      Procedure Put(Index: Integer; Const S: WideString); Virtual;
      Procedure PutObject(Index: Integer; AObject: TObject); Virtual;
      Procedure SetCapacity(NewCapacity: Integer); Virtual;
      Procedure SetTextStr(Const Value: WideString); Virtual;
      Procedure SetUpdateState(Updating: Boolean); Virtual;
   Public
      Constructor Create; Virtual;
      Destructor Destroy; Override;
      Procedure BeginUpdate;
      Procedure EndUpdate;
      Function AddObject(Const S: WideString; AObject: TObject): Integer; Virtual;
      Procedure Append(Const S: WideString);
      Procedure AddStrings(Strings: TStrings); Overload; Virtual;
      Procedure AddStrings(Strings: TD2WideStrings); Overload; Virtual;
      Procedure Assign(Source: TPersistent); Override;
      Procedure AssignTo(Dest: TPersistent); Override;
      Function Equals2d(Strings: TD2WideStrings): Boolean;
      Procedure Exchange(Index1, Index2: Integer); Virtual;
      Function IndexOf(Const S: WideString): Integer; Virtual;
      Function IndexOfName(Const Name: WideString): Integer; Virtual;
      Function IndexOfValue(Const Name: WideString): Integer; Virtual;
      Function IndexOfObject(AObject: TObject): Integer;
      Procedure Insert(Index: Integer; Const S: WideString); Virtual; Abstract;
      Procedure InsertObject(Index: Integer; Const S: WideString; AObject: TObject);
      Procedure LoadFromFile(Const FileName: String); Virtual;
      Procedure LoadFromStream(Stream: TStream); Virtual;
      Procedure Move(CurIndex, NewIndex: Integer); Virtual;
      Procedure SaveToFile(Const FileName: String); Virtual;
      Procedure SaveToAnsiFile(Const FileName: String); Virtual;
      Procedure SaveToStream(Stream: TStream); Virtual;
      Procedure SetText(Text: Pwidechar); Virtual;
      Procedure Sort; Virtual;
      Property Capacity: Integer read GetCapacity write SetCapacity;
      Property Names[Index: Integer]: WideString read GetName;
      Property Objects[Index: Integer]: TObject read GetObject write PutObject;
      Property Values[Const Name: WideString]: WideString read GetValue write SetValue;
      Property ValueFromIndex[Index: Integer]: WideString read GetValueFromIndex write SetValueFromIndex;
      Property Strings[Index: Integer]: WideString read Get write Put; Default;
      Property Text: WideString read GetTextStr write SetTextStr Stored True;
      Function Items(AIndex: Integer): WideString;
      Procedure SetItem(index: Integer; AText: WideString);
      Function Add(Const S: WideString): Integer; Virtual;
      Procedure Delete(Index: Integer); Virtual;
      Procedure Clear; Virtual;
      Property Count: Integer read GetCount;
      Property CommaText: WideString read GetCommaText write SetCommaText Stored False;
      Property CaseSensitive: Boolean read FCaseSensitive write SetCaseSensitive Stored False;
      Property Sorted: Boolean read FSorted write SetSorted Stored False;
   Published
   End;

   PWideStringItem = ^TWideStringItem;

   TWideStringItem = Record
      FString: WideString;
      FObject: TObject;
   End;

   PWideStringItemList = ^TWideStringItemList;
   TWideStringItemList = Array[0..MaxListSize] Of TWideStringItem;

   TD2WideStringList = Class(TD2WideStrings)
   Private
      FList: PWideStringItemList;
      FCount: Integer;
      FCapacity: Integer;
      FDuplicates: TDuplicates;
      FOnChange: TNotifyEvent;
      FOnChanging: TNotifyEvent;
      FSortByObject: Boolean;
      Procedure ExchangeItems(Index1, Index2: Integer);
      Procedure Grow;
      Procedure QuickSort(L, R: Integer);
      Procedure QuickSortByObject(L, R: Integer);
      Procedure InsertItem(Index: Integer; Const S: WideString);
   Protected
      Procedure Changed; Virtual;
      Procedure Changing; Virtual;
      Function CompareStrings(Const S1, S2: WideString): Integer; Override;
      Function Get(Index: Integer): WideString; Override;
      Function GetCapacity: Integer; Override;
      Function GetCount: Integer; Override;
      Function GetObject(Index: Integer): TObject; Override;
      Procedure Put(Index: Integer; Const S: WideString); Override;
      Procedure PutObject(Index: Integer; AObject: TObject); Override;
      Procedure SetCapacity(NewCapacity: Integer); Override;
      Procedure SetUpdateState(Updating: Boolean); Override;
   Public
      Destructor Destroy; Override;
      Procedure Delete(Index: Integer); Override;
      Procedure Exchange(Index1, Index2: Integer); Override;
      Function Find(Const S: WideString; Var Index: Integer): Boolean; Virtual;
      Function FindByObject(Const S: TObject; Var Index: Integer): Boolean; Virtual;
      Function IndexOf(Const S: WideString): Integer; Override;
      Procedure Insert(Index: Integer; Const S: WideString); Override;
      Procedure Sort; Override;
      Property Duplicates: TDuplicates read FDuplicates write FDuplicates;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
      Function Add(Const S: WideString): Integer; Override;
      Procedure Clear; Override;
      Property SortByObject: Boolean read FSortByObject write FSortByObject Default False;
   End;

   TD2StorageItem = Class(TPersistent)
   Private
      FStream: TMemoryStream;
      FType: TValueType;
      FAsString: WideString;
      FAsFloat: Single;
      FAsInteger: Integer;
      Function GetAsStream: TMemoryStream;
      Function GetAsBool: Boolean;
      Procedure SetAsString(Const Value: WideString);
      Procedure SetAsFloat(Const Value: Single);
      Procedure SetAsInteger(Const Value: Integer);
      Procedure SetAsBool(Const Value: Boolean);
   Protected
      Procedure ReadItem(R: TReader);
      Procedure WriteItem(W: TWriter);
   Public
      Constructor Create; Virtual;
      Destructor Destroy; Override;
      Property AsStream: TMemoryStream read GetAsStream;
      Property AsString: WideString read FAsString write SetAsString;
      Property AsFloat: Single read FAsFloat write SetAsFloat;
      Property AsInteger: Integer read FAsInteger write SetAsInteger;
      Property AsBool: Boolean read GetAsBool write SetAsBool;
   End;

   TD2Storage = Class(TPersistent)
   Private
      FItems: TD2WideStrings;
      Function GetValues(Name: String): TD2StorageItem;
   Public
      Constructor Create; Virtual;
      Destructor Destroy; Override;
      Procedure LoadFromStream(S: TStream);
      Procedure SaveToStream(S: TStream);
      Procedure LoadFromFile(FileName: String);
      Procedure SaveToFile(FileName: String);
      Procedure SaveToBinFile(FileName: String);
      Property Values[Name: String]: TD2StorageItem read GetValues;
   End;

   TD2SplineVector = Array [0..3] Of Single;
   TD2SplineMatrix = Array Of TD2SplineVector;

   TD2Spline = Class(TObject)
   Private
      len: Integer;
      matX, matY: TD2SplineMatrix;
   Public
      Constructor Create(Const Polygon: TD2Polygon);
      Destructor Destroy; Override;
      Procedure SplineXY(Const t: Single; Var X, Y: Single);
   End;

   TD2Bounds = Class(TPersistent)
   Private
      FLeft: Single;
      FTop: Single;
      FRight: Single;
      FBottom: Single;
      FOnChange: TNotifyEvent;
      FDefaultValue: TD2Rect;
      Procedure SetLeft(Const Value: Single);
      Procedure SetTop(Const Value: Single);
      Procedure SetRight(Const Value: Single);
      Procedure SetBottom(Const Value: Single);
      Function GetRect: TD2Rect;
      Procedure SetRect(Const Value: TD2Rect);
   Protected
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadRect(Reader: TReader);
      Procedure WriteRect(Writer: TWriter);
   Public
      Constructor Create(Const ADefaultValue: TD2Rect); Virtual;
      Procedure Assign(Source: TPersistent); Override;
      Function Width: Single;
      Function Height: Single;
      Property Rect: TD2Rect read GetRect write SetRect;
      Function MarginRect(Const R: TD2Rect): TD2Rect;
      Function PaddinRect(Const R: TD2Rect): TD2Rect;
      Function Empty: Boolean;
      Function MarginEmpty: Boolean;
      Property DefaultValue: TD2Rect read FDefaultValue write FDefaultValue;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   Published
      Property Left: Single read FLeft write SetLeft Stored False;
      Property Top: Single read FTop write SetTop Stored False;
      Property Right: Single read FRight write SetRight Stored False;
      Property Bottom: Single read FBottom write SetBottom Stored False;
   End;


   TD2PathData = Class(TPersistent)
   Private
      FOnChanged: TNotifyEvent;
      FResource: TD2PathObject;
      FResourceName: String;
      FStartPoint: TD2Point;
      Function GetPathString: Ansistring;
      Procedure SetPathString(Const Value: Ansistring);
      Procedure AddArcSd2Part(Const Center, Radius: TD2Point; StartAngle, SweepAngle: Single);
      Procedure AddArcSd2(Const P1, Radius: TD2Point; Angle: Single; Const LargeFlag, SweepFlag: Boolean; Const P2: TD2Point);
      Procedure SetResource(Const Value: TD2PathObject);
      Function GetResourceName: String;
      Procedure SetResourceName(Const Value: String);
      Function GetPath: TD2PathData;
   Protected
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadPath(Stream: TStream);
      Procedure WritePath(Stream: TStream);
   Public
      PathData: Array Of TD2PathPoint;
      Constructor Create; Virtual;
      Destructor Destroy; Override;
      Procedure Assign(Source: TPersistent); Override;
      Property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
      Function LastPoint: TD2Point;
      Procedure MoveTo(Const P: TD2Point);
      Procedure MoveToRel(Const P: TD2Point);
      Procedure LineTo(Const P: TD2Point);
      Procedure LineToRel(Const P: TD2Point);
      Procedure HLineTo(Const x: Single);
      Procedure HLineToRel(Const x: Single);
      Procedure VLineTo(Const y: Single);
      Procedure VLineToRel(Const y: Single);
      Procedure CurveTo(Const ControlPoint1, ControlPoint2, EndPoint: TD2Point);
      Procedure CurveToRel(Const ControlPoint1, ControlPoint2, EndPoint: TD2Point);
      Procedure SmoothCurveTo(Const ControlPoint2, EndPoint: TD2Point);
      Procedure SmoothCurveToRel(Const ControlPoint2, EndPoint: TD2Point);
      Procedure ClosePath;
      Procedure AddEllipse(Const ARect: TD2Rect);
      Procedure AddRectangle(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const ACornerType: TD2CornerType = d2CornerRound);
      Procedure AddArc(Const Center, Radius: TD2Point; StartAngle, SweepAngle: Single);
      Procedure Clear;
      Procedure Flatten(Const Flatness: Single = 0.25);
      Procedure Scale(Const scaleX, scaleY: Single);
      Procedure Offset(Const dX, dY: Single);
      Procedure ApplyMatrix(Const M: TD2Matrix);
      Function GetBounds: TD2Rect;
      Function FlattenToPolygon(Var Polygon: TD2Polygon; Const Flatness: Single = 0.25): TD2Point;
      Function IsEmpty: Boolean;
      Property ResourcePath: TD2PathData read GetPath;
   Published
      Property Data: Ansistring read GetPathString write SetPathString Stored False;
      Property Resource: TD2PathObject read FResource write SetResource Stored False;
      Property ResourceName: String read GetResourceName write SetResourceName;
   End;


   TD2Filter = Class(TPersistent)
   Public
      Class Function GetFileTypes: String; Virtual;
      Class Function GetImageSize(Const AFileName: String): TD2Point; Virtual;
      Function LoadThumbnailFromFile(Const AFileName: String; Const AFitWidth, AFitHeight: Single; Const UseEmbedded: Boolean; Var Bitmap: TD2Bitmap): Boolean; Virtual; Abstract;
      Function SaveToFile(Const AFileName: String; Var Bitmap: TD2Bitmap; Const Params: String = ''): Boolean; Virtual; Abstract;
      Function LoadFromFile(Const AFileName: String; Const Rotate: Single; Var Bitmap: TD2Bitmap): Boolean; Virtual; Abstract;
      Function SaveToStream(Const AStream: TStream; Var Bitmap: TD2Bitmap; Const Format: String; Const Params: String = ''): Boolean; Virtual; Abstract;
      Function LoadFromStream(Const AStream: TStream; Var Bitmap: TD2Bitmap): Boolean; Virtual; Abstract;
   End;

   TD2FilterClass = Class Of TD2Filter;


   TD2Bitmap = Class(TPersistent)
   Private
      FBits: PD2ColorArray;
      FHandle: THandle;
      FHeight: Integer;
      FOnChange: TNotifyEvent;
      FWidth: Integer;
      FNeedUpdate: Boolean;
      FOnThreadLoaded: TNotifyEvent;
      FOnBitmapCreate: TNotifyEvent;
      FOnBitmapDestroy: TNotifyEvent;
      FResource: TD2BitmapObject;
      FResourceName: String;
      Function GetCanvas: TD2Canvas;
      Function GetScanline(y: Integer): PD2ColorArray;
      Procedure SetHeight(Const Value: Integer);
      Procedure SetWidth(Const Value: Integer);
      Function GetPixels(x, y: Integer): TD2Color;
      Procedure SetResource(Const Value: TD2BitmapObject);
      Function GetResourceName: String;
      Procedure SetResourceName(Const Value: String);
      Function GetBitmap: TD2Bitmap;
   Protected
      FCanvas: TD2Canvas;
      FOnDestroyHandle: TNotifyEvent;
      Procedure Recreate;
      Procedure DoLoaded(Sender: TObject);
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadBitmap(Stream: TStream);
      Procedure WriteBitmap(Stream: TStream);
   Public
      Constructor Create(Const AWidth, AHeight: Integer; Const APremulAlpha: Boolean = True); Virtual;
      Constructor CreateFromStream(Const AStream: TStream); Virtual;
      Constructor CreateFromBitmapAndMask(Const Bitmap, Mask: TD2Bitmap);
      Procedure Assign(Source: TPersistent); Override;
      Procedure AssignTo(Dest: TPersistent); Override;
      Destructor Destroy; Override;
      Procedure SetSize(Const AWidth, AHeight: Integer);
      Procedure Clear(Const AColor: TD2Color = 0); Virtual;
      Procedure ClearRect(Const ARect: TD2Rect; Const AColor: TD2Color = 0); Virtual;
      Procedure BitmapChanged;
      Function IsEmpty: Boolean;
      Procedure DrawGraphic(Const Graphic: TGraphic; Const DstRect: TD2Rect);
      Procedure Rotate(Const Angle: Single);
      Procedure FlipHorizontal;
      Procedure FlipVertical;
      Procedure InvertAlpha;
      Procedure FillColor(Const Color: TD2Color);
      Function CreateMask: PByteArray;
      Procedure ApplyMask(Const Mask: PByteArray; Const DstX: Integer = 0; Const DstY: Integer = 0);
      Function CreateThumbnail(Const Width, Height: Integer): TD2Bitmap;
      Procedure LoadFromFile(Const AFileName: String; Const aRotate: Single = 0);
      Procedure LoadThumbnailFromFile(Const AFileName: String; Const AFitWidth, AFitHeight: Single; Const UseEmbedded: Boolean = True);
      Procedure SaveToFile(Const AFileName: String; Const Params: String = '');
      Procedure LoadFromStream(Stream: TStream);
      Procedure SaveToStream(Stream: TStream);
      Property Canvas: TD2Canvas read GetCanvas;
      Property Pixels[x, y: Integer]: TD2Color read GetPixels;
      Property Scanline[y: Integer]: PD2ColorArray read GetScanline;
      Property StartLine: PD2ColorArray read FBits;
      Property Width: Integer read FWidth write SetWidth;
      Property Height: Integer read FHeight write SetHeight;
      Property Handle: THandle read FHandle write FHandle;
      Property OnDestroyHandle: TNotifyEvent read FOnDestroyHandle write FOnDestroyHandle;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnThreadLoaded: TNotifyEvent read FOnThreadLoaded write FOnThreadLoaded;
      Property OnBitmapCreate: TNotifyEvent read FOnBitmapCreate write FOnBitmapCreate;
      Property OnBitmapDestroy: TNotifyEvent read FOnBitmapDestroy write FOnBitmapDestroy;
      Property NeedUpdate: Boolean read FNeedUpdate write FNeedUpdate;
      Property ResourceBitmap: TD2Bitmap read GetBitmap;
   Published
      Property Resource: TD2BitmapObject read FResource write SetResource Stored False;
      Property ResourceName: String read GetResourceName write SetResourceName;
   End;

   TD2Canvas = Class(TPersistent)
   Protected
      FWidth, FHeight: Integer;
      FMatrix: TD2Matrix;
      FFill: TD2Brush;
      FStroke: TD2Brush;
      FStrokeThickness: Single;
      FStrokeCap: TD2StrokeCap;
      FStrokeJoin: TD2StrokeJoin;
      FStrokeDash: TD2StrokeDash;
      FDash: Array Of Single;
      FDashOffset: Single;
      FFont: TD2Font;
      FBitmap: TD2Bitmap;
      FResized: Boolean;
      FSaveData: TD2SaveDataArray;
      FBuffered: Boolean;
      FBufferBits: Pointer;
      FHandle: THandle;
      FParent: THandle;
      FScene: Id2Scene;
      Procedure FontChanged(Sender: TObject); Virtual;
      Procedure SetStrokeDash(Const Value: TD2StrokeDash);
      Procedure AssignTo(Dest: TPersistent); Override;
   Public
      Constructor Create(Const AWidth, AHeight: Integer); Virtual;
      Constructor CreateFromBitmap(Const ABitmap: TD2Bitmap); Virtual;
      Destructor Destroy; Override;
      Function BeginScene: Boolean; Virtual;
      Procedure EndScene; Virtual;
      Procedure FlushBuffer(Const X, Y: Integer; Const DC: Cardinal); Virtual; Abstract;
      Procedure FlushBufferRect(Const X, Y: Integer; Const DC: Cardinal; Const ARect: TD2Rect); Virtual; Abstract;
      Procedure FreeBuffer; Virtual; Abstract;
      Procedure ResizeBuffer(Const AWidth, AHeight: Integer); Virtual; Abstract;
      Procedure Clear(Const Color: Cardinal); Virtual; Abstract;
      Procedure ClearRect(Const ARect: TD2Rect; Const AColor: TD2Color = 0); Virtual; Abstract;
      Class Function GetBitmapScanline(Bitmap: TD2Bitmap; y: Integer): PD2ColorArray; Virtual;
      Procedure SaveToStream(S: TStream);
      Procedure SaveToBits(Bits: Pointer);
      Procedure SetMatrix(Const M: TD2Matrix); Virtual;
      Procedure MultyMatrix(Const M: TD2Matrix); Virtual;
      Function SaveCanvas: Cardinal; Virtual; Abstract;
      Procedure RestoreCanvas(Const AState: Cardinal); Virtual; Abstract;
      Procedure SetClipRects(Const ARects: Array Of TD2Rect); Virtual; Abstract;
      Procedure IntersectClipRect(Const ARect: TD2Rect); Virtual; Abstract;
      Procedure ExcludeClipRect(Const ARect: TD2Rect); Virtual; Abstract;
      Procedure ResetClipRect; Virtual; Abstract;
      Procedure DrawLine(Const APt1, APt2: TD2Point; Const AOpacity: Single); Virtual; Abstract;
      Procedure FillRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound); Virtual; Abstract;
      Procedure DrawRect(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ACornerType: TD2CornerType = d2CornerRound); Virtual; Abstract;
      Procedure FillEllipse(Const ARect: TD2Rect; Const AOpacity: Single); Virtual; Abstract;
      Procedure DrawEllipse(Const ARect: TD2Rect; Const AOpacity: Single); Virtual; Abstract;
      Procedure FillArc(Const Center, Radius: TD2Point; StartAngle, SweepAngle: Single; Const AOpacity: Single);
      Procedure DrawArc(Const Center, Radius: TD2Point; StartAngle, SweepAngle: Single; Const AOpacity: Single);
      Function PtInPath(Const APoint: TD2Point; Const ARect: TD2Rect; Const APath: TD2PathData): Boolean; Virtual; Abstract;
      Procedure FillPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single); Virtual; Abstract;
      Procedure DrawPath(Const APath: TD2PathData; Const ARect: TD2Rect; Const AOpacity: Single); Virtual; Abstract;
      Procedure DrawBitmap(Const ABitmap: TD2Bitmap; Const SrcRect, DstRect: TD2Rect; Const AOpacity: Single; Const HighSpeed: Boolean = False); Virtual; Abstract;
      Procedure DrawThumbnail(Const ABitmap: TD2Bitmap; Const Width, Height: Single); Virtual; Abstract;
      Procedure DrawRectSides(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ASides: TD2Sides; Const ACornerType: TD2CornerType = d2CornerRound);
      Procedure FillPolygon(Const Points: TD2Polygon; Const AOpacity: Single); Virtual;
      Procedure DrawPolygon(Const Points: TD2Polygon; Const AOpacity: Single); Virtual;
      Function LoadFontFromStream(AStream: TStream): Boolean; Virtual;
      Procedure FillText(Const ARect, AClipRect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const AOpacity: Single; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter); Virtual; Abstract;
      Procedure MeasureText(Var ARect: TD2Rect; AClipRect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter); Virtual; Abstract;
      Function TextToPath(Path: TD2PathData; Const ARect: TD2Rect; Const AText: WideString; Const WordWrap: Boolean; Const ATextAlign: TD2TextAlign; Const AVTextAlign: TD2TextAlign = d2TextAlignCenter): Boolean; Virtual; Abstract;
      Function TextWidth(Const AText: WideString): Single;
      Function TextHeight(Const AText: WideString): Single;
      Procedure SetCustomDash(Dash: Array Of Single; Offset: Single);
      Property Stroke: TD2Brush read FStroke;
      Property StrokeThickness: Single read FStrokeThickness write FStrokeThickness;
      Property StrokeCap: TD2StrokeCap read FStrokeCap write FStrokeCap;
      Property StrokeDash: TD2StrokeDash read FStrokeDash write SetStrokeDash;
      Property StrokeJoin: TD2StrokeJoin read FStrokeJoin write FStrokeJoin;
      Property Fill: TD2Brush read FFill;
      Property Font: TD2Font read FFont;
      Property Matrix: TD2Matrix read FMatrix;
      Property Width: Integer read FWidth;
      Property Height: Integer read FHeight;
      Property Scene: Id2Scene read FScene write FScene;
      Property Handle: THandle read FHandle write FHandle;
      Property Parent: THandle read FParent write FParent;
      Property Buffered: Boolean read FBuffered;
      Property BufferBits: Pointer read FBufferBits;
   End;

   TD2CanvasClass = Class Of TD2Canvas;

   TD2Object = Class(TComponent)
   Private
      FStored: Boolean;
      FResourceName: String;
      FNotifyList: TList;
      FTagObject: TObject;
      FTagFloat: Single;
      FTagString: String;
      FBindingName: String;
      FIndex: Integer;
      Function GetScene: Id2Scene;
      Procedure ReaderSetName(Reader: TReader; Component: TComponent; Var aName: String);
      Procedure ReaderError(Reader: TReader; Const Message: String; Var Handled: Boolean);
      Procedure SetResourceName(Const Value: String);
      Procedure SetStored(Const Value: Boolean);
      Function GetChild(Index: Integer): TD2Object;
      Function GetChildrenCount: Integer;
      Procedure SetBindingName(Const Value: String);
      Function GetIndex: Integer;
      Procedure SetIndex(Idx: Integer);
   Protected
      FIsVisual: Boolean;
      FVisual: TD2VisualObject;
      FChildren: TList;
      FParent: TD2Object;
      FScene: Id2Scene;
      Procedure SetNewScene(AScene: Id2Scene); Virtual;
      Procedure DoReleaseTimer(Sender: TObject);
      Procedure GetChildren(Proc: TGetChildProc; Root: TComponent); Override;
      Procedure ChangeParent; Virtual;
      Procedure SetParent(Const Value: TD2Object); Virtual;
      Function HasClipParent: TD2VisualObject;
      Function GetBinding(Index: String): Variant; Virtual;
      Procedure SetBinding(Index: String; Const Value: Variant); Virtual;
      Function GetData: Variant; Virtual;
      Procedure SetData(Const Value: Variant); Virtual;
      Procedure IntLoadFromBinStream(Const AStream: TStream);
      Procedure IntSaveToBinStream(Const AStream: TStream);
      Procedure DoAniFinished(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure SetParentComponent(Value: TComponent); Override;
      Function GetParentComponent: TComponent; Override;
      Function HasParent: Boolean; Override;
      Procedure Release(Delay: Single = 0.1);
      Function ItemClass: String; Virtual;
      Function Clone(Const AOwner: TComponent): TD2Object; Overload;
      Procedure CloneChildFromStream(AStream: TStream);
      Procedure AddObject(AObject: TD2Object); Virtual;
      Procedure RemoveObject(AObject: TD2Object); Virtual;
      Procedure Exchange(AObject1, AObject2: TD2Object); Virtual;
      Procedure DeleteChildren; Virtual;
      Procedure BringToFront;
      Procedure SendToBack;
      Procedure AddObjectsToList(Const AList: TList);
      Procedure AddControlsToList(Const AList: TList);
      Procedure Sort(Compare: TD2ObjectSortCompare);
      Procedure AddFreeNotify(Const AObject: TObject);
      Procedure RemoveFreeNotify(Const AObject: TObject);
      Procedure LoadFromStream(Const AStream: TStream);
      Procedure SaveToStream(Const Stream: TStream);
      Procedure LoadFromBinStream(Const AStream: TStream);
      Procedure SaveToBinStream(Const AStream: TStream);
      Function FindResource(Const AResource: String): TD2Object; Virtual;
      Procedure UpdateResource; Virtual;
      Procedure StartAnimation(Const AName: WideString); Virtual;
      Procedure StopAnimation(Const AName: WideString); Virtual;
      Procedure StartTriggerAnimation(AInstance: TD2Object; ATrigger: String); Virtual;
      Procedure StartTriggerAnimationWait(AInstance: TD2Object; ATrigger: String); Virtual;
      Procedure StopTriggerAnimation(AInstance: TD2Object); Virtual;
      Procedure ApplyTriggerEffect(AInstance: TD2Object; ATrigger: String); Virtual;
      Procedure AnimateFloat(Const APropertyName: String; Const NewValue: Single; Duration: Single = 0.2; AType: TD2AnimationType = d2AnimationIn; AInterpolation: TD2InterpolationType = d2InterpolationLinear);
      Procedure AnimateColor(Const APropertyName: String; Const NewValue: String; Duration: Single = 0.2; AType: TD2AnimationType = d2AnimationIn; AInterpolation: TD2InterpolationType = d2InterpolationLinear);
      Procedure AnimateFloatDelay(Const APropertyName: String; Const NewValue: Single; Duration: Single = 0.2; Delay: Single = 0.0; AType: TD2AnimationType = d2AnimationIn; AInterpolation: TD2InterpolationType = d2InterpolationLinear);
      Procedure AnimateFloatWait(Const APropertyName: String; Const NewValue: Single; Duration: Single = 0.2; AType: TD2AnimationType = d2AnimationIn; AInterpolation: TD2InterpolationType = d2InterpolationLinear);
      Property IsVisual: Boolean read FIsVisual;
      Property Visual: TD2VisualObject read FVisual;
      Property Scene: Id2Scene read FScene;
      Property Stored: Boolean read FStored write SetStored;
      Property TagObject: TObject read FTagObject write FTagObject;
      Property TagFloat: Single read FTagFloat write FTagFloat;
      Property TagString: String read FTagString write FTagString;
      Property ChildrenCount: Integer read GetChildrenCount;
      Property Children[Index: Integer]: TD2Object read GetChild;
      Function FindBinding(Const ABinding: String): TD2Object;
      Property Data: Variant read GetData write SetData;
      Property Binding[Index: String]: Variant read GetBinding write SetBinding;
   Published
      Property Index: Integer read GetIndex write SetIndex Stored False;
      Property Parent: TD2Object read FParent write SetParent Stored False;
      Property BindingName: String read FBindingName write SetBindingName;
      Property ResourceName: String read FResourceName write SetResourceName;
   End;

   TD2Animation = Class(TD2Object)
   Private
      FDuration: Single;
      FDelay, FDelayTime: Single;
      FTime: Single;
      FInverse: Boolean;
      FTrigger, FTriggerInverse: String;
      FLoop: Boolean;
      FPause: Boolean;
      FRunning: Boolean;
      FOnFinish: TNotifyEvent;
      FOnProcess: TNotifyEvent;
      FHideOnFinish: Boolean;
      FInterpolation: TD2InterpolationType;
      FAnimationType: TD2AnimationType;
      FEnabled: Boolean;
      FAutoReverse: Boolean;
      Procedure SetEnabled(Const Value: Boolean);
   Protected
      Function NormalizedTime: Single;
      Procedure ProcessAnimation; Virtual;
      Procedure Loaded; Override;
      Procedure Finish;
      Procedure DoFinish; Virtual;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Virtual;
      Procedure Stop; Virtual;
      Procedure StopAtCurrent; Virtual;
      Procedure StartTrigger(AInstance: TD2Object; ATrigger: String); Virtual;
      Procedure ProcessTick(time, deltaTime: Single);
      Property Running: Boolean read FRunning;
      Property Pause: Boolean read FPause write FPause;
   Published
      Property AnimationType: TD2AnimationType read FAnimationType write FAnimationType Default d2AnimationIn;
      Property AutoReverse: Boolean read FAutoReverse write FAutoReverse Default False;
      Property Enabled: Boolean read FEnabled write SetEnabled Default False;
      Property Delay: Single read FDelay write FDelay;
      Property Duration: Single read FDuration write FDuration;
      Property Interpolation: TD2InterpolationType read FInterpolation write FInterpolation Default d2InterpolationLinear;
      Property Inverse: Boolean read FInverse write FInverse Default False;
      Property HideOnFinish: Boolean read FHideOnFinish write FHideOnFinish Default False;
      Property Loop: Boolean read FLoop write FLoop Default False;
      Property Trigger: String read FTrigger write FTrigger;
      Property TriggerInverse: String read FTriggerInverse write FTriggerInverse;
      Property OnProcess: TNotifyEvent read FOnProcess write FOnProcess;
      Property OnFinish: TNotifyEvent read FOnFinish write FOnFinish;
   End;

   TD2Effect = Class(TD2Object)
   Private
      FEnabled: Boolean;
      FTrigger: String;
      Procedure SetEnabled(Const Value: Boolean);
   Protected
      DisablePaint: Boolean;
      AfterPaint: Boolean;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetRect(Const ARect: TD2Rect): TD2Rect; Virtual;
      Function GetOffset: TD2Point; Virtual;
      Procedure ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single); Virtual;
      Procedure ApplyTrigger(AInstance: TD2Object; ATrigger: String); Virtual;
      Procedure UpdateParentEffects;
      Property GetDisablePaint: Boolean read DisablePaint;
   Published
      Property Trigger: String read FTrigger write FTrigger;
      Property Enabled: Boolean read FEnabled write SetEnabled Default True;
   End;


   TD2DragEnterEvent = Procedure(Sender: TObject; Const Data: TD2DragObject; Const Point: TD2Point) Of Object;
   TD2DragOverEvent = Procedure(Sender: TObject; Const Data: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean) Of Object;
   TD2DragDropEvent = Procedure(Sender: TObject; Const Data: TD2DragObject; Const Point: TD2Point) Of Object;

   TD2CanFocusedEvent = Procedure(Sender: TObject; Var ACanFocused: Boolean) Of Object;


   TD2Popup = Class;

   TD2VisualObject = Class(TD2Object)
   Private
      FOnMouseUp: TD2MouseEvent;
      FOnMouseDown: TD2MouseEvent;
      FOnMouseMove: TD2MouseMoveEvent;
      FOnMouseWheel: TD2MouseWheelEvent;
      FOnClick: TNotifyEvent;
      FOnDblClick: TNotifyEvent;
      FMouseInObject: Boolean;
      FHitTest: Boolean;
      FClipChildren: Boolean;
      FAutoCapture: Boolean;
      FMargins: TD2Bounds;
      FAlign: TD2Align;
      FDisableDefaultAlign: Boolean;
      FPadding: TD2Bounds;
      FTempCanvas: TD2Canvas;
      FRotateAngle: Single;
      FPosition: TD2Position;
      FScale: TD2Position;
      FSkew: TD2Position;
      FRotateCenter: TD2Position;
      FCanFocused: Boolean;
      FIsMouseOver: Boolean;
      FIsFocused: Boolean;
      FOnCanFocused: TD2CanFocusedEvent;
      FOnEnterFocus: TNotifyEvent;
      FOnKillFocus: TNotifyEvent;
      FDisableFocusEffect: Boolean;
      FClipParent: Boolean;
      FVelocity: TD2Position;
      FOnMouseLeave: TNotifyEvent;
      FOnMouseEnter: TNotifyEvent;
      FDesignHide: Boolean;
      FOnPaint: TOnPaintEvent;
      FOnBeforePaint: TOnPaintEvent;
      FCanClipped: Boolean;
      FCursor: TCursor;
      FDragMode: TD2DragMode;
      FDragDisableHighlight: Boolean;
      FOnDragEnter: TD2DragEnterEvent;
      FOnDragDrop: TD2DragDropEvent;
      FOnDragLeave: TNotifyEvent;
      FOnDragOver: TD2DragOverEvent;
      FOnDragEnd: TNotifyEvent;
      FIsDragOver: Boolean;
      FOnKeyDown: TD2KeyEvent;
      FOnKeyUp: TD2KeyEvent;
      FHint: WideString;
      FShowHint: Boolean;
      FPopupMenu: TPopupMenu;
      FPopup: TD2Popup;
      FRecalcEnabled, FEnabled, FAbsoluteEnabled: Boolean;
      FTabOrder: TTabOrder;
      FTabList: TList;
      FNeedAlign: Boolean;
      FOnApplyResource: TNotifyEvent;
      Procedure CreateCaret;
      Function GetInvertAbsoluteMatrix: TD2Matrix;
      Procedure SetRotateAngle(Const Value: Single);
      Procedure SetPosition(Const Value: TD2Position);
      Procedure SetHitTest(Const Value: Boolean);
      Procedure SetClipChildren(Const Value: Boolean);
      Function CheckHitTest(Const AHitTest: Boolean): Boolean;
      Function GetCanvas: TD2Canvas;
      Procedure SetLocked(Const Value: Boolean);
      Procedure SetTempCanvas(Const Value: TD2Canvas);
      Procedure SetOpacity(Const Value: Single);
      Procedure SetDesignHide(Const Value: Boolean);
      Procedure SetTabOrder(Const Value: TTabOrder);
      Procedure UpdateDesignHide(Const Value: Boolean);
      Function isOpacityStored: Boolean;
      Function GetChildrenRect: TD2Rect;
      Procedure SetCursor(Const Value: TCursor);
      Function GetAbsoluteWidth: Single;
      Function GetAbsoluteHeight: Single;
      Function GetTabOrder: TTabOrder;
      Procedure UpdateTabOrder(Value: TTabOrder);
   Protected
      FHeight, FLastHeight: Single;
      FWidth, FLastWidth: Single;
      FVisible: Boolean;
      FLocalMatrix: TD2Matrix;
      FAbsoluteMatrix: TD2Matrix;
      FRecalcAbsolute: Boolean;
      FUpdateEffects: Boolean;
      FEffectBitmap: TD2Bitmap;
      FLocked: Boolean;
      FOpacity, FAbsoluteOpacity: Single;
      FRecalcOpacity: Boolean;
      FInPaintTo: Boolean;
      FUpdateRect: TD2Rect;
      FRecalcUpdateRect: Boolean;
      FCaret: TD2VisualObject;
      FPressed, FDoubleClick: Boolean;
      FUpdating: Integer;
      FDisableAlign: Boolean;
      FDisableEffect: Boolean;
      FHasEffect: Boolean;
      Procedure SetEnabled(Const Value: Boolean); Virtual;
      Function GetHint: String; Virtual;
      Procedure SetHint(Const Value: String); Virtual;
      Function HasDisablePaintEffect: Boolean;
      Function HasAfterPaintEffect: Boolean;
      Procedure SetInPaintTo(Value: Boolean);
      Procedure Loaded; Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure SetVisible(Const Value: Boolean); Virtual;
      Procedure SetHeight(Const Value: Single); Virtual;
      Procedure SetWidth(Const Value: Single); Virtual;
      Procedure SetAlign(Const Value: TD2Align); Virtual;
      Function GetAbsoluteRect: TD2Rect; Virtual;
      Function GetAbsoluteMatrix: TD2Matrix; Virtual;
      Function GetChildrenMatrix: TD2Matrix; Virtual;
      Function GetAbsoluteScale: TD2Point; Virtual;
      Function GetLocalRect: TD2Rect; Virtual;
      Function GetUpdateRect: TD2Rect; Virtual;
      Function GetBoundsRect: TD2Rect; Virtual;
      Function GetParentedRect: TD2Rect; Virtual;
      Function GetClipRect: TD2Rect; Virtual;
      Function GetEffectsRect: TD2Rect; Virtual;
      Function GetAbsoluteEnabled: Boolean; Virtual;
      Procedure SetBoundsRect(Const Value: TD2Rect); Virtual;
      Procedure RecalcOpacity; Virtual;
      Procedure RecalcAbsolute; Virtual;
      Procedure RecalcAbsoluteNow;
      Procedure RecalcUpdateRect;
      Procedure RecalcNeedAlign;
      Procedure RecalcEnabled;
      Procedure RecalcHasEffect;
      Procedure FixupTabList;
      Function GetAbsoluteOpacity: Single; Virtual;
      Procedure DesignSelect; Virtual;
      Procedure DesignClick; Virtual;
      Procedure DesignInsert; Virtual;
      Procedure BeginAutoDrag; Virtual;
      Procedure Capture;
      Procedure ReleaseCapture;
      Procedure Click; Virtual;
      Procedure DblClick; Virtual;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Virtual;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Virtual;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Virtual;
      Procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean); Virtual;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Virtual;
      Procedure KeyUp(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Virtual;
      Procedure DialogKey(Var Key: Word; Shift: TShiftState); Virtual;
      Procedure MouseEnter; Virtual;
      Procedure MouseLeave; Virtual;
      Procedure ContextMenu(Const ScreenPosition: TD2Point); Virtual;
      Procedure DragEnter(Const aData: TD2DragObject; Const Point: TD2Point); Virtual;
      Procedure DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean); Virtual;
      Procedure DragDrop(Const aData: TD2DragObject; Const Point: TD2Point); Virtual;
      Procedure DragLeave; Virtual;
      Procedure DragEnd; Virtual;
      Function EnterFocusChildren(AObject: TD2VisualObject): Boolean; Virtual;
      Procedure EnterFocus; Virtual;
      Procedure KillFocus; Virtual;
      Procedure SetNewScene(AScene: Id2Scene); Override;
      Procedure ApplyResource; Virtual;
      Procedure BeforePaint; Virtual;
      Procedure Paint; Virtual;
      Procedure AfterPaint; Virtual;
      Procedure PaintChildren; Virtual;
      Procedure MarginsChanged(Sender: TObject); Virtual;
      Procedure PaddingChanged(Sender: TObject); Virtual;
      Procedure MatrixChanged(Sender: TObject); Virtual;
      Property MouseInObject: Boolean read FMouseInObject write FMouseInObject;
      Property TempCanvas: TD2Canvas read FTempCanvas write SetTempCanvas;
      Property Skew: TD2Position read FSkew write FSkew;
   Public
      DisableDesignResize: Boolean;
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure DeleteChildren; Override;
      Function AbsoluteToLocal(P: TD2Point): TD2Point; Virtual;
      Function LocalToAbsolute(P: TD2Point): TD2Point; Virtual;
      Function AbsoluteToLocalVector(P: TD2Vector): TD2Vector; Virtual;
      Function LocalToAbsoluteVector(P: TD2Vector): TD2Vector; Virtual;
      Function ObjectByPoint(X, Y: Single): TD2VisualObject; Virtual;
      Function PointInObject(X, Y: Single): Boolean; Virtual;
      Procedure SetBounds(X, Y, AWidth, AHeight: Single); Virtual;
      Procedure SetSizeWithoutChange(AWidth, AHeight: Single);
      Function CheckParentVisible: Boolean; Virtual;
      Function MakeScreenshot: TD2Bitmap;
      Function FindTarget(Const APoint: TD2Point; Const aData: TD2DragObject): TD2VisualObject;
      Procedure ShowCaretProc;
      Procedure SetCaretPos(Const APoint: TD2Point);
      Procedure SetCaretSize(Const ASize: TD2Point);
      Procedure SetCaretColor(Const AColor: TD2Color);
      Procedure HideCaret;
      Procedure BeginUpdate; Virtual;
      Procedure EndUpdate; Virtual;
      Procedure Realign; Virtual;
      Procedure UpdateEffects;
      Procedure GetTabOrderList(List: TList; aChildren: Boolean);
      Procedure SetFocus;
      Procedure PaintTo(Const ACanvas: TD2Canvas; Const ARect: TD2Rect; Const AParent: TD2Object = nil);
      Procedure ApplyEffect;
      Procedure Repaint;
      Procedure InvalidateRect(ARect: TD2Rect);
      Procedure Lock;
      Property HintW: WideString read fHint write fHint;
      Property AbsoluteMatrix: TD2Matrix read GetAbsoluteMatrix;
      Property AbsoluteOpacity: Single read GetAbsoluteOpacity;
      Property AbsoluteWidth: Single read GetAbsoluteWidth;
      Property AbsoluteHeight: Single read GetAbsoluteHeight;
      Property AbsoluteScale: TD2Point read GetAbsoluteScale;
      Property AbsoluteEnabled: Boolean read GetAbsoluteEnabled;
      Property InvertAbsoluteMatrix: TD2Matrix read GetInvertAbsoluteMatrix;
      Property LocalRect: TD2Rect read GetLocalRect;
      Property AbsoluteRect: TD2Rect read GetAbsoluteRect;
      Property UpdateRect: TD2Rect read GetUpdateRect;
      Property BoundsRect: TD2Rect read GetBoundsRect write SetBoundsRect;
      Property ParentedRect: TD2Rect read GetParentedRect;
      Property ClipRect: TD2Rect read GetClipRect;
      Property Canvas: TD2Canvas read GetCanvas;
      Property AutoCapture: Boolean read FAutoCapture write FAutoCapture Default False;
      Property CanFocused: Boolean read FCanFocused write FCanFocused Default False;
      Property DisableFocusEffect: Boolean read FDisableFocusEffect write FDisableFocusEffect Default False;
      Property DisableDefaultAlign: Boolean read FDisableDefaultAlign write FDisableDefaultAlign;
      Property TabOrder: TTabOrder read GetTabOrder write SetTabOrder Default -1;
   Published
      Property IsMouseOver: Boolean read FIsMouseOver;
      Property IsDragOver: Boolean read FIsDragOver;
      Property IsFocused: Boolean read FIsFocused;
      Property IsVisible: Boolean read FVisible;
      Property Align: TD2Align read FAlign write SetAlign Default vaNone;
      Property Cursor: TCursor read FCursor write SetCursor Default crDefault;
      Property DragMode: TD2DragMode read FDragMode write FDragMode Default d2DragManual;
      Property DragDisableHighlight: Boolean read FDragDisableHighlight write FDragDisableHighlight Default False;
      Property Enabled: Boolean read FEnabled write SetEnabled Default True;
      Property Position: TD2Position read FPosition write SetPosition;
      Property RotateAngle: Single read FRotateAngle write SetRotateAngle;
      Property RotateCenter: TD2Position read FRotateCenter write FRotateCenter;
      Property Locked: Boolean read FLocked write SetLocked Default False;
      Property Width: Single read FWidth write SetWidth;
      Property Height: Single read FHeight write SetHeight;
      Property Margins: TD2Bounds read FMargins write FMargins;
      Property Padding: TD2Bounds read FPadding write FPadding;
      Property Opacity: Single read FOpacity write SetOpacity Stored isOpacityStored;
      Property ClipChildren: Boolean read FClipChildren write SetClipChildren Default False;
      Property ClipParent: Boolean read FClipParent write FClipParent Default False;
      Property HitTest: Boolean read FHitTest write SetHitTest Default True;
      Property Hint: String read GetHint write SetHint;
      Property ShowHint: Boolean read FShowHint write FShowHint Default False;
      Property CanClipped: Boolean read FCanClipped write FCanClipped Default True;
      Property PopupMenu: TPopupMenu read FPopupMenu write FPopupMenu;
      Property Popup: TD2Popup read FPopup write FPopup;
      Property Scale: TD2Position read FScale write FScale;
      Property Visible: Boolean read FVisible write SetVisible Default True;
      Property DesignHide: Boolean read FDesignHide write SetDesignHide Default False;
      Property OnDragEnter: TD2DragEnterEvent read FOnDragEnter write FOnDragEnter;
      Property OnDragLeave: TNotifyEvent read FOnDragLeave write FOnDragLeave;
      Property OnDragOver: TD2DragOverEvent read FOnDragOver write FOnDragOver;
      Property OnDragDrop: TD2DragDropEvent read FOnDragDrop write FOnDragDrop;
      Property OnDragEnd: TNotifyEvent read FOnDragEnd write FOnDragEnd;
      Property OnKeyDown: TD2KeyEvent read FOnKeyDown write FOnKeyDown;
      Property OnKeyUp: TD2KeyEvent read FOnKeyUp write FOnKeyUp;
      Property OnClick: TNotifyEvent read FOnClick write FOnClick;
      Property OnDblClick: TNotifyEvent read FOnDblClick write FOnDblClick;
      Property OnCanFocused: TD2CanFocusedEvent read FOnCanFocused write FOnCanFocused;
      Property OnEnterFocus: TNotifyEvent read FOnEnterFocus write FOnEnterFocus;
      Property OnKillFocus: TNotifyEvent read FOnKillFocus write FOnKillFocus;
      Property OnMouseDown: TD2MouseEvent read FOnMouseDown write FOnMouseDown;
      Property OnMouseMove: TD2MouseMoveEvent read FOnMouseMove write FOnMouseMove;
      Property OnMouseUp: TD2MouseEvent read FOnMouseUp write FOnMouseUp;
      Property OnMouseWheel: TD2MouseWheelEvent read FOnMouseWheel write FOnMouseWheel;
      Property OnMouseEnter: TNotifyEvent read FOnMouseEnter write FOnMouseEnter;
      Property OnMouseLeave: TNotifyEvent read FOnMouseLeave write FOnMouseLeave;
      Property OnBeforePaint: TOnPaintEvent read FOnBeforePaint write FOnBeforePaint;
      Property OnPaint: TOnPaintEvent read FOnPaint write FOnPaint;
      Property OnApplyResource: TNotifyEvent read FOnApplyResource write FOnApplyResource;
   End;


   TD2PathObject = Class(TD2Object)
   Private
      FPath: TD2PathData;
   Protected
      Procedure SetName(Const NewName: TComponentName); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Path: TD2PathData read FPath write FPath;
   End;

   TD2BrushObject = Class(TD2Object)
   Private
      FBrush: TD2Brush;
   Protected
      Procedure SetName(Const NewName: TComponentName); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Brush: TD2Brush read FBrush write FBrush;
   End;

   TD2BitmapObject = Class(TD2Object)
   Private
      FBitmap: TD2Bitmap;
   Protected
      Procedure SetName(Const NewName: TComponentName); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Bitmap: TD2Bitmap read FBitmap write FBitmap;
   End;

   TD2Control = Class(TD2VisualObject)
   Private
      Procedure SetResource(Const Value: String);
      Procedure SetBindingSource(Const Value: TD2Control);
   Protected
      FResourceLink: TD2Object;
      FNeedResource: Boolean;
      FBindingObjects: TList;
      FBindingSource: TD2Control;
      FAutoTranslate: Boolean;
      FActionLink: TD2ControlActionLink;
      FHelpType: THelpType;
      FHelpKeyword: String;
      FHelpContext: THelpContext;
      Function IsHelpContextStored: Boolean;
      Procedure SetHelpContext(Const Value: THelpContext);
      Procedure SetHelpKeyword(Const Value: String);
      Function GetAction: TBasicAction; Virtual;
      Procedure SetAction(Value: TBasicAction);
      Procedure DoActionChange(Sender: TObject);
      Procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); Virtual;
      Procedure InitiateAction; Virtual;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure Loaded; Override;
      Procedure ApplyStyle; Virtual;
      Procedure FreeStyle; Virtual;
      Procedure EnterFocus; Override;
      Procedure BeforePaint; Override;
      Function GetResourceObject: TD2VisualObject;
      Procedure SetData(Const Value: Variant); Override;
      Procedure ToBindingObjects;
      Procedure AddBindingObject(AObject: TD2Control);
      Procedure RemoveBindingObject(AObject: TD2Control);
   Public
      FResource: String;
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function FindResource(Const AResource: String): TD2Object; Override;
      Procedure Paint; Override;
      Procedure ApplyResource; Override;
      Procedure UpdateResource; Override;
      Property BindingSource: TD2Control read FBindingSource write SetBindingSource;
      Property AutoTranslate: Boolean read FAutoTranslate write FAutoTranslate;
      Property Action: TBasicAction read GetAction write SetAction;
   Published
      Property HelpType: THelpType read FHelpType write FHelpType Default htContext;
      Property HelpKeyword: String read FHelpKeyword write SetHelpKeyword Stored IsHelpContextStored;
      Property HelpContext: THelpContext read FHelpContext write SetHelpContext Stored IsHelpContextStored Default 0;
      Property Resource: String read FResource write SetResource;
      Property TabOrder Default -1;
   End;

   TD2Background = Class(TD2Control)
   Private
      FFill: TD2Brush;
      Procedure SetFill(Const Value: TD2Brush);
   Protected
      Procedure BeforePaint; Override;
      Procedure PaintChildren; Override;
      Procedure FillChanged(Sender: TObject); Virtual;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
   Published
      Property Resource;
      Property Fill: TD2Brush read FFill write SetFill;
   End;

   TD2Content = Class(TD2VisualObject)
   Protected
      FParentAligning: Boolean;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetParentComponent: TComponent; Override;
      Procedure Realign; Override;
      Procedure Paint; Override;
   End;

   TD2Resources = Class(TComponent)
   Private
      FResource: TStrings;
      FRoot: TD2Object;
      FSceneList: TList;
      FFileName: String;
      Procedure SetResource(Const Value: TStrings);
      Procedure SetFileName(Const Value: String);
      Procedure DoResourceChanged(Sender: TObject);
   Protected
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure Loaded; Override;
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadResources(Stream: TStream);
      Procedure WriteResources(Stream: TStream);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure AddSceneUpdater(Const Scene: Id2Scene);
      Procedure RemoveSceneUpdater(Const Scene: Id2Scene);
      Procedure FillStrings;
      Procedure UpdateScenes;
      Function SaveToFile(Const aFilename: String): Boolean;
      Function LoadFromFile(Const aFilename: String): Boolean;
      Function SaveToTextFile(Const aFilename: String): Boolean;
      Function LoadFromTextFile(Const aFilename: String): Boolean;

      Property Root: TD2Object read FRoot write FRoot;
   Published
      Property Resource: TStrings read FResource write SetResource Stored False;
      Property FileName: String read FFileName write SetFileName;
   End;

   TD2Lang = Class(TComponent)
   Private
      FLang: String;
      FResources: TD2WideStrings;
      FOriginal: TD2WideStrings;
      FAutoSelect: Boolean;
      FFileName: String;
      FStoreInForm: Boolean;
      Procedure SetLang(Const Value: String);
      Function GetLangStr(Index: WideString): TD2WideStrings;
   Protected
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadResources(Stream: TStream);
      Procedure WriteResources(Stream: TStream);
      Procedure Loaded; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure AddLang(AName: WideString);
      Procedure LoadFromFile(AFileName: String);
      Procedure SaveToFile(AFileName: String);
      Property Original: TD2WideStrings read FOriginal;
      Property Resources: TD2WideStrings read FResources;
      Property LangStr[Index: WideString]: TD2WideStrings read GetLangStr;
   Published
      Property AutoSelect: Boolean read FAutoSelect write FAutoSelect Default True;
      Property FileName: String read FFileName write FFileName;
      Property StoreInForm: Boolean read FStoreInForm write FStoreInForm Default True;
      Property Lang: String read FLang write SetLang;
   End;

   TD2Designer = Class(TComponent)
   Private
      FScenes: TList;
   Protected
      Procedure CallDesignSelect(AObject: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure SelectObject(ADesignerForm: TComponent; AObject: TD2Object; MultiSelection: Array Of TD2Object); Virtual; Abstract;
      Procedure Modified(ADesignerForm: TComponent); Virtual; Abstract;
      Function UniqueName(ADesignerForm: TComponent; Const aClassName: String): String; Virtual; Abstract;
      Function IsSelected(ADesignerForm: TComponent; AObject: TObject): Boolean; Virtual; Abstract;
      Procedure AddScene(Scene: Id2Scene); Virtual;
      Procedure RemoveScene(Scene: Id2Scene); Virtual;
      Procedure EditStyle(Res: TD2Resources; Const ASelected: String); Virtual;
      Procedure AddObject(APersistent: TPersistent); Virtual;
      Procedure Add2DObject(AObject: TD2Object); Virtual;
      Procedure Delete2DObject(AObject: TD2Object); Virtual;
      Function AddMethod(aMethodName: String): TMethod; Virtual;
      Function GetMethodName(Method: TMethod): String; Virtual;
   End;

   TD2Frame = Class(TD2VisualObject)
   Private
      FNeedClone: Boolean;
      FSceneObject: TD2CustomScene;
      FBuffer: TD2Bitmap;
      Procedure SetSceneObject(Const Value: TD2CustomScene);
   Protected
      Procedure GetChildren(Proc: TGetChildProc; Root: TComponent); Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure Loaded; Override;
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property SceneObject: TD2CustomScene read FSceneObject write SetSceneObject;
   End;


   TD2CustomScene = Class(TCustomControl, Id2Scene)
   Private
      {$IFDEF ORCA2D_OS_WINDOWS}
    FPrevWndProc: WNDPROC;
    FDC: THandle;
      {$ENDIF}
      FShift: TShiftState;
      FCanvas: TD2Canvas;
      FDisableUpdate: Boolean;
      FChildren: TList;
      FDesignRoot, FSelected, FCaptured, FHovered, FFocused: TD2VisualObject;
      FSelection: Array Of TD2Object;
      FDesignPlaceObject: TD2VisualObject;
      FDesignGridLines: Array Of TD2VisualObject;
      FDesignPopup: TPopupMenu;
      FDesignChangeSelection: TNotifyEvent;
      FUnsnapMousePos, FMousePos, FDownPos: TD2Point;
      FMoving, FLeftTop, FRightTop, FLeftBottom, FRightBottom, FTop, FBottom, FLeft, FRight, FRotate: Boolean;
      FLeftTopHot, FRightTopHot, FLeftBottomHot, FRightBottomHot, FTopHot, FBottomHot, FLeftHot, FRightHot, FRotateHot: Boolean;
      FResizeSize: TPoint;
      FDragging, FResizing: Boolean;
      FDesignTime: Boolean;
      FFill: TD2Brush;
      FTransparency: Boolean;
      FSnapToGrid: Boolean;
      FSnapToLines: Boolean;
      FSnapGridShow: Boolean;
      FSnapGridSize: Single;
      FInsertObject: String;
      FAlignRoot: Boolean;
      FDesignPopupEnabled: Boolean;
      FOpenInFrame: TD2Frame;
      FCloneFrame: TForm;
      FDrawing: Boolean;
      FOnFlush: TNotifyEvent;
      FShowTimer: TD2Timer;
      FDBCSLeadChar: Word;
      FStyle: TD2Resources;
      FShowUpdateRects: Boolean;
      FLoadCursor: TCursor;
      FDragSourceObj: TCustomControl;
      FActiveControl: TD2Control;
      FAnimatedCaret: Boolean;
      FFixMouseMove: Boolean;
      Procedure SetActiveControl(AControl: TD2Control);
      Procedure DoShowTimer(Sender: TObject);
      {$IFDEF ORCA2D_OS_WINDOWS}
    function GetDataObject: TD2DragObject;
    function DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult; stdcall;
    function DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult; stdcall;
    function DragLeave: HResult; stdcall;
    function Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult; stdcall;
    procedure WMAddUpdateRect(var Msg: TMessage); message WM_ADDUPDATERECT;
      {$ENDIF}

      Procedure WMEraseBkgnd(Var Msg: TLMEraseBkgnd); Message LM_ERASEBKGND;
      Procedure CMMouseLeave(Var Message: TLMessage); Message CM_MOUSELEAVE;
      Procedure WMPaint(Var Msg: TLMPaint); Message LM_PAINT;
      Procedure CMShowingChanged(Var Message: TLMessage); Message CM_SHOWINGCHANGED;
      Procedure CMDesignHitTest(Var Msg: TLMMouse); Message CM_DESIGNHITTEST;
      Procedure CMHintShow(Var Message: TLMessage); Message CM_HINTSHOW;
      Function GetCount: Integer;
      Procedure SetChildren(Index: Integer; Const Value: TD2Object);
      Function GetChildrenObject(Index: Integer): TD2Object;
      Procedure SetFill(Const Value: TD2Brush);
      Procedure FillChanged(Sender: TObject);
      Procedure SetSnapGridShow(Const Value: Boolean);
      Procedure AddUpdateRectsFromGridLines;
      Function SnapToGridValue(Value: Single): Single;
      Procedure SetSnapGridSize(Const Value: Single);
      Procedure SnapToGridLines(AllowChangePosition: Boolean);
      Function SnapPointToGridLines(Const APoint: TD2Point): TD2Point;
      Procedure ReadDesignSnapGridShow(Reader: TReader);
      Procedure WriteDesignSnapGridShow(Writer: TWriter);
      Procedure ReadDesignSnapToGrid(Reader: TReader);
      Procedure WriteDesignSnapToGrid(Writer: TWriter);
      Procedure ReadDesignSnapToLines(Reader: TReader);
      Procedure WriteDesignSnapToLines(Writer: TWriter);
      Procedure doDesignTabOrderBtnClick(Sender: TObject);
      Procedure doDesignTabOrderRebuildList(ListBox: TD2VisualObject);
      Procedure doDesignPopupTabOrder(Sender: TObject);
      Procedure doDesignPopupEditStyle(Sender: TObject);
      Procedure doDesignPopupCreateStyle(Sender: TObject);
      Procedure doDesignPopupLoadFromFile(Sender: TObject);
      Procedure doDesignPopupSaveToFile(Sender: TObject);
      Procedure doDesignPopupDesignHide(Sender: TObject);
      Procedure doDesignPopupAddItem(Sender: TObject);
      Procedure doDesignPopupReorder(Sender: TObject);
      Procedure doDesignPopupGrid(Sender: TObject);
      Procedure doDesignPopupAdd(Sender: TObject);
      Procedure doDesignPopupDelete(Sender: TObject);
      Procedure doDesignPopupClone(Sender: TObject);
      Procedure doDesignPopupCut(Sender: TObject);
      Procedure doDesignPopupCopy(Sender: TObject);
      Procedure doDesignPopupPaste(Sender: TObject);
      Procedure doDesignPopupFixObj(Sender: TObject);
      Procedure doDesignPopupDeleteAnimations(Sender: TObject);
      Procedure doDesignPopupDeleteEffects(Sender: TObject);
      Function HasEffects(Value: TD2VisualObject): Boolean;
      Function HasAnimations(Value: TD2VisualObject): Boolean;
      Procedure DesignReCreateSubObjects(aObj, aParent: TD2Object);
      Function GetRoot: TD2Object;
      Procedure SetFocused(Const Value: TD2VisualObject);
      Procedure DoDesignSelect(AObject: TObject);
      Procedure UpdateLayer;
      Procedure SetSelected(Const Value: TD2VisualObject);
      Procedure SetStyle(Const Value: TD2Resources);

      Function GetActiveControl: TD2Control;
      Function GetDisableUpdate: Boolean;
      Function GetDesignTime: Boolean;
      Function GetCanvas: TD2Canvas;
      Function GetComponent: TComponent;
      Function GetSelected: TD2VisualObject;
      Function GetDesignPlaceObject: TD2VisualObject;
      Procedure SetDisableUpdate(Value: Boolean);
      Function GetUpdateRectsCount: Integer;
      Function GetUpdateRect(Const Index: Integer): TD2Rect;
      Procedure SetCaptured(Const Value: TD2VisualObject);
      Function GetCaptured: TD2VisualObject;
      Function GetFocused: TD2VisualObject;
      Procedure SetDesignRoot(Const Value: TD2VisualObject);
      Function GetMousePos: TD2Point;
      Function GetStyle: TD2Resources;
      Function GetTransparency: Boolean;
      Procedure SetTransparency(Const Value: Boolean);
      Procedure BeginDragObj(Source: TObject; ABitmap: TD2Bitmap);
      Procedure EndDragEvent(Sender, Target: TObject; X, Y: Integer);
      Function GetAnimatedCaret: Boolean;
      Function LocalToScreen(Const Point: TD2Point): TD2Point;
      Function ShowKeyboardForControl(AObject: TD2Object): Boolean;
      Function HideKeyboardForControl(AObject: TD2Object): Boolean;
   Protected
      FUpdateRects: Array Of TD2Rect;
      Function GetOwner: TComponent; Override;
      Procedure CreateHandle; Override;
      Procedure CreateWnd; Override;
      Procedure DestroyWnd; Override;
      Procedure Loaded; Override;
      Procedure Resize; Override;
      Procedure Draw; Virtual;
      Procedure Paint; Override;
      Procedure GetChildren(Proc: TGetChildProc; Root: TComponent); Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y: Integer); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Override;
      Procedure DoDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; Var Accept: Boolean);
      Procedure DoDragDrop(Sender, Source: TObject; X, Y: Integer);

      Procedure KeyUp(Var Key: Word; Shift: TShiftState); Override;
      Procedure KeyDown(Var Key: Word; Shift: TShiftState); Override;
      Procedure KeyPress(Var Key: Char); Override;
      Procedure UTF8KeyPress(Var UTF8Key: TUTF8Char); Override;

      Procedure UnicodeKeyUp(Var Key: Word; Var Char: System.Widechar; Shift: TShiftState);
      Procedure UnicodeKeyDown(Var Key: Word; Var Char: System.Widechar; Shift: TShiftState);
      Function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; Override;
      Function ObjectByPoint(X, Y: Single): TD2VisualObject;
   Public
      FPopupPos: TPoint;
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure InitiateAction; Override;
      Procedure DeleteChildren;
      Procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); Override;
      Procedure UpdateBuffer;
      Procedure UpdateResource;
      Property Canvas: TD2Canvas read FCanvas;
      Procedure AddObject(AObject: TD2Object);
      Procedure RemoveObject(AObject: TD2Object);
      Procedure RealignRoot;
      Procedure BeginDrag;
      Procedure BeginResize;
      Procedure AddUpdateRect(R: TD2Rect);
      Procedure OpenDesignPopup;
      Procedure InsertObject(Const aClassName: String);
      Property DesignTime: Boolean read FDesignTime write FDesignTime Stored False;
      Property ShowUpdateRects: Boolean read FShowUpdateRects write FShowUpdateRects Stored False;
      Property Count: Integer read GetCount;
      Property Root: TD2Object read GetRoot;
      Property Children[Index: Integer]: TD2Object read GetChildrenObject write SetChildren;
      Property Selected: TD2VisualObject read FSelected write SetSelected;
      Property Captured: TD2VisualObject read FCaptured;
      Property Hovered: TD2VisualObject read FHovered;
      Property Focused: TD2VisualObject read FFocused write SetFocused;
      Property DisableUpdate: Boolean read FDisableUpdate;
      Property IsDrawing: Boolean read FDrawing;
      Procedure CreateEmbedded(Const AWidth, AHeight: Integer; AOnFlush: TNotifyEvent);
      Procedure EmbeddedMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
      Procedure EmbeddedMouseMove(Shift: TShiftState; X, Y: Integer);
      Procedure EmbeddedMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
      Function EmbeddedMouseWheel(Shift: TShiftState; WheelDelta: Integer): Boolean;
      Procedure EmbeddedKeyUp(Var Key: Word; Var Char: System.Widechar; Shift: TShiftState);
      Procedure EmbeddedKeyDown(Var Key: Word; Var Char: System.Widechar; Shift: TShiftState);

      Property DesignPopupEnabled: Boolean read FDesignPopupEnabled write FDesignPopupEnabled;
      Property DesignSnapGridShow: Boolean read FSnapGridShow write SetSnapGridShow;
      Property DesignSnapToGrid: Boolean read FSnapToGrid write FSnapToGrid;
      Property DesignSnapToLines: Boolean read FSnapToLines write FSnapToLines;
      Property DesignChangeSelection: TNotifyEvent read FDesignChangeSelection write FDesignChangeSelection;
      Property AnimatedCaret: Boolean read FAnimatedCaret write FAnimatedCaret Default True;
      Property ActiveControl: TD2Control read FActiveControl write SetActiveControl;
      Property DesignSnapGridSize: Single read FSnapGridSize write SetSnapGridSize;
      Property Fill: TD2Brush read FFill write SetFill;
      Property FixMouseMove: Boolean read FFixMouseMove write FFixMouseMove Default False;
      Property Transparency: Boolean read FTransparency write SetTransparency Default False;
      Property Style: TD2Resources read FStyle write SetStyle;
      Property OnFlush: TNotifyEvent read FOnFlush write FOnFlush;
      Property Align;
      Property TabStop;
   Published
   End;

   TD2Scene = Class(TD2CustomScene)
   Published
      Property Align;
      Property AnimatedCaret;
      Property Fill;
      Property FixMouseMove;
      Property Transparency;
      Property Style;
      Property TabStop;
      Property ActiveControl; // must be last
   End;

   TD2PopupForm = Class(TCustomForm)
   Private
      FOwnerForm: TCustomForm;
      FPopup: TD2Popup;
      FNoFree: Boolean;
      Procedure WMDeactivate(Var Msg: TLMActivate); Message CM_DEACTIVATE;
   Protected
      Procedure DoClose(Var anAction: TCloseAction); Override;
      Procedure CreateParams(Var Params: TCreateParams); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   End;

   TD2Popup = Class(TD2Control)
   Private
      FSaveParent: TD2Object;
      FSaveFocused: TD2VisualObject;
      FSaveScale: TD2Point;
      FPopupForm: TCustomForm;
      FPopupScene: TD2Scene;
      FDragTimer: TD2Timer;
      FPopupLayout: TD2VisualObject;
      FIsOpen: Boolean;
      FStaysOpen: Boolean;
      FPlacement: TD2Placement;
      FPlacementTarget: TD2VisualObject;
      FPlacementRectangle: TD2Bounds;
      FHorizontalOffset: Single;
      FVerticalOffset: Single;
      FDragWithParent: Boolean;
      FAnimating: Boolean;
      FStyle: TD2Resources;
      FPlacementScene: TD2PlacementScene;
      FModalResult: TModalResult;
      FModal: Boolean;
      FOnClosePopup: TNotifyEvent;
      Procedure SetIsOpen(Const Value: Boolean);
      Procedure SetPlacementRectangle(Const Value: TD2Bounds);
      Procedure SetModalResult(Const Value: TModalResult);
      Procedure Dotimer(Sender: TObject);
   Protected
      Procedure ApplyPlacement; Virtual;
      Procedure KillFocus; Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure DialogKey(Var Key: Word; Shift: TShiftState); Override;
      Procedure DoFormClose(Sender: TObject; Var anAction: TCloseAction);
      Procedure DoPopupLayoutClick(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Function PopupModal: TModalResult; Virtual;
      Procedure Popup; Virtual;
      Procedure ClosePopup; Virtual;
      Property ModalResult: TModalResult read FModalResult write SetModalResult;
      Property PlacementScene: TD2PlacementScene read FPlacementScene write FPlacementScene Default d2PlacementSceneNew;
   Published
      Property IsOpen: Boolean read FIsOpen write SetIsOpen;
      Property HorizontalOffset: Single read FHorizontalOffset write FHorizontalOffset;
      Property VerticalOffset: Single read FVerticalOffset write FVerticalOffset;
      Property Placement: TD2Placement read FPlacement write FPlacement Default d2PlacementBottom;
      Property PlacementTarget: TD2VisualObject read FPlacementTarget write FPlacementTarget;
      Property PlacementRectangle: TD2Bounds read FPlacementRectangle write SetPlacementRectangle;
      Property StaysOpen: Boolean read FStaysOpen write FStaysOpen Default False;
      Property Style: TD2Resources read FStyle write FStyle;
      Property DragWithParent: Boolean read FDragWithParent write FDragWithParent Default False;
      Property OnClosePopup: TNotifyEvent read FOnClosePopup write FOnClosePopup;
      Property Resource;
      Property Visible Default False;
   End;

   TD2PopupItem = Class(TD2Control)
   Private
      FPlacement: TD2Placement;
      FHorizontalOffset: Single;
      FVerticalOffset: Single;
      Procedure SetPlacement(Value: TD2Placement);
      Procedure SetHorizontalOffset(Value: Single);
      Procedure SetVerticalOffset(Value: Single);
   Protected
      Procedure GetChildren(Proc: TGetChildProc; Root: TComponent); Override;
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadRect(Reader: TReader);
      Procedure WriteRect(Writer: TWriter);
      Procedure MouseEnter; Override;
      Procedure MouseLeave; Override;
      Procedure DesignClick; Override;
      Procedure ApplyPlacement;
      Procedure DoClosePopup(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure AddObject(AObject: TD2Object); Override;
      Procedure Realign; Override;
   Published
      Property Resource;
      Property Placement: TD2Placement read FPlacement write SetPlacement Default d2PlacementBottom;
      Property HorizontalOffset: Single read FHorizontalOffset write SetHorizontalOffset;
      Property VerticalOffset: Single read FVerticalOffset write SetVerticalOffset;
   End;

   TD2MessagePopup = Class(TD2Popup)
   Private
      FDisableScene: Boolean;
   Protected
      Procedure ApplyPlacement; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function PopupModal: TModalResult; Override;
   Published
      Property Resource;
      Property StaysOpen Default True;
      Property DragWithParent Default True;
      Property DisableScene: Boolean read FDisableScene write FDisableScene Default True;
   End;

Type

   TD2CustomTranslateProc = Function(AText: WideString): WideString;

   TD2ColorAnimation = Class(TD2Animation)
   Private
      FStartColor: TD2Color;
      FStopColor: TD2Color;
      FPath, FPropertyName: Ansistring;
      FInstance: TObject;
      FStartFromCurrent: Boolean;
      Function GetStartColor: String;
      Function GetStopColor: String;
      Procedure SetStartColor(Const Value: String);
      Procedure SetStopColor(Const Value: String);
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Override;
   Published
      Property StartValue: String read GetStartColor write SetStartColor;
      Property StartFromCurrent: Boolean read FStartFromCurrent write FStartFromCurrent Default False;
      Property StopValue: String read GetStopColor write SetStopColor;
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
   End;

   TD2GradientAnimation = Class(TD2Animation)
   Private
      FStartGradient: TD2Gradient;
      FStopGradient: TD2Gradient;
      FPath, FPropertyName: Ansistring;
      FInstance: TObject;
      FStartFromCurrent: Boolean;
      Procedure SetStartGradient(Const Value: TD2Gradient);
      Procedure SetStopGradient(Const Value: TD2Gradient);
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Override;
   Published
      Property StartValue: TD2Gradient read FStartGradient write SetStartGradient;
      Property StartFromCurrent: Boolean read FStartFromCurrent write FStartFromCurrent Default False;
      Property StopValue: TD2Gradient read FStopGradient write SetStopGradient;
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
   End;

   TD2FloatAnimation = Class(TD2Animation)
   Private
      FStartFloat: Single;
      FStopFloat: Single;
      FPath, FPropertyName: Ansistring;
      FInstance: TObject;
      FStartFromCurrent: Boolean;
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Override;
      Procedure Stop; Override;
   Published
      Property StartValue: Single read FStartFloat write FStartFloat Stored True;
      Property StartFromCurrent: Boolean read FStartFromCurrent write FStartFromCurrent Default False;
      Property StopValue: Single read FStopFloat write FStopFloat Stored True;
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
   End;

   TD2RectAnimation = Class(TD2Animation)
   Private
      FStartRect: TD2Bounds;
      FCurrent: TD2Bounds;
      FStopRect: TD2Bounds;
      FPath, FPropertyName: Ansistring;
      FInstance: TObject;
      FStartFromCurrent: Boolean;
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Override;
   Published
      Property StartValue: TD2Bounds read FStartRect write FStartRect;
      Property StartFromCurrent: Boolean read FStartFromCurrent write FStartFromCurrent Default False;
      Property StopValue: TD2Bounds read FStopRect write FStopRect;
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
   End;

   TD2BitmapAnimation = Class(TD2Animation)
   Private
      FPropertyName: Ansistring;
      FInstance: TObject;
      FStartBitmap: TD2Bitmap;
      FStopBitmap: TD2Bitmap;
      FCurrent: TD2Bitmap;
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property StartValue: TD2Bitmap read FStartBitmap write FStartBitmap;
      Property StopValue: TD2Bitmap read FStopBitmap write FStopBitmap;
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
   End;

   TD2BitmapListAnimation = Class(TD2Animation)
   Private
      FPropertyName: Ansistring;
      FInstance: TObject;
      FCurrent: TD2Bitmap;
      FAnimationCount: Integer;
      FAnimationBitmap: TD2Bitmap;
      FLastAnimationStep: Integer;
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
      Property AnimationBitmap: TD2Bitmap read FAnimationBitmap write FAnimationBitmap;
      Property AnimationCount: Integer read FAnimationCount write FAnimationCount;
   End;


   TD2Key = Class(TCollectionItem)
   Private
      FKey: Single;
      Procedure SetKey(Const Value: Single);
   Public
   Published
      Property Key: Single read FKey write SetKey;
   End;

   TD2Keys = Class(TCollection)
   Private
   Public
      Function FindKeys(Const Time: Single; Var Key1, Key2: TD2Key): Boolean;
   End;

   TD2ColorKey = Class(TD2Key)
   Private
      FValue: String;
   Public
   Published
      Property Value: String read FValue write FValue;
   End;

   TD2ColorKeyAnimation = Class(TD2Animation)
   Private
      FPropertyName: Ansistring;
      FInstance: TObject;
      FKeys: TD2Keys;
      FPath: Ansistring;
      FStartFromCurrent: Boolean;
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Override;
   Published
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
      Property Keys: TD2Keys read FKeys write FKeys;
      Property StartFromCurrent: Boolean read FStartFromCurrent write FStartFromCurrent;
   End;

   TD2FloatKey = Class(TD2Key)
   Private
      FValue: Single;
   Public
   Published
      Property Value: Single read FValue write FValue;
   End;

   TD2FloatKeyAnimation = Class(TD2Animation)
   Private
      FPropertyName: Ansistring;
      FInstance: TObject;
      FKeys: TD2Keys;
      FPath: Ansistring;
      FStartFromCurrent: Boolean;
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Override;
   Published
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
      Property Keys: TD2Keys read FKeys write FKeys;
      Property StartFromCurrent: Boolean read FStartFromCurrent write FStartFromCurrent;
   End;


   TD2PathAnimation = Class(TD2Animation)
   Private
      FPath: TD2PathData;
      FPolygon: TD2Polygon;
      FObj: TD2VisualObject;
      FStart: TD2Point;
      FRotate: Boolean;
      FSpline: TD2Spline;
      Procedure SetPath(Const Value: TD2PathData);
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Override;
   Published
      Property Path: TD2PathData read FPath write SetPath;
      Property Rotate: Boolean read FRotate write FRotate Default False;
   End;

   TD2PathSwitcher = Class(TD2Animation)
   Private
      FPath, FPropertyName: Ansistring;
      FInstance: TObject;
      FPathTrue: String;
      FPathFalse: String;
      Procedure SetPathFalse(Const Value: String);
      Procedure SetPathTrue(Const Value: String);
   Protected
      Procedure ProcessAnimation; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Start; Override;
   Published
      Property PathTrue: String read FPathTrue write SetPathTrue;
      Property PathFalse: String read FPathFalse write SetPathFalse;
      Property PropertyName: Ansistring read FPropertyName write FPropertyName;
   End;

   TD2ShadowEffect = Class(TD2Effect)
   Private
      FDistance: Single;
      FSoftness: Single;
      FShadowColor: TD2Color;
      FOpacity: Single;
      FDirection: Single;
      Procedure SetDistance(Const Value: Single);
      Procedure SetSoftness(Const Value: Single);
      Procedure SetShadowColor(Const Value: String);
      Procedure SetOpacity(Const Value: Single);
      Function GetShadowColor: String;
      Procedure SetDirection(Const Value: Single);
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetRect(Const ARect: TD2Rect): TD2Rect; Override;
      Function GetOffset: TD2Point; Override;
      Procedure ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single); Override;
   Published
      Property Distance: Single read FDistance write SetDistance;
      Property Direction: Single read FDirection write SetDirection;
      Property Softness: Single read FSoftness write SetSoftness;
      Property Opacity: Single read FOpacity write SetOpacity;
      Property ShadowColor: String read GetShadowColor write SetShadowColor;
   End;

   TD2BlurEffect = Class(TD2Effect)
   Private
      FSoftness: Single;
      Procedure SetSoftness(Const Value: Single);
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetRect(Const ARect: TD2Rect): TD2Rect; Override;
      Function GetOffset: TD2Point; Override;
      Procedure ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single); Override;
   Published
      Property Softness: Single read FSoftness write SetSoftness;
   End;

   TD2GlowEffect = Class(TD2Effect)
   Private
      FSoftness: Single;
      FGlowColor: TD2Color;
      FOpacity: Single;
      Procedure SetSoftness(Const Value: Single);
      Procedure SetOpacity(Const Value: Single);
      Function GetGlowColor: String;
      Procedure SetGlowColor(Const Value: String);
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetRect(Const ARect: TD2Rect): TD2Rect; Override;
      Function GetOffset: TD2Point; Override;
      Procedure ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single); Override;
   Published
      Property Softness: Single read FSoftness write SetSoftness;
      Property Opacity: Single read FOpacity write SetOpacity;
      Property GlowColor: String read GetGlowColor write SetGlowColor;
   End;

   TD2InnerGlowEffect = Class(TD2GlowEffect)
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetRect(Const ARect: TD2Rect): TD2Rect; Override;
      Function GetOffset: TD2Point; Override;
      Procedure ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single); Override;
   End;

   TD2BevelEffect = Class(TD2Effect)
   Private
      FDirection: Single;
      FSize: Integer;
      Procedure SetDirection(Const Value: Single);
      Procedure SetSize(Const Value: Integer);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetRect(Const ARect: TD2Rect): TD2Rect; Override;
      Function GetOffset: TD2Point; Override;
      Procedure ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single); Override;
   Published
      Property Direction: Single read FDirection write SetDirection;
      Property Size: Integer read FSize write SetSize;
   End;

   TD2ReflectionEffect = Class(TD2Effect)
   Private
      FOffset: Integer;
      FOpacity: Single;
      FLength: Single;
      Procedure SetOpacity(Const Value: Single);
      Procedure SetOffset(Const Value: Integer);
      Procedure SetLength(Const Value: Single);
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetRect(Const ARect: TD2Rect): TD2Rect; Override;
      Function GetOffset: TD2Point; Override;
      Procedure ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single); Override;
   Published
      Property Opacity: Single read FOpacity write SetOpacity;
      Property Offset: Integer read FOffset write SetOffset;
      Property Length: Single read FLength write SetLength;
   End;

Type

   TD2Shape = Class(TD2VisualObject)
   Private
      FFill: TD2Brush;
      FStrokeThickness: Single;
      FStroke: TD2Brush;
      FStrokeCap: TD2StrokeCap;
      FStrokeJoin: TD2StrokeJoin;
      FStrokeDash: TD2StrokeDash;
      Procedure SetFill(Const Value: TD2Brush);
      Procedure SetStroke(Const Value: TD2Brush);
      Procedure SetStrokeThickness(Const Value: Single);
      Function isStrokeThicknessStored: Boolean;
      Procedure SetStrokeCap(Const Value: TD2StrokeCap);
      Procedure SetStrokeJoin(Const Value: TD2StrokeJoin);
      Procedure SetStrokeDash(Const Value: TD2StrokeDash);
   Protected
      Procedure FillChanged(Sender: TObject); Virtual;
      Procedure StrokeChanged(Sender: TObject); Virtual;
      Function GetShapeRect: TD2Rect;
      Procedure BeforePaint; Override;
      Procedure AfterPaint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Property Fill: TD2Brush read FFill write SetFill;
      Property Stroke: TD2Brush read FStroke write SetStroke;
      Property StrokeThickness: Single read FStrokeThickness write SetStrokeThickness Stored isStrokeThicknessStored;
      Property StrokeCap: TD2StrokeCap read FStrokeCap write SetStrokeCap Default d2CapFlat;
      Property StrokeDash: TD2StrokeDash read FStrokeDash write SetStrokeDash Default d2DashSolid;
      Property StrokeJoin: TD2StrokeJoin read FStrokeJoin write SetStrokeJoin Default d2JoinMiter;
      Property ShapeRect: TD2Rect read GetShapeRect;
   End;

   TD2Line = Class(TD2Shape)
   Private
      FLineType: TD2LineType;
      Procedure SetLineType(Const Value: TD2LineType);
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Procedure Paint; Override;
   Published
      Property Stroke;
      Property StrokeCap;
      Property StrokeDash;
      Property StrokeJoin;
      Property StrokeThickness;
      Property LineType: TD2LineType read FLineType write SetLineType;
   End;

   TD2Rectangle = Class(TD2Shape)
   Private
      FyRadius: Single;
      FxRadius: Single;
      FCorners: TD2Corners;
      FCornerType: TD2CornerType;
      FSides: TD2Sides;
      Function IsCornersStored: Boolean;
      Function IsSidesStored: Boolean;
   Protected
      Procedure SetxRadius(Const Value: Single); Virtual;
      Procedure SetyRadius(Const Value: Single); Virtual;
      Procedure SetCorners(Const Value: TD2Corners); Virtual;
      Procedure SetCornerType(Const Value: TD2CornerType); Virtual;
      Procedure SetSides(Const Value: TD2Sides); Virtual;
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property Fill;
      Property Stroke;
      Property StrokeCap;
      Property StrokeDash;
      Property StrokeJoin;
      Property StrokeThickness;
      Property xRadius: Single read FxRadius write SetxRadius;
      Property yRadius: Single read FyRadius write SetyRadius;
      Property Corners: TD2Corners read FCorners write SetCorners Stored IsCornersStored;
      Property CornerType: TD2CornerType read FCornerType write SetCornerType Default d2CornerRound;
      Property Sides: TD2Sides read FSides write SetSides Stored IsSidesStored;
   End;

   TD2SidesRectangle = Class(TD2Rectangle)
   End;

   TD2BlurRectangle = Class(TD2Rectangle)
   Private
      FBuffer: TD2Bitmap;
      FSoftness: Single;
      FRecreate: Boolean;
      Procedure SetSoftness(Const Value: Single);
   Protected
      Procedure FillChanged(Sender: TObject); Override;
      Procedure SetxRadius(Const Value: Single); Override;
      Procedure SetyRadius(Const Value: Single); Override;
      Procedure SetCorners(Const Value: TD2Corners); Override;
      Procedure SetCornerType(Const Value: TD2CornerType); Override;
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Softness: Single read FSoftness write SetSoftness;
   End;

   TD2RoundRect = Class(TD2Shape)
   Private
      FCorners: TD2Corners;
      Function IsCornersStored: Boolean;
   Protected
      Procedure SetCorners(Const Value: TD2Corners); Virtual;
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property Fill;
      Property Stroke;
      Property StrokeCap;
      Property StrokeDash;
      Property StrokeJoin;
      Property StrokeThickness;
      Property Corners: TD2Corners read FCorners write SetCorners Stored IsCornersStored;
   End;

   TD2BlurRoundRect = Class(TD2RoundRect)
   Private
      FBuffer: TD2Bitmap;
      FSoftness: Single;
      FRecreate: Boolean;
      Procedure SetSoftness(Const Value: Single);
   Protected
      Procedure SetCorners(Const Value: TD2Corners); Override;
      Procedure FillChanged(Sender: TObject); Override;
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Softness: Single read FSoftness write SetSoftness;
   End;

   TD2CalloutRectangle = Class(TD2Rectangle)
   Private
      FPath: TD2PathData;
      FCalloutWidth: Single;
      FCalloutLength: Single;
      FCalloutPosition: TD2CalloutPosition;
      FCalloutOffset: Single;
      Procedure SetCalloutWidth(Const Value: Single);
      Procedure SetCalloutLength(Const Value: Single);
      Procedure SetCalloutPosition(Const Value: TD2CalloutPosition);
      Procedure SetCalloutOffset(Const Value: Single);
   Protected
      Procedure CreatePath;
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Fill;
      Property CalloutWidth: Single read FCalloutWidth write SetCalloutWidth;
      Property CalloutLength: Single read FCalloutLength write SetCalloutLength;
      Property CalloutPosition: TD2CalloutPosition read FCalloutPosition write SetCalloutPosition Default d2CalloutTop;
      Property CalloutOffset: Single read FCalloutOffset write SetCalloutOffset;
      Property Stroke;
      Property StrokeCap;
      Property StrokeDash;
      Property StrokeJoin;
      Property StrokeThickness;
   End;

   TD2Ellipse = Class(TD2Shape)
   Private
   Protected
      Procedure Paint; Override;
   Public
      Function PointInObject(X, Y: Single): Boolean; Override;
   Published
      Property Fill;
      Property Stroke;
      Property StrokeCap;
      Property StrokeDash;
      Property StrokeJoin;
      Property StrokeThickness;
   End;

   TD2Circle = Class(TD2Ellipse)
   Protected
      Procedure Paint; Override;
   End;

   TD2Pie = Class(TD2Ellipse)
   Private
      FStartAngle: Single;
      FEndAngle: Single;
      Procedure SetEndAngle(Const Value: Single);
      Procedure SetStartAngle(Const Value: Single);
   Protected
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Function PointInObject(X, Y: Single): Boolean; Override;
   Published
      Property StartAngle: Single read FStartAngle write SetStartAngle;
      Property EndAngle: Single read FEndAngle write SetEndAngle;
   End;

   TD2Arc = Class(TD2Ellipse)
   Private
      FStartAngle: Single;
      FEndAngle: Single;
      Procedure SetEndAngle(Const Value: Single);
      Procedure SetStartAngle(Const Value: Single);
   Protected
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Function PointInObject(X, Y: Single): Boolean; Override;
   Published
      Property StartAngle: Single read FStartAngle write SetStartAngle;
      Property EndAngle: Single read FEndAngle write SetEndAngle;
   End;

   TD2CustomPath = Class(TD2Shape)
   Private
      FData: TD2PathData;
      FWrapMode: TD2PathWrap;
      Procedure SetData2d(Const Value: TD2PathData);
      Procedure SetWrapMode(Const Value: TD2PathWrap);
   Protected
      Procedure DoChanged(Sender: TObject);
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function PointInObject(X, Y: Single): Boolean; Override;
      Property Data: TD2PathData read FData write SetData2d;
      Property WrapMode: TD2PathWrap read FWrapMode write SetWrapMode Default d2PathStretch;
   Published
      Property Fill;
      Property Stroke;
      Property StrokeCap;
      Property StrokeDash;
      Property StrokeJoin;
      Property StrokeThickness;
   End;

   TD2Path = Class(TD2CustomPath)
   Published
      Property Data;
      Property WrapMode;
   End;

   TD2Text = Class(TD2Shape)
   Private
      FText: WideString;
      FFont: TD2Font;
      FVertTextAlign: TD2TextAlign;
      FHorzTextAlign: TD2TextAlign;
      FWordWrap: Boolean;
      FAutoSize: Boolean;
      FStretch: Boolean;
      Procedure SetFont(Const Value: TD2Font);
      Procedure SetHorzTextAlign(Const Value: TD2TextAlign);
      Procedure SetVertTextAlign(Const Value: TD2TextAlign);
      Procedure SetWordWrap(Const Value: Boolean);
      Procedure SetAutoSize(Const Value: Boolean);
      Procedure SetStretch(Const Value: Boolean);
   Protected
      Function GetTextW: WideString; Virtual;
      Procedure SetTextW(Const Value: WideString); Virtual;
      Function GetText: String; Virtual;
      Procedure SetText(Const Value: String); Virtual;

      Procedure FontChanged(Sender: TObject); Virtual;
      Procedure Paint; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure AdjustSize;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
      Property TextW: WideString read FText write SetTextW;
   Published
      Property AutoSize: Boolean read FAutoSize write SetAutoSize Default False;
      Property Fill;
      Property Font: TD2Font read FFont write SetFont;
      Property HorzTextAlign: TD2TextAlign read FHorzTextAlign write SetHorzTextAlign Default d2TextAlignCenter;
      Property VertTextAlign: TD2TextAlign read FVertTextAlign write SetVertTextAlign Default d2TextAlignCenter;
      Property Text: String read GetText write SetText;
      Property Stretch: Boolean read FStretch write SetStretch Default False;
      Property WordWrap: Boolean read FWordWrap write SetWordWrap Default True;
   End;

   TD2Image = Class(TD2VisualObject)
   Private
      FBitmap: TD2Bitmap;
      FBuffer: TD2Bitmap;
      FStretchThread: TThread;
      FOnBitmapLoaded: TNotifyEvent;
      FBitmapMargins: TD2Bounds;
      FWrapMode: TD2ImageWrap;
      FDisableInterpolation: Boolean;
      Procedure SetBitmap(Const Value: TD2Bitmap);
      Procedure SetWrapMode(Const Value: TD2ImageWrap);
   Protected
      Procedure DoBitmapLoaded(Sender: TObject);
      Procedure DoBitmapDestroy(Sender: TObject);
      Procedure DoBitmapChanged(Sender: TObject); Virtual;
      Procedure Loaded; Override;
      Procedure Paint; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
   Published
      Property Bitmap: TD2Bitmap read FBitmap write SetBitmap;
      Property BitmapMargins: TD2Bounds read FBitmapMargins write FBitmapMargins;
      Property WrapMode: TD2ImageWrap read FWrapMode write SetWrapMode;
      Property DisableInterpolation: Boolean read FDisableInterpolation write FDisableInterpolation Default False;
      Property OnBitmapLoaded: TNotifyEvent read FOnBitmapLoaded write FOnBitmapLoaded;
   End;

   TD2PaintBox = Class(TD2VisualObject)
   Private
   Protected
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   End;

   TD2Selection = Class(TD2VisualObject)
   Private
      FParentBounds: Boolean;
      FOnChange: TNotifyEvent;
      FHideSelection: Boolean;
      FMinSize: Integer;
      FOnTrack: TNotifyEvent;
      FProportional: Boolean;
      FGripSize: Single;
      Procedure SetHideSelection(Const Value: Boolean);
      Procedure SetMinSize(Const Value: Integer);
      Procedure SetGripSize(Const Value: Single);
   Protected
      FRatio: Single;
      FMove, FLeftTop, FLeftBottom, FRightTop, FRightBottom: Boolean;
      FLeftTopHot, FLeftBottomHot, FRightTopHot, FRightBottomHot: Boolean;
      FDownPos, FMovePos: TD2Point;
      Function GetAbsoluteRect: TD2Rect; Override;
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function PointInObject(X, Y: Single): Boolean; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseLeave; Override;
   Published
      Property GripSize: Single read FGripSize write SetGripSize;
      Property ParentBounds: Boolean read FParentBounds write FParentBounds Default True;
      Property HideSelection: Boolean read FHideSelection write SetHideSelection;
      Property MinSize: Integer read FMinSize write SetMinSize Default 15;
      Property Proportional: Boolean read FProportional write FProportional;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnTrack: TNotifyEvent read FOnTrack write FOnTrack;
   End;

   TD2SelectionPoint = Class(TD2VisualObject)
   Private
      FOnChange: TNotifyEvent;
      FOnTrack: TNotifyEvent;
      FParentBounds: Boolean;
      FGripSize: Single;
      Procedure SetGripSize(Const Value: Single);
   Protected
      Procedure Paint; Override;
      Procedure SetHeight(Const Value: Single); Override;
      Procedure SetWidth(Const Value: Single); Override;
      Function GetUpdateRect: TD2Rect; Override;
      Procedure MouseEnter; Override;
      Procedure MouseLeave; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function PointInObject(X, Y: Single): Boolean; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
   Published
      Property GripSize: Single read FGripSize write SetGripSize;
      Property ParentBounds: Boolean read FParentBounds write FParentBounds Default True;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnTrack: TNotifyEvent read FOnTrack write FOnTrack;
   End;

   TD2DesignFrame = Class(TD2VisualObject)
   Private
      FOnChange: TNotifyEvent;
      FOnTrack: TNotifyEvent;
   Protected
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseLeave; Override;
   Published
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnTrack: TNotifyEvent read FOnTrack write FOnTrack;
   End;

   TD2ScrollArrowLeft = Class(TD2CustomPath)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2ScrollArrowRight = Class(TD2CustomPath)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2SelectionItem = Class(TD2Control)
   Protected
      Procedure PaintChildren; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Procedure Paint; Override;
   Published
      Property Resource;
   End;

   TD2Panel = Class(TD2Control)
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property Resource;
   End;

   TD2CalloutPanel = Class(TD2Panel)
   Private
      FCalloutLength: Single;
      FCalloutWidth: Single;
      FCalloutPosition: TD2CalloutPosition;
      FCalloutOffset: Single;
      Procedure SetCalloutLength(Const Value: Single);
      Procedure SetCalloutPosition(Const Value: TD2CalloutPosition);
      Procedure SetCalloutWidth(Const Value: Single);
      Procedure SetCalloutOffset(Const Value: Single);
   Protected
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property CalloutWidth: Single read FCalloutWidth write SetCalloutWidth;
      Property CalloutLength: Single read FCalloutLength write SetCalloutLength;
      Property CalloutPosition: TD2CalloutPosition read FCalloutPosition write SetCalloutPosition Default d2CalloutTop;
      Property CalloutOffset: Single read FCalloutOffset write SetCalloutOffset;
   End;

   TD2StatusBar = Class(TD2Control)
   Private
      FShowSizeGrip: Boolean;
      Procedure SetShowSizeGrip(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property Align Default vaBottom;
      Property Resource;
      Property ShowSizeGrip: Boolean read FShowSizeGrip write SetShowSizeGrip;
   End;

   TD2ToolBar = Class(TD2Control)
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property Align Default vaTop;
      Property Resource;
   End;

   TD2TextControl = Class(TD2Control)
   Private
      FText: WideString;
      FFont: TD2Font;
      FTextAlign: TD2TextAlign;
      FVertTextAlign: TD2TextAlign;
      FFontFill: TD2Brush;
      FWordWrap: Boolean;
      Procedure SetFont(Const Value: TD2Font);
      Procedure SetTextAlign(Const Value: TD2TextAlign);
      Procedure SetVertTextAlign(Const Value: TD2TextAlign);
      Procedure SetFontFill(Const Value: TD2Brush);
      Procedure FontFillChanged(Sender: TObject);
      Procedure SetWordWrap(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
      Function GetTextW: WideString; Virtual;
      Procedure SetTextW(Const Value: WideString); Virtual;
      Function GetText: String; Virtual;
      Procedure SetText(Const Value: String); Virtual;
      Procedure FontChanged(Sender: TObject); Virtual;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Property Font: TD2Font read FFont write SetFont;
      Property FontFill: TD2Brush read FFontFill write SetFontFill;
      Property TextW: WideString read FText write SetTextW;
      Property Text: String read GetText write SetText;
      Property VertTextAlign: TD2TextAlign read FVertTextAlign write SetVertTextAlign;
      Property TextAlign: TD2TextAlign read FTextAlign write SetTextAlign;
      Property WordWrap: Boolean read FWordWrap write SetWordWrap Default False;
   End;

   TD2Label = Class(TD2TextControl)
   Private
      FAutoSize: Boolean;
      Procedure SetWordWrap(Const Value: Boolean);
      Procedure SetAutoSize(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
      Procedure SetText(Const Value: String); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property AutoSize: Boolean read FAutoSize write SetAutoSize Default False;
      Property AutoTranslate Default True;
      Property BindingSource;
      Property Font;
      Property TextAlign;
      Property VertTextAlign;
      Property Text;
      Property Resource;
      Property HitTest Default False;
      Property WordWrap: Boolean read FWordWrap write SetWordWrap Default True;
   End;

   TD2ValueLabel = Class(TD2Label)
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property AutoTranslate Default False;
      Property WordWrap Default False;
   End;

   TD2CustomButton = Class(TD2TextControl)
   Private
      FPressing: Boolean;
      FIsPressed: Boolean;
      FModalResult: TModalResult;
      FStaysPressed: Boolean;
      FRepeatTimer: TD2Timer;
      FRepeat: Boolean;
      Procedure SetIsPressed(Const Value: Boolean);
   Protected
      Procedure Click; Override;
      Procedure DblClick; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure ApplyStyle; Override;
      Procedure DoRepeatTimer(Sender: TObject);
      Procedure DoRepeatDelayTimer(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
   Published
      Property Action;
      Property AutoTranslate Default True;
      Property StaysPressed: Boolean read FStaysPressed write FStaysPressed;
      Property IsPressed: Boolean read FIsPressed write SetIsPressed;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Font;
      Property ModalResult: TModalResult read FModalResult write FModalResult Default mrNone;
      Property TextAlign;
      Property Text;
      Property RepeatClick: Boolean read FRepeat write FRepeat Default False;
      Property WordWrap Default False;
      Property Resource;
   End;

   TD2Button = Class(TD2CustomButton)
   Private
      FDefault: Boolean;
      FCancel: Boolean;
   Protected
      Procedure DialogKey(Var Key: Word; Shift: TShiftState); Override;
   Public
   Published
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property Default: Boolean read FDefault write FDefault Default False;
      Property Cancel: Boolean read FCancel write FCancel Default False;
      Property TabOrder;
   End;

   TD2RoundButton = Class(TD2Button)
   End;

   TD2CircleButton = Class(TD2Button)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2PopupButton = Class(TD2Button)
   Protected
      Procedure Click; Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property PopupMenu: TPopupMenu read FPopupMenu write FPopupMenu;
   End;

   TD2BitmapButton = Class(TD2CustomButton)
   Private
      FBitmap: TD2Bitmap;
      FBitmapLayout: TD2ButtonLayout;
      FBitmapSpacing: Single;
      FBitmapSize: Single;
      FBitmapPadding: Single;
      Procedure SetBitmap(Const Value: TD2Bitmap);
      Procedure SetBitmapLayout(Const Value: TD2ButtonLayout);
      Procedure SetBitmapSpacing(Const Value: Single);
      Procedure SetBitmapSize(Const Value: Single);
      Procedure SetBitmapPadding(Const Value: Single);
   Protected
      Procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); Override;
      Procedure DoBitmapChanged(Sender: TObject);
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Bitmap: TD2Bitmap read FBitmap write SetBitmap;
      Property BitmapLayout: TD2ButtonLayout read FBitmapLayout write SetBitmapLayout Default d2GlyphLeft;
      Property BitmapSpacing: Single read FBitmapSpacing write SetBitmapSpacing;
      Property BitmapSize: Single read FBitmapSize write SetBitmapSize;
      Property BitmapPadding: Single read FBitmapPadding write SetBitmapPadding;
   End;

   TD2PathButton = Class(TD2CustomButton)
   Private
      FPath: TD2PathData;
      FPathLayout: TD2ButtonLayout;
      FPathSize: Single;
      FPathSpacing: Single;
      FPathPadding: Single;
      FStrokeThickness: Single;
      FFill: TD2Brush;
      FStroke: TD2Brush;
      FStrokeCap: TD2StrokeCap;
      FStrokeDash: TD2StrokeDash;
      FStrokeJoin: TD2StrokeJoin;
      Procedure SetPath(Const Value: TD2PathData);
      Procedure SetPathLayout(Const Value: TD2ButtonLayout);
      Procedure SetPathPadding(Const Value: Single);
      Procedure SetPathSize(Const Value: Single);
      Procedure SetPathSpacing(Const Value: Single);
      Function isStrokeThicknessStored: Boolean;
      Procedure SetFill(Const Value: TD2Brush);
      Procedure SetStroke(Const Value: TD2Brush);
      Procedure SetStrokeCap(Const Value: TD2StrokeCap);
      Procedure SetStrokeDash(Const Value: TD2StrokeDash);
      Procedure SetStrokeJoin(Const Value: TD2StrokeJoin);
      Procedure SetStrokeThickness(Const Value: Single);
   Protected
      Procedure DoPathChanged(Sender: TObject);
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Path: TD2PathData read FPath write SetPath;
      Property PathLayout: TD2ButtonLayout read FPathLayout write SetPathLayout Default d2GlyphLeft;
      Property PathSpacing: Single read FPathSpacing write SetPathSpacing;
      Property PathSize: Single read FPathSize write SetPathSize;
      Property PathPadding: Single read FPathPadding write SetPathPadding;
      Property PathFill: TD2Brush read FFill write SetFill;
      Property PathStroke: TD2Brush read FStroke write SetStroke;
      Property PathStrokeThickness: Single read FStrokeThickness write SetStrokeThickness Stored isStrokeThicknessStored;
      Property PathStrokeCap: TD2StrokeCap read FStrokeCap write SetStrokeCap Default d2CapFlat;
      Property PathStrokeDash: TD2StrokeDash read FStrokeDash write SetStrokeDash Default d2DashSolid;
      Property PathStrokeJoin: TD2StrokeJoin read FStrokeJoin write SetStrokeJoin Default d2JoinMiter;
   End;

   TD2ToolButton = Class(TD2BitmapButton)
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property CanFocused Default False;
      Property TabOrder;
      Property BitmapLayout Default d2GlyphTop;
   End;

   TD2ToolPathButton = Class(TD2PathButton)
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property CanFocused Default False;
      Property TabOrder;
      Property PathLayout Default d2GlyphTop;
   End;

   TD2BitmapStateButton = Class(TD2CustomButton)
   Private
      FBitmap: TD2Bitmap;
      FBitmapDown: TD2Bitmap;
      FBitmapHot: TD2Bitmap;
      Procedure SetBitmap(Const Value: TD2Bitmap);
      Procedure SetBitmapDown(Const Value: TD2Bitmap);
      Procedure SetBitmapHot(Const Value: TD2Bitmap);
   Protected
      Procedure DoBitmapChanged(Sender: TObject);
      Procedure ApplyStyle; Override;
      Procedure MouseEnter; Override;
      Procedure MouseLeave; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Procedure StartTriggerAnimation(AInstance: TD2Object; ATrigger: String); Override;
   Published
      Property Bitmap: TD2Bitmap read FBitmap write SetBitmap;
      Property BitmapHot: TD2Bitmap read FBitmapHot write SetBitmapHot;
      Property BitmapDown: TD2Bitmap read FBitmapDown write SetBitmapDown;
   End;

   TD2SpeedButton = Class(TD2CustomButton)
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property CanFocused Default False;
      Property RepeatClick Default True;
      Property TabOrder;
   End;

   TD2ColorButton = Class(TD2CustomButton)
   Private
      FFill: TD2Shape;
      FColor: String;
      FOnChange: TNotifyEvent;
      FUseStandardDialog: Boolean;
      Procedure SetColor(Const Value: String);
   Protected
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure Click; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property AutoTranslate Default False;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Color: String read FColor write SetColor;
      Property UseStandardDialog: Boolean read FUseStandardDialog write FUseStandardDialog Default True;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2CornerButton = Class(TD2CustomButton)
   Private
      FyRadius: Single;
      FxRadius: Single;
      FCorners: TD2Corners;
      FCornerType: TD2CornerType;
      FSides: TD2Sides;
      Function IsCornersStored: Boolean;
      Procedure SetxRadius(Const Value: Single);
      Procedure SetyRadius(Const Value: Single);
      Procedure SetCorners(Const Value: TD2Corners);
      Procedure SetCornerType(Const Value: TD2CornerType);
      Procedure SetSides(Const Value: TD2Sides);
      Function IsSidesStored: Boolean;
   Protected
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property xRadius: Single read FxRadius write SetxRadius;
      Property yRadius: Single read FyRadius write SetyRadius;
      Property Corners: TD2Corners read FCorners write SetCorners Stored IsCornersStored;
      Property CornerType: TD2CornerType read FCornerType write SetCornerType Default d2CornerRound;
      Property Sides: TD2Sides read FSides write SetSides Stored IsSidesStored;
   End;

   TD2CheckBox = Class(TD2TextControl)
   Private
      FPressing: Boolean;
      FOnChange: TNotifyEvent;
      FIsPressed: Boolean;
      FIsChecked: Boolean;
      Procedure SetIsChecked(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Published
      Property IsPressed: Boolean read FIsPressed;
      Property IsChecked: Boolean read FIsChecked write SetIsChecked;
      Property AutoTranslate Default True;
      Property BindingSource;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Font;
      Property TextAlign;
      Property Text;
      Property Resource;
      Property WordWrap;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2PathCheckBox = Class(TD2CheckBox)
   Private
      FPath: TD2PathData;
      Procedure SetPath(Const Value: TD2PathData);
   Protected
      Procedure ApplyStyle; Override;
      Procedure DoPathChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Resource;
      Property Path: TD2PathData read FPath write SetPath;
   End;

   TD2RadioButton = Class(TD2TextControl)
   Private
      FPressing: Boolean;
      FOnChange: TNotifyEvent;
      FIsPressed: Boolean;
      FIsChecked: Boolean;
      FGroupName: String;
      Procedure SetIsChecked(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
      Procedure EnterFocus; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Published
      Property IsPressed: Boolean read FIsPressed;
      Property IsChecked: Boolean read FIsChecked write SetIsChecked;
      Property AutoTranslate Default True;
      Property BindingSource;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Font;
      Property TextAlign;
      Property Text;
      Property Resource;
      Property WordWrap;
      Property GroupName: String read FGroupName write FGroupName;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2GroupBox = Class(TD2TextControl)
   Protected
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property AutoTranslate Default True;
      Property Font;
      Property TextAlign;
      Property Text;
      Property Resource;
   End;

   TD2CloseButton = Class(TD2Control)
   Private
      FPressing: Boolean;
      FCloseForm: Boolean;
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
   Published
      Property Resource;
      Property CloseForm: Boolean read FCloseForm write FCloseForm Default True;
   End;

   TD2SizeGrip = Class(TD2Control, Id2SizeGrip)
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Resource;
   End;

   TD2Splitter = Class(TD2Control)
   Private
      FControl: TD2VisualObject;
      FDownPos: TD2Point;
      FMinSize: Single;
      FMaxSize: Single;
      FNewSize, FOldSize: Single;
      FSplit: Single;
      FShowGrip: Boolean;
      Procedure SetShowGrip(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
      Procedure SetAlign(Const Value: TD2Align); Override;
      Function FindObject: TD2VisualObject;
      Procedure CalcSplitSize(X, Y: Single; Var NewSize, Split: Single);
      Procedure UpdateSize(X, Y: Single);
      Function DoCanResize(Var NewSize: Single): Boolean;
      Procedure UpdateControlSize;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
   Published
      Property MinSize: Single read FMinSize write FMinSize;
      Property ShowGrip: Boolean read FShowGrip write SetShowGrip Default True;
   End;

   TD2ProgressBar = Class(TD2Control)
   Private
      FMin: Single;
      FValue: Single;
      FMax: Single;
      FOrientation: TD2Orientation;
      Procedure SetMax(Const Value: Single);
      Procedure SetMin(Const Value: Single);
      Procedure SetOrientation(Const Value: TD2Orientation);
      Procedure SetValue(Const Value: Single);
   Protected
      Procedure ApplyStyle; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
   Published
      Property BindingSource;
      Property Min: Single read FMin write SetMin;
      Property Max: Single read FMax write SetMax;
      Property Orientation: TD2Orientation read FOrientation write SetOrientation;
      Property Value: Single read FValue write SetValue;
      Property Resource;
   End;

   TD2CustomTrack = Class;
   TD2ScrollBar = Class;

   TD2Thumb = Class(TD2Control)
   Private
      FTrack: TD2CustomTrack;
      FDownOffset: TD2Point;
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
   Published
      Property Resource;
   End;

   TD2CustomTrack = Class(TD2Control)
   Private
      Function GetThumb: TD2Thumb;
      Procedure SetFrequency(Const Value: Single);
      Function GetIsTracking: Boolean;
   Protected
      FOnChange, FOnTracking: TNotifyEvent;
      FValue: Single;
      FMin: Single;
      FMax: Single;
      FViewportSize: Single;
      FOrientation: TD2Orientation;
      FTracking: Boolean;
      FFrequency: Single;
      Procedure SetMax(Const Value: Single); Virtual;
      Procedure SetMin(Const Value: Single); Virtual;
      Procedure SetValue(Value: Single);
      Procedure SetViewportSize(Const Value: Single);
      Procedure SetOrientation(Const Value: TD2Orientation);
      Function GetThumbRect: TD2Rect;
      Property Thumb: TD2Thumb read GetThumb;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
      Property IsTracking: Boolean read GetIsTracking;
      Property Min: Single read FMin write SetMin;
      Property Max: Single read FMax write SetMax;
      Property Frequency: Single read FFrequency write SetFrequency;
      Property Orientation: TD2Orientation read FOrientation write SetOrientation;
      Property Value: Single read FValue write SetValue;
      Property ViewportSize: Single read FViewportSize write SetViewportSize;
      Property Tracking: Boolean read FTracking write FTracking Default True;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnTracking: TNotifyEvent read FOnTracking write FOnTracking;
   End;

   TD2Track = Class(TD2CustomTrack)
   Published
      Property BindingSource;
      Property Resource;
      Property Min: Single read FMin write SetMin;
      Property Max: Single read FMax write SetMax;
      Property Frequency: Single read FFrequency write SetFrequency;
      Property Orientation: TD2Orientation read FOrientation write SetOrientation;
      Property Value: Single read FValue write SetValue;
      Property ViewportSize: Single read FViewportSize write SetViewportSize;
      Property Tracking: Boolean read FTracking write FTracking;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2TrackBar = Class(TD2CustomTrack)
   Protected
      Procedure SetMax(Const aValue: Single); Override;
      Procedure SetMin(Const aValue: Single); Override;
      Procedure Loaded; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property BindingSource;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Resource;
      Property Min: Single read FMin write SetMin;
      Property Max: Single read FMax write SetMax;
      Property Frequency: Single read FFrequency write SetFrequency;
      Property Orientation: TD2Orientation read FOrientation write SetOrientation;
      Property Value: Single read FValue write SetValue;
      Property Tracking: Boolean read FTracking write FTracking;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2ScrollBar = Class(TD2Control)
   Private
      FOnChange: TNotifyEvent;
      FValue: Single;
      FMin: Single;
      FMax: Single;
      FViewportSize: Single;
      FOrientation: TD2Orientation;
      FSmallChange: Single;
      Procedure SetMax(Const Value: Single);
      Procedure SetMin(Const Value: Single);
      Procedure SetValue(Const Value: Single);
      Procedure SetViewportSize(Const Value: Single);
      Procedure SetOrientation(Const Value: TD2Orientation);
   Protected
      Procedure DoTrackChanged(Sender: TObject);
      Procedure DoMinButtonClick(Sender: TObject);
      Procedure DoMaxButtonClick(Sender: TObject);
      Function Track: TD2CustomTrack;
      Function MinButton: TD2CustomButton;
      Function MaxButton: TD2CustomButton;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
   Published
      Property BindingSource;
      Property Resource;
      Property Min: Single read FMin write SetMin;
      Property Max: Single read FMax write SetMax;
      Property Orientation: TD2Orientation read FOrientation write SetOrientation;
      Property Value: Single read FValue write SetValue;
      Property ViewportSize: Single read FViewportSize write SetViewportSize;
      Property SmallChange: Single read FSmallChange write FSmallChange;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2SmallScrollBar = Class(TD2ScrollBar)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2AniIndicator = Class(TD2Control)
   Private
      FDragTimer: TD2Timer;
      FLayout: TD2VisualObject;
      FAni: TD2FloatAnimation;
      FStyle: TD2AniIndicatorStyle;
      Procedure SetEnabled(Const Value: Boolean);
      Procedure SetStyle(Const Value: TD2AniIndicatorStyle);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
   Published
      Property Enabled: Boolean read FEnabled write SetEnabled;
      Property Style: TD2AniIndicatorStyle read FStyle write SetStyle;
   End;

   TD2AngleButton = Class(TD2Control)
   Private
      FPressing: Boolean;
      FOnChange: TNotifyEvent;
      FOldPos: TD2Point;
      FSaveValue, FValue: Single;
      FFrequency: Single;
      FTracking: Boolean;
      FShowValue: Boolean;
      Procedure SetValue(Const Value: Single);
      Procedure SetShowValue(Const Value: Boolean);
   Protected
      Function Tick: TD2VisualObject;
      Function Text: TD2Text;
      Procedure ApplyStyle; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
   Published
      Property BindingSource;
      Property Resource;
      Property Frequency: Single read FFrequency write FFrequency;
      Property Tracking: Boolean read FTracking write FTracking Default True;
      Property ShowValue: Boolean read FShowValue write SetShowValue Default False;
      Property Value: Single read FValue write SetValue;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2ExpanderButton = Class(TD2CustomButton)
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property CanFocused Default False;
   End;

   TD2Expander = Class(TD2TextControl)
   Private
      FShowCheck: Boolean;
      FIsChecked: Boolean;
      FOnCheckChange: TNotifyEvent;
      Procedure DoButtonClick(Sender: TObject);
      Procedure SetIsChecked(Const Value: Boolean);
      Procedure SetShowCheck(Const Value: Boolean);
   Protected
      FIsExpanded: Boolean;
      FContent: TD2Content;
      FButton: TD2CustomButton;
      FCheck: TD2CheckBox;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure SetIsExpanded(Const Value: Boolean); Virtual;
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadContentSize(Reader: TReader);
      Procedure WriteContentSize(Writer: TWriter);
      Procedure DesignClick; Override;
      Procedure DoCheckChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
      Procedure AddObject(AObject: TD2Object); Override;
   Published
      Property Font;
      Property TextAlign;
      Property Text;
      Property Resource;
      Property AutoTranslate Default True;
      Property IsExpanded: Boolean read FIsExpanded write SetIsExpanded Default True;
      Property IsChecked: Boolean read FIsChecked write SetIsChecked Default True;
      Property ShowCheck: Boolean read FShowCheck write SetShowCheck;
      Property OnCheckChange: TNotifyEvent read FOnCheckChange write FOnCheckChange;
   End;

   TD2PopupBox = Class(TD2CustomButton)
   Private
      FItems: TD2WideStrings;
      FItemIndex: Integer;
      FOnChange: TNotifyEvent;
      Procedure SetItems(Const Value: TD2WideStrings);
      Procedure SetItemIndex(Const Value: Integer);
   Protected
      Procedure ApplyStyle; Override;
      Procedure Click; Override;
      Procedure DoItemsChanged(Sender: TObject); Virtual;
      Procedure DoItemClick(Sender: TObject);
      Procedure DoPopup; Virtual;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure SetText(Const Value: String); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property BindingSource;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Text Stored False;
      Property Items: TD2WideStrings read FItems write SetItems;
      Property ItemIndex: Integer read FItemIndex write SetItemIndex;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2Window = Class(TD2TextControl)
   Private
      FShowCloseButton: Boolean;
      FShowSizeGrip: Boolean;
      FOnCloseClick: TNotifyEvent;
      Procedure SetShowCloseButton(Const Value: Boolean);
      Procedure SetShowSizeGrip(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property ShowCloseButton: Boolean read FShowCloseButton write SetShowCloseButton Default True;
      Property ShowSizeGrip: Boolean read FShowSizeGrip write SetShowSizeGrip Default True;
      Property OnCloseClick: TNotifyEvent read FOnCloseClick write FOnCloseClick;
      Property AutoTranslate Default True;
      Property Font;
      Property TextAlign;
      Property Text;
      Property Resource;
   End;

   TD2HudWindow = Class(TD2Window)
   Private
      FDisableShadowOnOSX: Boolean;
      FFill: TD2Brush;
      FStrokeThickness: Single;
      FStroke: TD2Brush;
      FStrokeCap: TD2StrokeCap;
      FStrokeDash: TD2StrokeDash;
      FStrokeJoin: TD2StrokeJoin;
      FCloseAlign: TD2CloseAlign;
      FShowCaption: Boolean;
      Procedure SetDisableShadowOnOSX(Const Value: Boolean);
      Procedure SetFill(Const Value: TD2Brush);
      Function isStrokeThicknessStored: Boolean;
      Procedure SetStroke(Const Value: TD2Brush);
      Procedure SetStrokeCap(Const Value: TD2StrokeCap);
      Procedure SetStrokeDash(Const Value: TD2StrokeDash);
      Procedure SetStrokeJoin(Const Value: TD2StrokeJoin);
      Procedure SetStrokeThickness(Const Value: Single);
      Procedure SetCloseAlign(Const Value: TD2CloseAlign);
      Procedure SetShowCaption(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
      Procedure DoFillChanged(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property DisableShadowOnOSX: Boolean read FDisableShadowOnOSX write SetDisableShadowOnOSX Default True;
      Property ButtonAlign: TD2CloseAlign read FCloseAlign write SetCloseAlign Default d2ButtonAlignLeft;
      Property Fill: TD2Brush read FFill write SetFill;
      Property Stroke: TD2Brush read FStroke write SetStroke;
      Property StrokeThickness: Single read FStrokeThickness write SetStrokeThickness Stored isStrokeThicknessStored;
      Property StrokeCap: TD2StrokeCap read FStrokeCap write SetStrokeCap Default d2CapFlat;
      Property StrokeDash: TD2StrokeDash read FStrokeDash write SetStrokeDash Default d2DashSolid;
      Property StrokeJoin: TD2StrokeJoin read FStrokeJoin write SetStrokeJoin Default d2JoinMiter;
      Property ShowCaption: Boolean read FShowCaption write SetShowCaption Default True;
   End;

   TD2ImageControl = Class(TD2Control)
   Private
      FImage: TD2Image;
      FOnChange: TNotifyEvent;
      FBitmap: TD2Bitmap;
      FEnableOpenDialog: Boolean;
      Procedure SetBitmap(Const Value: TD2Bitmap);
   Protected
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure Click; Override;
      Procedure DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean); Override;
      Procedure DragDrop(Const aData: TD2DragObject; Const Point: TD2Point); Override;
      Procedure DoBitmapChanged(Sender: TObject); Virtual;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property EnableOpenDialog: Boolean read FEnableOpenDialog write FEnableOpenDialog Default True;
      Property TabOrder;
      Property Bitmap: TD2Bitmap read FBitmap write SetBitmap;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2HudLabel = Class(TD2Label)
   End;

   TD2HudButton = Class(TD2Button)
   End;

   TD2HudRoundButton = Class(TD2Button)
   End;

   TD2HudCircleButton = Class(TD2CircleButton)
   End;

   TD2HudCornerButton = Class(TD2CornerButton)
   End;

   TD2HudSpeedButton = Class(TD2SpeedButton)
   End;

   TD2HudCheckBox = Class(TD2CheckBox)
   End;

   TD2HudRadioButton = Class(TD2RadioButton)
   End;

   TD2HudGroupBox = Class(TD2GroupBox)
   End;

   TD2HudPopupBox = Class(TD2PopupBox)
   End;

   TD2HudAngleButton = Class(TD2AngleButton)
   End;

   TD2HudTrack = Class(TD2Track)
   End;

   TD2HudTrackBar = Class(TD2TrackBar)
   End;

   TD2HudScrollBar = Class(TD2ScrollBar)
   End;

   TD2LayerWindow = Class(TD2HudWindow)
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   End;

   TD2HudPanel = Class(TD2Panel)
   End;

   TD2HudCloseButton = Class(TD2CloseButton)
   End;

   TD2HudSizeGrip = Class(TD2SizeGrip)
   End;

   TD2HudStatusBar = Class(TD2StatusBar)
   End;

   TD2Layout = Class(TD2VisualObject)
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
   Published
      Property HitTest Default False;
   End;

   TD2ScaledLayout = Class(TD2VisualObject)
   Private
      FOriginalWidth: Single;
      FOriginalHeight: Single;
      Procedure SetOriginalWidth(Const Value: Single);
      Procedure SetOriginalHeight(Const Value: Single);
   Protected
      Function GetChildrenMatrix: TD2Matrix; Override;
      Procedure SetHeight(Const Value: Single); Override;
      Procedure SetWidth(Const Value: Single); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Procedure Realign; Override;
   Published
      Property OriginalWidth: Single read FOriginalWidth write SetOriginalWidth;
      Property OriginalHeight: Single read FOriginalHeight write SetOriginalHeight;
   End;

   TD2ScrollContent = Class(TD2Content)
   Private
   Protected
      Function GetClipRect: TD2Rect; Override;
      Procedure PaintChildren; Override;
      Function GetUpdateRect: TD2Rect; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Procedure Paint; Override;
      Function ObjectByPoint(X, Y: Single): TD2VisualObject; Override;
      Procedure AddObject(AObject: TD2Object); Override;
      Procedure RemoveObject(AObject: TD2Object); Override;
   End;

   TD2ScrollBox = Class(TD2Control)
   Private
      FAutoHide: Boolean;
      FDisableMouseWheel: Boolean;
      FDown: Boolean;
      FHScrollAni: TD2FloatAnimation;
      FVScrollAni: TD2FloatAnimation;
      FAnimated: Boolean;
      FShowScrollBars: Boolean;
      FShowSizeGrip: Boolean;
      FMouseTracking: Boolean;
      FUseSmallScrollBars: Boolean;
      Procedure SetShowScrollBars(Const Value: Boolean);
      Procedure SetShowSizeGrip(Const Value: Boolean);
      Procedure SetUseSmallScrollBars(Const Value: Boolean);
   Protected
      FScrollDesign: TD2Point;
      FContent: TD2ScrollContent;
      FHScrollBar: TD2ScrollBar;
      FVScrollBar: TD2ScrollBar;
      FContentLayout: TD2VisualObject;
      FDownPos, FLastDelta, FCurrentPos: TD2Point;
      Procedure Loaded; Override;
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadScrollDesign(Reader: TReader);
      Procedure WriteScrollDesign(Writer: TWriter);
      Procedure ContentAddObject(AObject: TD2Object); Virtual;
      Procedure ContentBeforeRemoveObject(AObject: TD2Object); Virtual;
      Procedure ContentRemoveObject(AObject: TD2Object); Virtual;
      Procedure HScrollChange(Sender: TObject); Virtual;
      Procedure VScrollChange(Sender: TObject); Virtual;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure CreateVScrollAni;
      Procedure CreateHScrollAni;
      Function ContentRect: TD2Rect;
      Function VScrollBarValue: Single;
      Function HScrollBarValue: Single;
      Function GetContentBounds: TD2Rect; Virtual;
      Procedure ContentPaint(aCanvas: TD2Canvas; Const ARect: TD2Rect); Virtual;
      Procedure RealignContent(R: TD2Rect); Virtual;
      Property ContentLayout: TD2VisualObject read FContentLayout;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure AddObject(AObject: TD2Object); Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean); Override;
      Procedure Realign; Override;
      Procedure Centre;
      Procedure ScrollTo(Const Dx, Dy: Single);
      Procedure InViewRect(Const Rect: TD2Rect);
      Function ClientWidth: Single;
      Function ClientHeight: Single;
      Property HScrollBar: TD2ScrollBar read FHScrollBar;
      Property VScrollBar: TD2ScrollBar read FVScrollBar;
   Published
      Property AutoHide: Boolean read FAutoHide write FAutoHide Default True;
      Property Animated: Boolean read FAnimated write FAnimated Default True;
      Property DisableMouseWheel: Boolean read FDisableMouseWheel write FDisableMouseWheel Default False;
      Property MouseTracking: Boolean read FMouseTracking write FMouseTracking Default False;
      Property ShowScrollBars: Boolean read FShowScrollBars write SetShowScrollBars Default True;
      Property ShowSizeGrip: Boolean read FShowSizeGrip write SetShowSizeGrip Default False;
      Property UseSmallScrollBars: Boolean read FUseSmallScrollBars write SetUseSmallScrollBars Default False;
   End;

   TD2VertScrollBox = Class(TD2ScrollBox)
   Protected
      Function GetContentBounds: TD2Rect; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
   End;

   TD2FramedScrollBox = Class(TD2ScrollBox)
   End;

   TD2FramedVertScrollBox = Class(TD2VertScrollBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2GridLayout = Class(TD2VisualObject)
   Private
      FItemWidth: Single;
      FItemHeight: Single;
      FOrientation: TD2Orientation;
      Procedure SetItemHeight(Const Value: Single);
      Procedure SetItemWidth(Const Value: Single);
      Procedure SetOrientation(Const Value: TD2Orientation);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Procedure Realign; Override;
   Published
      Property ItemHeight: Single read FItemHeight write SetItemHeight;
      Property ItemWidth: Single read FItemWidth write SetItemWidth;
      Property Orientation: TD2Orientation read FOrientation write SetOrientation;
   End;

   TD2SplitLayout = Class(TD2VisualObject)
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property HitTest Default False;
   End;

   TD2Nond2Layout = Class(TD2Layout)
   Private
      FControl: TControl;
      FVisibleTimer: TD2Timer;
      Procedure SetControl(Const Value: TControl);
   Protected
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure DoVisibleTimer(Sender: TObject);
      Procedure MatrixChanged(Sender: TObject); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function CheckParentVisible: Boolean; Override;
      Procedure Realign; Override;
   Published
      Property Nond2Control: TControl read FControl write SetControl;
   End;


Type

   TD2ListBox = Class;
   TD2ComboBox = Class;

   TD2ListBoxItem = Class(TD2TextControl)
   Private
      FIsChecked: Boolean;
      FCheck: TD2CheckBox;
      FIsSelected: Boolean;
      Procedure SetIsChecked(Const Value: Boolean);
      Procedure DoCheckClick(Sender: TObject);
      Procedure UpdateCheck;
      Procedure SetIsSelected(Const Value: Boolean);
   Protected
      Function ListBox: TD2ListBox;
      Function ComboBox: TD2ComboBox;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure DesignInsert; Override;
      Procedure DesignSelect; Override;
      Function EnterFocusChildren(AObject: TD2VisualObject): Boolean; Override;
      Procedure DragEnd; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function GetParentComponent: TComponent; Override;
      Procedure Paint; Override;
   Published
      Property IsChecked: Boolean read FIsChecked write SetIsChecked;
      Property IsSelected: Boolean read FIsSelected write SetIsSelected;
      Property AutoTranslate Default True;
      Property Font;
      Property Resource;
      Property Text;
      Property TextAlign Default d2TextAlignNear;
      Property WordWrap;
   End;

   TOnCompareListBoxItemEvent = Function(Item1, Item2: TD2ListBoxItem): Integer Of Object;
   TOnListBoxDragChange = Procedure(SourceItem, DestItem: TD2ListBoxItem; Allow: Boolean) Of Object;

   TD2ListBox = Class(TD2ScrollBox)
   Private
      FMouseSelecting: Boolean;
      FOnChange: TNotifyEvent;
      FHideSelectionUnfocused: Boolean;
      FShowCheckboxes: Boolean;
      FOnChangeCheck: TNotifyEvent;
      FSorted: Boolean;
      FOnCompare: TOnCompareListBoxItemEvent;
      FMultiSelect: Boolean;
      FAlternatingRowBackground: Boolean;
      FAllowDrag: Boolean;
      FDragItem: TD2ListBoxItem;
      FOnDragChange: TOnListBoxDragChange;
      Function GetCount: Integer;
      Function GetSelected: TD2ListBoxItem;
      Procedure SetColumns(Const Value: Integer);
      Procedure SetItemHeight(Const Value: Single);
      Procedure SetItemWidth(Const Value: Single);
      Procedure SetListStyle(Const Value: TD2ListStyle);
      Procedure SetShowCheckboxes(Const Value: Boolean);
      Function GetItem(aIndex: Integer): TD2ListBoxItem;
      Procedure SetSorted(Const Value: Boolean);
      Procedure SetAlternatingRowBackground(Const Value: Boolean);
      Procedure SetMultiSelect(Const Value: Boolean);
      Procedure SetAllowDrag(Const Value: Boolean);
   Protected
      FColumns: Integer;
      FItemWidth: Single;
      FItemHeight: Single;
      FListStyle: TD2ListStyle;
      FFirstSelect: TD2ListBoxItem;
      FSelection: TD2VisualObject;
      FSelections: TList;
      FOddFill: TD2Brush;
      FItemIndex: Integer;
      Procedure SortItems; Virtual;
      Procedure SetItemIndex(Const Value: Integer); Virtual;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure KeyUp(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean); Override;
      Procedure DragDrop(Const aData: TD2DragObject; Const Point: TD2Point); Override;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure EnterFocus; Override;
      Procedure KillFocus; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Function GetContentBounds: TD2Rect; Override;
      Procedure DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
      Procedure HScrollChange(Sender: TObject); Override;
      Procedure VScrollChange(Sender: TObject); Override;
      Procedure ContentAddObject(AObject: TD2Object); Override;
      Procedure ContentBeforeRemoveObject(AObject: TD2Object); Override;
      Procedure ContentRemoveObject(AObject: TD2Object); Override;
      Procedure UpdateSelection;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Function ItemClass: String; Override;
      Procedure Clear; Virtual;
      Procedure SelectAll;
      Procedure ClearSelection;
      Procedure SelectRange(Item1, Item2: TD2ListBoxItem);
      Function ItemByPoint(Const X, Y: Single): TD2ListBoxItem;
      Function ItemByIndex(Const Idx: Integer): TD2ListBoxItem;
      Procedure Exchange2d(Item1, Item2: TD2ListBoxItem);
      Procedure AddObject(AObject: TD2Object); Override;
      Property Count: Integer read GetCount;
      Property Selected: TD2ListBoxItem read GetSelected;
      Property Items[aIndex: Integer]: TD2ListBoxItem read GetItem;
   Published
      Property Resource;
      Property AllowDrag: Boolean read FAllowDrag write SetAllowDrag Default False;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property AlternatingRowBackground: Boolean read FAlternatingRowBackground write SetAlternatingRowBackground Default False;
      Property Columns: Integer read FColumns write SetColumns Default 1;
      Property HideSelectionUnfocused: Boolean read FHideSelectionUnfocused write FHideSelectionUnfocused Default True;
      Property ItemIndex: Integer read FItemIndex write SetItemIndex Default -1;
      Property ItemWidth: Single read FItemWidth write SetItemWidth;
      Property ItemHeight: Single read FItemHeight write SetItemHeight;
      Property ListStyle: TD2ListStyle read FListStyle write SetListStyle Default d2ListVertical;
      Property MultiSelect: Boolean read FMultiSelect write SetMultiSelect Default False;
      Property Sorted: Boolean read FSorted write SetSorted Default False;
      Property ShowCheckboxes: Boolean read FShowCheckboxes write SetShowCheckboxes Default False;
      Property BindingSource;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnChangeCheck: TNotifyEvent read FOnChangeCheck write FOnChangeCheck;
      Property OnCompare: TOnCompareListBoxItemEvent read FOnCompare write FOnCompare;
      Property OnDragChange: TOnListBoxDragChange read FOnDragChange write FOnDragChange;
   End;

   TD2ComboListBox = Class(TD2Listbox)
   Protected
      FComboBox: TD2Control;
      Procedure GetChildren(Proc: TGetChildProc; Root: TComponent); Override;
      Procedure KillFocus; Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure ApplyResource; Override;
      Function GetParentComponent: TComponent; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean); Override;
   End;

   TD2ComboBox = Class(TD2Control)
   Private
      FDropDownCount: Integer;
      FListBox: TD2ComboListBox;
      FOnChange: TNotifyEvent;
      FPlacement: TD2Placement;
      Procedure SetItemIndex(Const Value: Integer);
      Function GetItemIndex: Integer;
      Function GetCount: Integer;
      Procedure SetListBoxResource(Const Value: String);
      Function GetListBoxResource: String;
      Function GetItemHeight: Single;
      Procedure SetItemHeight(Const Value: Single);
   Protected
      Procedure GetChildren(Proc: TGetChildProc; Root: TComponent); Override;
      Procedure ApplyStyle; Override;
      Procedure DoListBoxChange(Sender: TObject);
      Procedure DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect); Virtual;
      Procedure DesignClick; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function ItemClass: String; Override;
      Procedure Paint; Override;
      Procedure PaintChildren; Override;
      Procedure Realign; Override;
      Procedure Clear; Virtual;
      Procedure DropDown; Virtual;
      Procedure AddObject(AObject: TD2Object); Override;
      Property ListBox: TD2ComboListBox read FListBox write FListBox;
      Property Count: Integer read GetCount;
   Published
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Resource;
      Property ItemIndex: Integer read GetItemIndex write SetItemIndex;
      Property ItemHeight: Single read GetItemHeight write SetItemHeight;
      Property DropDownCount: Integer read FDropDownCount write FDropDownCount Default 8;
      Property Placement: TD2Placement read FPlacement write FPlacement;
      Property BindingSource;
      Property ListBoxResource: String read GetListBoxResource write SetListBoxResource;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2StringListBox = Class(TD2ListBox)
   Private
      FItems: TD2WideStrings;
      FTextAlign: TD2TextAlign;
      FItemStyle: String;
      FFont: TD2Font;
      Procedure SetItems(Const Value: TD2WideStrings);
      Procedure SetTextAlign(Const Value: TD2TextAlign);
      Procedure SetFont(Const Value: TD2Font);
   Protected
      Procedure SortItems; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure ApplyStyle; Override;
      Procedure RebuildList;
      Procedure DoItemsChanged(Sender: TObject);
      Procedure FontChanged(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure EndUpdate; Override;
   Published
      Property AutoTranslate Default True;
      Property TextAlign: TD2TextAlign read FTextAlign write SetTextAlign Default d2TextAlignCenter;
      Property Font: TD2Font read FFont write SetFont;
      Property Items: TD2WideStrings read FItems write SetItems;
      Property ItemIndex;
      Property BindingSource;
   End;

   TD2StringComboBox = Class(TD2ComboBox)
   Private
      FItemHeight: Single;
      FItems: TD2WideStrings;
      FTextAlign: TD2TextAlign;
      FItemStyle: String;
      Procedure SetItemHeight(Const Value: Single);
      Procedure SetItems(Const Value: TD2WideStrings);
      Procedure SetTextAlign(Const Value: TD2TextAlign);
   Protected
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure ApplyStyle; Override;
      Procedure RebuildList;
      Procedure DoItemsChanged(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Clear; Override;
   Published
      Property AutoTranslate Default True;
      Property TextAlign: TD2TextAlign read FTextAlign write SetTextAlign Default d2TextAlignCenter;
      Property ItemHeight: Single read FItemHeight write SetItemHeight;
      Property Items: TD2WideStrings read FItems write SetItems;
      Property ItemIndex;
   End;

   TD2HorzListBox = Class(TD2ListBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property ListStyle Default d2ListHorizontal;
   End;

   TD2ImageThread = Class(TThread)
   Private
      FImage: TD2Image;
      FTempBitmap: TD2Bitmap;
      FFileName: String;
      FUseThumbnails: Boolean;
   Protected
      Procedure Execute; Override;
      Procedure Finished;
   Public
      Constructor Create(Const AImage: TD2Image; Const AFileName: String; Const AUseThumbnails: Boolean);
      Destructor Destroy; Override;
   End;

   TD2ImageListBoxItem = Class(TD2ListBoxItem)
   Private
      Function TextBorder: TD2VisualObject;
   Protected
      Procedure ApplyStyle; Override;
   Public
      Function Text: TD2Text;
   End;

   TD2ImageListBox = Class(TD2ListBox)
   Private
      FFolder: String;
      FShowFileName: Boolean;
      FUseThumbnails: Boolean;
      Function GetSelectedFileName: String;
      Procedure SetShowFileName(Const Value: Boolean);
      Procedure SetItemHeight(Const Value: Single);
      Function GetSelectedImage: TD2Image;
      Function GetImage(aIndex: Integer): TD2Image;
   Protected
      Procedure DoApplyResource(Sender: TObject);
      Procedure BeginAutoDrag; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Procedure AddFolder(Const Folder: String);
      Procedure AddFile(Const AFile: String);
      Procedure AddBitmap(Const AFile: String; Const ABitmap: TD2Bitmap);
      Procedure Clear; Override;
      Property Images[aIndex: Integer]: TD2Image read GetImage;
      Property SelectedFileName: String read GetSelectedFileName;
      Property SelectedImage: TD2Image read GetSelectedImage;
   Published
      Property ShowFileName: Boolean read FShowFileName write SetShowFileName;
      Property UseThumbnails: Boolean read FUseThumbnails write FUseThumbnails Default True;
      Property ItemHeight: Single read FItemHeight write SetItemHeight;
   End;

   TD2HudImageListBox = Class(TD2ImageListBox)
   End;

   TD2HorzImageListBox = Class(TD2ImageListBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property ListStyle Default d2ListHorizontal;
   End;

   TD2HudHorzImageListBox = Class(TD2HorzImageListBox)
   End;

   TD2HudListBox = Class(TD2ListBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2HudHorzListBox = Class(TD2HorzListBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2HudStringListBox = Class(TD2StringListBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2HudComboBox = Class(TD2ComboBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2HudStringComboBox = Class(TD2StringComboBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2TreeView = Class;

   TD2TreeViewItem = Class(TD2TextControl)
   Private
      FIsExpanded: Boolean;
      FButton: TD2CustomButton;
      FCheck: TD2CheckBox;
      FGlobalIndex: Integer;
      FIsChecked: Boolean;
      FIsSelected: Boolean;
      Procedure SetIsExpanded(Const Value: Boolean);
      Procedure DoButtonClick(Sender: TObject);
      Procedure DoCheckClick(Sender: TObject);
      Function GetCount: Integer;
      Procedure SetIsChecked(Const Value: Boolean);
      Procedure UpdateCheck;
      Function GetItem(Const aIndex: Integer): TD2TreeViewItem;
      Procedure SetIsSelected(Const Value: Boolean);
   Protected
      Procedure DesignClick; Override;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure DragEnd; Override;
      Function EnterFocusChildren(AObject: TD2VisualObject): Boolean; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Procedure Paint; Override;
      Procedure Realign; Override;
      Procedure AddObject(AObject: TD2Object); Override;
      Procedure RemoveObject(AObject: TD2Object); Override;
      Function ItemClass: String; Override;
      Function ItemByPoint(Const X, Y: Single): TD2TreeViewItem;
      Function ItemByIndex(Const Idx: Integer): TD2TreeViewItem;
      Property Count: Integer read GetCount;
      Property GlobalIndex: Integer read FGlobalIndex write FGlobalIndex;
      Function TreeView: TD2TreeView;
      Function Level: Integer;
      Property Items[aIndex: Integer]: TD2TreeViewItem read GetItem;
   Published
      Property IsChecked: Boolean read FIsChecked write SetIsChecked;
      Property IsExpanded: Boolean read FIsExpanded write SetIsExpanded;
      Property IsSelected: Boolean read FIsSelected write SetIsSelected;
      Property AutoTranslate Default True;
      Property Font;
      Property Resource;
      Property Text;
      Property TextAlign Default d2TextAlignNear;
   End;

   TOnCompareTreeViewItemEvent = Function(Item1, Item2: TD2TreeViewItem): Integer Of Object;
   TOnTreeViewDragChange = Procedure(SourceItem, DestItem: TD2TreeViewItem; Allow: Boolean) Of Object;

   TD2TreeView = Class(TD2ScrollBox)
   Private
      FMouseSelecting: Boolean;
      FOnChange: TNotifyEvent;
      FSelected: TD2TreeViewItem;
      FItemHeight: Single;
      FCountExpanded: Integer;
      FHideSelectionUnfocused: Boolean;
      FGlobalCount: Integer;
      FShowCheckboxes: Boolean;
      FOnChangeCheck: TNotifyEvent;
      FSorted: Boolean;
      FOnCompare: TOnCompareTreeViewItemEvent;
      FMultiSelect: Boolean;
      FFirstSelect: TD2TreeViewItem;
      FSelection: TD2VisualObject;
      FSelections: TList;
      FAllowDrag: Boolean;
      FDragItem: TD2TreeViewItem;
      FOnDragChange: TOnTreeViewDragChange;
      FGlobalList: TList;
      Procedure SetItemHeight(Const Value: Single);
      Procedure SetShowCheckboxes(Const Value: Boolean);
      Function GetItem(Const aIndex: Integer): TD2TreeViewItem;
      Procedure SetSorted(Const Value: Boolean);
      Procedure SortItems;
      Function GetSelection: TD2SelectionItem;
      Procedure ClearSelection;
      Procedure SelectAll;
      Procedure SelectRange(Item1, Item2: TD2TreeViewItem);
      Procedure UpdateSelection;
      Procedure SetAllowDrag(Const Value: Boolean);
      Function GetCount: Integer;
   Protected
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure SetSelected(Const Value: TD2TreeViewItem); Virtual;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure EnterFocus; Override;
      Procedure KillFocus; Override;
      Procedure HScrollChange(Sender: TObject); Override;
      Procedure VScrollChange(Sender: TObject); Override;
      Function GetContentBounds: TD2Rect; Override;
      Procedure UpdateGlobalIndexes;
      Procedure ContentAddObject(AObject: TD2Object); Override;
      Procedure ContentRemoveObject(AObject: TD2Object); Override;
      Function GetItemRect(Item: TD2TreeViewItem): TD2Rect;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function ItemClass: String; Override;
      Procedure EndUpdate; Override;
      Procedure Clear;
      Procedure ExpandAll;
      Procedure CollapseAll;
      Function ItemByPoint(Const X, Y: Single): TD2TreeViewItem;
      Function ItemByIndex(Const Idx: Integer): TD2TreeViewItem;
      Function ItemByGlobalIndex(Const Idx: Integer): TD2TreeViewItem;
      Procedure AddObject(AObject: TD2Object); Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure KeyUp(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean); Override;
      Procedure DragDrop(Const aData: TD2DragObject; Const Point: TD2Point); Override;
      Property Count: Integer read GetCount;
      Property GlobalCount: Integer read FGlobalCount;
      Property CountExpanded: Integer read FCountExpanded;
      Property Selected: TD2TreeViewItem read FSelected write SetSelected;
      Property Items[aIndex: Integer]: TD2TreeViewItem read GetItem;
   Published
      Property Resource;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property AllowDrag: Boolean read FAllowDrag write SetAllowDrag Default False;
      Property ItemHeight: Single read FItemHeight write SetItemHeight;
      Property HideSelectionUnfocused: Boolean read FHideSelectionUnfocused write FHideSelectionUnfocused Default True;
      Property MultiSelect: Boolean read FMultiSelect write FMultiSelect Default False;
      Property ShowCheckboxes: Boolean read FShowCheckboxes write SetShowCheckboxes Default False;
      Property Sorted: Boolean read FSorted write SetSorted Default False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnChangeCheck: TNotifyEvent read FOnChangeCheck write FOnChangeCheck;
      Property OnCompare: TOnCompareTreeViewItemEvent read FOnCompare write FOnCompare;
      Property OnDragChange: TOnTreeViewDragChange read FOnDragChange write FOnDragChange;
   End;


   TD2CustomTextBox = Class(TD2Control)
   Private
      FText: WideString;
      FFontFill: TD2Brush;
      FFont: TD2Font;
      FTextAlign: TD2TextAlign;
      FOnChange: TNotifyEvent;
      FReadOnly: Boolean;
      FSelStart: Integer;
      FSelLength: Integer;
      FCaretPosition: Integer;
      FMaxLength: Integer;
      FFirstVisibleChar: Integer;
      FLMouseSelecting: Boolean;
      FDisableCaret: Boolean;
      FPassword: Boolean;
      FOnTyping: TNotifyEvent;
      FSelectionFill: TD2Brush;
      FOnChangeTracking: TNotifyEvent;
      Procedure InsertText(Const AText: WideString);
      Function GetSelLength: Integer;
      Function GetSelStart: Integer;
      Function GetSelText: WideString;
      Procedure SetSelLength(Const Value: Integer);
      Procedure SetSelStart(Const Value: Integer);
      Function GetSelRect: TD2Rect;
      Procedure SetCaretPosition(Const Value: Integer);
      Function GetCoordinatePosition(x: Single): Integer;
      Procedure SetMaxLength(Const Value: Integer);
      Function GetNextWordBeging(StartPosition: Integer): Integer;
      Function GetPrivWordBeging(StartPosition: Integer): Integer;
      Procedure UpdateFirstVisibleChar;
      Procedure UpdateCaretePosition;
      Procedure SetPassword(Const Value: Boolean);
      Procedure CreatePopupMenu;
      Procedure DoCopy(Sender: TObject);
      Procedure DoCut(Sender: TObject);
      Procedure DoDelete(Sender: TObject);
      Procedure DoPaste(Sender: TObject);
      Procedure UpdatePopupMenuItems;
      Procedure DoSelectAll(Sender: TObject);
      Procedure SetFont(Const Value: TD2Font);
      Procedure SetTextAlign(Const Value: TD2TextAlign);
   Protected
      FNeedChange: Boolean;
      FFilterChar: WideString;
      FShowCaret: Boolean;
      FLastKey: Word;
      FLastChar: System.Widechar;
      Procedure ApplyStyle; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Function GetPasswordCharWidth: Single;
      Function TextWidth(Const Str: WideString): Single;
      Procedure SetTextW(Const Value: WideString); Virtual;
      Function GetTextW: WideString; Virtual;
      Function GetText: String; Virtual;
      Procedure SetText(Const Value: String); Virtual;
      Procedure FontChanged(Sender: TObject); Virtual;
      Procedure DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure KeyUp(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure EnterFocus; Override;
      Procedure KillFocus; Override;
      Procedure DblClick; Override;
      Procedure Change; Virtual;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Procedure ContextMenu(Const ScreenPosition: TD2Point); Override;
      Procedure ClearSelection;
      Procedure CopyToClipboard;
      Procedure CutToClipboard;
      Procedure PasteFromClipboard;
      Procedure SelectAll;
      Function GetCharX(a: Integer): Single;
      Function ContentRect: TD2Rect;
      Property CaretPosition: Integer read FCaretPosition write SetCaretPosition;
      Property SelStart: Integer read GetSelStart write SetSelStart;
      Property SelLength: Integer read GetSelLength write SetSelLength;
      Property SelText: WideString read GetSelText;
      Property MaxLength: Integer read FMaxLength write SetMaxLength Default 0;
      Property ShowCaret: Boolean read FShowCaret write FShowCaret Default True;
      Property FontFill: TD2Brush read FFontFill;
      Property SelectionFill: TD2Brush read FSelectionFill;
      Property Password: Boolean read FPassword write SetPassword;
      Property TextW: WideString read FText write SetTextW;
      Property Text: String read GetText write SetText;
      Property FilterChar: WideString read FFilterChar write FFilterChar;
   Published
      Property Cursor Default crIBeam;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Font: TD2Font read FFont write SetFont;
      Property TextAlign: TD2TextAlign read FTextAlign write SetTextAlign Default d2TextAlignNear;
      Property Resource;
      Property BindingSource;
      Property ReadOnly: Boolean read FReadOnly write FReadOnly;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnChangeTracking: TNotifyEvent read FOnChangeTracking write FOnChangeTracking;
      Property OnTyping: TNotifyEvent read FOnTyping write FOnTyping;
   End;

   TD2TextBox = Class(TD2CustomTextBox)
   Published
      Property Password;
      Property Text;
   End;

   TD2NumberBox = Class(TD2CustomTextBox)
   Private
      FValue: Single;
      FMin: Single;
      FMax: Single;
      FPressedPos: TD2Point;
      FPressedVert: Boolean;
      FPressedInc: Single;
      FValueType: TD2ValueType;
      FHorzIncrement: Single;
      FVertIncrement: Single;
      FDecimalDigits: Integer;
      Procedure SetMax(Const Value: Single);
      Procedure SetMin(Const Value: Single);
      Procedure SetValue(Const AValue: Single);
      Procedure SetValueType(Const Value: TD2ValueType);
      Procedure SetDecimalDigits(Const Value: Integer);
   Protected
      Procedure Change; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure SetText(Const Value: String); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Procedure PaintChildren; Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
   Published
      Property DecimalDigits: Integer read FDecimalDigits write SetDecimalDigits Default 2;
      Property Min: Single read FMin write SetMin;
      Property Max: Single read FMax write SetMax;
      Property ShowCaret Default True;
      Property Value: Single read FValue write SetValue;
      Property ValueType: TD2ValueType read FValueType write SetValueType;
      Property HorzIncrement: Single read FHorzIncrement write FHorzIncrement;
      Property VertIncrement: Single read FVertIncrement write FVertIncrement;
   End;

   TD2SpinBox = Class(TD2CustomTextBox)
   Private
      FValue: Single;
      FMin: Single;
      FMax: Single;
      FValueType: TD2ValueType;
      FMinus, FPlus: TD2CustomButton;
      FIncrement: Single;
      FDecimalDigits: Integer;
      Procedure SetMax(Const Value: Single);
      Procedure SetMin(Const Value: Single);
      Procedure SetValue(Const AValue: Single);
      Procedure SetValueType(Const Value: TD2ValueType);
      Procedure SetDecimalDigits(Const Value: Integer);
   Protected
      Procedure Change; Override;
      Procedure SetText(Const Value: String); Override;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure DoMinusClick(Sender: TObject);
      Procedure DoPlusClick(Sender: TObject);
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property DecimalDigits: Integer read FDecimalDigits write SetDecimalDigits Default 2;
      Property Min: Single read FMin write SetMin;
      Property Max: Single read FMax write SetMax;
      Property Increment: Single read FIncrement write FIncrement;
      Property ShowCaret Default True;
      Property Value: Single read FValue write SetValue;
      Property ValueType: TD2ValueType read FValueType write SetValueType;
      Property TextAlign Default d2TextAlignCenter;
   End;

   TD2ComboTextBox = Class(TD2CustomTextBox)
   Private
      FDropDownCount: Integer;
      FListBox: TD2ComboListBox;
      FPlacement: TD2Placement;
      FItems: TD2WideStrings;
      FItemHeight: Single;
      Procedure DoItemsChanged(Sender: TObject);
      Procedure RebuildList;
      Procedure SetItemHeight(Const Value: Single);
      Procedure SetItems(Const Value: TD2WideStrings);
      Function GetItemIndex: Integer;
      Procedure SetItemIndex(Const Value: Integer);
      Function GetCount: Integer;
      Function GetListBoxResource: String;
      Procedure SetListBoxResource(Const Value: String);
   Protected
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure ChangeParent; Override;
      Procedure DoTyping(Sender: TObject);
      Procedure DoClosePopup(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
      Procedure DropDown;
      Property ListBox: TD2ComboListBox read FListBox write FListBox;
      Property Count: Integer read GetCount;
   Published
      Property Cursor Default crDefault;
      Property DropDownCount: Integer read FDropDownCount write FDropDownCount Default 8;
      Property ItemHeight: Single read FItemHeight write SetItemHeight;
      Property ItemIndex: Integer read GetItemIndex write SetItemIndex;
      Property Items: TD2WideStrings read FItems write SetItems;
      Property ListBoxResource: String read GetListBoxResource write SetListBoxResource;
      Property Text;
   End;

   TD2ComboTrackBar = Class(TD2CustomTextBox)
   Private
      FTrackBar: TD2TrackBar;
      FPlacement: TD2Placement;
      Function GetFrequency: Single;
      Function GetMax: Single;
      Function GetMin: Single;
      Procedure SetFrequency(Const Value: Single);
      Procedure SetMax(Const Value: Single);
      Procedure SetMin(Const Value: Single);
      Procedure SetValue(Const AValue: Single);
      Function GetValue: Single;
   Protected
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure ChangeParent; Override;
      Procedure DoTrackChange(Sender: TObject); Virtual;
      Procedure DropDown;
      Procedure DoClosePopup(Sender: TObject);
      Procedure Change; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Property TrackBar: TD2TrackBar read FTrackBar write FTrackBar;
   Published
      Property Min: Single read GetMin write SetMin;
      Property Max: Single read GetMax write SetMax;
      Property Value: Single read GetValue write SetValue;
      Property Frequency: Single read GetFrequency write SetFrequency;
      Property Text Stored False;
   End;

   TD2TextBoxClearBtn = Class(TD2CustomTextBox)
   Private
      FClearBtn: TD2CustomButton;
   Protected
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure DoClearBtnClick(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Password;
      Property Text;
   End;

   TD2RoundTextBox = Class(TD2TextBox)
   End;

   TD2HudTextBox = Class(TD2TextBox)
   End;

   TD2HudRoundTextBox = Class(TD2TextBox)
   End;

   TD2HudNumberBox = Class(TD2NumberBox)
   End;

   TD2HudSpinBox = Class(TD2SpinBox)
   End;

   TD2HudComboTextBox = Class(TD2ComboTextBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2HudComboTrackBar = Class(TD2ComboTrackBar)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

Type
   TD2Memo = Class;

   TEdtActionStack = Class(TStack)
   Private
      FOwner: TD2Memo;
   Public
      Constructor Create(AOwner: TD2Memo);
      Destructor Destroy; Override;
      Procedure FragmentInserted(StartPos, FragmentLength: Integer; IsPairedWithPriv: Boolean);
      Procedure FragmentDeleted(StartPos: Integer; Fragment: WideString);
      Procedure CaretMovedBy(Shift: Integer);
      Function RollBackAction: Boolean;
   End;

   TD2MemoLines = Class(TD2WideStrings)
   Private
      FMemo: TD2Memo;
   Protected
      Function Get(Index: Integer): WideString; Override;
      Function GetCount: Integer; Override;
      Procedure SetUpdateState(Updating: Boolean); Override;
   Public
      Procedure Clear; Override;
      Procedure Delete(Index: Integer); Override;
      Procedure Insert(Index: Integer; Const S: WideString); Override;
   End;

   TD2Memo = Class(TD2ScrollBox)
   Private
      FNeedChange: Boolean;
      FText: WideString;
      FFontFill: TD2Brush;
      FFont: TD2Font;
      FTextAlign: TD2TextAlign;
      FInternalMustUpdateLines: Boolean;
      FLMouseSelecting: Boolean;
      FOldMPt: TD2Point;
      FCaretPosition: TCaretPosition;
      FFirstVisibleChar: Integer;
      FUnwrapLines: TD2WideStrings;
      FAutoSelect: Boolean;
      FCharCase: TEditCharCase;
      FHideSelection: Boolean;
      FMaxLength: Integer;
      FReadOnly: Boolean;
      FOnChange: TNotifyEvent;
      FTextAlignment: TAlignment;
      FActionStack: TEdtActionStack;
      FLines: TD2WideStrings;
      FWordWrap: Boolean;
      FLinesBegs: Array Of Integer;
      FSelStart: TCaretPosition;
      FSelEnd: TCaretPosition;
      FSelected: Boolean;
      FOldSelStartPos, FOldSelEndPos, FOldCaretPos: Integer;
      FSelectionFill: TD2Brush;
      FOnChangeTracking: TNotifyEvent;
      Function GetSelBeg: TCaretPosition;
      Function GetSelEnd: TCaretPosition;
      Procedure StorePositions;
      Procedure RestorePositions;
      Procedure SelectAtPos(APos: TCaretPosition);
      Procedure SetCaretPosition(Const Value: TCaretPosition);
      Procedure SetSelLength(Const Value: Integer);
      Procedure SetSelStart(Const Value: Integer);
      Function GetSelStart: Integer;
      Function GetSelLength: Integer;
      Procedure UpdateHScrlBarByCaretPos;
      Procedure UpdateVScrlBarByCaretPos;
      Function GetSelText: WideString;
      Procedure SetAutoSelect(Const Value: Boolean);
      Procedure SetCharCase(Const Value: TEditCharCase);
      Procedure SetHideSelection(Const Value: Boolean);
      Procedure SetMaxLength(Const Value: Integer);
      Procedure SelectAtMousePoint;
      Function GetNextWordBeging(StartPosition: TCaretPosition): TCaretPosition;
      Function GetPrivWordBeging(StartPosition: TCaretPosition): TCaretPosition;
      Function GetPositionShift(APos: TCaretPosition; Delta: Integer): TCaretPosition;
      Procedure MoveCareteBy(Delta: Integer);
      Procedure MoveCaretLeft;
      Procedure MoveCaretRight;
      Procedure MoveCaretVertical(LineDelta: Integer);
      Procedure MoveCaretDown;
      Procedure MoveCaretUp;
      Procedure MoveCaretPageUp;
      Procedure MoveCaretPageDown;
      Procedure UpdateCaretPosition(UpdateScrllBars: Boolean);
      Procedure SetTextAlignment(Const Value: TAlignment);
      Procedure SetLines(Const Value: TD2WideStrings);
      Procedure GetLineBounds(LineIndex: Integer; Var LineBeg, LineLength: Integer);
      Function GetLineCount: Integer;
      Function GetLine(Const aIndex: Integer): WideString;
      Function GetLineInternal(Const aIndex: Integer): WideString;
      Procedure InsertLine(Const aIndex: Integer; Const S: WideString);
      Procedure DeleteLine(Const aIndex: Integer);
      Procedure ClearLines;
      Procedure SetWordWrap(Const Value: Boolean);
      Function GetPageSize: Single;
      Function GetLineWidth(LineNum: Integer): Single;
      Function GetWidestLine: Integer;
      Function FillLocalLinesBegs(PText: PWideString; ABegChar, AEndChar: Integer; TmpLinesBegs: PLinesBegs): Integer;
      Procedure UpdateRngLinesBegs(PText: PWideString; AUpdBegLine, AUpdEndLine, AUpdBegChar, AUpdEndChar, ACharDelta, AOldWideslLineWidth: Integer);
      Function GetShowSelection: Boolean;
      Function GetLineRealEnd(AStartPos: TCaretPosition; PText: PWideString): TCaretPosition;
      Procedure SetFont(Const Value: TD2Font);
      Procedure SetTextAlign(Const Value: TD2TextAlign);
      Function TextWidth(Const Str: WideString): Single;
      Procedure HScrlBarChange(Sender: TObject);
      Procedure SetUpdateState(Const aUpdating: Boolean);
      Function GetUnwrapLines: TD2WideStrings;
   Protected
      FUpdatingBool: Boolean;
      FWidesLineIndex: Integer;
      FTextWidth: Array Of Integer;
      Function GetLineHeight: Single;
      Function GetPointPosition(Pt: TD2Point): TCaretPosition;
      Function GetTextW: WideString; Virtual;
      Procedure SetTextW(Const Value: WideString);
      Function GetText: String; Virtual;
      Procedure SetText(Const Value: String);

      Function GetSelArea: TSelArea; Virtual;
      Procedure DrawPasswordChar(SymbolRect: TD2Rect; Selected: Boolean); Virtual;
      Procedure CreatePopupMenu; Virtual;
      Procedure UpdatePopupMenuItems; Virtual;
      Procedure ApplyStyle; Override;
      Function ContentPos: TD2Point;
      Procedure Change; Virtual;
      Procedure DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
      Function ValidText(NewText: WideString): Boolean; Virtual;
      Function CanAutoSize(Var NewWidth, NewHeight: Integer): Boolean;
      Procedure ContextMenu(Const ScreenPosition: TD2Point); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure SelectWord;
      Procedure FontChanged(Sender: TObject);
      Procedure DoUndo(Sender: TObject);
      Procedure DoCut(Sender: TObject);
      Procedure DoCopy(Sender: TObject);
      Procedure DoPaste(Sender: TObject);
      Procedure DoDelete(Sender: TObject);
      Procedure DoSelectAll(Sender: TObject);
      Procedure UpdateLines;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure EnterFocus; Override;
      Procedure KillFocus; Override;
      Procedure VScrollChange(Sender: TObject);
      Function GetContentBounds: TD2Rect; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; x, y: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; x, y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; x, y, dx, dy: Single); Override;
      Procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean); Override;
      Procedure CopyToClipboard;
      Procedure PasteFromClipboard;
      Procedure CutToClipboard;
      Procedure ClearSelection;
      Procedure SelectAll;
      Procedure GoToTextEnd;
      Procedure GoToTextBegin;
      Procedure GotoLineEnd;
      Procedure GoToLineBegin;
      Function GetPositionPoint(ACaretPos: TCaretPosition): TD2Point;
      Procedure UnDo;
      Procedure InsertAfter(Const aPosition: TCaretPosition; S: WideString; Options: TInsertOptions);
      Procedure DeleteFrom(Const aPosition: TCaretPosition; ALength: Integer; Options: TDeleteOptions);
      Function TextPosToPos(APos: Integer): TCaretPosition;
      Function PosToTextPos(APostion: TCaretPosition): Integer;
      Property SelStart: Integer read GetSelStart write SetSelStart;
      Property SelLength: Integer read GetSelLength write SetSelLength;
      Property SelText: WideString read GetSelText;
      Property CaretPosition: TCaretPosition read FCaretPosition write SetCaretPosition;
      Property LineWidth[LineNum: Integer]: Single read GetLineWidth;
      Property UnwrapLines: TD2WideStrings read GetUnwrapLines;
      Property FontFill: TD2Brush read FFontFill;
      Property SelectionFill: TD2Brush read FSelectionFill;
      Property TextW: WideString read FText write SetTextW;
   Published
      Property Cursor Default crIBeam;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property AutoSelect: Boolean read FAutoSelect write SetAutoSelect Default True;
      Property CharCase: TEditCharCase read FCharCase write SetCharCase Default d2ecNormal;
      Property Enabled;
      Property HideSelection: Boolean read FHideSelection write SetHideSelection Default True;
      Property Lines: TD2WideStrings read FLines write SetLines Stored False;
      Property MaxLength: Integer read FMaxLength write SetMaxLength Default 0;
      Property ReadOnly: Boolean read FReadOnly write FReadOnly Default False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnChangeTracking: TNotifyEvent read FOnChangeTracking write FOnChangeTracking;
      Property WordWrap: Boolean read FWordWrap write SetWordWrap;
      Property Font: TD2Font read FFont write SetFont;
      Property Text: String read GetText write SetText Stored True;
      Property TextAlign: TD2TextAlign read FTextAlign write SetTextAlign Default d2TextAlignNear;
      Property Resource;
   End;

   TD2HudMemo = Class(TD2Memo)
   End;

   TD2BitmapTrackBar = Class(TD2TrackBar)
   Private
      FBitmap: TD2Bitmap;
      FBackground: TD2Shape;
   Protected
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure UpdateBitmap;
      Procedure FillBitmap; Virtual;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
   End;

   TD2HueTrackBar = Class(TD2BitmapTrackBar)
   Protected
      Procedure FillBitmap; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2AlphaTrackBar = Class(TD2BitmapTrackBar)
   Protected
      Procedure FillBitmap; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2BWTrackBar = Class(TD2BitmapTrackBar)
   Protected
      Procedure FillBitmap; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2ColorBox = Class(TD2Control)
   Private
      FColor: TD2Color;
      Procedure SetColor(Const Value: TD2Color);
   Protected
   Public
      Constructor Create(AOwner: TComponent); Override;
      Procedure Paint; Override;
      Property Color: TD2Color read FColor write SetColor;
   End;

   TD2ColorQuad = Class(TD2Control)
   Private
      FColorBox: TD2ColorBox;
      FColorBitmap: TD2Bitmap;
      FHue: Single;
      FSat: Single;
      FLum: Single;
      FOnChange: TNotifyEvent;
      FAlpha: Single;
      Procedure SetHue(Const Value: Single);
      Procedure SetLum(Const Value: Single);
      Procedure SetSat(Const Value: Single);
      Procedure SetAlpha(Const Value: Single);
      Procedure SetColorBox(Const Value: TD2ColorBox);
   Protected
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Function GetAbsoluteRect: TD2Rect; Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Function pointInObject(X, Y: Single): Boolean; Override;
   Published
      Property Hue: Single read FHue write SetHue;
      Property Lum: Single read FLum write SetLum;
      Property Sat: Single read FSat write SetSat;
      Property Alpha: Single read FAlpha write SetAlpha;
      Property ColorBox: TD2ColorBox read FColorBox write SetColorBox;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2ColorPicker = Class(TD2Control)
   Private
      FHueBitmap: TD2Bitmap;
      FHue: Single;
      FColorQuad: TD2ColorQuad;
      Procedure SetHue(Const Value: Single);
      Function GetColor: TD2Color;
      Procedure SetColor(Const Value: TD2Color);
   Protected
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Function GetAbsoluteRect: TD2Rect; Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Function pointInObject(X, Y: Single): Boolean; Override;
      Property Color: TD2Color read GetColor write SetColor;
   Published
      Property Hue: Single read FHue write SetHue;
      Property ColorQuad: TD2ColorQuad read FColorQuad write FColorQuad;
   End;

   TD2GradientEdit = Class(TD2Control)
   Private
      FBitmap: TD2Bitmap;
      FGradient: TD2Gradient;
      FCurrentPoint: Integer;
      FCurrentPointInvisible: Boolean;
      FMoving: Boolean;
      FOnChange: TNotifyEvent;
      FOnSelectPoint: TNotifyEvent;
      FColorPicker: TD2ColorPicker;
      Procedure SetGradient(Const Value: TD2Gradient);
      Function GetPointRect(Const Point: Integer): TD2Rect;
      Procedure DoChanged(Sender: TObject);
      Procedure SetCurrentPoint(Const Value: Integer);
      Procedure SetColorPicker(Const Value: TD2ColorPicker);
   Protected
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Paint; Override;
      Procedure UpdateGradient;
      Property Gradient: TD2Gradient read FGradient write SetGradient;
      Property CurrentPoint: Integer read FCurrentPoint write SetCurrentPoint;
   Published
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnSelectPoint: TNotifyEvent read FOnSelectPoint write FOnSelectPoint;
      Property ColorPicker: TD2ColorPicker read FColorPicker write SetColorPicker;
   End;

   TD2ColorPanel = Class(TD2Control)
   Private
      FOnChange: TNotifyEvent;
      FColorQuad: TD2ColorQuad;
      FAlphaTrack: TD2AlphaTrackBar;
      FHueTrack: TD2HueTrackBar;
      FColorBox: TD2ColorBox;
      FUseAlpha: Boolean;
      Function GetColor: String;
      Procedure SetColor(Const Value: String);
      Procedure SetColorBox(Const Value: TD2ColorBox);
      Procedure SetUseAlpha(Const Value: Boolean);
   Protected
      Procedure DoAlphaChange(Sender: TObject);
      Procedure DoHueChange(Sender: TObject);
      Procedure DoQuadChange(Sender: TObject);
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure Loaded; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property Color: String read GetColor write SetColor;
      Property ColorBox: TD2ColorBox read FColorBox write SetColorBox;
      Property UseAlpha: Boolean read FUseAlpha write SetUseAlpha Default True;
   End;

   TD2ComboColorBox = Class(TD2Control)
   Private
      FColorPanel: TD2ColorPanel;
      FColorBox: TD2ColorBox;
      FColorText: TD2TextBox;
      FPlacement: TD2Placement;
      FOnChange: TNotifyEvent;
      Function GetValue: String;
      Procedure SetValue(Const Value: String);
      Function GetUseAlpha: Boolean;
      Procedure SetUseAlpha(Const Value: Boolean);
   Protected
      Procedure ApplyStyle; Override;
      Procedure DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure ChangeParent; Override;
      Procedure DoColorChange(Sender: TObject); Virtual;
      Procedure DoTextChange(Sender: TObject); Virtual;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure DropDown;
   Published
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Color: String read GetValue write SetValue;
      Property UseAlpha: Boolean read GetUseAlpha write SetUseAlpha Default True;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2HudHueTrackBar = Class(TD2HueTrackBar)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2HudAlphaTrackBar = Class(TD2AlphaTrackBar)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2HudBWTrackBar = Class(TD2BitmapTrackBar)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2HudComboColorBox = Class(TD2ComboColorBox)
   Public
      Constructor Create(AOwner: TComponent); Override;
   End;

   TD2TabItem = Class(TD2TextControl)
   Private
      FTabIndex: Integer;
      FLayout: TD2VisualObject;
      FIsSelected: Boolean;
      Procedure SetTabIndex(Const Value: Integer);
   Protected
      Procedure ApplyStyle; Override;
      Procedure DesignSelect; Override;
      Procedure DesignInsert; Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure SetVisible(Const Value: Boolean); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure Realign; Override;
      Procedure Select(ASelected: Boolean);
   Published
      Property IsSelected: Boolean read FIsSelected;
      Property AutoTranslate Default True;
      Property Font;
      Property TabIndex: Integer read FTabIndex write SetTabIndex;
      Property TextAlign;
      Property VertTextAlign;
      Property Text;
      Property Layout: TD2VisualObject read FLayout write FLayout;
      Property Resource;
   End;

   TD2HackTabItem = Class(TD2TabItem);

   TD2TabControl = Class(TD2Control)
   Private
      FItemIndex: Integer;
      FOnChange: TNotifyEvent;
      FItemHeight: Single;
      FFullSize: Boolean;
      FBackground: TD2VisualObject;
      Procedure SetItemIndex(Const Value: Integer);
      Procedure SetItemHeight(Const Value: Single);
      Procedure SetFullSize(Const Value: Boolean);
   Protected
      Function TabItem(AIndex: Integer): TD2TabItem;
      Function TabCount: Integer;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure PaintChildren; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function ItemClass: String; Override;
      Procedure Realign; Override;
      Procedure AddObject(AObject: TD2Object); Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: Widechar; Shift: TShiftState);
      Procedure KeyUp(Var Key: Word; Var KeyChar: Widechar; Shift: TShiftState);
      Procedure SetItem(AItem: TD2TabItem);
   Published
      Property Resource;
      Property FullSize: Boolean read FFullSize write SetFullSize Default False;
      Property ItemIndex: Integer read FItemIndex write SetItemIndex Default -1;
      Property ItemHeight: Single read FItemHeight write SetItemHeight;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2HudTabItem = Class(TD2TabItem)
   End;

   TD2HudTabControl = Class(TD2TabControl)
   Public
      Function ItemClass: String; Override;
   End;

   TD2IPhoneButton = Class(TD2BitmapButton)
   Private
      FBackground: TD2Brush;
      Procedure SetBackground(Const Value: TD2Brush);
   Protected
      Procedure BackChanged(Sender: TObject);
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Background: TD2Brush read FBackground write SetBackground;
      Property Resource;
   End;

   TD2DockBar = Class(TD2Control)
   Private
      FMousePos: TD2Point;
      FMaxSize: Single;
      FMinSize: Single;
      FAmplitude: Single;
      Procedure SetMaxSize(Const Value: Single);
      Procedure SetMinSize(Const Value: Single);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseLeave; Override;
      Procedure Paint; Override;
   Published
      Property MinSize: Single read FMinSize write SetMinSize;
      Property MaxSize: Single read FMaxSize write SetMaxSize;
      Property Resource;
   End;

   TD2DropTarget = Class(TD2TextControl)
   Private
      FOnDrop: TD2DragDropEvent;
      FFilter: String;
   Protected
      Procedure DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean); Override;
      Procedure DragDrop(Const aData: TD2DragObject; Const Point: TD2Point); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property Filter: String read FFilter write FFilter;
      Property Font;
      Property Text;
      Property OnDroped: TD2DragDropEvent read FOnDrop write FOnDrop;
   End;

   TD2PlotGrid = Class(TD2VisualObject)
   Private
      FMarks: Single;
      FFrequency: Single;
      FLineFill: TD2Brush;
      Procedure SetFrequency(Const Value: Single);
      Procedure SetMarks(Const Value: Single);
      Procedure SetLineFill(Const Value: TD2Brush);
      Procedure LineFillChanged(Sender: TObject);
   Protected
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property LineFill: TD2Brush read FLineFill write SetLineFill;
      Property Marks: Single read FMarks write SetMarks;
      Property Frequency: Single read FFrequency write SetFrequency;
   End;

   TD2ImageViewer = Class(TD2ScrollBox)
   Private
      FBack: TD2Rectangle;
      FImage: TD2Image;
      FBitmapScale: Single;
      FMouseScaling: Boolean;
      FShowBackground: Boolean;
      Function GetBitmap: TD2Bitmap;
      Procedure SetBitmap(Const Value: TD2Bitmap);
      Procedure SetBitmapScale(Const Value: Single);
      Function GetBackgroundFill: TD2Brush;
      Procedure SetBackgroundFill(Const Value: TD2Brush);
      Procedure SetShowBackground(Const Value: Boolean);
   Protected
      Function GetContentBounds: TD2Rect; Override;
      Procedure DoBitmapChange(Sender: TObject);
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean); Override;
      Procedure BestFit;
   Published
      Property BackgroundFill: TD2Brush read GetBackgroundFill write SetBackgroundFill;
      Property Bitmap: TD2Bitmap read GetBitmap write SetBitmap;
      Property BitmapScale: Single read FBitmapScale write SetBitmapScale;
      Property ShowBackground: Boolean read FShowBackground write SetShowBackground Default False;
      Property MouseScaling: Boolean read FMouseScaling write FMouseScaling Default True;
   End;


   TD2Calendar = Class(TD2Control)
   Private
      FDateTime: TDateTime;
      FDays: TD2ListBox;
      FToday, FPrev, FNext: TD2Button;
      FMonths: TD2PopupBox;
      FYears: TD2PopupBox;
      FWeeks: TD2GridLayout;
      FFirstDayOfWeek: TD2CalDayOfWeek;
      FFirstDayOfWeekNum: Integer;
      FWeek: TD2GridLayout;
      FTodayDefault: Boolean;
      FOnChange: TNotifyEvent;
      FWeekNumbers: Boolean;
      FOnDayChange: TNotifyEvent;
      Function GetDate: TDate;
      Procedure SetDate(Value: TDate);
      Procedure SetDateTime(Const Value: TDateTime);
      Procedure SetFirstDayOfWeek(Const Value: TD2CalDayOfWeek);
      Procedure SetTodayDefault(Const Value: Boolean);
      Procedure SetWeekNumbers(Const Value: Boolean);
   Protected
      FDisableDayChange: Integer;
      Procedure DoPrevClick(Sender: TObject);
      Procedure DoNextClick(Sender: TObject);
      Procedure DoTodayClick(Sender: TObject);
      Procedure DoDayChange(Sender: TObject);
      Procedure DoMonthChange(Sender: TObject);
      Procedure DoYearChange(Sender: TObject);
      Procedure FillList;
      Function GetData: Variant; Override;
      Procedure SetData(Const Value: Variant); Override;
      Procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
      Property DateTime: TDateTime read FDateTime write SetDateTime;
   Published
      Property BindingSource;
      Property Date: TDate read GetDate write SetDate;
      Property FirstDayOfWeek: TD2CalDayOfWeek read FFirstDayOfWeek write SetFirstDayOfWeek Default d2LocaleDefault;
      Property TodayDefault: Boolean read FTodayDefault write SetTodayDefault Default False;
      Property WeekNumbers: Boolean read FWeekNumbers write SetWeekNumbers Default False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
      Property OnDayChange: TNotifyEvent read FOnDayChange write FOnDayChange;
   End;

   TD2CalendarBox = Class(TD2TextControl)
   Private
      FCalendar: TD2Calendar;
      FPlacement: TD2Placement;
      Function GetDate: TDate;
      Procedure SetDate(Const Value: TDate);
   Protected
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure DoClosePopup(Sender: TObject);
      Procedure DoCalendarChanged(Sender: TObject);
      Procedure DoDayChanged(Sender: TObject);
      Procedure DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure DropDown;
      Property Calendar: TD2Calendar read FCalendar;
   Published
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property TabOrder;
      Property Cursor Default crDefault;
      Property Date: TDate read GetDate write SetDate;
      Property TextAlign Default d2TextAlignNear;
   End;

   TD2CalendarTextBox = Class(TD2CustomTextBox)
   Private
      FCalendar: TD2Calendar;
      FPlacement: TD2Placement;
      Function GetDate: TDate;
      Procedure SetDate(Const Value: TDate);
   Protected
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure DoClosePopup(Sender: TObject);
      Procedure DoCalendarChanged(Sender: TObject);
      Procedure DoDayChanged(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure DropDown;
      Property Calendar: TD2Calendar read FCalendar;
   Published
      Property Cursor Default crDefault;
      Property Date: TDate read GetDate write SetDate;
   End;


   TD2CompoundTrackBar = Class(TD2Control)
   Private
      FValueLabel: TD2Label;
      FTextLabel: TD2Label;
      FTrackBar: TD2TrackBar;
      FDecimalDigits: Integer;
      FOnChange: TNotifyEvent;
      FSuffix: WideString;
      Function GetValue: Single;
      Procedure SetValue(Const Value: Single);
      Procedure SetDecimalDigits(Const Value: Integer);
      Procedure SetSuffix(Const Value: WideString);
   Protected
      Procedure DoTrack(Sender: TObject);
      Procedure DoTracking(Sender: TObject);
      Procedure UpdateLabel;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property DecimalDigits: Integer read FDecimalDigits write SetDecimalDigits Default 2;
      Property TextLabel: TD2Label read FTextLabel;
      Property TrackBar: TD2TrackBar read FTrackBar;
      Property ValueLabel: TD2Label read FValueLabel;
      Property Suffix: WideString read FSuffix write SetSuffix;
      Property Value: Single read GetValue write SetValue Stored False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2CompoundAngleBar = Class(TD2Control)
   Private
      FValueLabel: TD2Label;
      FTextLabel: TD2Label;
      FAngleBar: TD2AngleButton;
      FDecimalDigits: Integer;
      FOnChange: TNotifyEvent;
      Function GetValue: Single;
      Procedure SetValue(Const Value: Single);
   Protected
      Procedure DoChange(Sender: TObject);
      Procedure UpdateLabel;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property TextLabel: TD2Label read FTextLabel;
      Property AngleButton: TD2AngleButton read FAngleBar;
      Property ValueLabel: TD2Label read FValueLabel;
      Property Value: Single read GetValue write SetValue Stored False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2CompoundTextBox = Class(TD2Control)
   Private
      FTextLabel: TD2Label;
      FTextBox: TD2TextBox;
      FOnChange: TNotifyEvent;
   Protected
      Function GetTextW: WideString; Virtual;
      Procedure SetTextW(Const Value: WideString); Virtual;
      Function GetText: String; Virtual;
      Procedure SetText(Const Value: String); Virtual;
      Procedure DoChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Property ValueW: WideString read GetTextW write SetTextW;
   Published
      Property TextLabel: TD2Label read FTextLabel;
      Property TextBox: TD2TextBox read FTextBox;
      Property Value: String read GetText write SetText Stored False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2CompoundMemo = Class(TD2Control)
   Private
      FTextLabel: TD2Label;
      FMemo: TD2Memo;
      FOnChange: TNotifyEvent;
   Protected
      Function GetTextW: WideString;
      Procedure SetTextW(Const Value: WideString);
      Function GetText: String;
      Procedure SetText(Const Value: String);
      Procedure DoChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Property ValueW: WideString read GetTextW write SetTextW Stored False;
   Published
      Property TextLabel: TD2Label read FTextLabel;
      Property Memo: TD2Memo read FMemo;
      Property Value: String read GetText write SetText Stored False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2CompoundNumberBox = Class(TD2Control)
   Private
      FTextLabel: TD2Label;
      FNumberBox: TD2NumberBox;
      FOnChange: TNotifyEvent;
      Function GetValue: Single;
      Procedure SetValue(Const Value: Single);
   Protected
      Procedure DoChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property TextLabel: TD2Label read FTextLabel;
      Property NumberBox: TD2NumberBox read FNumberBox;
      Property Value: Single read GetValue write SetValue Stored False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2CompoundPopupBox = Class(TD2Control)
   Private
      FTextLabel: TD2Label;
      FPopupBox: TD2PopupBox;
      FOnChange: TNotifyEvent;
      Function GetItemIndex: Integer;
      Procedure SetItemIndex(Const Value: Integer);
   Protected
      Procedure DoChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property TextLabel: TD2Label read FTextLabel;
      Property PopupBox: TD2PopupBox read FPopupBox;
      Property Value: Integer read GetItemIndex write SetItemIndex;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2CompoundColorButton = Class(TD2Control)
   Private
      FTextLabel: TD2Label;
      FColorButton: TD2ColorButton;
      FOnChange: TNotifyEvent;
      Function GetValue: String;
      Procedure SetValue(Const Value: String);
   Protected
      Procedure DoChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property TextLabel: TD2Label read FTextLabel;
      Property ColorButton: TD2ColorButton read FColorButton;
      Property Value: String read GetValue write SetValue Stored False;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2CompoundImage = Class(TD2Control)
   Private
      FTextLabel: TD2Label;
      FImage: TD2ImageControl;
      FOnChange: TNotifyEvent;
   Protected
      Procedure DoChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property TextLabel: TD2Label read FTextLabel;
      Property Image: TD2ImageControl read FImage;
      Property OnChange: TNotifyEvent read FOnChange write FOnChange;
   End;

   TD2ImageList = Class(TCustomImageList)
   Private
      FImages: TList;
      Function GetCount: Integer;
      Function GetBitmap(Index: Integer): TD2Bitmap;
      Procedure UpdateList;
   Protected
      Procedure DefineProperties(Filer: TFiler); Override;
      Procedure ReadImage(Stream: TStream);
      Procedure WriteImage(Stream: TStream);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function Add(Bitmap: TD2Bitmap): Integer;
      Procedure Clear;
      Procedure ReadData(Stream: TStream); Override;
      Procedure WriteData(Stream: TStream); Override;
      Property Images[Index: Integer]: TD2Bitmap read GetBitmap;
      Property Count: Integer read GetCount;
   Published
      Property Height Default 32;
      Property Width Default 32;
   End;

   TD2BrushStyles = Set Of TD2BrushStyle;

   TD2OnChangeProperty = Procedure(Sender: TObject; PropertyName: String) Of Object;

   TD2Inspector = Class(TD2TreeView)
   Private
      FShowEvents: Boolean;
      FShowProperties: Boolean;
      FEditButton: TD2Button;
      FComboBox: TD2PopupBox;
      FSelectedObject: TComponent;
      FEditBox: TD2TextBox;
      FDisabledProperties: TStrings;
      FOnChangeProperty: TD2OnChangeProperty;
      Procedure SetDisabledProperties(Const Value: TStrings);
      Procedure SetSelectedObject(Const Value: TComponent);
      Procedure SetShowEvents(Const Value: Boolean);
      Procedure SetShowProperties(Const Value: Boolean);
      Procedure UpdateEditorPos;
      Procedure RebuildList;
      Procedure RebuildEditor;
   Protected
      Function Editor: TD2Control;
      Procedure InsAddObject(ItemRoot: TD2Object; Root: TObject);
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure DialogKey(Var Key: Word; Shift: TShiftState); Override;
      Procedure SetSelected(Const Value: TD2TreeViewItem); Override;
      Procedure VScrollChange(Sender: TObject); Override;
      Procedure DoEditorChange(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property DisabledProperties: TStrings read FDisabledProperties write SetDisabledProperties;
      Property SelectedObject: TComponent read FSelectedObject write SetSelectedObject;
      Property ShowProperties: Boolean read FShowProperties write SetShowProperties Default True;
      Property ShowEvents: Boolean read FShowEvents write SetShowEvents Default False;
      Property OnChangeProperty: TD2OnChangeProperty read FOnChangeProperty write FOnChangeProperty;
   End;

Type

   TD2CustomGrid = Class;
   TD2Header = Class;

   TD2HeaderItem = Class(TD2CornerButton)
   Private
      FSplitter: TD2VisualObject;
      FLeftSplitter: TD2VisualObject;
   Protected
      Procedure DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean); Override;
      Procedure DragDrop(Const aData: TD2DragObject; Const Point: TD2Point); Override;
      Procedure DragEnd; Override;
      Procedure DoSplitterMouseMove(Sender: TObject; Shift: TShiftState; X, Y, Dx, Dy: Single);
      Procedure DoLeftSplitterMouseMove(Sender: TObject; Shift: TShiftState; X, Y, Dx, Dy: Single);
      Function Header: TD2Header;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property CanFocused Default False;
      Property TextAlign Default d2TextAlignNear;
      Property DragMode Default d2DragAutomatic;
   End;

   TD2OnRealignItemEvent = Procedure(Sender: TObject; OldIndex, NewIndex: Integer) Of Object;
   TD2OnResizeItemEvent = Procedure(Sender: TObject; Var NewSize: Single) Of Object;

   TD2Header = Class(TD2Control)
   Private
      FOnRealignItem: TD2OnRealignItemEvent;
      FOnResizeItem: TD2OnResizeItemEvent;
      FOffset: Single;
      FLastItem: TD2HeaderItem;
      FRadius: Single;
      FSides: TD2Sides;
      Function GetItem(Const aIndex: Integer): TD2HeaderItem;
      Procedure SetRadius(Const Value: Single);
      Procedure SetSides(Const Value: TD2Sides);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Procedure Paint; Override;
      Procedure Realign; Override;
      Function ItemClass: String; Override;
      Property Items[aIndex: Integer]: TD2HeaderItem read GetItem;
   Published
      Property CanFocused Default False;
      Property ClipChildren Default True;
      Property OnRealignItem: TD2OnRealignItemEvent read FOnRealignItem write FOnRealignItem;
      Property OnResizeItem: TD2OnResizeItemEvent read FOnResizeItem write FOnResizeItem;
      Property Radius: Single read FRadius write SetRadius;
      Property Sides: TD2Sides read FSides write SetSides;
   End;

   TD2TextCell = Class(TD2TextBox)
   End;

   TD2CheckCell = Class(TD2CheckBox)
   End;

   TD2ProgressCell = Class(TD2ProgressBar)
   End;

   TD2PopupCell = Class(TD2PopupBox)
   End;

   TD2ImageCell = Class(TD2ImageControl)
   End;

   TD2Column = Class(TD2Control)
   Private
      FReadOnly: Boolean;
      Procedure SetHeader(Const Value: WideString);
   Protected
      FCellControls: Array Of TD2Control;
      FUpdateColumn: Boolean;
      FHeader: WideString;
      FSaveData: Variant;
      FDisableChange: Boolean;
      Function Grid: TD2CustomGrid;
      Procedure UpdateColumn; Virtual;
      Procedure UpdateSelected;
      Procedure ClearColumn;
      Function CreateCellControl: TD2Control; Virtual;
      Procedure DoTextChanged(Sender: TObject);
      Procedure DoCanFocused(Sender: TObject; Var ACanFocused: Boolean);
      Procedure DoEnterFocus(Sender: TObject);
      Procedure DoKeyDown(Sender: TObject; Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Function CellControlByPoint(X, Y: Single): TD2Control;
      Function CellControlByRow(Row: Integer): TD2Control;
   Published
      Property Resource;
      Property Header: WideString read FHeader write SetHeader;
      Property ReadOnly: Boolean read FReadOnly write FReadOnly Default False;
   End;

   TD2CheckColumn = Class(TD2Column)
   Private
      Procedure DoCheckChanged(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Function CreateCellControl: TD2Control; Override;
   End;

   TD2ProgressColumn = Class(TD2Column)
   Private
      FMin: Single;
      FMax: Single;
   Protected
      Function CreateCellControl: TD2Control; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property Min: Single read FMin write FMin;
      Property Max: Single read FMax write FMax;
   End;

   TD2PopupColumn = Class(TD2Column)
   Private
      FItems: TD2WideStrings;
      Procedure SetItems(Const Value: TD2WideStrings);
   Protected
      Function CreateCellControl: TD2Control; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Items: TD2WideStrings read FItems write SetItems;
   End;

   TD2ImageColumn = Class(TD2Column)
   Public
      Constructor Create(AOwner: TComponent); Override;
      Function CreateCellControl: TD2Control; Override;
   End;

   TOnGetValue = Procedure(Sender: TObject; Const Col, Row: Integer; Var Value: Variant) Of Object;
   TOnSetValue = Procedure(Sender: TObject; Const Col, Row: Integer; Const Value: Variant) Of Object;
   TOnEdititingDone = Procedure(Sender: TObject; Const Col, Row: Integer) Of Object;

   TD2CustomGrid = Class(TD2ScrollBox)
   Private
      FMouseSelecting: Boolean;
      FItemHeight: Single;
      FSelection: TD2VisualObject;
      FFocus: TD2VisualObject;
      FRowCount: Integer;
      FOnGetValue: TOnGetValue;
      FOnSetValue: TOnSetValue;
      FSelections: TList;
      FAlternatingRowBackground: Boolean;
      FOddFill: TD2Brush;
      FLineFill: TD2Brush;
      FShowHorzLines: Boolean;
      FShowVertLines: Boolean;
      FReadOnly: Boolean;
      FColumnIndex: Integer;
      FHeader: TD2Header;
      FShowHeader: Boolean;
      FShowSelectedCell: Boolean;
      FOnEdititingDone: TOnEdititingDone;
      FMultiSelect: Boolean;
      Function GetColumnCount: Integer;
      Function GetColumn(Const aIndex: Integer): TD2Column;
      Procedure SetRowCount(Const Value: Integer);
      Procedure SetRowHeight(Const Value: Single);
      Function GetVisibleRows: Integer;
      Procedure SetAlternatingRowBackground(Const Value: Boolean);
      Procedure SetShowHorzLines(Const Value: Boolean);
      Procedure SetShowVertLines(Const Value: Boolean);
      Procedure SetColumnIndex(Const Value: Integer);
      Procedure SetShowHeader(Const Value: Boolean);
      Procedure SetShowSelectedCell(Const Value: Boolean);
   Protected
      FSelected: Integer;
      FRowHeight: Single;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure ApplyStyle; Override;
      Procedure FreeStyle; Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure HScrollChange(Sender: TObject); Override;
      Procedure VScrollChange(Sender: TObject); Override;
      Function GetContentBounds: TD2Rect; Override;
      Procedure UpdateColumns; Virtual;
      Procedure UpdateHeader;
      Procedure UpdateSelection;
      Procedure Reset; Virtual;
      Procedure DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
      Procedure DoContentPaint2(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
      Function GetTopRow: Integer; Virtual;
      Function GetValue(Col, Row: Integer): Variant; Virtual;
      Procedure SetValue(Col, Row: Integer; Const Value: Variant); Virtual;
      Function IsSelected(Row: Integer): Boolean;
      Procedure SetSelected(Const Value: Integer); Virtual;
      Function CanEditAcceptKey(Key: System.Widechar): Boolean; Virtual;
      Function CanEditModify: Boolean; Virtual;
      Procedure DoRealignItem(Sender: TObject; OldIndex, NewIndex: Integer);
      Procedure DoResizeItem(Sender: TObject; Var NewSize: Single);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Function ItemClass: String; Override;
      Function ColumnByIndex(Const Idx: Integer): TD2Column;
      Function ColumnByPoint(Const X, Y: Single): TD2Column;
      Function RowByPoint(Const X, Y: Single): Integer;
      Procedure AddObject(AObject: TD2Object); Override;
      Procedure RemoveObject(AObject: TD2Object); Override;
      Property TopRow: Integer read GetTopRow;
      Property VisibleRows: Integer read GetVisibleRows;
      Property ColumnCount: Integer read GetColumnCount;
      Property ColumnIndex: Integer read FColumnIndex write SetColumnIndex;
      Property Columns[aIndex: Integer]: TD2Column read GetColumn;
      Property RowCount: Integer read FRowCount write SetRowCount;
      Property Selected: Integer read FSelected write SetSelected;
      Property OnGetValue: TOnGetValue read FOnGetValue write FOnGetValue;
      Property OnSetValue: TOnSetValue read FOnSetValue write FOnSetValue;
   Published
      Property Resource;
      Property AlternatingRowBackground: Boolean read FAlternatingRowBackground write SetAlternatingRowBackground Default False;
      Property CanFocused Default True;
      Property DisableFocusEffect;
      Property RowHeight: Single read FRowHeight write SetRowHeight;
      Property ShowSelectedCell: Boolean read FShowSelectedCell write SetShowSelectedCell Default True;
      Property ShowVertLines: Boolean read FShowVertLines write SetShowVertLines Default True;
      Property ShowHorzLines: Boolean read FShowHorzLines write SetShowHorzLines Default True;
      Property ShowHeader: Boolean read FShowHeader write SetShowHeader Default True;
      Property ReadOnly: Boolean read FReadOnly write FReadOnly Default False;
      Property TabOrder;
      Property OnEdititingDone: TOnEdititingDone read FOnEdititingDone write FOnEdititingDone;
   End;

   TD2Grid = Class(TD2CustomGrid)
   Published
      Property RowCount;
      Property OnGetValue;
      Property OnSetValue;
   End;

   TD2StringColumn = Class(TD2Column)
   Private
      FCells: Array Of WideString;
   Protected
      Procedure UpdateColumn; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   End;

   TD2StringGrid = Class(TD2CustomGrid)
   Private
      Function GetCells(ACol, ARow: Integer): WideString;
      Procedure SetCells(ACol, ARow: Integer; Const Value: WideString);
   Protected
      Function GetValue(Col, Row: Integer): Variant; Override;
      Procedure SetValue(Col, Row: Integer; Const Value: Variant); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function ItemClass: String; Override;
      Property Cells[ACol, ARow: Integer]: WideString read GetCells write SetCells;
   Published
      Property RowCount;
   End;

Type
   TD2NavButton = Class;
   TD2NavDataLink = Class;

   Ed2NavClick = Procedure(Sender: TObject; Button: TD2NavigateBtn) Of Object;

   TD2DBNavigator = Class(TD2Layout)
   Private
      FDataLink: TD2NavDataLink;
      FVisibleButtons: TD2NavButtonSet;
      FHints: TStrings;
      FDefHints: TStrings;
      ButtonWidth: Integer;
      MinBtnSize: TD2Point;
      FOnNavClick: Ed2NavClick;
      FBeforeAction: Ed2NavClick;
      FocusedButton: TD2NavigateBtn;
      FConfirmDelete: Boolean;
      FFlat: Boolean;
      FyRadius: Single;
      FxRadius: Single;
      FCornerType: TD2CornerType;
      FCorners: TD2Corners;
      Procedure BtnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
      Procedure ClickHandler(Sender: TObject);
      Function GetDataSource: TDataSource;
      Function GetHints: TStrings;
      Procedure HintsChanged(Sender: TObject);
      Procedure InitButtons;
      Procedure InitHints;
      Procedure SetDataSource(Value: TDataSource);
      Procedure SetHints(Value: TStrings);
      Procedure SetSize(Var W: Single; Var H: Single);
      Procedure SetVisible(Value: TD2NavButtonSet);
      Procedure SetCornerType(Const Value: TD2CornerType);
      Procedure SetxRadius(Const Value: Single);
      Procedure SetyRadius(Const Value: Single);
      Function IsCornersStored: Boolean;
      Procedure SetCorners(Const Value: TD2Corners);
   Protected
      Buttons: Array[TD2NavigateBtn] Of TD2NavButton;
      Procedure DataChanged;
      Procedure EditingChanged;
      Procedure ActiveChanged;
      Procedure Loaded; Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure CalcMinSize(Var W, H: Single);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure Realign; Override;
      Procedure BtnClick(Const aIndex: TD2NavigateBtn); Virtual;
   Published
      Property DataSource: TDataSource read GetDataSource write SetDataSource;
      Property VisibleButtons: TD2NavButtonSet read FVisibleButtons write SetVisible Default [nbFirst, nbPrior, nbNext, nbLast, nbInsert, nbDelete, nbEdit, nbPost, nbCancel, nbRefresh];
      Property Align;
      Property Enabled;
      Property CornerType: TD2CornerType read FCornerType write SetCornerType Default d2CornerRound;
      Property Corners: TD2Corners read FCorners write SetCorners Stored IsCornersStored;
      Property xRadius: Single read FxRadius write SetxRadius;
      Property yRadius: Single read FyRadius write SetyRadius;
      Property Hints: TStrings read GetHints write SetHints;
      Property PopupMenu;
      Property ConfirmDelete: Boolean read FConfirmDelete write FConfirmDelete Default True;
      Property ShowHint;
      Property Visible;
      Property BeforeAction: Ed2NavClick read FBeforeAction write FBeforeAction;
      Property OnClick: Ed2NavClick read FOnNavClick write FOnNavClick;
   End;

   TD2NavButton = Class(TD2CornerButton)
   Private
      FBtnIndex: TD2NavigateBtn;
      FNavStyle: TD2NavButtonStyle;
      FPath: TD2Path;
      Procedure TimerExpired(Sender: TObject);
   Protected
      Procedure ApplyStyle; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); Override;
      Property NavStyle: TD2NavButtonStyle read FNavStyle write FNavStyle;
      Property BtnIndex: TD2NavigateBtn read FBtnIndex write FBtnIndex;
   End;

   TD2NavDataLink = Class(TDataLink)
   Private
      FNavigator: TD2DBNavigator;
   Protected
      Procedure EditingChanged; Override;
      Procedure DataSetChanged; Override;
      Procedure ActiveChanged; Override;
   Public
      Constructor Create(ANav: TD2DBNavigator);
      Destructor Destroy; Override;
   End;

   TD2DBLabel = Class(TD2Label)
   Private
      FDataLink: TFieldDataLink;
      Procedure DataChange(Sender: TObject);
      Function GetDataField: String;
      Function GetDataSource: TDataSource;
      Procedure SetDataField(Const Value: String);
      Procedure SetDataSource(Const Value: TDataSource);
      Function GetFieldText: String;
   Protected
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property DataField: String read GetDataField write SetDataField;
      Property DataSource: TDataSource read GetDataSource write SetDataSource;
      Property TextAlign Default d2TextAlignNear;
   End;

   TD2DBImage = Class(TD2Image)
   Private
      FDataLink: TFieldDataLink;
      Procedure DataChange(Sender: TObject);
      Procedure UpdateData(Sender: TObject);
      Function GetDataField: String;
      Function GetDataSource: TDataSource;
      Procedure SetDataField(Const Value: String);
      Procedure SetDataSource(Const Value: TDataSource);
      Function GetFieldText: String;
   Protected
      Procedure DoBitmapChanged(Sender: TObject); Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure Paint; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property DataField: String read GetDataField write SetDataField;
      Property DataSource: TDataSource read GetDataSource write SetDataSource;
   End;

   TD2DBTextBox = Class(TD2TextBox)
   Private
      FDataLink: TFieldDataLink;
      Procedure DataChange(Sender: TObject);
      Function GetDataField: String;
      Function GetDataSource: TDataSource;
      Procedure SetDataField(Const Value: String);
      Procedure SetDataSource(Const Value: TDataSource);
      Function GetFieldText: String;
      Procedure UpdateData(Sender: TObject);
   Protected
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure Change; Override;
      Procedure EnterFocus; Override;
      Procedure KillFocus; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property DataField: String read GetDataField write SetDataField;
      Property DataSource: TDataSource read GetDataSource write SetDataSource;
   End;

   TD2DBMemo = Class(TD2Memo)
   Private
      FDataLink: TFieldDataLink;
      Procedure DataChange(Sender: TObject);
      Function GetDataField: String;
      Function GetDataSource: TDataSource;
      Procedure SetDataField(Const Value: String);
      Procedure SetDataSource(Const Value: TDataSource);
      Function GetFieldText: String;
      Procedure UpdateData(Sender: TObject);
   Protected
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure Change; Override;
      Procedure EnterFocus; Override;
      Procedure KillFocus; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property DataField: String read GetDataField write SetDataField;
      Property DataSource: TDataSource read GetDataSource write SetDataSource;
   End;

   TD2DBGrid = Class;

   TD2GridDataLink = Class(TDataLink)
   Private
      FGrid: TD2DBGrid;
      FFieldCount: Integer;
      FFieldMap: Array Of Integer;
      FModified: Boolean;
      FInUpdateData: Boolean;
      FSparseMap: Boolean;
      Function GetDefaultFields: Boolean;
      Function GetFields(I: Integer): TField;
   Protected
      Procedure ActiveChanged; Override;
      Procedure BuildAggMap;
      Procedure DataSetChanged; Override;
      Procedure DataSetScrolled(Distance: Integer); Override;
      Procedure FocusControl(Field: TFieldRef); Override;
      Procedure EditingChanged; Override;
      Function IsAggRow(Value: Integer): Boolean; Virtual;
      Procedure LayoutChanged; Override;
      Procedure RecordChanged(Field: TField); Override;
      Procedure UpdateData; Override;
      Function GetMappedIndex(ColIndex: Integer): Integer;
   Public
      Constructor Create(AGrid: TD2DBGrid);
      Destructor Destroy; Override;
      Procedure Modified;
      Procedure Reset;
      Property DefaultFields: Boolean read GetDefaultFields;
      Property FieldCount: Integer read FFieldCount;
      Property Fields[I: Integer]: TField read GetFields;
      Property SparseMap: Boolean read FSparseMap write FSparseMap;
      Property Grid: TD2DBGrid read FGrid;
   End;

   TD2DBColumn = Class(TD2Column)
   Private
      FField: TField;
      FFieldName: String;
      Procedure SetFieldName(Const Value: String);
      Function GetField: TField;
      Procedure SetField(Value: TField);
   Protected
      Procedure SetData(Value: Variant); Virtual;
      Function GetData: Variant; Virtual;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Property Field: TField read GetField write SetField;
   Published
      Property FieldName: String read FFieldName write SetFieldName;
   End;

   TD2DBCheckColumn = Class(TD2DBColumn)
   Private
   Protected
      Function CreateCellControl: TD2Control; Override;
      Procedure DoCheckChanged(Sender: TObject);
      Function GetData: Variant; Override;
   Public
   End;

   TD2DBPopupColumn = Class(TD2DBColumn)
   Private
      FItems: TD2WideStrings;
      Procedure SetItems(Const Value: TD2WideStrings);
   Protected
      Function CreateCellControl: TD2Control; Override;
      Procedure DoPopupChanged(Sender: TObject);
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
   Published
      Property Items: TD2WideStrings read FItems write SetItems;
   End;

   TD2DBImageColumn = Class(TD2DBColumn)
   Private
      FCurrent: TD2Bitmap;
   Protected
      Function CreateCellControl: TD2Control; Override;
      Procedure DoImageChanged(Sender: TObject);
      Procedure SetData(Value: Variant); Override;
      Function GetData: Variant; Override;
   Public
      Destructor Destroy; Override;
   End;

   TD2DBProgressColumn = Class(TD2DBColumn)
   Private
      FMin: Single;
      FMax: Single;
   Protected
      Function CreateCellControl: TD2Control; Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
   Published
      Property Min: Single read FMin write FMin;
      Property Max: Single read FMax write FMax;
   End;

   TD2DBGrid = Class(TD2CustomGrid)
   Private
      FDataLink: TD2GridDataLink;
      FDisableMove: Boolean;
      FEditValue: Variant;
      FNeedUpdate: Boolean;
      FFirstRecord: Integer;
      Function GetDataSource: TDataSource;
      Procedure SetDataSource(Const Value: TDataSource);
      Function GetSelectedField: TField;
      Procedure SetSelectedField(Const Value: TField);
      Procedure UpdateRowCount;
   Protected
      Function GetValue(Col, Row: Integer): Variant; Override;
      Procedure SetValue(Col, Row: Integer; Const Value: Variant); Override;
      Function CanEditAcceptKey(Key: System.Widechar): Boolean; Override;
      Function CanEditModify: Boolean; Override;
      Procedure KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState); Override;
      Procedure Reset; Override;
      Procedure Notification(AComponent: TComponent; Operation: TOperation); Override;
      Procedure Loaded; Override;
      Procedure DataSetChanged;
      Procedure DataChanged;
      Procedure EditingChanged;
      Procedure RecordChanged(Field: TField);
      Procedure UpdateData;
      Procedure LinkActive(Value: Boolean);
      Function GetContentBounds: TD2Rect; Override;
      Procedure VScrollChange(Sender: TObject); Override;
      Procedure SetSelected(Const Value: Integer); Override;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function ItemClass: String; Override;
      Property DataLink: TD2GridDataLink read FDataLink;
      Property SelectedField: TField read GetSelectedField write SetSelectedField;
   Published
      Property DataSource: TDataSource read GetDataSource write SetDataSource;
   End;

   TD2CustomBrushDialog = Class(TComponent)
   Protected
      FShowStyles: TD2BrushStyles;
      FBrush: TD2Brush;
      Procedure SetBrush(Const Value: TD2Brush); Virtual;
   Public
      Constructor Create(AOwner: TComponent); Override;
      Destructor Destroy; Override;
      Function Execute: Boolean; Virtual;
      Property Brush: TD2Brush read FBrush write SetBrush;
      Property ShowStyles: TD2BrushStyles read FShowStyles write FShowStyles;
   End;

   TD2CustomBitmapEditor = Class(TForm)
   Public
      Procedure AssignFromBitmap(B: TD2Bitmap); Virtual;
      Procedure AssignToBitmap(B: TD2Bitmap); Virtual;
   End;

   TD2CustomPathDataDesigner = Class(TForm)
   Protected
      Function GetLines: TD2WideStrings; Virtual;
   Public
      Property Lines: TD2WideStrings read GetLines;
   End;

   TD2CreateACustomBrushDialog = Function(Sender: TObject): TD2CustomBrushDialog;   // ct8888
   TD2CreateABitmapEditor = Function(Sender: TObject): TD2CustomBitmapEditor;
   TD2CreateAPathDataDesigner = Function(Sender: TObject): TD2CustomPathDataDesigner;
   TD2SelectInDesign = Procedure(AObject: TObject; AComp: TPersistent);
   TD2ShowGradientDialog = Procedure(Const Gradient: TD2Gradient);

   //=============================================================================
   //=============== GLobal Functions ============================================
   //=============================================================================

// Strings
Function d2PointToString(R: TD2Point): Ansistring;
Function d2StringToPoint(S: Ansistring): TD2Point;
Function d2RectToString(R: TD2Rect): Ansistring;
Function d2StringToRect(S: Ansistring): TD2Rect;
Function d2ColorToStr(Value: TD2Color): String;
Function d2StrToColor(Value: String): TD2Color;
Function d2FloatToStr(Value: Single): String;
Function d2StrToFloat(Value: String): Single;

// Geometry
Procedure d2SinCos(Const Theta: Single; Var Sin, Cos: Single);
Function d2RadToDeg(Const Degrees: Single): Single;
Function d2DegToRad(Const Degrees: Single): Single;
Function d2NormalizeAngle(Const angle: Single): Single;
Function d2Point(Const X, Y: Single): TD2Point;
Function d2MinPoint(P1, P2: TD2Point): TD2Point;
Function d2ScalePoint(P: TD2Point; dx, dy: Single): TD2Point;
Function d2Rect(Const ALeft, ATop, ARight, ABottom: Single): TD2Rect;
Function d2NormalizeRect(Const Pts: Array Of TD2Point): TD2Rect;
Function d2NormalizeRect2(Const ARect: TD2Rect): TD2Rect;
Function d2RectWidth(Const R: TD2Rect): Single;
Function d2RectHeight(Const R: TD2Rect): Single;
Function d2RectCenter(Var R: TD2Rect; Bounds: TD2Rect): TD2Rect;
Function d2FitRect(Var R: TD2Rect; BoundsRect: TD2Rect): Single;
Function d2IsRectEmpty(Rect: TD2Rect): Boolean;
Procedure d2OffsetRect(Var R: TD2Rect; Const Dx, Dy: Single);
Procedure d2MultiplyRect(Var R: TD2Rect; Const Dx, Dy: Single);
Procedure d2InflateRect(Var R: TD2Rect; Const Dx, Dy: Single);
Function d2IntersectRect(Const Rect1, Rect2: TD2Rect): Boolean; Overload;
Function d2IntersectRect(Var DestRect: TD2Rect; Const SrcRect1, SrcRect2: TD2Rect): Boolean; Overload;
Function d2PtInRect(Const P: TD2Point; Const Rect: TD2Rect): Boolean; Overload;
Function d2PtInRect(Const PX, PY, RLeft, RRight, RTop, RBottom: Single): Boolean; Overload;
Function d2UnionRect(Const ARect1, ARect2: TD2Rect): TD2Rect;
Function d2PointFromVector(Const v: TD2Vector): TD2Point;
Function d2MatrixMultiply(Const M1, M2: TD2Matrix): TD2Matrix;
Function d2MatrixDeterminant(Const M: TD2Matrix): Single;
Procedure d2AdjointMatrix(Var M: TD2Matrix);
Procedure d2ScaleMatrix(Var M: TD2Matrix; Const factor: Single);
Procedure d2InvertMatrix(Var M: TD2Matrix);
Function d2Vector(Const x, y: Single; Const w: Single = 1.0): TD2Vector; Overload;
Function d2Vector(Const P: TD2Point; Const w: Single = 1.0): TD2Vector; Overload;
Function d2VectorTransform(Const V: TD2Vector; Const M: TD2Matrix): TD2Vector;
Function d2CreateRotationMatrix(angle: Single): TD2Matrix;
Function d2VectorAdd(Const v1: TD2Vector; Const v2: TD2Vector): TD2Vector;
Function d2VectorSubtract(Const v1: TD2Vector; Const v2: TD2Vector): TD2Vector;
Function d2VectorNorm(Const v: TD2Vector): Single;
Function d2VectorNormalize(Const v: TD2Vector): TD2Vector;
Function d2VectorScale(Const v: TD2Vector; factor: Single): TD2Vector;
Function d2VectorLength(Const v: TD2Vector): Single;
Function d2VectorDotProduct(Const V1, V2: TD2Vector): Single;
Function d2VectorAngleCosine(Const V1, V2: TD2Vector): Single;
Function d2VectorCrossProductZ(Const V1, V2: TD2Vector): Single;
Function d2VectorCombine2(Const V1, V2: TD2Vector; Const F1, F2: Single): TD2Vector;
Function d2VectorReflect(Const V, N: TD2Vector): TD2Vector;
Function d2VectorAngle(Const V, N: TD2Vector): Single;

// Colors
Function d2AppendColor(start, stop: TD2Color): TD2Color;
Function d2SubtractColor(start, stop: TD2Color): TD2Color;
Function d2RGBtoBGR(Const C: TD2Color): TD2Color;
Function d2ColorFromVCL(Const C: TColor): TD2Color;
Function d2CorrectColor(Const C: TD2Color): TD2Color;
Function d2PremultyAlpha(Const C: TD2Color): TD2Color;
Function d2UnpremultyAlpha(Const C: TD2Color): TD2Color;
Function d2Opacity(Const C: TD2Color; Const AOpacity: Single): TD2Color;
Function d2Color(R, G, B: Byte; A: Byte = $FF): TD2Color;
Function d2HSLtoRGB(H, S, L: Single): TD2Color;
Procedure d2RGBtoHSL(RGB: TD2Color; out H, S, L: Single);
Function d2ChangeHSL(Const C: TD2Color; dH, dS, dL: Single): TD2Color;

// Animation
Function d2InterpolateSingle(Const start, stop, t: Single): Single;
Function d2InterpolateRotation(start, stop, t: Single): Single;
Function d2InterpolateColor(start, stop: TD2Color; t: Single): TD2Color;
Function d2InterpolateLinear(T, B, C, D: Double): Double;
Function d2InterpolateSine(T, B, C, D: Double; aType: TD2AnimationType): Double;
Function d2InterpolateQuint(T, B, C, D: Double; aType: TD2AnimationType): Double;
Function d2InterpolateQuart(T, B, C, D: Double; aType: TD2AnimationType): Double;
Function d2InterpolateQuad(T, B, C, D: Double; aType: TD2AnimationType): Double;
Function d2InterpolateExpo(T, B, C, D: Double; aType: TD2AnimationType): Double;
Function d2InterpolateElastic(T, B, C, D, A, P: Double; aType: TD2AnimationType): Double;
Function d2InterpolateCubic(T, B, C, D: Double; aType: TD2AnimationType): Double;
Function d2InterpolateCirc(T, B, C, D: Double; aType: TD2AnimationType): Double;
Function d2InterpolateBounce(T, B, C, D: Double; aType: TD2AnimationType): Double;
Function d2InterpolateBack(T, B, C, D, S: Double; aType: TD2AnimationType): Double;

// Help functions

Procedure d2ReverseBytes(p: Pointer; Count: Integer);
Procedure d2MoveLongword(Const Src: Pointer; Dst: Pointer; Count: Integer);
Procedure d2FillLongword(Src: Pointer; Count: Integer; Value: Longword);
Procedure d2FillAlpha(Src: Pointer; Count: Integer; Alpha: Byte);
Procedure d2FillLongwordRect(Src: Pointer; W, H, X1, Y1, X2, Y2: Integer; Value: Longword);
Function d2GetToken(Var S: Ansistring; Separators: Ansistring; Stop: Ansistring = ''): Ansistring;
Function d2WideGetToken(Var Pos: Integer; Const S: WideString; Separators: WideString; Stop: WideString = ''): WideString;
Function d2MaxFloat(A1, A2: Single): Single;
Function d2MinFloat(A1, A2: Single): Single;
Function d2MinMax(x, mi, ma: Single): Single;

// Other
Procedure Blur(Const Canvas: TD2Canvas; Const Bitmap: TD2Bitmap; Const Radius: Integer; UseAlpha: Boolean = True);
Function ComposeCaretPos(ALine, APos: Integer): TCaretPosition;

Function MessagePopup(Const ACaption, AMessage: WideString; AType: TD2MessageType; Buttons: TD2MessageButtons; Const AOwner: Id2Scene; Const Target: TD2VisualObject = nil; Const ADisableScene: Boolean = True;
   Const ABitmap: TD2Bitmap = nil; Const AStyle: TD2Resources = nil): Integer;


// Variants
Function VarIsObject(Value: Variant): Boolean;
Function ObjectToVariant(Const AObject: TObject): Variant;
Function VariantToObject(Const Value: Variant): TObject;
Function VarIsEvent(Value: Variant): Boolean;
Function EventToVariant(Const AMethod: TNotifyEvent): Variant;
Function VariantToEvent(Const Value: Variant): TNotifyEvent;

// Resources
Procedure AddResource(Const AObject: TD2Object);
Procedure RemoveResource(Const AObject: TD2Object);
Function FindResource(Const AResource: String): TD2Object;

// Scenes
Procedure AddScene(Const AScene: Id2Scene);
Procedure RemoveScene(Const AScene: Id2Scene);

// Lang
Procedure LoadLangFromFile(AFileName: String);
Procedure LoadLangFromStrings(AStr: TD2WideStrings);
Procedure ResetLang;
Procedure UpdateLang;
Procedure CollectLangStart;
Procedure CollectLangFinish;

Function CollectLangStrings: TD2WideStrings;
Function Translate(Const AText: WideString): WideString;
Function TranslateText(Const AText: WideString): WideString;

{$IFDEF ORCA2D_OS_WINDOWS}
   procedure InitGDIP;
   procedure FreeGDIP;
   procedure SetD2DDefault;
   procedure UseDirect2DCanvas;
{$ENDIF}

// ---- Objects Registration -----
Procedure RegisterD2Object(Const Category: String; Const AObject: TD2ObjectClass);
Procedure RegisterD2Objects(Const Category: String; AClasses: Array Of TD2ObjectClass);
Function CreateD2ObjectFromStream(AOwner: TComponent; Const AStream: TStream): TD2Object;
Function CreateD2ObjectFromBinStream(AOwner: TComponent; Const AStream: TStream): TD2Object;
Function LoadD2ObjectFromStream(AObject: TD2Object; Const AStream: TStream): TD2Object;

//=============================================================================
//=============== GLobal Variables ============================================
//=============================================================================

Var
   GvarD2SceneCount: Integer = 0;
   GvarD2aniThread: TD2Timer;
   GvarD2ObjectList: TStringList;
   GvarD2Designer: TD2Designer;
   GvarD2DefaultCanvasClass: TD2CanvasClass;
   GvarD2DefaultPrinterCanvasClass: TD2CanvasClass;
   GvarD2DefaultFilterClass: TD2FilterClass;
   GvarD2DefaultStyles: TD2Object;

   GvarD2GlobalDisableFocusEffect: Boolean = False;
   GvarD2CustomTranslateProc: TD2CustomTranslateProc;

Var
   VarCreateABrushDialog: TD2CreateACustomBrushDialog = nil;   // ct8888
   VarCreateABitmapEditor: TD2CreateABitmapEditor = nil;
   VarCreateAPathDataDesigner: TD2CreateAPathDataDesigner = nil;
   VarSelectInDesign: TD2SelectInDesign = nil;
   VarShowGradientDialog: TD2ShowGradientDialog = nil;

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

Implementation

{$R *.res}
{$R orca_scene2d_popup.res}

Const
   ModalResults: Array[TD2MessageButton] Of Integer = (
      mrYes, mrNo, mrOk, mrCancel, mrAbort,
      mrRetry, mrIgnore, mrAll, mrNoToAll,
      mrYesToAll, 0);

   MessageButtonNames: Array[TD2MessageButton] Of WideString = (
      'Yes', 'No', 'OK', 'Cancel',
      'Abort', 'Retry', 'Ignore',
      'All', 'NoToAll', 'YesToAll',
      'Help');

Type

   TD2HackVisual = Class(TD2VisualObject);
   TD2HackVisualObject = Class(TD2VisualObject);
   TD2HackBackground = Class(TD2Background);
   TD2HackObject = Class(TD2VisualObject);
   THackComponent = Class(TComponent);


Var
   varD2PopupList: TList;
   varD2FTarget: TD2VisualObject = nil;
   varD2SceneList: TList;
   varD2NeedResetLang: Boolean;
   varD2CollectLang, varD2Lang: TD2WideStrings;
   varD2ResourceList: TList;

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

{$IFDEF ORCA2D_OS_WINDOWS}
     {$I orca_scene2d_canvas_dgiplus.inc}
     {$IFDEF ORCA3D_USE_DIRECTX9}
     {$I orca_scene2d_canvas_directx9.inc}
     {$ENDIF}
     {$IFDEF ORCA3D_USE_DIRECTX11}
     {$I orca_scene2d_canvas_directx11.inc}
     {$ENDIF}
     {$IFDEF ORCA3D_USE_DIRECTX12}
     {$I orca_scene2d_canvas_directx12.inc}
     {$ENDIF}
{$ENDIF}

{$IFDEF ORCA2D_USE_CARBON}
     {$I orca_scene2d_canvas_carbon.inc}
{$ENDIF}
{$IFDEF ORCA2D_USE_COCOA}
     {$I orca_scene2d_canvas_cocoa.inc}
{$ENDIF}
{$IFDEF ORCA2D_USE_GTK2}
     {$I orca_scene2d_canvas_gtk2.inc}
{$ENDIF}
{$IFDEF ORCA2D_USE_GTK3}
     {$I orca_scene2d_canvas_gtk3.inc}
{$ENDIF}
{$IFDEF ORCA2D_USE_GTK4}
     {$I orca_scene2d_canvas_gtk4.inc}
{$ENDIF}
{$IFDEF ORCA2D_USE_QT4}
     {$I orca_scene2d_canvas_qt4.inc}
{$ENDIF}
{$IFDEF ORCA2D_USE_QT5}
     {$I orca_scene2d_canvas_qt5.inc}
{$ENDIF}
{$IFDEF ORCA2D_USE_QT6}
     {$I orca_scene2d_canvas_qt6.inc}
{$ENDIF}


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

Procedure CloseAllPopups;
Var
   i: Integer;
Begin
   If (varD2PopupList.Count > 0) Then
   Begin
      For i := varD2PopupList.Count - 1 Downto 0 Do
         TD2Popup(varD2PopupList[i]).ClosePopup;
   End;
End;

//-------------------------------------------------------------

Function WeekOfYear(aDate: tDateTime): Byte;
Var
   t, m, year: Word;
   xnewyear: tDateTime;
   xKW: Word;
   xwtag_ny: Word;
Begin
   DecodeDate(aDate, year, m, t);
   xnewyear := EncodeDate(year, 1, 1);
   xwtag_ny := Ord(DayofWeek(xnewyear));
   xKW := Trunc(((aDate - xnewyear + ((xwtag_ny + 1) Mod 7) - 3) / 7) + 1);
   If (xKW = 0) Then
   Begin
      xKW := 0;
   End;
   Result := xKW;
End;

Function ReadString(S: TStream): WideString;
Var
   L: Integer;
Begin
   L := 0;
   S.Read(L, SizeOf(L));
   SetLength(Result, L);
   S.Read(Pointer(Result)^, L * 2);
End;

Procedure WriteString(S: TStream; Value: WideString);
Var
   L: Integer;
Begin
   L := Length(Value);
   S.Write(L, SizeOf(L));
   S.Write(Pointer(Value)^, L * 2);
End;

Function VarIsObject(Value: Variant): Boolean;
Var
   S: String;
Begin
   S := Value;
   If (S <> '') And (Pos('d2obj', S) = 1) Then
      Result := True
   Else
      Result := False;
End;

Function ObjectToVariant(Const AObject: TObject): Variant;
Begin
   Result := 'd2obj' + IntToStr(Integer(Pointer(AObject)));
End;

Function VariantToObject(Const Value: Variant): TObject;
Var
   S: String;
Begin
   S := Value;
   If (S <> '') And (Pos('d2obj', S) = 1) Then
      Result := TObject(Pointer(StrToInt(Copy(S, 6, 10))))
   Else
      Result := nil;
End;

Function VarIsEvent(Value: Variant): Boolean;
Var
   S: String;
Begin
   If VarIsStr(Value) Then
   Begin
      S := Value;
      If (S <> '') And (Pos('d2met', S) = 1) Then
         Result := True
      Else
         Result := False;
   End Else
      Result := False;
End;

Function EventToVariant(Const AMethod: TNotifyEvent): Variant;
Begin
   Result := 'd2met' + IntToHex(Integer(TMethod(AMethod).Data), 8) + IntToHex(Integer(TMethod(AMethod).Code), 8);
End;

Function VariantToEvent(Const Value: Variant): TNotifyEvent;
Var
   S: String;
Begin
   S := Value;
   If (S <> '') And (Pos('d2met', S) = 1) Then
   Begin
      TMethod(Result).Data := Pointer(StrToInt('$' + Copy(S, 6, 8)));
      TMethod(Result).Code := Pointer(StrToInt('$' + Copy(S, 14, 8)));
   End Else Begin
      Result := nil;
   End;
End;


Procedure RegisterD2Object(Const Category: String; Const AObject: TD2ObjectClass);
Begin
   If GvarD2ObjectList = nil Then
      GvarD2ObjectList := TStringList.Create;

   GvarD2ObjectList.InsertObject(0, Category, TObject(AObject));
   GvarD2ObjectList.Sort;
End;

Procedure RegisterD2Objects(Const Category: String; AClasses: Array Of TD2ObjectClass);
Var
   I: Integer;
Begin
   For I := Low(AClasses) To High(AClasses) Do
   Begin
      RegisterD2Object(Category, AClasses[I]);
      RegisterClass(AClasses[I]);
   End;
End;

Procedure AddResource(Const AObject: TD2Object);
Begin
   If varD2ResourceList = nil Then
   Begin
      varD2ResourceList := TList.Create;
      varD2ResourceList.Capacity := 100;
   End;

   If varD2ResourceList.IndexOf(AObject) < 0 Then
      varD2ResourceList.Add(AObject);
End;

Procedure RemoveResource(Const AObject: TD2Object);
Var
   Idx: Integer;
Begin
   If varD2ResourceList <> nil Then
   Begin
      Idx := varD2ResourceList.IndexOf(AObject);
      If Idx >= 0 Then
         varD2ResourceList[Idx] := nil;
   End;
End;

Function FindResource(Const AResource: String): TD2Object;
Var
   i: Integer;
Begin
   Result := nil;
   If varD2ResourceList <> nil Then
      For i := varD2ResourceList.Count - 1 Downto 0 Do
         If (varD2ResourceList[i] <> nil) And TD2Object(varD2ResourceList[i]).Stored Then
            If CompareText(TD2Object(varD2ResourceList[i]).ResourceName, AResource) = 0 Then
            Begin
               Result := TD2Object(varD2ResourceList[i]);
               Break;
            End;
End;

Function CreateD2ObjectFromStream(AOwner: TComponent; Const AStream: TStream): TD2Object;
Var
   xReader: TReader;
   xClassName: String;
   xObjClass: TD2ObjectClass;
   xBinStream: TStream;
Begin
   Result := nil;
   Try
      xBinStream := TMemoryStream.Create;
      Try
         ObjectTextToBinary(AStream, xBinStream);
         xBinStream.Position := 0;

         xReader := TReader.Create(xBinStream, 4096);
         xReader.Driver.BeginRootComponent;
         xClassName := xReader.Driver.ReadStr;
         xObjClass := TD2ObjectClass(GetClass(xClassName));
         Result := xObjClass.Create(AOwner);
         If Result <> nil Then
         Begin
            xBinStream.Position := 0;
            Result.IntLoadFromBinStream(xBinStream);
         End;
         xReader.Free;
      Finally
         xBinStream.Free;
      End;
   Except
      Result := nil;
   End;
End;

Function CreateD2ObjectFromBinStream(AOwner: TComponent; Const AStream: TStream): TD2Object;
Var
   xPos: Longint;
   xReader: TReader;
   xClassName: String;
   xObjClass: TD2ObjectClass;
Begin
   Result := nil;
   Try
      xPos := AStream.Position;

      xReader := TReader.Create(AStream, 4096);
      xReader.Driver.BeginRootComponent;
      xClassName := xReader.Driver.ReadStr;
      xObjClass := TD2ObjectClass(GetClass(xClassName));
      Result := xObjClass.Create(AOwner);
      If Result <> nil Then
      Begin
         AStream.Position := xPos;
         Result.IntLoadFromBinStream(AStream);
      End;
      xReader.Free;
   Except
      Result := nil;
   End;
End;

Function LoadD2ObjectFromStream(AObject: TD2Object; Const AStream: TStream): TD2Object;
Var
   xReader: TReader;
   xBinStream: TStream;
Begin
   Result := nil;
   Try
      xBinStream := TMemoryStream.Create;
      Try
         ObjectTextToBinary(AStream, xBinStream);
         xBinStream.Position := 0;
         Result := AObject;
         If Result <> nil Then
         Begin
            xBinStream.Position := 0;
            Result.IntLoadFromBinStream(xBinStream);
         End;
         xReader.Free;
      Finally
         xBinStream.Free;
      End;
   Except
      Result := nil;
   End;
End;

Procedure AddScene(Const AScene: Id2Scene);
Begin
   If varD2SceneList = nil Then
      varD2SceneList := TList.Create;

   If varD2SceneList.IndexOf(Pointer(AScene)) < 0 Then
      varD2SceneList.Add(Pointer(AScene));
End;

Procedure RemoveScene(Const AScene: Id2Scene);
Begin
   varD2SceneList.Remove(Pointer(AScene));
End;

Procedure CallLoaded(Obj: TD2Object);
Var
   i: Integer;
Begin
   Obj.Loaded;
   For i := 0 To Obj.ChildrenCount - 1 Do
      CallLoaded(Obj.Children[i]);
End;

Procedure WriteValue(W: TWriter; Value: TValueType);
Var
   b: Byte;
Begin
   b := Byte(Value);
   W.Write(b, 1);
End;

Function ReadStr(R: TReader): String;
Var
   L: Byte;
Begin
   R.Read(L, SizeOf(Byte));
   System.SetString(Result, PChar(nil), L);
   R.Read(Result[1], L);
End;

Procedure WriteStr(W: TWriter; Const Value: String);
Var
   i: Integer;
   b: Byte;
Begin
   i := Length(Value);
   If i > 255 Then
      i := 255;
   b := i;
   W.Write(b, 1);
   If i > 0 Then
      W.Write(Value[1], i);
End;

Function D2GetTickCount: Single;
Var
   H, M, S, MS: Word;
Begin
   DecodeTime(time, H, M, S, MS);
   Result := ((((H * 60 * 60) + (M * 60) + S) * 1000) + MS);
End;

//------------------------------

Procedure fnCholeskyDecomposition(Const b: Array Of Single; Const Count: Integer; Var Result: Array Of Single);
Var
   Y, xM1, xM2: Array Of Single;
   i, k, D, F: Integer;
Begin
   D := 0;
   F := Count - 1;
   SetLength(xM1, Count);

   SetLength(xM2, Count - 1);
   xM1[D] := sqrt(2);
   xM2[D] := 1.0 / xM1[D];
   For k := D + 1 To F - 1 Do
   Begin
      xM1[K] := Sqrt(4 - xM2[K - 1] * xM2[K - 1]);
      xM2[K] := 1.0 / xM1[K];
   End;
   xM1[F] := Sqrt(2 - xM2[F - 1] * xM2[F - 1]);

   SetLength(Y, Count);
   Y[D] := B[D] / xM1[D];
   For i := D + 1 To F Do
      Y[i] := (B[i] - Y[i - 1] * xM2[i - 1]) / xM1[i];

   Result[F] := Y[F] / xM1[F];
   For i := F - 1 Downto D Do
      Result[i] := (Y[i] - Result[i + 1] * xM2[i]) / xM1[i];
End;

Procedure fnCalcHermiteFactors(Const Values: Array Of Single; Var Spline: TD2SplineMatrix);
Var
   a, b, c, d: Single;
   i, n: Integer;
   xM1, xM2: Array Of Single;
Begin
   If (Length(Values) > 0) Then
   Begin
      n := Length(Values) - 1;

      SetLength(xM1, Length(Values));
      xM1[0] := 3 * (Values[1] - Values[0]);
      xM1[n] := 3 * (Values[n] - Values[n - 1]);
      For i := 1 To n - 1 Do
         xM1[I] := 3 * (Values[I + 1] - Values[I - 1]);

      SetLength(xM2, Length(Values));
      fnCholeskyDecomposition(xM1, Length(Values), xM2);

      SetLength(Spline, n);
      For i := 0 To n - 1 Do
      Begin
         a := Values[I];
         b := xM2[I];
         c := 3 * (Values[I + 1] - Values[I]) - 2 * xM2[I] - xM2[I + 1];
         d := -2 * (Values[I + 1] - Values[I]) + xM2[I] + xM2[I + 1];

         Spline[I][3] := a + I * (I * (c - I * d) - b);
         Spline[I][2] := b + I * (3 * I * d - 2 * c);
         Spline[I][1] := c - 3 * I * d;
         Spline[I][0] := d;
      End;
   End;
End;

Function HermitInterpolate(Const Spline: TD2SplineMatrix; Const x: Single; Const Count: Integer): Single;
Var
   i: Integer;
Begin
   If Length(Spline) > 0 Then
   Begin
      If x <= 0 Then
         i := 0
      Else
      If x > Count - 1 Then
         i := Count - 1
      Else
         i := trunc(x);
      If i = (Count - 1) Then
         Dec(i);
      Result := ((Spline[i][0] * x + Spline[i][1]) * x + Spline[i][2]) * x + Spline[i][3];
   End Else
      Result := 0;
End;


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

{$IFDEF ORCA2D_OS_WINDOWS}
  type

    PBlendFunction = ^TBlendFunction;
    _BLENDFUNCTION = packed record
      BlendOp: BYTE;
      BlendFlags: BYTE;
      SourceConstantAlpha: BYTE;
      AlphaFormat: BYTE;
    end;
    TBlendFunction = _BLENDFUNCTION;
    BLENDFUNCTION = _BLENDFUNCTION;

  const

    WS_EX_LAYERED = $00080000;
    LWA_COLORKEY = $00000001;
    LWA_ALPHA = $00000002;
    ULW_COLORKEY = $00000001;
    ULW_ALPHA = $00000002;
    ULW_OPAQUE = $00000004;

  var
    VarUser32Lib: THandle;

    VarUpdateLayeredWindow: function (hWnd: HWND; hdcDst: HDC; pptDst: PPOINT;
                                   psize: PSIZE; hdcSrc: HDC; pptSrc: PPOINT;
                                   crKey: COLORREF; pblend: PBlendFunction; dwFlags: DWORD): BOOL; stdcall;

    VarSetLayeredWindowAttributes: function (hwnd: HWND; crKey: COLORREF; bAlpha:BYTE; dwFlags:DWORD): BOOL; stdcall;

    VarPrintWindow: function(hwnd: HWND; hdcBlt: HDC; nFlags: DWORD): BOOL; stdcall;



  function WndCallback(Ahwnd: HWND; uMsg: UINT; wParam: WParam; lParam: LParam): LRESULT; stdcall;
    var
      xWin: TWinControl;
    begin
      xWin := FindControl(Ahwnd);
      if (xWin is TD2CustomScene) then
      begin
        if not (csDestroying in xWin.ComponentState) then
        begin
          if (uMsg = WM_PAINT) or (uMsg = WM_ADDUPDATERECT) then
          begin
            Result := xWin.Perform(uMsg, wParam, lParam);
            exit;
          end;
        end;
        result := CallWindowProcW(@TD2CustomScene(xWin).FPrevWndProc, Ahwnd, uMsg, WParam, LParam);
        Exit;
      end;

      result := CallWindowProcW(@DefWindowProcW, Ahwnd, uMsg, WParam, LParam);
    end;

procedure UseDirect2DCanvas;
begin
  SetD2DDefault;
end;
{$ENDIF}

{$IFDEF ORCA2D_USE_CARBON}
  function WndEventHandler(inHandlerCallRef: EventHandlerCallRef; inEvent: EventRef; inUserData: Pointer): OSStatus; stdcall;
  var
    myContext: CGContextRef;
    myRect: CGRect;
    rgnCode: WindowRegionCode;
    rgn: RgnHandle;
    bool: longbool;
  begin
    Result := CallNextEventHandler(inHandlerCallRef, inEvent);
    Result := eventNotHandledErr;
    if GetEventClass(inEvent) = kEventClassControl then
    begin
      case GetEventKind(inEvent) of
        kEventControlDraw:
          begin
            GetEventParameter (inEvent,
                              kEventParamCGContextRef,
                              typeCGContextRef,
                              nil,
                              sizeof (CGContextRef),
                              nil,
                              @myContext);
            if myContext <> nil then
            begin
              myRect := GetMacRectFromRect(d2Rect(0, 0, TD2CustomScene(inUserData).Parent.Width, TD2CustomScene(inUserData).Parent.Height));
              CGContextClearRect(myContext, myRect);
            end;
            Result := noErr;
          end;
      end;
    end;
    if GetEventClass(inEvent) = kEventClassWindow then
    begin
      case GetEventKind(inEvent) of
        kEventWindowGetRegion:
          begin
            TD2CustomScene(inUserData).Invalidate;
            Result := noErr;
          end;
      end;
    end;
  end;

  type
    byte8array = array [1..8] of byte;

  function RegionToRectsCallback(message: UInt16; rgn: RgnHandle; const rect_: byte8array; data: Pointer): OSStatus; cdecl;
  var
    R: TD2Rect;
  begin
    if (message = kQDRegionToRectsMsgParse) then
    begin
      {$ifdef FPC_BIG_ENDIAN}
      R := d2Rect(rect_[4] or (rect_[3] shl 8), rect_[2] or (rect_[1] shl 8), rect_[8] or (rect_[7] shl 8), rect_[6] or (rect_[5] shl 8));
      {$else}
      R := d2Rect(rect_[3] or (rect_[4] shl 8), rect_[1] or (rect_[2] shl 8), rect_[7] or (rect_[8] shl 8), rect_[5] or (rect_[6] shl 8));
      {$endif}
      SetLength(TD2CustomScene(data).FUpdateRects, Length(TD2CustomScene(data).FUpdateRects) + 1);
      TD2CustomScene(data).FUpdateRects[High(TD2CustomScene(data).FUpdateRects)] := R;
    end;
    Result := noErr;
  end;

  function CreateFileURLFromPasteboard(inPasteboard: PasteboardRef): TD2DragObject;
  var
    inIndex: CFIndex;
    inCount: ItemCount;
  	item: PasteboardItemID;
  	fileURL: CFURLRef;
    fileURLData: CFDataRef;
  	info: LSItemInfoRecord;
  	uti: CFStringRef;
  begin
    Fillchar(Result, sizeOf(Result), 0);

  	if PasteboardGetItemCount(inPasteboard, inCount) <> noErr then Exit;
    SetLength(Result.Files, inCount);
    for inIndex := 1 to inCount do
    begin
    	if PasteboardGetItemIdentifier(inPasteboard, inIndex, item) <> noErr then Exit;
    	if PasteboardCopyItemFlavorData(inPasteboard, item, kUTTypeFileURL, fileURLData) <> noErr then Exit;


    	fileURL := CFURLCreateAbsoluteURLWithBytes( kCFAllocatorDefault, CFDataGetBytePtr( fileURLData ), CFDataGetLength( fileURLData ), kCFStringEncodingMacRoman, nil, true);
      if fileURL <> nil then
      begin
        uti := CFURLCopyFileSystemPath(fileURL, kCFURLPOSIXPathStyle);
        Result.Files[inIndex - 1] := CFStringToStr(uti);
      	CFRelease(uti);
    		CFRelease(fileURL);
      end;
      CFRelease(fileURLData);

      if inIndex = 1 then
        Result.Data := Result.Files[inIndex - 1];
    end;
  end;

  function CtrlEventHandler(inHandlerCallRef: EventHandlerCallRef;
                            inEvent: EventRef;
                            inUserData: Pointer): OSStatus; stdcall;
  var
    myContext: CGContextRef;
    myRect: CGRect;
    rgn: RgnHandle;
    bool: longbool;
    proc: RegionToRectsUPP;
    err: OSStatus;
    Part: ControlPartCode;
    drag: DragRef;
    pasteboard: PasteboardRef;
    str: string;
    mouseP: MacOSAll.Point;
    P: TD2Point;
    NewTarget: TD2VisualObject;
    Data: TD2DragObject;
    Accept:boolean;
  begin
    Result := CallNextEventHandler(inHandlerCallRef, inEvent);
    Result := eventNotHandledErr;
    if GetEventClass(inEvent) = kEventClassControl then
    begin
      case GetEventKind(inEvent) of
        kEventControlDraw:
          begin
            GetEventParameter (inEvent, kEventParamCGContextRef, typeCGContextRef, nil, sizeof (CGContextRef), nil, @myContext);
            GetEventParameter (inEvent, kEventParamRgnHandle, typeQDRgnHandle, nil, sizeof(rgn), nil, @rgn);
            if rgn <> nil then
            begin
              proc := NewRegionToRectsUPP(@RegionToRectsCallback);

              SetLength(TD2CustomScene(inUserData).FUpdateRects, 0);
              err := QDRegionToRects(rgn, kQDParseRegionFromBottomRight, proc, inUserData);
              DisposeRegionToRectsUPP(proc);
            end;

            if TD2CustomScene(inUserData).Canvas.FBuffered then
              TD2CustomScene(inUserData).Canvas.Parent := THandle(myContext)
            else
              TD2CustomScene(inUserData).Canvas.Handle := THandle(myContext);

            TD2CustomScene(inUserData).Draw;
            if TD2CustomScene(inUserData).Canvas.FBuffered then
              TD2CustomScene(inUserData).Canvas.Parent := 0
            else
              TD2CustomScene(inUserData).Canvas.Handle := 0;
            Result := noErr;
          end;
        kEventControlDragEnter:
          begin
             bool := true;
             SetEventParameter(inEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof(bool), @bool);
             Result := noErr;
          end;
        kEventControlDragWithin:
          begin
            GetEventParameter(inEvent, kEventParamDragRef, typeDragRef, nil, sizeof(DragRef), nil, @drag);
            if drag <> nil then
            begin
              GetDragPasteboard(drag, pasteboard);
              if pasteboard <> nil then
              begin
                if TD2CustomScene(inUserData).Root = nil then Exit;

                Data := CreateFileURLFromPasteboard(pasteboard);

                GetDragMouse(drag, mouseP, mouseP);
                with TD2CustomScene(inUserData).ScreenToClient(Point(mouseP.h, mouseP.v)) do
                  P := d2Point(X, Y);
                NewTarget := TD2CustomScene(inUserData).Root.Visual.FindTarget(P, Data);
              end;
            end;
            Result := noErr;
          end;
        kEventControlDragLeave:
          begin

          end;
        kEventControlDragReceive:
          begin
            GetEventParameter(inEvent, kEventParamDragRef, typeDragRef, nil, sizeof(DragRef), nil, @drag);
            if drag <> nil then
            begin
              GetDragPasteboard(drag, pasteboard);
              if pasteboard <> nil then
              begin
                if TD2CustomScene(inUserData).Root = nil then Exit;

                Data := CreateFileURLFromPasteboard(pasteboard);

              end;
            end;

            Result := noErr;
          end;
      end;
    end;
  end;

  var
    EventKinds: array [0..20] of EventTypeSpec;
    WndEventHandlerUPP: EventHandlerUPP;
{$ENDIF}

{$ifdef ORCA2D_OS_WINDOWS}
  procedure GetLanguageIDs(var Lang, FallbackLang: string);
  var
    Buffer: array[1..4] of {$ifdef Wince}WideChar{$else}char{$endif};
    Country: string;
    UserLCID: LCID;
  begin

    Lang := '';
    FallbackLang:='';
    UserLCID := GetUserDefaultLCID;

    if GetLocaleInfo(UserLCID, LOCALE_SABBREVLANGNAME, @Buffer[1], 4)<>0 then
      FallbackLang := lowercase(copy(Buffer,1,2));

    if GetLocaleInfo(UserLCID, LOCALE_SABBREVCTRYNAME, @Buffer[1], 4)<>0 then begin
      Country := copy(Buffer,1,2);

      if (Buffer='PRT') then Country:='PT';

      Lang := FallbackLang+'_'+Country;
    end;
  end;

{$else}

Procedure GetLanguageIDs(Var Lang, FallbackLang: String);
Begin
   lang := GetEnvironmentVariable('LC_ALL');

   If Length(lang) = 0 Then
   Begin
      lang := GetEnvironmentVariable('LC_MESSAGES');
      If Length(lang) = 0 Then
      Begin
         lang := GetEnvironmentVariable('LANG');
         If Length(lang) = 0 Then
            exit;
      End;
   End;

   FallbackLang := Copy(lang, 1, 2);
End;

{$endif}


// ======================TD2ImageList ====================================================

Constructor TD2ImageList.Create(AOwner: TComponent);
Begin
   Inherited;
   FImages := TList.Create;
   Width := 32;
   Height := 32;
End;

Destructor TD2ImageList.Destroy;
Begin
   Clear;
   FImages.Free;
   Inherited;
End;

Procedure TD2ImageList.Clear;
Var
   i: Integer;
Begin
   Inherited Clear;

   For i := 0 To FImages.Count - 1 Do
      TD2Bitmap(FImages[i]).Free;
   FImages.Clear;
End;

Function TD2ImageList.Add(Bitmap: TD2Bitmap): Integer;
Var
   B: TD2Bitmap;
   R: TD2Rect;
Begin
   B := TD2Bitmap.Create(Width, Height);
   R := d2Rect(0, 0, Bitmap.Width, Bitmap.Height);
   d2FitRect(R, d2Rect(0, 0, Width, Height));
   B.Canvas.DrawBitmap(Bitmap, d2Rect(0, 0, Bitmap.Width, Bitmap.Height), R, 1);

   Result := FImages.Add(B);
   UpdateList;
End;

Procedure TD2ImageList.ReadImage(Stream: TStream);
Var
   i: Integer;
   S, C: Cardinal;
   xMS: TMemoryStream;
   B: TD2Bitmap;
Begin
   Stream.Read(C, 4);
   For i := 0 To C - 1 Do
   Begin
      xMS := TMemoryStream.Create;

      Stream.Read(S, 4);
      xMS.SetSize(S);
      Stream.Read(xMS.Memory^, S);

      B := TD2Bitmap.CreateFromStream(xMS);
      FImages.Add(B);

      xMS.Free;
   End;
   UpdateList;
End;

Procedure TD2ImageList.WriteImage(Stream: TStream);
Var
   i: Integer;
   S: Cardinal;
   xMS: TMemoryStream;
Begin
   S := FImages.Count;
   Stream.Write(S, 4);
   For i := 0 To FImages.Count - 1 Do
   Begin
      xMS := TMemoryStream.Create;
      TD2Bitmap(FImages[i]).SaveToStream(xMS);

      S := xMS.Size;
      Stream.Write(S, 4);
      Stream.Write(xMS.Memory^, xMS.Size);

      xMS.Free;
   End;
End;

Procedure TD2ImageList.DefineProperties(Filer: TFiler);
Begin
   Inherited DefineProperties(Filer);
   Filer.DefineBinaryProperty('Images', ReadImage, WriteImage, FImages.Count > 0);
End;

Function TD2ImageList.GetCount: Integer;
Begin
   Result := FImages.Count;
End;

Function TD2ImageList.GetBitmap(Index: Integer): TD2Bitmap;
Begin
   If (Index >= 0) And (Index < Count) Then
      Result := TD2Bitmap(FImages[Index])
   Else
      Result := nil;
End;

Procedure TD2ImageList.UpdateList;
Var
   i: Integer;
   B: TBitmap;
Begin
   If Not (csDesigning In ComponentState) Then
      Exit;

   Inherited Clear;

   For i := 0 To FImages.Count - 1 Do
   Begin
      If (Images[i].Width <> Width) Or (Images[i].Height <> Height) Then
         Continue;

      B := TBitmap.Create;
      B.Assign(Images[i]);
      Inherited AddMasked(B, 0);
      B.Free;
   End;
End;

Procedure TD2ImageList.ReadData(Stream: TStream);
Begin
   //....
End;

Procedure TD2ImageList.WriteData(Stream: TStream);
Begin
   //....
End;

//=============== TD2AniThread ===================================

Constructor TD2AniThread.Create(AOwner: TComponent);
Begin
   Inherited;
   Interval := Trunc(1000 / 30);
   OnTimer := DoSyncTimer;

   FAniList := TList.Create;
   FStartTime := D2GetTickCount / 1000;
End;

Destructor TD2AniThread.Destroy;
Begin
   FAniList.Free;
   Inherited;
End;

Procedure TD2AniThread.DoSyncTimer(Sender: TObject);
Begin
   OneStep;
End;

Procedure TD2AniThread.OneStep;
Var
   i: Integer;
   xNTime: Single;
Begin
   xNTime := (D2GetTickCount / 1000) - FStartTime;
   If xNTime <= FTime Then
      Exit;
   FDeltaTime := xNTime - FTime;
   FTime := xNTime;
   If FAniList.Count > 0 Then
   Begin
      i := FAniList.Count - 1;
      While i >= 0 Do
      Begin
         If TD2Animation(FAniList[i]).FRunning Then
         Begin
            If (TD2Animation(FAniList[i]).BindingName <> '') And (CompareText(TD2Animation(FAniList[i]).BindingName, 'caret') = 0) Then
            Begin
               TD2Animation(FAniList[i]).Tag := TD2Animation(FAniList[i]).Tag + 1;
               If TD2Animation(FAniList[i]).Tag Mod 3 = 0 Then
               Begin
                  TD2Animation(FAniList[i]).ProcessTick(FTime, FDeltaTime);
               End;
            End Else Begin
               TD2Animation(FAniList[i]).ProcessTick(FTime, FDeltaTime);
            End;
         End;
         Dec(i);
         If i >= FAniList.Count Then
            i := FAniList.Count - 1;
      End;
   End;
End;

//================== TD2Animation ==================================

Constructor TD2Animation.Create(AOwner: TComponent);
Begin
   Inherited;
   FEnabled := False;
   Duration := 0.2;
End;

Destructor TD2Animation.Destroy;
Begin
   If GvarD2aniThread <> nil Then
   Begin
      TD2AniThread(GvarD2aniThread).FAniList.Remove(Self);
   End;
   Inherited;
End;

Procedure TD2Animation.Loaded;
Begin
   Inherited;
   If Not (Assigned(FScene) And (FScene.GetDesignTime)) And Enabled Then
      Start;
End;

Procedure TD2Animation.DoFinish;
Begin
   If (Parent <> nil) And (Parent.IsVisual) And (FHideOnFinish) Then
      TD2VisualObject(Parent).Visible := False;
   Finish;
End;

Procedure TD2Animation.Finish;
Begin
   If Assigned(FOnFinish) Then
      FOnFinish(Self);
End;

Procedure TD2Animation.SetEnabled(Const Value: Boolean);
Begin
   If FEnabled <> Value Then
   Begin
      FEnabled := Value;
      If Not (Assigned(Scene) And Scene.GetDesignTime) And Not (csLoading In ComponentState) Then
      Begin
         If FEnabled Then
            Start
         Else
            Stop;
      End;
   End;
End;

Function TD2Animation.NormalizedTime: Single;
Begin
   If (FDuration > 0) And (FDelayTime <= 0) Then
   Begin
      Case FInterpolation Of
         d2InterpolationSinusoidal: Result := d2InterpolateSine(FTime, 0, 1, FDuration, FAnimationType);
         d2InterpolationLinear: Result := d2InterpolateLinear(FTime, 0, 1, FDuration);
         d2InterpolationCubic: Result := d2InterpolateCubic(FTime, 0, 1, FDuration, FAnimationType);
         d2InterpolationQuartic: Result := d2InterpolateQuart(FTime, 0, 1, FDuration, FAnimationType);
         d2InterpolationQuintic: Result := d2InterpolateQuint(FTime, 0, 1, FDuration, FAnimationType);
         d2InterpolationBack: Result := d2InterpolateBack(FTime, 0, 1, FDuration, 0, FAnimationType);
         d2InterpolationExponential: Result := d2InterpolateExpo(FTime, 0, 1, FDuration, FAnimationType);
         d2InterpolationCircular: Result := d2InterpolateCirc(FTime, 0, 1, FDuration, FAnimationType);
         d2InterpolationElastic: Result := d2InterpolateElastic(FTime, 0, 1, FDuration, 0, 0, FAnimationType);
         d2InterpolationBounce: Result := d2InterpolateBounce(FTime, 0, 1, FDuration, FAnimationType);
         d2InterpolationQuadratic: Result := d2InterpolateQuad(FTime, 0, 1, FDuration, FAnimationType);
      End;
   End Else Begin
      Result := 0;
   End;
End;

Procedure TD2Animation.ProcessAnimation;
Begin
End;

Procedure TD2Animation.ProcessTick(time, deltaTime: Single);
Begin
   Inherited;
   If Assigned(FScene) And (FScene.GetDesignTime) Then
      Exit;
   If csDestroying In ComponentState Then
      Exit;

   If (Parent <> nil) And (Parent.IsVisual) And (Not TD2VisualObject(Parent).Visible) Then
      Stop;

   If Not FRunning Then
      Exit;
   If FPause Then
      Exit;

   If (FDelay > 0) And (FDelayTime <> 0) Then
   Begin
      If FDelayTime > 0 Then
      Begin
         FDelayTime := FDelayTime - deltaTime;
         If FDelayTime <= 0 Then
         Begin
            Start;
            FDelayTime := 0;
         End;
      End;
      Exit;
   End;

   If FInverse Then
      FTime := FTime - deltaTime
   Else
      FTime := FTime + deltaTime;

   If FTime >= FDuration Then
   Begin
      FTime := FDuration;

      If FLoop Then
      Begin
         If FAutoReverse Then
         Begin
            FInverse := True;
            FTime := FDuration;
         End Else Begin
            FTime := 0;
         End;
      End Else Begin
         FRunning := False;
      End;
   End Else
   If FTime <= 0 Then
   Begin
      FTime := 0;
      If FLoop Then
      Begin
         If FAutoReverse Then
         Begin
            FInverse := False;
            FTime := 0;
         End Else Begin
            FTime := FDuration;
         End;
      End Else Begin
         FRunning := False;
      End;
   End;

   ProcessAnimation;
   If Assigned(FOnProcess) Then
      FOnProcess(Self);

   If (FScene <> nil) Then
      If Not FRunning Then
      Begin
         If GvarD2aniThread <> nil Then
            TD2AniThread(GvarD2aniThread).FAniList.Remove(Self);

         DoFinish;
      End;
End;

Procedure TD2Animation.Start;
Begin
   If (Parent <> nil) And (Parent.IsVisual) And (Not TD2VisualObject(Parent).Visible) Then
      Exit;

   If (Abs(FDuration) < 0.001) Or (FScene = nil) Or (Assigned(FScene) And (FScene.GetDesignTime)) Then
   Begin
      FDelayTime := 0;

      If FInverse Then
      Begin
         FTime := 0;
         FDuration := 1;
      End Else Begin
         FTime := 1;
         FDuration := 1;
      End;

      FRunning := True;
      ProcessAnimation;
      FRunning := False;
      FTime := 0;
      FDuration := 0.00001;
      DoFinish;

      FEnabled := False;
   End Else Begin
      FDelayTime := FDelay;
      FRunning := True;

      If FInverse Then
         FTime := FDuration
      Else
         FTime := 0;

      If FDelay = 0 Then
         ProcessAnimation;

      If (FScene <> nil) Then
      Begin
         If GvarD2aniThread = nil Then
            GvarD2aniThread := TD2AniThread.Create(nil);

         If TD2AniThread(GvarD2aniThread).FAniList.IndexOf(Self) < 0 Then
            TD2AniThread(GvarD2aniThread).FAniList.Add(Self);
      End;
      FEnabled := True;
   End;
End;

Procedure TD2Animation.Stop;
Begin
   If Not FRunning Then
      Exit;

   If GvarD2aniThread <> nil Then
   Begin
      TD2AniThread(GvarD2aniThread).FAniList.Remove(Self);
   End;

   If FInverse Then
      FTime := 0
   Else
      FTime := FDuration;

   ProcessAnimation;
   FRunning := False;
   FEnabled := False;
   DoFinish;
End;

Procedure TD2Animation.StopAtCurrent;
Begin
   If Not FRunning Then
      Exit;

   If GvarD2aniThread <> nil Then
   Begin
      TD2AniThread(GvarD2aniThread).FAniList.Remove(Self);
   End;

   If FInverse Then
      FTime := 0
   Else
      FTime := FDuration;

   FRunning := False;
   FEnabled := False;
   If Assigned(FOnFinish) Then
      FOnFinish(Self);
End;

Procedure TD2Animation.StartTrigger(AInstance: TD2Object; ATrigger: String);
Var
   StartValue: Boolean;
   Line, Setter, Prop, Value: Ansistring;
Begin
   If AInstance = nil Then
      Exit;
   If (FTriggerInverse <> '') And (Pos(LowerCase(ATrigger), LowerCase(FTriggerInverse)) > 0) Then
   Begin
      Line := FTriggerInverse;
      Setter := d2GetToken(Line, ';');
      StartValue := False;
      While Setter <> '' Do
      Begin
         Prop := d2GetToken(Setter, '=');
         Value := Setter;
         If GetPropInfo(AInstance, Prop, [tkBool]) <> nil Then
         Begin
            StartValue := False;
            If (CompareText(Value, 'true') = 0) And (GetOrdProp(AInstance, Prop) > 0) Then
               StartValue := True;
            If (CompareText(Value, 'false') = 0) And (GetOrdProp(AInstance, Prop) = 0) Then
               StartValue := True;

            If Not StartValue Then
               Break;
         End;
         Setter := d2GetToken(Line, ';');
      End;
      If StartValue Then
      Begin
         Inverse := True;
         Start;
         Exit;
      End;
   End;
   If (FTrigger <> '') And (Pos(LowerCase(ATrigger), LowerCase(FTrigger)) > 0) Then
   Begin
      Line := FTrigger;
      Setter := d2GetToken(Line, ';');
      StartValue := False;
      While Setter <> '' Do
      Begin
         Prop := d2GetToken(Setter, '=');
         Value := Setter;
         If GetPropInfo(AInstance, Prop, [tkBool]) <> nil Then
         Begin
            StartValue := False;
            If (CompareText(Value, 'true') = 0) And (GetOrdProp(AInstance, Prop) > 0) Then
               StartValue := True;
            If (CompareText(Value, 'false') = 0) And (GetOrdProp(AInstance, Prop) = 0) Then
               StartValue := True;
            If Not StartValue Then
               Exit;
         End;
         Setter := d2GetToken(Line, ';');
      End;
      If StartValue Then
      Begin
         If FTriggerInverse <> '' Then
            Inverse := False;
         Start;
      End;
   End;
End;


//================== TD2ColorAnimation =======================================

Constructor TD2ColorAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   Duration := 0.2;
   FStartColor := $FFFFFFFF;
   FStartColor := $FFFFFFFF;
End;

Destructor TD2ColorAnimation.Destroy;
Begin
   Inherited;
End;

Procedure TD2ColorAnimation.Start;
Var
   Persistent: String;
Begin
   If (Parent <> nil) And (FPropertyName <> '') Then
   Begin
      If FInstance = nil Then
      Begin
         FInstance := Parent;
         FPath := FPropertyName;
         While Pos('.', FPath) > 0 Do
         Begin
            Persistent := d2GetToken(FPath, '.');
            If GetPropInfo(FInstance.ClassInfo, Persistent, [tkClass]) <> nil Then
               FInstance := GetObjectProp(FInstance, Persistent);
         End;
      End;
   End;
   If (FInstance <> nil) And StartFromCurrent Then
   Begin
      If GetPropInfo(FInstance.ClassInfo, FPath, [tkString, tkLString, tkWString, tkAString, tkUString]) <> nil Then
         StartValue := GetStrProp(FInstance, FPath);

      If GetPropInfo(FInstance.ClassInfo, FPath, [tkInteger]) <> nil Then
         StartValue := d2ColorToStr(GetOrdProp(FInstance, FPath));
   End;
   Inherited;
End;

Procedure TD2ColorAnimation.ProcessAnimation;
Begin
   If FInstance <> nil Then
   Begin
      If GetPropInfo(FInstance.ClassInfo, FPath, [tkString, tkLString, tkWString, tkAString, tkUString]) <> nil Then
         SetStrProp(FInstance, FPath, d2ColorToStr(d2InterpolateColor(FStartColor, FStopColor, NormalizedTime)));

      If GetPropInfo(FInstance.ClassInfo, FPath, [tkInteger]) <> nil Then
         SetOrdProp(FInstance, FPath, d2InterpolateColor(FStartColor, FStopColor, NormalizedTime));
   End;
End;

Function TD2ColorAnimation.GetStartColor: String;
Begin
   Result := d2ColorToStr(FStartColor);
End;

Procedure TD2ColorAnimation.SetStartColor(Const Value: String);
Begin
   FStartColor := d2StrToColor(Value);
End;

Function TD2ColorAnimation.GetStopColor: String;
Begin
   Result := d2ColorToStr(FStopColor);
End;

Procedure TD2ColorAnimation.SetStopColor(Const Value: String);
Begin
   FStopColor := d2StrToColor(Value);
End;

//=============== TD2Animation =============================

Constructor TD2GradientAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   Duration := 0.2;
   FStartGradient := TD2Gradient.Create;
   FStopGradient := TD2Gradient.Create;
End;

Destructor TD2GradientAnimation.Destroy;
Begin
   FStartGradient.Free;
   FStopGradient.Free;
   Inherited;
End;

Procedure TD2GradientAnimation.Start;
Var
   Persistent: String;
Begin
   If (Parent <> nil) And (FPropertyName <> '') Then
   Begin
      If FInstance = nil Then
      Begin
         FInstance := Parent;
         FPath := FPropertyName;
         While Pos('.', FPath) > 0 Do
         Begin
            Persistent := d2GetToken(FPath, '.');
            If GetPropInfo(FInstance.ClassInfo, Persistent, [tkClass]) <> nil Then
               FInstance := GetObjectProp(FInstance, Persistent);
         End;
      End;
   End;
   If (FInstance <> nil) And StartFromCurrent Then
   Begin
      If GetPropInfo(FInstance.ClassInfo, FPath, [tkClass]) <> nil Then
      Begin
         StartValue := TD2Gradient(GetObjectProp(FInstance, FPath, TD2Gradient));
      End;
   End;
   Inherited;
End;

Procedure TD2GradientAnimation.ProcessAnimation;
Var
   i: Integer;
Begin
   If FInstance <> nil Then
   Begin
      If GetPropInfo(FInstance.ClassInfo, FPath, [tkClass]) <> nil Then
      Begin
         With TD2Gradient(GetObjectProp(FInstance, FPath, TD2Gradient)) Do
         Begin
            For i := 0 To Points.Count - 1 Do
            Begin
               If (i < FStopGradient.Points.Count) Or (i < FStartGradient.Points.Count) Then
                  Points[i].Color := d2ColorToStr(d2InterpolateColor(d2StrToColor(FStartGradient.Points[i].Color), d2StrToColor(FStopGradient.Points[i].Color), NormalizedTime));
            End;
            Change;
         End;
      End;
   End;
End;

Procedure TD2GradientAnimation.SetStartGradient(Const Value: TD2Gradient);
Begin
   FStartGradient.Assign(Value);
End;

Procedure TD2GradientAnimation.SetStopGradient(Const Value: TD2Gradient);
Begin
   FStopGradient.Assign(Value);
End;

//===================== TD2Animation ==============================

Constructor TD2FloatAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   Duration := 0.2;
   FStartFloat := 0;
   FStopFloat := 0;
End;

Destructor TD2FloatAnimation.Destroy;
Begin
   Inherited;
End;

Procedure TD2FloatAnimation.Start;
Var
   Persistent: String;
Begin
   If (Parent <> nil) And (FPropertyName <> '') Then
   Begin
      If FInstance = nil Then
      Begin
         FInstance := Parent;
         FPath := FPropertyName;
         While Pos('.', FPath) > 0 Do
         Begin
            Persistent := d2GetToken(FPath, '.');
            If GetPropInfo(FInstance.ClassInfo, Persistent, [tkClass]) <> nil Then
               FInstance := GetObjectProp(FInstance, Persistent);
         End;
      End;
   End;
   If (FInstance <> nil) And StartFromCurrent Then
   Begin
      If GetPropInfo(FInstance.ClassInfo, FPath, [tkFloat]) <> nil Then
      Begin
         StartValue := GetFloatProp(FInstance, FPath);
      End;
   End;
   Inherited;
End;

Procedure TD2FloatAnimation.Stop;
Begin
   Inherited;
   FInstance := nil;
End;

Procedure TD2FloatAnimation.ProcessAnimation;
Begin
   If FInstance <> nil Then
   Begin
      If GetPropInfo(FInstance.ClassInfo, FPath, [tkFloat]) <> nil Then
      Begin
         SetFloatProp(FInstance, FPath, d2InterpolateSingle(FStartFloat, FStopFloat, NormalizedTime));
      End;
   End;
End;

//===================== TD2Animation =================================

Constructor TD2RectAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   Duration := 0.2;
   FStartRect := TD2Bounds.Create(d2Rect(0, 0, 0, 0));
   FStopRect := TD2Bounds.Create(d2Rect(0, 0, 0, 0));
   FCurrent := TD2Bounds.Create(d2Rect(0, 0, 0, 0));
End;

Destructor TD2RectAnimation.Destroy;
Begin
   FCurrent.Free;
   FStartRect.Free;
   FStopRect.Free;
   Inherited;
End;

Procedure TD2RectAnimation.Start;
Var
   Persistent: String;
   Value: TObject;
Begin
   If (Parent <> nil) And (FPropertyName <> '') Then
   Begin
      If FInstance = nil Then
      Begin
         FInstance := Parent;
         FPath := FPropertyName;
         While Pos('.', FPath) > 0 Do
         Begin
            Persistent := d2GetToken(FPath, '.');
            If GetPropInfo(FInstance.ClassInfo, Persistent, [tkClass]) <> nil Then
               FInstance := GetObjectProp(FInstance, Persistent);
         End;
      End;
   End;
   If (FInstance <> nil) And StartFromCurrent Then
   Begin
      If GetPropInfo(FInstance.ClassInfo, FPropertyName, [tkClass]) <> nil Then
      Begin
         Value := GetObjectProp(FInstance, FPropertyName);
         If (Value <> nil) And (Value Is TPersistent) Then
            FStartRect.Assign(TPersistent(Value));
      End;
   End;
   Inherited;
End;

Procedure TD2RectAnimation.ProcessAnimation;
Var
   Value: TObject;
Begin
   If FInstance <> nil Then
   Begin
      FCurrent.Left := d2InterpolateSingle(FStartRect.Left, FStopRect.Left, NormalizedTime);
      FCurrent.Top := d2InterpolateSingle(FStartRect.Top, FStopRect.Top, NormalizedTime);
      FCurrent.Right := d2InterpolateSingle(FStartRect.Right, FStopRect.Right, NormalizedTime);
      FCurrent.Bottom := d2InterpolateSingle(FStartRect.Bottom, FStopRect.Bottom, NormalizedTime);

      If GetPropInfo(FInstance.ClassInfo, FPath, [tkClass]) <> nil Then
      Begin
         Value := GetObjectProp(FInstance, FPath);
         If (Value <> nil) And (Value Is TPersistent) Then
            TPersistent(Value).Assign(FCurrent);
      End;
   End;
End;

//===================== TD2Animation =========================

Constructor TD2BitmapAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   Duration := 0.2;
   FStartBitmap := TD2Bitmap.Create(1, 1);
   FStopBitmap := TD2Bitmap.Create(1, 1);
   FCurrent := TD2Bitmap.Create(1, 1);
End;

Destructor TD2BitmapAnimation.Destroy;
Begin
   FCurrent.Free;
   FStartBitmap.Free;
   FStopBitmap.Free;
   Inherited;
End;

Procedure TD2BitmapAnimation.ProcessAnimation;
Var
   xsss: String;
   xObj: TObject;
Begin
   If (Parent <> nil) And (FPropertyName <> '') Then
   Begin
      If FInstance = nil Then
      Begin
         FInstance := Parent;

         While Pos('.', FPropertyName) > 0 Do
         Begin
            xsss := d2GetToken(FPropertyName, '.');
            If GetPropInfo(FInstance.ClassInfo, xsss, [tkClass]) <> nil Then
               FInstance := GetObjectProp(FInstance, xsss);
         End;
      End;

      If FInstance <> nil Then
      Begin
         If GetPropInfo(FInstance.ClassInfo, FPropertyName, [tkClass]) <> nil Then
         Begin
            xObj := GetObjectProp(FInstance, FPropertyName);

            If (xObj <> nil) And (xObj Is TPersistent) Then
            Begin
               If Inverse Then
               Begin
                  FCurrent.Assign(FStopBitmap);

                  If FCurrent.Canvas.BeginScene Then
                  Begin
                     FCurrent.Canvas.Clear(0);
                     FCurrent.Canvas.DrawBitmap(FStartBitmap, d2Rect(0, 0, FCurrent.Width, FCurrent.Height),
                        d2Rect(0, 0, FStartBitmap.Width, FStartBitmap.Height), 1 - NormalizedTime);
                     FCurrent.Canvas.EndScene;
                  End;
               End Else Begin
                  FCurrent.Assign(FStartBitmap);

                  If FCurrent.Canvas.BeginScene Then
                  Begin
                     FCurrent.Canvas.Clear(0);
                     FCurrent.Canvas.DrawBitmap(FStopBitmap, d2Rect(0, 0, FCurrent.Width, FCurrent.Height),
                        d2Rect(0, 0, FStopBitmap.Width, FStopBitmap.Height), NormalizedTime);
                     FCurrent.Canvas.EndScene;
                  End;
               End;

               TPersistent(xObj).Assign(FCurrent);
            End;
         End;
      End;
   End;
End;

//===================== TD2BitmapListAnimation ============================

Constructor TD2BitmapListAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   Duration := 0.2;
   FCurrent := TD2Bitmap.Create(0, 0);
   FAnimationBitmap := TD2Bitmap.Create(0, 0);
   FAnimationCount := 1;
   FLastAnimationStep := 0;
End;

Destructor TD2BitmapListAnimation.Destroy;
Begin
   FCurrent.Free;
   FAnimationBitmap.Free;
   Inherited;
End;

Procedure TD2BitmapListAnimation.ProcessAnimation;
Var
   Persistent: String;
   Value: TObject;
   LeftPos, CurrentIndex: Integer;
   NowValue: Single;
Begin
   If (Parent <> nil) And (FPropertyName <> '') Then
   Begin
      If FInstance = nil Then
      Begin
         FInstance := Parent;
         While Pos('.', FPropertyName) > 0 Do
         Begin
            Persistent := d2GetToken(FPropertyName, '.');
            If GetPropInfo(FInstance.ClassInfo, Persistent, [tkClass]) <> nil Then
               FInstance := GetObjectProp(FInstance, Persistent);
         End;
      End;

      If FInstance <> nil Then
      Begin
         { is Bitmap prop }
         If GetPropInfo(FInstance.ClassInfo, FPropertyName, [tkClass]) <> nil Then
         Begin
            { calc new value }
            Value := GetObjectProp(FInstance, FPropertyName);
            If (Value <> nil) And (Value Is TD2Bitmap) Then
            Begin
               NowValue := d2InterpolateSingle(0, FAnimationCount - 1, NormalizedTime);

               FCurrent.SetSize(TD2Bitmap(Value).Height, TD2Bitmap(Value).Width);

               CurrentIndex := Trunc(NowValue);

               LeftPos := CurrentIndex * (FAnimationBitmap.Width Div FAnimationCount) + CurrentIndex;

               If FCurrent.Canvas.BeginScene Then
               Begin
                  FCurrent.Canvas.Clear(0);
                  FCurrent.Canvas.DrawBitmap(FAnimationBitmap,
                     d2Rect(LeftPos, 0, LeftPos + FCurrent.Width, FCurrent.Height),
                     d2Rect(0, 0, FAnimationBitmap.Width Div FAnimationCount, FAnimationBitmap.Height), 1);
                  FCurrent.Canvas.EndScene;
               End;

               TPersistent(Value).Assign(FCurrent);
            End;
         End;
      End;
   End;
End;

//===================== Key Animation ====================================

Procedure TD2Key.SetKey(Const Value: Single);
Begin
   FKey := Value;
   If FKey < 0 Then
      FKey := 0;
   If FKey > 1 Then
      FKey := 1;
End;

Function TD2Keys.FindKeys(Const Time: Single; Var Key1, Key2: TD2Key): Boolean;
Var
   i: Integer;
Begin
   Result := False;
   If Count < 2 Then
      Exit;
   For i := 0 To Count - 2 Do
      If ((Time >= TD2Key(Items[i]).Key) And (Time <= TD2Key(Items[i + 1]).Key)) Then
      Begin
         Result := True;
         Key1 := TD2Key(Items[i]);
         Key2 := TD2Key(Items[i + 1]);
         Exit;
      End;
End;

//===================== TD2ColorKeyAnimation ==============================

Constructor TD2ColorKeyAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   FKeys := TD2Keys.Create(TD2ColorKey);
End;

Destructor TD2ColorKeyAnimation.Destroy;
Begin
   FKeys.Free;
   Inherited;
End;

Procedure TD2ColorKeyAnimation.Start;
Var
   Persistent: String;
Begin
   If (Parent <> nil) And (FPropertyName <> '') Then
   Begin
      If FInstance = nil Then
      Begin
         FInstance := Parent;
         FPath := FPropertyName;
         While Pos('.', FPath) > 0 Do
         Begin
            Persistent := d2GetToken(FPath, '.');
            If GetPropInfo(FInstance.ClassInfo, Persistent, [tkClass]) <> nil Then
               FInstance := GetObjectProp(FInstance, Persistent);
         End;
      End;
   End;
   If (FInstance <> nil) And StartFromCurrent Then
   Begin
      If Keys.Count > 0 Then
      Begin
         If GetPropInfo(FInstance.ClassInfo, FPath, [tkString, tkLString, tkWString, tkAString, tkUString]) <> nil Then
            TD2ColorKey(Keys.Items[0]).Value := GetStrProp(FInstance, FPath);

         If GetPropInfo(FInstance.ClassInfo, FPath, [tkInteger]) <> nil Then
            TD2ColorKey(Keys.Items[0]).Value := d2ColorToStr(GetOrdProp(FInstance, FPath));
      End;
   End;
   Inherited;
End;

Procedure TD2ColorKeyAnimation.ProcessAnimation;
Var
   Key1, Key2: TD2Key;
Begin
   If FInstance <> nil Then
   Begin
      If FKeys.FindKeys(NormalizedTime, Key1, Key2) Then
      Begin
         If (TD2FloatKey(Key2).Key - TD2FloatKey(Key1).Key) = 0 Then
            Exit;

         If GetPropInfo(FInstance.ClassInfo, FPath, [tkString, tkLString, tkWString, tkAString]) <> nil Then
            SetStrProp(FInstance, FPath, d2ColorToStr(d2InterpolateColor(d2StrToColor(TD2ColorKey(Key1).Value), d2StrToColor(TD2ColorKey(Key2).Value), (NormalizedTime - TD2FloatKey(Key1).Key) / (TD2FloatKey(Key2).Key - TD2FloatKey(Key1).Key))));

         If GetPropInfo(FInstance.ClassInfo, FPath, [tkInteger]) <> nil Then
            SetOrdProp(FInstance, FPath, d2InterpolateColor(d2StrToColor(TD2ColorKey(Key1).Value), d2StrToColor(TD2ColorKey(Key2).Value), (NormalizedTime - TD2FloatKey(Key1).Key) / (TD2FloatKey(Key2).Key - TD2FloatKey(Key1).Key)));
      End;
   End;
End;

//===================== TD2FloatKeyAnimation =============================

Constructor TD2FloatKeyAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   FKeys := TD2Keys.Create(TD2FloatKey);
End;

Destructor TD2FloatKeyAnimation.Destroy;
Begin
   FKeys.Free;
   Inherited;
End;

Procedure TD2FloatKeyAnimation.Start;
Var
   Persistent: String;
Begin
   If (Parent <> nil) And (FPropertyName <> '') Then
   Begin
      If FInstance = nil Then
      Begin
         FInstance := Parent;
         FPath := FPropertyName;
         While Pos('.', FPath) > 0 Do
         Begin
            Persistent := d2GetToken(FPath, '.');
            If GetPropInfo(FInstance.ClassInfo, Persistent, [tkClass]) <> nil Then
               FInstance := GetObjectProp(FInstance, Persistent);
         End;
      End;
   End;
   If (FInstance <> nil) And StartFromCurrent Then
   Begin
      If Keys.Count > 0 Then
      Begin
         If GetPropInfo(FInstance.ClassInfo, FPath, [tkFloat]) <> nil Then
            TD2FloatKey(Keys.Items[0]).Value := GetFloatProp(FInstance, FPath);
      End;
   End;
   Inherited;
End;

Procedure TD2FloatKeyAnimation.ProcessAnimation;
Var
   Key1, Key2: TD2Key;
Begin
   If FInstance <> nil Then
   Begin
      If FKeys.FindKeys(NormalizedTime, Key1, Key2) Then
      Begin
         If (TD2FloatKey(Key2).Key - TD2FloatKey(Key1).Key) = 0 Then
            Exit;
         If GetPropInfo(FInstance.ClassInfo, FPath, [tkFloat]) <> nil Then
            SetFloatProp(FInstance, FPath, d2InterpolateSingle(TD2FloatKey(Key1).Value, TD2FloatKey(Key2).Value, (NormalizedTime - TD2FloatKey(Key1).Key) / (TD2FloatKey(Key2).Key - TD2FloatKey(Key1).Key)));
      End;
   End;
End;

//===================== TD2PathAnimation ======================================

Constructor TD2PathAnimation.Create(AOwner: TComponent);
Begin
   Inherited;
   FPath := TD2PathData.Create;
End;

Destructor TD2PathAnimation.Destroy;
Begin
   If FSpline <> nil Then
      FreeAndNil(FSpline);
   FPath.Free;
   Inherited;
End;

Procedure TD2PathAnimation.ProcessAnimation;
Var
   OldP, P1: TD2Point;
Begin
   If (Length(FPolygon) > 0) And (FObj <> nil) Then
   Begin
      OldP := FObj.Position.Point;
      FSpline.SplineXY(NormalizedTime * Length(FPolygon), P1.X, P1.Y);
      FObj.Position.X := FStart.X + P1.X;
      FObj.Position.Y := FStart.Y + P1.Y;
      If FRotate And (NormalizedTime <> 0) And (NormalizedTime <> 1) And ((OldP.X <> FObj.Position.X) And (OldP.Y <> FObj.Position.Y)) Then
      Begin
         If Inverse Then
         Begin
            If d2VectorCrossProductZ(d2Vector(FObj.Position.X - OldP.X, FObj.Position.Y - OldP.Y), d2Vector(0, 1)) < 0 Then
               FObj.RotateAngle := 180 + d2RadToDeg(ArcCos(d2VectorAngleCosine(d2Vector(FObj.Position.X - OldP.X, FObj.Position.Y - OldP.Y), d2Vector(0, 1))))
            Else
               FObj.RotateAngle := 180 - d2RadToDeg(ArcCos(d2VectorAngleCosine(d2Vector(FObj.Position.X - OldP.X, FObj.Position.Y - OldP.Y), d2Vector(0, 1))));
         End Else Begin
            If d2VectorCrossProductZ(d2Vector(FObj.Position.X - OldP.X, FObj.Position.Y - OldP.Y), d2Vector(0, 1)) < 0 Then
               FObj.RotateAngle := d2RadToDeg(ArcCos(d2VectorAngleCosine(d2Vector(FObj.Position.X - OldP.X, FObj.Position.Y - OldP.Y), d2Vector(0, 1))))
            Else
               FObj.RotateAngle := -d2RadToDeg(ArcCos(d2VectorAngleCosine(d2Vector(FObj.Position.X - OldP.X, FObj.Position.Y - OldP.Y), d2Vector(0, 1))));
         End;
      End;
   End;
End;

Procedure TD2PathAnimation.SetPath(Const Value: TD2PathData);
Begin
   FPath.Assign(Value);
End;

Procedure TD2PathAnimation.Start;
Var
   i: Integer;
Begin
   Inherited;
   If FSpline <> nil Then
      FreeAndNil(FSpline);
   SetLength(FPolygon, 0);
   If Assigned(Scene) And (Scene.GetCanvas <> nil) Then
   Begin
      FPath.FlattenToPolygon(FPolygon);
      If Length(FPolygon) > 1 Then
         For i := 1 To High(FPolygon) Do
            If (FPolygon[i].X = ClosePolygon.X) And (FPolygon[i].Y = ClosePolygon.Y) Then
               FPolygon[i] := FPolygon[i - 1];
      FSpline := TD2Spline.Create(FPolygon);
   End;
   If (Parent <> nil) And (Parent.IsVisual) Then
      FObj := Parent.Visual
   Else
      FObj := nil;
   If FObj <> nil Then
      FStart := FObj.Position.Point;
End;

//===================== TD2PathSwitcher ================================

Constructor TD2PathSwitcher.Create(AOwner: TComponent);
Begin
   Inherited;
   Duration := 0.001;
End;

Destructor TD2PathSwitcher.Destroy;
Begin
   Inherited;
End;

Procedure TD2PathSwitcher.Start;
Begin
   Inherited;
End;

Procedure TD2PathSwitcher.ProcessAnimation;
Begin
   Inherited;
End;

Procedure TD2PathSwitcher.SetPathFalse(Const Value: String);
Begin
   FPathFalse := Value;
End;

Procedure TD2PathSwitcher.SetPathTrue(Const Value: String);
Begin
   FPathTrue := Value;
End;

//=============== TD2CustomTrack ==============================================

Constructor TD2CustomTrack.Create(AOwner: TComponent);
Begin
   Inherited;
   FViewportSize := 0;
   FMax := 100;
   FTracking := True;
   Width := 100;
   Height := 15;
End;

Destructor TD2CustomTrack.Destroy;
Begin
   Inherited;
End;

Function TD2CustomTrack.GetData: Variant;
Begin
   Result := Value;
End;

Procedure TD2CustomTrack.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
   If VarIsNumeric(Value) Then
      Self.Value := Value
   Else
      Self.Value := Min;
End;

Function TD2CustomTrack.GetThumbRect: TD2Rect;
Begin
   Result := LocalRect;
   If (FMax - FMin) > 0 Then
   Begin
      If Orientation = d2Horizontal Then
      Begin
         Result := d2Rect(0, 0, (FViewportSize / (FMax - FMin)) * Width, Height);
         d2OffsetRect(Result, Round((((FValue - FMin) / (FMax - FMin))) * Width), 0);
         If Result.Right - Result.Left < Height Then
         Begin
            Result.Right := Result.Left + trunc(Height / 2);
            Result.Left := Result.Left - trunc(Height / 2);
         End;
         If Result.Right > Width Then
            d2OffsetRect(Result, Width - Result.Right, 0);
         If Result.Left < 0 Then
            d2OffsetRect(Result, -Result.Left, 0);
      End Else Begin
         Result := d2Rect(0, 0, Width, (FViewportSize / (FMax - FMin)) * Height);
         If Result.Bottom - Result.Top < Width Then
         Begin
            Result.Bottom := Result.Top + trunc(Width / 2);
            Result.Top := Result.Top - trunc(Width / 2);
         End;
         d2OffsetRect(Result, 0, Round((((FValue - FMin) / (FMax - FMin))) * Height));
         If Result.Bottom > Height Then
            d2OffsetRect(Result, 0, Height - Result.Bottom);
         If Result.Top < 0 Then
            d2OffsetRect(Result, 0, -Result.Top);
      End;
   End;

   If (Thumb <> nil) And (Thumb.Parent <> nil) And (Thumb.Parent.IsVisual) Then
   Begin
      If d2RectWidth(Result) > TD2VisualObject(Thumb.Parent).Margins.Left + Thumb.Padding.Left + TD2VisualObject(Thumb.Parent).Margins.Right - Thumb.Padding.Right Then
      Begin
         Result.Left := Result.Left + TD2VisualObject(Thumb.Parent).Margins.Left + Thumb.Padding.Left;
         Result.Right := Result.Right - TD2VisualObject(Thumb.Parent).Margins.Right - Thumb.Padding.Right;
      End;

      Result.Top := Result.Top + TD2VisualObject(Thumb.Parent).Margins.Top + Thumb.Padding.Top;
      Result.Bottom := Result.Bottom - TD2VisualObject(Thumb.Parent).Margins.Bottom - Thumb.Padding.Bottom;
   End;
End;

Function TD2CustomTrack.GetThumb: TD2Thumb;
Var
   T: TD2Object;
Begin
   T := FindResource('thumb');
   If (T <> nil) And (T Is TD2Thumb) Then
   Begin
      Result := TD2Thumb(T);
      Result.FTrack := Self;
   End Else
      Result := nil;
End;

Procedure TD2CustomTrack.Realign;
Begin
   Inherited;
   If Thumb <> nil Then
   Begin
      With GetThumbRect Do
      Begin
         Thumb.Position.X := Left;
         Thumb.Position.Y := Top;
         If Round(Right - Left) > 0 Then
            Thumb.Width := Round(Right - Left);
         If Round(Bottom - Top) > 0 Then
            Thumb.Height := Round(Bottom - Top);
      End;
   End;
End;

Procedure TD2CustomTrack.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      If Orientation = d2Horizontal Then
      Begin
         If FViewportSize = 0 Then
            Value := Min + ((X / Width) * (FMax - FMin))
         Else Begin
            If Min + ((X / Width) * (FMax - FMin)) > Value Then
               Value := Value + FViewportSize
            Else
               Value := Value - FViewportSize;
         End;
      End Else Begin
         If FViewportSize = 0 Then
            Value := Min + ((Y / Height) * (FMax - FMin))
         Else Begin
            If Min + ((Y / Height) * (FMax - FMin)) > Value Then
               Value := Value + FViewportSize
            Else
               Value := Value - FViewportSize;
         End;
      End;
   End;
End;

Procedure TD2CustomTrack.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
End;

Procedure TD2CustomTrack.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
End;

Procedure TD2CustomTrack.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Var
   Inc: Single;
Begin
   Inc := Frequency;
   If Inc = 0 Then
      Inc := 1;
   Inherited;
   Case Key Of
      VK_HOME: Value := Min;
      VK_END: Value := Max;
      VK_UP: Value := Value - Inc;
      VK_DOWN: Value := Value + Inc;
      VK_LEFT: Value := Value - Inc;
      VK_RIGHT: Value := Value + Inc;
      Else Exit;
   End;
   If Not Tracking And Assigned(FOnChange) Then
      FOnChange(Self);
   Key := 0;
End;

Procedure TD2CustomTrack.SetMax(Const Value: Single);
Begin
   If FMax <> Value Then
   Begin
      FMax := Value;
      If FMax < FMin Then
         FMax := FMin + 0.001;
      If FViewportSize > (FMax - FMin) Then
         FViewportSize := FMax - FMin;
      If FValue > FMax - FViewportSize Then
         FValue := FMax - FViewportSize;
      Realign;
   End;
End;

Procedure TD2CustomTrack.SetMin(Const Value: Single);
Begin
   If FMin <> Value Then
   Begin
      FMin := Value;
      Realign;
   End;
End;

Procedure TD2CustomTrack.SetOrientation(Const Value: TD2Orientation);
Var
   T: Single;
Begin
   If FOrientation <> Value Then
   Begin
      FOrientation := Value;

      If Assigned(FScene) And (FScene.GetDesignTime) And Not (csLoading In ComponentState) Then
      Begin
         T := Width;
         Width := Height;
         Height := T;
      End;
      Realign;
   End;
End;

Function TD2CustomTrack.GetIsTracking: Boolean;
Begin
   Result := (Thumb <> nil) And Thumb.FPressed;
End;

Procedure TD2CustomTrack.SetFrequency(Const Value: Single);
Begin
   If FFrequency <> Value Then
   Begin
      FFrequency := Value;
      If FFrequency <> 0 Then
         Self.Value := Round(Self.Value / Frequency) * Frequency;
   End;
End;

Procedure TD2CustomTrack.SetValue(Value: Single);
Begin
   If FFrequency <> 0 Then
      Value := Round(Value / Frequency) * Frequency;

   If FValue <> Value Then
   Begin
      FValue := Value;
      If FValue > FMax - FViewportSize Then
         FValue := FMax - FViewportSize;
      If FValue < FMin Then
         FValue := FMin;

      If GetIsTracking And Assigned(FOnTracking) Then
         FOnTracking(Self);
      If Tracking And Assigned(FBindingObjects) Then
         ToBindingObjects;

      If GetIsTracking And Tracking And Assigned(FOnChange) Then
         FOnChange(Self)
      Else
      If Not GetIsTracking And Assigned(FOnChange) Then
         FOnChange(Self);

      Realign;
   End;
End;

Procedure TD2CustomTrack.SetViewportSize(Const Value: Single);
Begin
   If FViewportSize <> Value Then
   Begin
      FViewportSize := Value;
      If FViewportSize > (FMax - FMin) Then
         FViewportSize := FMax - FMin;
      If FValue > FMax - FViewportSize Then
         Self.Value := FMax - FViewportSize;
      Realign;
   End;
End;

//=============== TD2TrackBar ==================================

Constructor TD2TrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FViewportSize := 0;
   CanFocused := True;
End;

//============ TD2BitmapTrackBar =================================

Constructor TD2BitmapTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'trackbarstyle';
End;

Destructor TD2BitmapTrackBar.Destroy;
Begin
   If FBitmap <> nil Then
      FreeAndNil(FBitmap);
   Inherited;
End;

Procedure TD2BitmapTrackBar.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('background');
   If (T <> nil) And (T Is TD2Shape) Then
   Begin
      FBackground := TD2Shape(T);
      UpdateBitmap;
   End;
End;

Procedure TD2BitmapTrackBar.FreeStyle;
Begin
   FBackground := nil;
   Inherited;
End;

Procedure TD2BitmapTrackBar.Realign;
Begin
   Inherited;
   UpdateBitmap;
End;

Procedure TD2BitmapTrackBar.UpdateBitmap;
Begin
   If FBackground = nil Then
      Exit;

   If FBitmap <> nil Then
      If (FBitmap.Width <> Trunc(FBackground.Width)) Or (FBitmap.Height <> Trunc(FBackground.Height)) Then
      Begin
         FreeAndNil(FBitmap);
      End;

   If FBitmap = nil Then
   Begin
      FBitmap := TD2Bitmap.Create(Trunc(FBackground.Width), Trunc(FBackground.Height));
      FillBitmap;
   End;

   FBackground.Fill.Style := d2BrushBitmap;
   FBackground.Fill.Bitmap.Bitmap := FBitmap;

   Repaint;
End;

Procedure TD2BitmapTrackBar.FillBitmap;
Begin
End;

//============  TD2HueTrackBar ==============================

Constructor TD2HueTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   Max := 1;
   Value := 0.5;
End;

Procedure TD2HueTrackBar.FillBitmap;
Var
   i, j: Integer;
Begin
   For j := 0 To FBitmap.Height - 1 Do
   Begin
      For i := 0 To FBitmap.Width - 1 Do
      Begin
         If Orientation = d2Horizontal Then
            FBitmap.Scanline[j][i] := d2CorrectColor(d2HSLtoRGB(i / FBitmap.Width, 0.9, 0.5))
         Else
            FBitmap.Scanline[j][i] := d2CorrectColor(d2HSLtoRGB(j / FBitmap.Height, 0.9, 0.5));
      End;
   End;
End;

//============  TD2AlphaTrackBar =====================================

Constructor TD2AlphaTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   Max := 1;
   Value := 1;
End;

Procedure TD2AlphaTrackBar.FillBitmap;
Var
   i, j: Integer;
Begin
   For j := 0 To FBitmap.Height - 1 Do
   Begin
      For i := 0 To FBitmap.Width - 1 Do
      Begin
         If odd(i Div 3) And Not odd(j Div 3) Then
            FBitmap.Scanline[j][i] := d2CorrectColor($FFA0A0A0)
         Else
         If Not odd(i Div 3) And odd(j Div 3) Then
            FBitmap.Scanline[j][i] := d2CorrectColor($FFA0A0A0)
         Else
            FBitmap.Scanline[j][i] := d2CorrectColor($FFFFFFFF);
      End;
   End;

   If FBitmap.Canvas.BeginScene Then
   Begin
      FBitmap.Canvas.Fill.Style := d2BrushGradient;
      FBitmap.Canvas.Fill.Gradient.Points[0].Color := '$00FFFFFF';
      FBitmap.Canvas.Fill.Gradient.Points[1].Color := '$FFFFFFFF';
      FBitmap.Canvas.Fill.Gradient.StopPosition.Point := d2Point(1, 0);
      FBitmap.Canvas.FillRect(d2Rect(0, 0, FBitmap.Width, FBitmap.Height), 0, 0, [], 1);
      FBitmap.Canvas.EndScene;
   End;
End;

//============  TD2BWTrackBar =================

Constructor TD2BWTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   Max := 1;
   Value := 0.5;
End;

Procedure TD2BWTrackBar.FillBitmap;
Var
   i, j: Integer;
   a: Byte;
Begin
   For j := 0 To FBitmap.Height - 1 Do
   Begin
      For i := 0 To FBitmap.Width - 1 Do
      Begin
         If Orientation = d2Horizontal Then
            a := round((i / FBitmap.Width) * $FF)
         Else
            a := round((j / FBitmap.Height) * $FF);
         FBitmap.Scanline[j][i] := d2CorrectColor(d2Color(a, a, a));
      End;
   End;
End;

//=============== TD2HudHueTrackBar ===========================

Constructor TD2HudHueTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'hudtrackbarstyle';
End;

//=============== TD2HudAlphaTrackBar ==========================

Constructor TD2HudAlphaTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'hudtrackbarstyle';
End;

//=============== TD2HudBWTrackBar =============================

Constructor TD2HudBWTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'hudtrackbarstyle';
End;

//=============== TD2CompoundTrackBar ===========================

Constructor TD2CompoundTrackBar.Create(AOwner: TComponent);
Var
   C: TD2Control;
Begin
   Inherited;
   FDecimalDigits := 2;

   Width := 200;
   Height := 20;
   FTextLabel := TD2Label.Create(Self);
   With FTextLabel Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      Name := 'TextLabel';
      TextW := 'Caption';
      Width := 70;
      Padding.right := 5;
      WordWrap := False;
      TextAlign := d2TextAlignFar;
      SetSubComponent(True);
   End;

   C := TD2Control.Create(Self);
   With C Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaClient;
   End;

   FTrackBar := TD2Trackbar.Create(Self);
   With FTrackBar Do
   Begin
      Parent := C;
      Stored := False;
      Locked := True;
      Align := vaVertCenter;
      Name := 'TrackBar';
      SetSubComponent(True);
      OnChange := DoTrack;
      OnTracking := DoTracking;
   End;

   C := TD2Control.Create(Self);
   With C Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaRight;
   End;

   FValueLabel := TD2ValueLabel.Create(Self);
   With FValueLabel Do
   Begin
      Parent := C;
      Stored := False;
      Locked := True;
      Align := vaVertCenter;
      Name := 'ValueLabel';
      TextW := '0';
      Padding.left := 5;
      SetSubComponent(True);
   End;
End;

Procedure TD2CompoundTrackBar.UpdateLabel;
Begin
   If (FTrackBar.Frequency <> 0) And (frac(FTrackBar.Frequency) = 0) Then
      FValueLabel.TextW := IntToStr(Trunc(Value)) + Suffix
   Else
      FValueLabel.TextW := Format('%.' + IntToStr(FDecimalDigits) + 'f', [Self.Value]) + Suffix;
End;

Procedure TD2CompoundTrackBar.DoTrack(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Sender);
End;

Procedure TD2CompoundTrackBar.DoTracking(Sender: TObject);
Begin
   UpdateLabel;
End;

Function TD2CompoundTrackBar.GetValue: Single;
Begin
   Result := FTrackBar.Value;
End;

Procedure TD2CompoundTrackBar.SetValue(Const Value: Single);
Begin
   FTrackBar.Value := Value;
   UpdateLabel;
End;

Procedure TD2CompoundTrackBar.SetDecimalDigits(Const Value: Integer);
Begin
   If FDecimalDigits <> Value Then
   Begin
      FDecimalDigits := Value;
      UpdateLabel;
   End;
End;

Procedure TD2CompoundTrackBar.SetSuffix(Const Value: WideString);
Begin
   If FSuffix <> Value Then
   Begin
      FSuffix := Value;
      UpdateLabel;
   End;
End;

//=============== TD2ComboBox ===============================

Constructor TD2ComboBox.Create(AOwner: TComponent);
Begin
   Inherited;
   DropDownCount := 8;
   CanFocused := True;
   FPopup := TD2Popup.Create(Self);
   FPopup.PlacementTarget := Self;
   FPopup.StaysOpen := False;
   FPopup.Stored := False;
   FPopup.Parent := Self;
   FPopup.Locked := True;
   FPopup.DesignHide := True;
   FListBox := TD2ComboListBox.Create(Self);
   FListBox.Parent := FPopup;
   FListBox.FComboBox := Self;
   FListBox.Stored := False;
   FListBox.Align := vaClient;
   FListBox.ShowCheckboxes := False;
   Width := 100;
   Height := 22;
End;

Destructor TD2ComboBox.Destroy;
Begin
   Inherited;
End;

Function TD2ComboBox.ItemClass: String;
Begin
   Result := 'TD2ListBoxItem';
End;

Procedure TD2ComboBox.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('Content');
   If (T <> nil) And (T Is TD2Content) Then
   Begin
      TD2Content(T).OnPaint := DoContentPaint;

      If (FListBox <> nil) And (FListBox.ItemByIndex(ItemIndex) <> nil) Then
      Begin
         FListBox.ItemByIndex(ItemIndex).Width := TD2Content(T).Width;
      End;
   End;
End;

Procedure TD2ComboBox.Realign;
Begin
   Inherited;
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;

   If FPopup <> nil Then
   Begin
      FPopup.Width := Width;
      FListBox.Width := Width;
   End;

   FDisableAlign := False;
End;

Procedure TD2ComboBox.Paint;
Begin
   Inherited;
End;

Procedure TD2ComboBox.PaintChildren;
Begin
   Inherited;
End;

Procedure TD2ComboBox.DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
Var
   SOpacity: Single;
   Item: TD2ListBoxItem;
   SaveSize: TD2Point;
   SaveScene: Id2Scene;
Begin
   If FListBox <> nil Then
   Begin
      Item := FListBox.ItemByIndex(FListBox.ItemIndex);
      If Item <> nil Then
      Begin
         Item.ApplyResource;
         SOpacity := Item.FAbsoluteOpacity;
         SaveSize := d2Point(Item.Width, Item.Height);
         SaveScene := Item.Scene;
         Item.SetNewScene(Scene);
         Item.SetSizeWithoutChange(d2RectWidth(ARect), d2RectHeight(ARect));
         Item.FAbsoluteOpacity := Opacity;
         Item.RecalcOpacity;
         Item.FRecalcOpacity := False;
         Item.PaintTo(aCanvas, ARect, TD2Object(Sender));
         Item.FAbsoluteOpacity := SOpacity;
         Item.RecalcOpacity;
         Item.SetSizeWithoutChange(SaveSize.X, SaveSize.Y);
         Item.SetNewScene(SaveScene);
      End;
   End;
End;

Procedure TD2ComboBox.DropDown;
Var
   Count: Integer;
Begin
   If Not FPopup.IsOpen Then
   Begin
      FPopup.Placement := FPlacement;
      FPopup.Width := Width;
      Count := DropDownCount;
      If FListBox.Count < Count Then
         Count := FListBox.Count;

      If FListbox.ItemHeight > 0 Then
         FPopup.Height := (Count * FListbox.ItemHeight) + 4
      Else
         FPopup.Height := Count * (Height - 4);

      FPopup.IsOpen := True;
      If FPopup.IsOpen Then
         FListBox.SetFocus;
   End Else Begin
      FPopup.IsOpen := False;
   End;
End;

Procedure TD2ComboBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      DropDown;
   End;
End;

Procedure TD2ComboBox.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
End;

Procedure TD2ComboBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
End;

Procedure TD2ComboBox.MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean);
Begin
   Inherited;

   If WheelDelta < 0 Then
   Begin
      If ItemIndex < Count - 1 Then
         ItemIndex := ItemIndex + 1;
   End Else Begin
      If ItemIndex > 0 Then
         ItemIndex := ItemIndex - 1;
   End;
   Handled := True;
End;

Procedure TD2ComboBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Var
   i: Integer;
Begin
   Inherited;
   If Count > 0 Then
   Begin
      If KeyChar <> #0 Then
      Begin
         For i := 0 To Count - 1 Do
            If (FListBox.Items[i].TextW <> '') And (WideLowerCase(FListBox.Items[i].TextW[1]) = WideLowerCase(KeyChar)) Then
            Begin
               ItemIndex := i;
               Break;
            End;
         KeyChar := #0;
      End;

      Case Key Of
         VK_END: ItemIndex := Count - 1;
         VK_HOME: ItemIndex := 0;
         VK_LEFT: If ItemIndex > 0 Then
               ItemIndex := ItemIndex - 1;
         VK_RIGHT: If ItemIndex < Count - 1 Then
               ItemIndex := ItemIndex + 1;
         VK_UP: If ItemIndex > 0 Then
            Begin
               ItemIndex := ItemIndex - 1;
               If ItemIndex < 0 Then
                  ItemIndex := 0;
            End;
         VK_DOWN:
         Begin
            If ssAlt In Shift Then
            Begin
               DropDown;
            End Else Begin
               If ItemIndex < Count - 1 Then
                  ItemIndex := ItemIndex + 1;
               If ItemIndex > Count - 1 Then
                  ItemIndex := Count - 1;
            End;
         End;
         Else Exit;
      End;

      Key := 0;
   End;
End;

Procedure TD2ComboBox.Clear;
Begin
   If FListBox <> nil Then
      FListBox.Clear;
End;

Procedure TD2ComboBox.AddObject(AObject: TD2Object);
Begin
   If (FListBox <> nil) And ((AObject Is TD2ListBoxItem) Or (AObject Is TD2SelectionItem)) Then
   Begin
      FListBox.AddObject(AObject);
   End Else
      Inherited;
End;

Procedure TD2ComboBox.DoListBoxChange(Sender: TObject);
Begin
   //....
End;

Function TD2ComboBox.GetCount: Integer;
Begin
   If FListBox <> nil Then
      Result := FListBox.Count
   Else
      Result := 0;
End;

Procedure TD2ComboBox.SetItemIndex(Const Value: Integer);
Begin
   If FListBox <> nil Then
   Begin
      FListBox.ItemIndex := Value;
      If Assigned(FBindingObjects) Then
         ToBindingObjects;
      If Assigned(FOnChange) Then
         FOnChange(Self);
      If (FResourceLink <> nil) Then
         FResourceLink.Visual.UpdateEffects;
      Repaint;
   End;
End;

Function TD2ComboBox.GetItemIndex: Integer;
Begin
   If FListBox <> nil Then
      Result := FListBox.ItemIndex
   Else
      Result := -1;
End;

Procedure TD2ComboBox.DesignClick;
Begin
   Inherited;
   FPopup.DesignHide := Not FPopup.DesignHide;
   FPopup.Width := Width;
   If FListbox.ItemHeight > 0 Then
      FPopup.Height := DropDownCount * FListbox.ItemHeight
   Else
      FPopup.Height := DropDownCount * (Height - 4);
   FPopup.Position.X := 0;
   FPopup.Position.Y := Height;
   FPopup.Locked := FPopup.DesignHide;
End;

Procedure TD2ComboBox.GetChildren(Proc: TGetChildProc; Root: TComponent);
Var
   j: Integer;
Begin
   Inherited;
   If (FListBox <> nil) And (FListBox.FContent <> nil) Then
      If (FListBox.FContent.ChildrenCount > 0) Then
      Begin
         For j := 0 To FListBox.FContent.ChildrenCount - 1 Do
            If FListBox.FContent.Children[j].Stored Then
               Proc(TComponent(FListBox.FContent.Children[j]));
      End;
End;

Function TD2ComboBox.GetListBoxResource: String;
Begin
   Result := FListBox.Resource;
End;

Procedure TD2ComboBox.SetListBoxResource(Const Value: String);
Begin
   FListBox.Resource := Value;
End;

Function TD2ComboBox.GetItemHeight: Single;
Begin
   Result := FListBox.ItemHeight;
End;

Procedure TD2ComboBox.SetItemHeight(Const Value: Single);
Begin
   FListBox.ItemHeight := Value;
End;

//=============== TD2ComboListBox =================================

Constructor TD2ComboListBox.Create(AOwner: TComponent);
Begin
   Inherited;
   HideSelectionUnfocused := False;
End;

Destructor TD2ComboListBox.Destroy;
Begin
   Inherited;
End;

Procedure TD2ComboListBox.ApplyResource;
Begin
   Inherited;
End;

Procedure TD2ComboListBox.GetChildren(Proc: TGetChildProc; Root: TComponent);
Begin
   Inherited;
End;

Function TD2ComboListBox.GetParentComponent: TComponent;
Begin
   Result := Inherited GetParentComponent;
End;

Procedure TD2ComboListBox.KillFocus;
Begin
   Inherited;
End;

Procedure TD2ComboListBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
End;

Procedure TD2ComboListBox.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
End;

Procedure TD2ComboListBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
   If Key = VK_RETURN Then
   Begin
      If (Parent Is TD2Popup) And (TD2Popup(Parent).IsOpen) And (FComboBox <> nil) And (FComboBox Is TD2ComboBox) Then
      Begin
         TD2ComboBox(FComboBox).ItemIndex := ItemIndex;
         TD2Popup(Parent).IsOpen := False;
      End;

      If (Parent Is TD2Popup) And (TD2Popup(Parent).IsOpen) And (FComboBox <> nil) And (FComboBox Is TD2ComboTextBox) Then
      Begin
         TD2ComboTextBox(FComboBox).ItemIndex := ItemIndex;
         TD2Popup(Parent).IsOpen := False;
      End;
   End;
End;

Procedure TD2ComboListBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;

   If (Parent Is TD2Popup) And (TD2Popup(Parent).IsOpen) And (FComboBox <> nil) And (FComboBox Is TD2ComboBox) Then
   Begin
      If d2PtInRect(d2Point(X, Y), LocalRect) Then
         If ItemByPoint(X, Y) <> nil Then
            TD2ComboBox(FComboBox).ItemIndex := ItemByPoint(X, Y).Index;
      TD2Popup(Parent).IsOpen := False;
   End;

   If (Parent Is TD2Popup) And (TD2Popup(Parent).IsOpen) And (FComboBox <> nil) And (FComboBox Is TD2ComboTextBox) Then
   Begin
      If d2PtInRect(d2Point(X, Y), LocalRect) Then
         If ItemByPoint(X, Y) <> nil Then
            TD2ComboTextBox(FComboBox).ItemIndex := ItemByPoint(X, Y).Index;

      TD2Popup(Parent).IsOpen := False;
   End;
End;

Procedure TD2ComboListBox.MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean);
Begin
   Inherited;
End;

//================== TD2ComboColorBox ==================================

Constructor TD2ComboColorBox.Create(AOwner: TComponent);
Begin
   Inherited;
   Width := 60;
   Height := 22;
   CanFocused := True;
   AutoCapture := True;
   FResource := 'comboboxstyle';
   FPopup := TD2Popup.Create(Self);
   FPopup.FResource := 'combopopupstyle';
   FPopup.PlacementTarget := Self;
   FPopup.StaysOpen := False;
   FPopup.Stored := False;
   FPopup.Parent := Self;
   FPopup.Locked := True;
   FPopup.DesignHide := True;
   FPopup.Width := 240;
   FPopup.Height := 160;
   FPopup.Margins.Rect := d2Rect(5, 5, 5, 5);
   FColorBox := TD2ColorBox.Create(Self);
   FColorBox.Width := 50;
   FColorBox.Parent := FPopup;
   FColorBox.Stored := False;
   FColorBox.Align := vaRight;
   FColorBox.Padding.Rect := d2Rect(15, 70, 15, 30);
   FColorText := TD2TextBox.Create(Self);
   FColorText.Parent := FPopup;
   FColorText.Stored := False;
   FColorText.Locked := True;
   FColorText.FilterChar := '#0123456789abcdefABCDEF';
   FColorText.SetBounds(160, 20, 70, 22);
   FColorText.Align := vaTopRight;
   FColorText.DisableFocusEffect := True;
   FColorText.OnChange := DoTextChange;
   FColorPanel := TD2ColorPanel.Create(Self);
   FColorPanel.Parent := FPopup;
   FColorPanel.Stored := False;
   FColorPanel.DisableFocusEffect := True;
   FColorPanel.Align := vaClient;
   FColorPanel.OnChange := DoColorChange;
   FColorPanel.ColorBox := FColorBox;
End;

Destructor TD2ComboColorBox.Destroy;
Begin
   Inherited;
End;

Procedure TD2ComboColorBox.DoTextChange(Sender: TObject);
Var
   S: String;
Begin
   Try
      S := Color;
      Color := FColorText.TextW;
   Except
      Color := S;
   End;
End;

Procedure TD2ComboColorBox.DoColorChange(Sender: TObject);
Begin
   FColorText.TextW := Color;
   Repaint;
   If Assigned(FOnChange) Then
      FOnChange(Self);
End;

Procedure TD2ComboColorBox.DropDown;
Begin
   If Not FPopup.IsOpen Then
   Begin
      FPopup.Placement := FPlacement;
      FColorPanel.ApplyResource;
      FPopup.IsOpen := True;
   End Else Begin
      FPopup.IsOpen := False;
   End;
End;

Procedure TD2ComboColorBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If (Button = mbLeft) Then
   Begin
      DropDown;
   End;
End;

Procedure TD2ComboColorBox.ChangeParent;
Begin
   Inherited;
End;

Function TD2ComboColorBox.GetValue: String;
Begin
   Result := FColorPanel.Color;
End;

Procedure TD2ComboColorBox.SetValue(Const Value: String);
Begin
   FColorPanel.Color := Value;
End;

Procedure TD2ComboColorBox.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('Content');
   If (T <> nil) And (T Is TD2Content) Then
   Begin
      TD2Content(T).OnPaint := DoContentPaint;
   End;
End;

Procedure TD2ComboColorBox.DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
Var
   R: TD2Rect;
   i, j, SaveIndex: Integer;
Begin
   R := ARect;
   d2InflateRect(R, -0.5 - 2, -0.5 - 2);

   SaveIndex := aCanvas.SaveCanvas;
   aCanvas.IntersectClipRect(R);
   aCanvas.Stroke.Style := d2BrushNone;
   aCanvas.Fill.Style := d2BrushSolid;
   aCanvas.Fill.SolidColor := $FFFFFFFF;
   aCanvas.FillRect(R, 0, 0, AllCorners, AbsoluteOpacity);
   aCanvas.Fill.SolidColor := $FFD3D3D3;

   For i := 0 To Trunc(d2RectWidth(R) / 5) + 1 Do
      For j := 0 To Trunc(d2RectHeight(R) / 5) + 1 Do
      Begin
         If Odd(i + j) Then
         Begin
            aCanvas.FillRect(d2Rect(i * 5, j * 5, (i + 1) * 5, (j + 1) * 5), 0, 0, AllCorners, AbsoluteOpacity);
         End;
      End;

   aCanvas.RestoreCanvas(SaveIndex);
   aCanvas.Fill.Style := d2BrushSolid;
   aCanvas.Fill.Color := Color;
   aCanvas.FillRect(R, 0, 0, AllCorners, AbsoluteOpacity);
   aCanvas.Stroke.Color := vcBlack;
   aCanvas.Stroke.Style := d2BrushSolid;
   aCanvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
End;

Function TD2ComboColorBox.GetUseAlpha: Boolean;
Begin
   Result := FColorPanel.UseAlpha;
End;

Procedure TD2ComboColorBox.SetUseAlpha(Const Value: Boolean);
Begin
   FColorPanel.UseAlpha := Value;
End;

Function TD2ComboColorBox.GetData: Variant;
Begin
   Result := Color;
End;

Procedure TD2ComboColorBox.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      Onchange := VariantToEvent(Value)
   Else
   If VarIsStr(Value) Then
      Color := Value
   Else
   If VarIsOrdinal(Value) Then
      Color := d2ColorToStr(Value);
End;


//=============== TD2HudComboColorBox ===========================

Constructor TD2HudComboColorBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'hudcomboboxstyle';
   FPopup.FResource := 'hudcombopopupstyle';
End;


//================== TD2ComboTextBox =========================================

Type
   TD2HackComboListBox = Class(TD2ComboListBox);

Constructor TD2ComboTextBox.Create(AOwner: TComponent);
Begin
   Inherited;

   DropDownCount := 8;
   Cursor := crDefault;
   FItemHeight := 19;
   FItems := TD2WideStringList.Create;
   TD2WideStringList(FItems).OnChange := DoItemsChanged;
   FPopup := TD2Popup.Create(Self);
   FPopup.PlacementTarget := Self;
   FPopup.StaysOpen := False;
   FPopup.Stored := False;
   FPopup.Parent := Self;
   FPopup.Locked := True;
   FPopup.DesignHide := True;
   FPopup.OnClosePopup := DoClosePopup;
   FListBox := TD2ComboListBox.Create(Self);
   FListBox.Parent := FPopup;
   TD2HackComboListBox(FListBox).FComboBox := Self;
   FListBox.ItemHeight := ItemHeight;
   FListBox.Stored := False;
   FListBox.Align := vaClient;
   FListBox.ShowCheckboxes := False;
   FListBox.ItemIndex := -1;
   OnTyping := DoTyping;
End;

Destructor TD2ComboTextBox.Destroy;
Begin
   FreeAndNil(FItems);
   Inherited;
End;

Procedure TD2ComboTextBox.DoItemsChanged(Sender: TObject);
Begin
   RebuildList;
End;

Procedure TD2ComboTextBox.RebuildList;
Var
   SaveI, i: Integer;
   Item: TD2ListBoxItem;
Begin
   If csDestroying In ComponentState Then
      Exit;
   If Items = nil Then
      Exit;
   BeginUpdate;
   SaveI := FListbox.ItemIndex;
   TD2HackComboListBox(FListBox).FItemIndex := -1;
   FListBox.Clear;

   For i := 0 To FItems.Count - 1 Do
   Begin
      Item := TD2ListBoxItem.Create(Self);
      Item.AutoTranslate := FAutoTranslate;
      Item.Parent := FListBox;
      Item.Height := FItemHeight;
      Item.Stored := False;
      Item.Locked := True;
      Item.TextW := FItems[i];
   End;

   EndUpdate;
   TD2HackComboListBox(FListbox).FItemIndex := SaveI;

   If TD2HackComboListBox(FListbox).FItemIndex >= FListbox.Count Then
      TD2HackComboListBox(FListbox).FItemIndex := FListbox.Count - 1;
End;

Procedure TD2ComboTextBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Var
   i: Integer;
Begin
   Inherited;
   If Count > 0 Then
   Begin
      If ReadOnly And (KeyChar <> #0) Then
      Begin
         For i := 0 To Count - 1 Do
            If (FListBox.Items[i].TextW <> '') And (WideLowerCase(FListBox.Items[i].TextW[1]) = WideLowerCase(KeyChar)) Then
            Begin
               ItemIndex := i;
               Break;
            End;
         KeyChar := #0;
      End;

      Case Key Of
         VK_DOWN:
         Begin
            If ItemIndex < Count - 1 Then
               ItemIndex := ItemIndex + 1;
            If ItemIndex > Count - 1 Then
               ItemIndex := Count - 1;
         End;
         VK_UP: If ItemIndex > 0 Then
            Begin
               ItemIndex := ItemIndex - 1;
               If ItemIndex < 0 Then
                  ItemIndex := 0;
            End;
         Else Exit;
      End;

      Key := 0;
   End;
End;

Procedure TD2ComboTextBox.Realign;
Begin
   Inherited;
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;

   If FPopup <> nil Then
      FPopup.Width := Width;
   If FListBox <> nil Then
      FListBox.Width := Width;

   FDisableAlign := False;
End;

Procedure TD2ComboTextBox.DoClosePopup(Sender: TObject);
Begin
   If ShowCaret And IsFocused Then
      ShowCaretProc;
End;

Procedure TD2ComboTextBox.DropDown;
Var
   Count: Integer;
Begin
   If Not FPopup.IsOpen Then
   Begin
      If ShowCaret Then
         HideCaret;
      FPopup.Placement := FPlacement;
      FPopup.Width := Width;
      Count := DropDownCount;
      If FListBox.Count < Count Then
         Count := FListBox.Count;

      If FListbox.ItemHeight > 0 Then
         FPopup.Height := Count * (FListbox.ItemHeight) + 4
      Else
         FPopup.Height := Count * (Height - 4);

      TD2HackComboListBox(FListBox).FNeedResource := True;
      FListBox.ApplyResource;
      FPopup.IsOpen := True;
      FListBox.SetFocus;
      TD2HackComboListBox(FListbox).UpdateSelection;
   End Else Begin
      FPopup.IsOpen := False;
   End;
End;

Procedure TD2ComboTextBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If (Button = mbLeft) And Not d2PtInRect(d2Point(X, Y), ContentRect) Then
   Begin
      DropDown;
   End;
End;

Procedure TD2ComboTextBox.SetItemHeight(Const Value: Single);
Begin
   If FItemHeight <> Value Then
   Begin
      FItemHeight := Value;
      FListBox.ItemHeight := FItemHeight;
   End;
End;

Procedure TD2ComboTextBox.SetItems(Const Value: TD2WideStrings);
Begin
   FItems.Assign(Value);
End;

Procedure TD2ComboTextBox.ChangeParent;
Begin
   Inherited;
End;

Function TD2ComboTextBox.GetItemIndex: Integer;
Begin
   If FListBox <> nil Then
      Result := FListBox.ItemIndex
   Else
      Result := -1;
End;

Procedure TD2ComboTextBox.SetItemIndex(Const Value: Integer);
Begin
   If FListBox <> nil Then
   Begin
      FListBox.ItemIndex := Value;
      If (ItemIndex >= 0) And (ItemIndex < Items.Count) Then
      Begin
         TextW := Items[ItemIndex];
         FNeedChange := False;
         If Not Scene.ShowKeyboardForControl(Self) Then
            SelectAll;
         If Assigned(FBindingObjects) Then
            ToBindingObjects;
         If Assigned(FOnChange) Then
            FOnChange(Self);
         If (FResourceLink <> nil) Then
            FResourceLink.Visual.UpdateEffects;
         Repaint;
      End;
   End;
End;

Function TD2ComboTextBox.GetCount: Integer;
Begin
   If FListBox <> nil Then
      Result := FListBox.Count
   Else
      Result := 0;
End;

Procedure TD2ComboTextBox.DoTyping(Sender: TObject);
Var
   i, l: Integer;
   UT: WideString;
Begin
   If (FLastChar = #0) And ((FLastKey = VK_DELETE) Or (FLastKey = 8)) Then
      Exit;

   {$IFDEF ORCA2D_USE_CARBON}
  Exit;
   {$ENDIF}

   UT := WideUpperCase(TextW);
   exit;
   For i := 0 To Items.Count - 1 Do
      If pos(UT, WideUpperCase(Items.Strings[i])) = 1 Then
      Begin
         l := length(TextW);
         TextW := Items.Strings[i];
         SelStart := l;
         SelLength := length(Items.Strings[i]) - l;
         Exit;
      End;
End;

Function TD2ComboTextBox.GetListBoxResource: String;
Begin
   Result := FListBox.Resource;
End;

Procedure TD2ComboTextBox.SetListBoxResource(Const Value: String);
Begin
   FListBox.Resource := Value;
End;

//===================== TD2ComboTrackBar ====================================

Constructor TD2ComboTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FFilterChar := '0123456789.,-+';
   TextW := '0';
   Width := 100;
   Height := 22;
   FResource := 'combotextboxstyle';
   FPopup := TD2Popup.Create(Self);
   FPopup.FResource := 'combopopupstyle';
   FPopup.PlacementTarget := Self;
   FPopup.StaysOpen := False;
   FPopup.Stored := False;
   FPopup.Parent := Self;
   FPopup.Locked := True;
   FPopup.DesignHide := True;
   FPopup.Margins.Rect := d2Rect(5, 2, 5, 2);
   FPopup.OnClosePopup := DoClosePopup;
   FTrackBar := TD2TrackBar.Create(Self);
   FTrackBar.Parent := FPopup;
   FTrackBar.Stored := False;
   FTrackBar.DisableFocusEffect := True;
   FTrackBar.Align := vaVertCenter;
   FTrackBar.OnChange := DoTrackChange;
End;

Destructor TD2ComboTrackBar.Destroy;
Begin
   Inherited;
End;

Procedure TD2ComboTrackBar.DoTrackChange(Sender: TObject);
Begin
   TextW := d2FloatToStr(FTrackBar.Value);
   SelectAll;
   If Assigned(FOnChange) Then
      FOnChange(Self);
End;

Procedure TD2ComboTrackBar.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
{  if Count > 0 then
  begin
    case Key of
      VK_UP:If ItemIndex > 0 then
        begin
          ItemIndex:=ItemIndex - 1;
          if ItemIndex < 0 then ItemIndex:=0;
        end;
      VK_DOWN:
        begin
          If ItemIndex < Count - 1 then ItemIndex:=ItemIndex + 1;
          if ItemIndex > Count - 1 then ItemIndex:=Count - 1;
        end;
    else
      Exit;
    end;
    Key:=0;
  end; }
End;

Procedure TD2ComboTrackBar.DoClosePopup(Sender: TObject);
Begin
   If ShowCaret And IsFocused Then
      ShowCaretProc;
End;

Procedure TD2ComboTrackBar.DropDown;
Begin
   If Not FPopup.IsOpen Then
   Begin
      If ShowCaret Then
         HideCaret;

      FPopup.Placement := FPlacement;

      If Width < 100 Then
         FPopup.Width := 100
      Else
         FPopup.Width := Width;

      FPopup.Height := 30;
      FTrackBar.ApplyResource;
      FPopup.IsOpen := True;
   End Else Begin
      FPopup.IsOpen := False;
   End;
End;

Procedure TD2ComboTrackBar.Change;
Begin
   Try
      Value := StrToFloat(TextW);
   Except
      FText := FloatToStr(Value);
   End;
   Repaint;
   Inherited;
End;

Procedure TD2ComboTrackBar.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If (Button = mbLeft) And Not d2PtInRect(d2Point(X, Y), ContentRect) Then
   Begin
      DropDown;
   End;
End;

Procedure TD2ComboTrackBar.ChangeParent;
Begin
   Inherited;
   FPopup.Parent := Parent;
End;

Function TD2ComboTrackBar.GetFrequency: Single;
Begin
   Result := FTrackBar.Frequency;
End;

Procedure TD2ComboTrackBar.SetFrequency(Const Value: Single);
Begin
   FTrackBar.Frequency := Value;
End;

Function TD2ComboTrackBar.GetMax: Single;
Begin
   Result := FTrackBar.Max;
End;

Procedure TD2ComboTrackBar.SetMax(Const Value: Single);
Begin
   FTrackBar.Max := Value;
End;

Function TD2ComboTrackBar.GetMin: Single;
Begin
   Result := FTrackBar.Min;
End;

Procedure TD2ComboTrackBar.SetMin(Const Value: Single);
Begin
   FTrackBar.Min := Value;
End;

Procedure TD2ComboTrackBar.SetValue(Const AValue: Single);
Begin
   FTrackBar.Value := AValue;
End;

Function TD2ComboTrackBar.GetValue: Single;
Begin
   Result := FTrackBar.Value;
End;

//================= TD2TextBoxClearBtn ======================================

Constructor TD2TextBoxClearBtn.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2TextBoxClearBtn.Destroy;
Begin
   Inherited;
End;

Procedure TD2TextBoxClearBtn.ApplyStyle;
Var
   B: TD2Object;
Begin
   Inherited;
   B := FindResource('clearbutton');
   If (B <> nil) And (B Is TD2CustomButton) Then
   Begin
      FClearBtn := TD2CustomButton(B);
      FClearBtn.OnClick := DoClearBtnClick;
   End;
End;

Procedure TD2TextBoxClearBtn.DoClearBtnClick(Sender: TObject);
Begin
   SetFocus;
   TextW := '';
   Change;
End;

Procedure TD2TextBoxClearBtn.FreeStyle;
Begin
   FClearBtn := nil;
   Inherited;
End;

//==================== TD2HudComboTextBox ==============================

Constructor TD2HudComboTextBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FListBox.FResource := 'hudcombolistboxstyle';
   FResource := 'hudcombotextboxstyle';
End;

//==================== TD2HudComboTrackBar =============================

Constructor TD2HudComboTrackBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'hudcombotextboxstyle';
   FPopup.FResource := 'hudcombopopupstyle';
   FTrackBar.FResource := 'hudtrackbarstyle';
End;


//=============== TD2StringComboBox =======================================

Constructor TD2StringComboBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FAutoTranslate := True;
   FItems := TD2WideStringList.Create;
   TD2WideStringList(FItems).OnChange := DoItemsChanged;
   FItemHeight := 19;
   FNeedResource := True;
   FResource := 'comboboxstyle';
   FItemStyle := 'listboxitemstyle';
End;

Destructor TD2StringComboBox.Destroy;
Begin
   FreeAndNil(FItems);
   Inherited;
End;

Procedure TD2StringComboBox.Clear;
Begin
   Inherited;
End;

Procedure TD2StringComboBox.DoItemsChanged(Sender: TObject);
Begin
   RebuildList;
End;

Procedure TD2StringComboBox.RebuildList;
Var
   SaveI, i: Integer;
   Item: TD2ListBoxItem;
Begin
   If csDestroying In ComponentState Then
      Exit;
   If Items = nil Then
      Exit;
   BeginUpdate;
   SaveI := FListbox.ItemIndex;
   FListbox.FItemIndex := -1;
   Clear;

   For i := 0 To FItems.Count - 1 Do
   Begin
      Item := TD2ListBoxItem.Create(Self);
      Item.AutoTranslate := FAutoTranslate;
      Item.Parent := Self;
      Item.Height := FItemHeight;
      Item.Stored := False;
      Item.Locked := True;
      Item.FResource := FItemStyle;
      Item.TextW := FItems[i];
      Item.TextAlign := TextAlign;
   End;

   EndUpdate;
   FListbox.FItemIndex := SaveI;
   If FListbox.FItemIndex >= FListbox.Count Then
      FListbox.FItemIndex := FListbox.Count - 1;
End;

Procedure TD2StringComboBox.SetItemHeight(Const Value: Single);
Begin
   If FItemHeight <> Value Then
   Begin
      FItemHeight := Value;
      RebuildList;
   End;
End;

Procedure TD2StringComboBox.SetItems(Const Value: TD2WideStrings);
Begin
   FItems.Assign(Value);
End;

Procedure TD2StringComboBox.SetTextAlign(Const Value: TD2TextAlign);
Begin
   If FTextAlign <> Value Then
   Begin
      FTextAlign := Value;
      If Not (csLoading In ComponentState) Then
         RebuildList;
   End;
End;

Procedure TD2StringComboBox.ApplyStyle;
Begin
   Inherited;
End;

Function TD2StringComboBox.GetData: Variant;
Begin
   If (ItemIndex >= 0) And (ItemIndex < FItems.Count) Then
      Result := FItems[ItemIndex]
   Else
      Result := '';
End;

Procedure TD2StringComboBox.SetData(Const Value: Variant);
Begin
   If (ItemIndex >= 0) And (ItemIndex < FItems.Count) Then
      FItems[ItemIndex] := Value;
End;

//=============== TD2HudStringComboBox ==================================

Constructor TD2HudStringComboBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FNeedResource := True;
   FResource := 'hudcomboboxstyle';
   FListBox.FResource := 'hudcombolistboxstyle';
   FItemStyle := 'hudlistboxitemstyle';
End;

//=============== TD2HudComboBox =======================================

Constructor TD2HudComboBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FListBox.FResource := 'hudcombolistboxstyle';
End;

//=============== TD2Panel ===========================================

Constructor TD2Panel.Create(AOwner: TComponent);
Begin
   Inherited;
   Width := 120;
   Height := 100;
End;

//=============== TD2CalloutPanel =====================================

Constructor TD2CalloutPanel.Create(AOwner: TComponent);
Begin
   Inherited;
   FCalloutWidth := 23;
   FCalloutLength := 11;
End;

Procedure TD2CalloutPanel.ApplyStyle;
Var
   Back: TD2Object;
Begin
   Inherited;
   Back := FindResource('Backgound');
   If (Back = nil) And (FResourceLink Is TD2CalloutRectangle) Then
      Back := FResourceLink;
   If (Back <> nil) And (Back Is TD2CalloutRectangle) Then
   Begin
      TD2CalloutRectangle(Back).CalloutWidth := FCalloutWidth;
      TD2CalloutRectangle(Back).CalloutLength := FCalloutLength;
      TD2CalloutRectangle(Back).CalloutPosition := FCalloutPosition;
      TD2CalloutRectangle(Back).CalloutOffset := FCalloutOffset;
   End;
End;

Procedure TD2CalloutPanel.SetCalloutLength(Const Value: Single);
Begin
   If FCalloutLength <> Value Then
   Begin
      FCalloutLength := Value;
      ApplyStyle;
   End;
End;

Procedure TD2CalloutPanel.SetCalloutPosition(Const Value: TD2CalloutPosition);
Begin
   If FCalloutPosition <> Value Then
   Begin
      FCalloutPosition := Value;
      ApplyStyle;
   End;
End;

Procedure TD2CalloutPanel.SetCalloutWidth(Const Value: Single);
Begin
   If FCalloutWidth <> Value Then
   Begin
      FCalloutWidth := Value;
      ApplyStyle;
   End;
End;

Procedure TD2CalloutPanel.SetCalloutOffset(Const Value: Single);
Begin
   If FCalloutOffset <> Value Then
   Begin
      FCalloutOffset := Value;
      ApplyStyle;
   End;
End;

//================== TD2ColorPanel ================================

Constructor TD2ColorPanel.Create(AOwner: TComponent);
Begin
   Inherited;
   FUseAlpha := True;
   Width := 150;
   Height := 150;
   FAlphaTrack := TD2AlphaTrackBar.Create(Self);
   FAlphaTrack.Parent := Self;
   FAlphaTrack.Align := vaBottom;
   FAlphaTrack.Stored := False;
   FAlphaTrack.Locked := True;
   FAlphaTrack.Padding.Rect := d2Rect(0, 0, 15, 0);
   FAlphaTrack.Height := 15;
   FAlphaTrack.DisableFocusEffect := True;
   FAlphaTrack.OnChange := DoAlphaChange;

   FHueTrack := TD2HueTrackBar.Create(Self);
   FHueTrack.Parent := Self;
   FHueTrack.Align := vaRight;
   FHueTrack.Stored := False;
   FHueTrack.Locked := True;
   FHueTrack.Padding.Rect := d2Rect(0, 0, 0, 0);
   FHueTrack.Orientation := d2Vertical;
   FHueTrack.Width := 15;
   FHueTrack.DisableFocusEffect := True;
   FHueTrack.OnChange := DoHueChange;

   FColorQuad := TD2ColorQuad.Create(Self);
   FColorQuad.Parent := Self;
   FColorQuad.Align := vaClient;
   FColorQuad.Stored := False;
   FColorQuad.Locked := True;
   FColorQuad.Padding.Rect := d2Rect(5, 5, 3, 3);
   FColorQuad.OnChange := DoQuadChange;

   Color := vcWhite;
End;

Destructor TD2ColorPanel.Destroy;
Begin
   Inherited;
End;

Procedure TD2ColorPanel.DoAlphaChange(Sender: TObject);
Begin
   FColorQuad.Alpha := FAlphaTrack.Value;
End;

Procedure TD2ColorPanel.DoHueChange(Sender: TObject);
Begin
   FColorQuad.Hue := FHueTrack.Value;
End;

Procedure TD2ColorPanel.DoQuadChange(Sender: TObject);
Begin
   If FColorBox <> nil Then
      FColorBox.Color := d2StrToColor(Color);
   If Assigned(OnChange) Then
      OnChange(Self);
End;

Function TD2ColorPanel.GetColor: String;
Begin
   Result := d2ColorToStr(d2Opacity(d2HSLtoRGB(FColorQuad.Hue, FColorQuad.Sat, FColorQuad.Lum), FColorQuad.Alpha));
End;

Procedure TD2ColorPanel.Loaded;
Begin
   Inherited;
   Color := Color;
End;

Procedure TD2ColorPanel.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FColorBox) Then
      ColorBox := nil;
End;

Procedure TD2ColorPanel.SetColor(Const Value: String);
Var
   H, S, L: Single;
   C: TD2Color;
   SaveOnChange: TNotifyEvent;
Begin
   SaveOnChange := FOnChange;
   FOnChange := nil;

   C := d2StrToColor(Value);
   d2RGBtoHSL(C, H, S, L);
   FColorQuad.Lum := L;
   FColorQuad.Sat := S;
   FHueTrack.Value := H;
   FAlphaTrack.Value := TD2ColorRec(C).A / $FF;

   FOnChange := SaveOnChange;

   If Not (csLoading In ComponentState) Then
      DoQuadChange(Self);
End;

Procedure TD2ColorPanel.SetColorBox(Const Value: TD2ColorBox);
Begin
   If FColorBox <> Value Then
   Begin
      FColorBox := Value;
      If FColorBox <> nil Then
         FColorBox.Color := d2StrToColor(Color);
   End;
End;

Procedure TD2ColorPanel.SetUseAlpha(Const Value: Boolean);
Begin
   If FUseAlpha <> Value Then
   Begin
      FUseAlpha := Value;
      FAlphaTrack.Visible := FUseAlpha;
   End;
End;

//=============== TD2StatusBar =====================================

Constructor TD2StatusBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FShowSizeGrip := True;
   Height := 22;
   Align := vaBottom;
End;

Procedure TD2StatusBar.ApplyStyle;
Var
   sizeGrip: TD2Object;
Begin
   Inherited;
   sizeGrip := FindResource('sizegrip');
   If (sizeGrip <> nil) And (sizeGrip Is TD2VisualObject) Then
   Begin
      TD2VisualObject(sizeGrip).Visible := FShowSizeGrip;
      If (Scene <> nil) And Not (Scene.GetDesignTime) Then
      Begin
         TD2VisualObject(sizeGrip).Locked := False;
         TD2VisualObject(sizeGrip).HitTest := True;
      End;
   End;
End;

Procedure TD2StatusBar.SetShowSizeGrip(Const Value: Boolean);
Begin
   If FShowSizeGrip <> Value Then
   Begin
      FShowSizeGrip := Value;
      Resource := FResource;
   End;
End;

//=============== TD2ToolBar =========================================

Constructor TD2ToolBar.Create(AOwner: TComponent);
Begin
   Inherited;
   Height := 40;
   Align := vaTop;
End;


//=============== TD2DockBar ========================================

Constructor TD2DockBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FMinSize := 32;
   FMaxSize := 64;
End;

Destructor TD2DockBar.Destroy;
Begin
   Inherited;
End;

Procedure TD2DockBar.MouseLeave;
Begin
   Inherited;
   FMousePos := d2Point(0, 0);
   Realign;
End;

Procedure TD2DockBar.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
End;

Procedure TD2DockBar.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   FMousePos := d2Point(X, Y);
   Realign;
End;

Procedure TD2DockBar.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
End;

Procedure TD2DockBar.Paint;
Begin
   Inherited;
End;

Procedure TD2DockBar.Realign;
Var
   i, j: Integer;
   dist, Pos, MaxWidth, Amplitude: Single;
   hot: Boolean;
   List: TList;
Begin
   Inherited;
   If FChildren = nil Then
      Exit;
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;
   Try

      List := TList.Create;
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If Not TD2Object(FChildren[i]).isVisual Then
            Continue;
         If Not TD2VisualObject(FChildren[i]).Visible Then
            Continue;

         If List.Count > 0 Then
         Begin
            For j := 0 To List.Count - 1 Do
            Begin
               If TD2VisualObject(FChildren[i]).Position.X < TD2VisualObject(List[j]).Position.X Then
                  Break;
            End;
            List.Insert(j, FChildren[i]);
         End Else
            List.Add(FChildren[i]);
      End;

      If FMousePos.X = 0 Then
      Begin
         MaxWidth := List.Count * MinSize;
         For i := 0 To List.Count - 1 Do
         Begin
            TD2VisualObject(List[i]).Position.Y := Height - MinSize;
            TD2VisualObject(List[i]).Position.X := (Width / 2) - (MaxWidth / 2) + (i * MinSize);
            TD2VisualObject(List[i]).Height := MinSize;
            TD2VisualObject(List[i]).Width := MinSize;
         End;
      End Else Begin
         Amplitude := FMaxSize * 1.5;
         MaxWidth := (List.Count * MinSize);

         hot := False;
         For i := 0 To List.Count - 1 Do
         Begin
            If (FMousePos.X >= TD2VisualObject(List[i]).Position.X) And (FMousePos.X <= TD2VisualObject(List[i]).Position.X + TD2VisualObject(List[i]).Width) Then
            Begin
               hot := True;
               Break;
            End;
         End;

         For i := 0 To List.Count - 1 Do
         Begin
            If (FMousePos.X >= TD2VisualObject(List[i]).Position.X) And (FMousePos.X <= TD2VisualObject(List[i]).Position.X + TD2VisualObject(List[i]).Width) Then
            Begin
               TD2VisualObject(List[i]).Width := MaxSize;
               TD2VisualObject(List[i]).Height := MaxSize;
               MaxWidth := MaxWidth + (MaxSize - MinSize);
            End Else Begin
               dist := (TD2VisualObject(List[i]).Position.X + (TD2VisualObject(List[i]).Width / 2)) - FMousePos.X;

               If (Abs(dist) < Amplitude) And (hot) Then
               Begin
                  TD2VisualObject(List[i]).Width := MinSize + Sin((Pi / 2) + ((dist / Amplitude) * (Pi / 2))) * (MaxSize - MinSize);
                  TD2VisualObject(List[i]).Height := TD2VisualObject(List[i]).Width;
                  MaxWidth := MaxWidth + (TD2VisualObject(List[i]).Width - MinSize);
               End Else Begin
                  TD2VisualObject(List[i]).Width := MinSize;
                  TD2VisualObject(List[i]).Height := MinSize;
               End;
            End;
         End;

         Pos := ((Width / 2) - (MaxWidth / 2));

         For i := 0 To List.Count - 1 Do
         Begin
            TD2VisualObject(List[i]).Position.Y := Height - TD2VisualObject(List[i]).Height - 1;
            TD2VisualObject(List[i]).Position.X := Pos;
            Pos := Pos + TD2VisualObject(List[i]).Height;
         End;
      End;

      List.Free;
   Finally
      FDisableAlign := False;
   End;
End;

Procedure TD2DockBar.SetMaxSize(Const Value: Single);
Begin
   FMaxSize := Value;
End;

Procedure TD2DockBar.SetMinSize(Const Value: Single);
Begin
   FMinSize := Value;
End;

//=============== TD2TextControl =============================

Constructor TD2TextControl.Create(AOwner: TComponent);
Begin
   Inherited;
   FFont := TD2Font.Create;
   FFont.OnChanged := FontChanged;
   FFontFill := TD2Brush.Create(d2BrushSolid, $FF000000);
   FFontFill.OnChanged := FontFillChanged;
   FWordWrap := False;
End;

Destructor TD2TextControl.Destroy;
Begin
   FFontFill.Free;
   FFont.Free;
   Inherited;
End;

Function TD2TextControl.GetData: Variant;
Begin
   Result := TextW;
End;

Procedure TD2TextControl.SetData(Const Value: Variant);
Begin
   If VarIsNull(Value) Then
      TextW := ''
   Else
   If VarIsType(Value, varDate) Then
      TextW := DateTimeToStr(VarToDateTime(Value))
   Else
      TextW := VarToWideStr(Value);
End;

Procedure TD2TextControl.ApplyStyle;
Var
   S: TD2Object;
   NewT: WideString;
Begin
   Inherited;

   S := FindResource('foreground');
   If (S <> nil) And (S Is TD2BrushObject) Then
      FontFill.Assign(TD2BrushObject(S).Brush);

   S := FindResource('text');
   If (S <> nil) And (S Is TD2Text) Then
   Begin
      TD2Text(S).TextW := FText;
      TD2Text(S).HorzTextAlign := FTextAlign;
      TD2Text(S).VertTextAlign := FVertTextAlign;
      TD2Text(S).WordWrap := FWordWrap;
      TD2Text(S).Font.Assign(FFont);
   End;

   If FAutoTranslate Then
   Begin
      NewT := Translate(TextW);
      If (FScene <> nil) And Not (FScene.GetDesignTime) Then
         TextW := NewT;
   End;
End;

Procedure TD2TextControl.FontChanged(Sender: TObject);
Var
   T: TD2Object;
Begin
   T := FindResource('text');
   If (T <> nil) And (T Is TD2Text) Then
      TD2Text(T).Font.Assign(FFont);
End;

Procedure TD2TextControl.FontFillChanged(Sender: TObject);
Var
   T: TD2Object;
Begin
   T := FindResource('text');
   If (T <> nil) And (T Is TD2Text) Then
      TD2Text(T).Fill.Assign(FontFill);
End;

Procedure TD2TextControl.SetTextW(Const Value: WideString);
Var
   T: TD2Object;
Begin
   If FText <> Value Then
   Begin
      FText := Value;
      T := FindResource('text');
      If (T <> nil) And (T Is TD2Text) Then
      Begin
         TD2Text(T).TextW := FText;
         TD2Text(T).UpdateEffects;
      End Else
      If (FResourceLink <> nil) And (FResourceLink Is TD2Text) Then
         TD2Text(FResourceLink).TextW := FText
      Else
         Repaint;
      UpdateEffects;
   End;
End;

Function TD2TextControl.GetTextW: WideString;
Begin
   Result := Ftext;
End;

Procedure TD2TextControl.SetText(Const Value: String);
Var
   ss: WideString;
Begin
   ss := UTF8Decode(Value);
   SetTextW(ss);
End;

Function TD2TextControl.GetText: String;
Begin
   Result := UTF8Encode(Ftext);
End;

Procedure TD2TextControl.SetFontFill(Const Value: TD2Brush);
Begin
   FFontFill := Value;
End;

Procedure TD2TextControl.SetFont(Const Value: TD2Font);
Begin
   FFont.Assign(Value);
End;

Procedure TD2TextControl.SetTextAlign(Const Value: TD2TextAlign);
Var
   T: TD2Object;
Begin
   FTextAlign := Value;
   T := FindResource('text');
   If (T <> nil) And (T Is TD2Text) Then
      TD2Text(T).HorzTextAlign := FTextAlign
   Else
      Repaint;
End;

Procedure TD2TextControl.SetVertTextAlign(Const Value: TD2TextAlign);
Var
   T: TD2Object;
Begin
   FVertTextAlign := Value;
   T := FindResource('text');
   If (T <> nil) And (T Is TD2Text) Then
      TD2Text(T).VertTextAlign := FVertTextAlign
   Else
      Repaint;
End;

Procedure TD2TextControl.SetWordWrap(Const Value: Boolean);
Var
   T: TD2Object;
Begin
   FWordWrap := Value;
   T := FindResource('text');
   If (T <> nil) And (T Is TD2Text) Then
      TD2Text(T).WordWrap := Value;
End;

//=============== TD2Label =========================================

Constructor TD2Label.Create(AOwner: TComponent);
Begin
   Inherited;
   FAutoTranslate := True;
   Width := 120;
   Height := 15;
   FWordWrap := True;
   HitTest := False;
End;

Procedure TD2Label.ApplyStyle;
Var
   T: TD2Object;
   S: TD2Align;
Begin
   Inherited;
   T := FindResource('text');
   If (T <> nil) And (T Is TD2Text) Then
   Begin
      TD2Text(T).WordWrap := WordWrap;
      If AutoSize Then
      Begin
         FWordWrap := False;
         TD2Text(T).WordWrap := False;
         TD2Text(T).VertTextAlign := d2TextAlignNear;
         TD2Text(T).HorzTextAlign := d2TextAlignNear;

         S := TD2Text(T).Align;
         TD2Text(T).Align := vaNone;
         TD2Text(T).AutoSize := True;
         Width := TD2Text(T).Width;
         Height := TD2Text(T).Height;
         TD2Text(T).AutoSize := False;
         TD2Text(T).Align := S;
      End;
   End;
End;

Procedure TD2Label.SetWordWrap(Const Value: Boolean);
Var
   T: TD2Object;
Begin
   If FWordWrap <> Value Then
   Begin
      FWordWrap := Value;
      T := FindResource('text');
      If (T <> nil) And (T Is TD2Text) Then
         TD2Text(T).WordWrap := Value;
   End;
End;

Procedure TD2Label.SetAutoSize(Const Value: Boolean);
Begin
   If FAutoSize <> Value Then
   Begin
      FAutoSize := Value;
      If FAutoSize Then
      Begin
         ApplyStyle;
      End;
   End;
End;

Procedure TD2Label.SetText(Const Value: String);
Var
   ss: WideString;
Begin
   ss := UTF8Decode(Value);

   If FText <> ss Then
   Begin
      Inherited;
      If FAutoSize Then
         ApplyStyle;
   End Else Begin
      Inherited;
   End;
End;

//=============== TD2ValueLabel ==============================

Constructor TD2ValueLabel.Create(AOwner: TComponent);
Begin
   Inherited;
   FAutoTranslate := False;
   FWordWrap := False;
End;

//=============== TD2CustomButton ================================

Procedure TD2CustomButton.ApplyStyle;
Begin
   Inherited;
   StartTriggerAnimation(Self, 'IsPressed');
   ApplyTriggerEffect(Self, 'IsPressed');
End;

Constructor TD2CustomButton.Create(AOwner: TComponent);
Begin
   Inherited;
   FAutoTranslate := True;
   FWordWrap := False;
   Width := 80;
   Height := 22;
   AutoCapture := True;
   CanFocused := True;
End;

Destructor TD2CustomButton.Destroy;
Begin
   If FRepeatTimer <> nil Then
      FRepeatTimer.Free;
   Inherited;
End;

Procedure TD2CustomButton.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
   If (Key = VK_RETURN) Or (Key = VK_SPACE) Then
   Begin
      Click;
   End;
End;

Procedure TD2CustomButton.DoRepeatTimer(Sender: TObject);
Begin
   Click;
End;

Procedure TD2CustomButton.DoRepeatDelayTimer(Sender: TObject);
Begin
   FRepeatTimer.OnTimer := DoRepeatTimer;
   FRepeatTimer.Interval := 100;
End;


Procedure TD2CustomButton.Click;
Var
   O: TComponent;
Begin
   If Assigned(Self) And (ModalResult <> mrNone) Then
   Begin
      O := Scene.GetComponent;
      While O <> nil Do
      Begin
         If (O Is TCustomForm) Then
         Begin
            TCustomForm(O).ModalResult := FModalResult;
            Break;
         End;
         O := O.Owner;
      End;
   End;
   Inherited;
End;

Procedure TD2CustomButton.DblClick;
Begin
   Inherited;
   Click;
End;

Procedure TD2CustomButton.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      OnClick := VariantToEvent(Value);
End;

Procedure TD2CustomButton.SetIsPressed(Const Value: Boolean);
Begin
   If FStaysPressed Then
   Begin
      If Value <> FIsPressed Then
      Begin
         FIsPressed := Value;
         StartTriggerAnimation(Self, 'IsPressed');
         ApplyTriggerEffect(Self, 'IsPressed');
      End;
   End;
End;

Procedure TD2CustomButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      FPressing := True;
      If FStaysPressed Then
         FIsPressed := Not FIsPressed
      Else Begin
         FIsPressed := True;
         If FRepeat Then
         Begin
            If FRepeatTimer = nil Then
            Begin
               FRepeatTimer := TD2Timer.Create(Self);
               FRepeatTimer.Interval := 500;
            End;
            FRepeatTimer.OnTimer := DoRepeatDelayTimer;
            FRepeatTimer.Enabled := True;
         End;
      End;
      StartTriggerAnimation(Self, 'IsPressed');
      ApplyTriggerEffect(Self, 'IsPressed');
   End;
End;

Procedure TD2CustomButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   If FPressing Then
   Begin
      If FRepeatTimer <> nil Then
         FRepeatTimer.Enabled := False;
      FPressing := False;
      If Not FStaysPressed Then
      Begin
         FIsPressed := False;
         StartTriggerAnimation(Self, 'IsPressed');
         ApplyTriggerEffect(Self, 'IsPressed');
      End;
   End;
   Inherited;
End;

Procedure TD2CustomButton.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If (ssLeft In Shift) And (FPressing) Then
   Begin
      If FIsPressed <> d2PtInRect(d2Point(X, Y), LocalRect) Then
      Begin
         If Not FStaysPressed Then
         Begin
            FIsPressed := d2PtInRect(d2Point(X, Y), LocalRect);
            StartTriggerAnimation(Self, 'IsPressed');
            ApplyTriggerEffect(Self, 'IsPressed');
         End;
      End;
   End;
End;


//=============== TD2Button ==================================

Procedure TD2Button.DialogKey(Var Key: Word; Shift: TShiftState);
Begin
   Inherited;
   If Default And (Key = VK_RETURN) Then
   Begin
      Click;
      Key := 0;
   End;
   If Cancel And (Key = VK_ESCAPE) Then
   Begin
      Click;
      Key := 0;
   End;
End;

//=============== TD2CircleButton =============================

Constructor TD2CircleButton.Create(AOwner: TComponent);
Begin
   Inherited;
   Width := 21;
   Height := 21;
End;

//=============== TD2PopupButton =================================

Constructor TD2PopupButton.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Procedure TD2PopupButton.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FPopupMenu) Then
      FPopupMenu := nil;
End;

Procedure TD2PopupButton.Click;
Var
   VP: TD2Point;
Begin
   Inherited;
   If FPopupMenu = nil Then
      Exit;
   If Scene <> nil Then
   Begin
      VP := LocalToAbsolute(d2Point(0, Height));
      VP := Scene.LocalToScreen(VP);
      FPopupMenu.Popup(round(VP.X), round(VP.Y));
   End;
End;

//=============== TD2BitmapButton ==================================

Constructor TD2BitmapButton.Create(AOwner: TComponent);
Begin
   Inherited;
   FBitmap := TD2Bitmap.Create(1, 1);
   FBitmap.OnChange := DoBitmapChanged;
   FBitmapLayout := d2GlyphLeft;
   FBitmapSize := 32;
   FBitmapPadding := 2;
   Width := 50;
   Height := 60;
End;

Destructor TD2BitmapButton.Destroy;
Begin
   FreeAndNil(FBitmap);
   Inherited;
End;

Procedure TD2BitmapButton.ActionChange(Sender: TObject; CheckDefaults: Boolean);

   Procedure CopyImage(ImageList: TCustomImageList; Index: Integer);
   Begin
      BitmapSize := ImageList.Width;
      If ImageList Is TD2ImageList Then
         Bitmap.Assign(TD2ImageList(ImageList).Images[Index]);
   End;

Begin
   Inherited ActionChange(Sender, CheckDefaults);
   If Sender Is TCustomAction Then
      With TCustomAction(Sender) Do
      Begin
         If (Bitmap.Width <= 1) And (ActionList <> nil) And (ActionList.Images <> nil) And (ActionList.Images Is TD2ImageList) And (ImageIndex >= 0) And (ImageIndex < TD2ImageList(ActionList.Images).Count) Then
            CopyImage(ActionList.Images, ImageIndex);
      End;
End;

Procedure TD2BitmapButton.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   DoBitmapChanged(Self);
   T := FindResource('text');
   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      TD2VisualObject(T).Padding.Rect := d2Rect(FBitmapPadding, FBitmapPadding, FBitmapPadding, FBitmapPadding);
   End;
End;

Procedure TD2BitmapButton.DoBitmapChanged(Sender: TObject);
Var
   T: TD2Object;
Begin
   T := FindResource('image');
   If (T <> nil) And (T Is TD2Image) Then
   Begin
      TD2Image(T).Bitmap.Assign(FBitmap);
      TD2VisualObject(T).Padding.Rect := d2Rect(FBitmapPadding, FBitmapPadding, FBitmapPadding, FBitmapPadding);
      If ((TD2Image(T).Bitmap.Width = 1) Or (TD2Image(T).Bitmap.Height = 1)) And (TD2Image(T).Bitmap.ResourceName = '') Then
      Begin
         TD2VisualObject(T).Align := vaNone;
      End Else
         Case FBitmapLayout Of
            d2GlyphLeft:
            Begin
               TD2VisualObject(T).Align := vaLeft;
               TD2VisualObject(T).Width := FBitmapSize;
               TD2VisualObject(T).Padding.right := FBitmapSpacing;
            End;
            d2GlyphRight:
            Begin
               TD2VisualObject(T).Align := vaRight;
               TD2VisualObject(T).Width := FBitmapSize;
               TD2VisualObject(T).Padding.left := FBitmapSpacing;
            End;
            d2GlyphCenter:
            Begin
               TD2VisualObject(T).Align := vaCenter;
               TD2VisualObject(T).Width := FBitmapSize;
               TD2VisualObject(T).Height := FBitmapSize;
               TD2VisualObject(T).Padding.Rect := d2Rect(FBitmapSpacing, FBitmapSpacing, FBitmapSpacing, FBitmapSpacing);
            End;
            d2GlyphTop:
            Begin
               TD2VisualObject(T).Align := vaTop;
               TD2VisualObject(T).Height := FBitmapSize;
               TD2VisualObject(T).Padding.bottom := FBitmapSpacing;
            End;
            d2GlyphBottom:
            Begin
               TD2VisualObject(T).Align := vaBottom;
               TD2VisualObject(T).Height := FBitmapSize;
               TD2VisualObject(T).Padding.top := FBitmapSpacing;
            End;
         End;
   End;
End;

Procedure TD2BitmapButton.SetBitmap(Const Value: TD2Bitmap);
Begin
   FBitmap.Assign(Value);
End;

Procedure TD2BitmapButton.SetBitmapLayout(Const Value: TD2ButtonLayout);
Begin
   If FBitmapLayout <> Value Then
   Begin
      FBitmapLayout := Value;
      ApplyStyle;
   End;
End;

Procedure TD2BitmapButton.SetBitmapSpacing(Const Value: Single);
Begin
   If FBitmapSpacing <> Value Then
   Begin
      FBitmapSpacing := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2BitmapButton.SetBitmapPadding(Const Value: Single);
Begin
   If FBitmapPadding <> Value Then
   Begin
      FBitmapPadding := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2BitmapButton.SetBitmapSize(Const Value: Single);
Begin
   If FBitmapSize <> Value Then
   Begin
      FBitmapSize := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

//=============== TD2PathButton =================================

Constructor TD2PathButton.Create(AOwner: TComponent);
Begin
   Inherited;
   FPath := TD2PathData.Create;
   FPath.OnChanged := DoPathChanged;
   FPathLayout := d2GlyphLeft;
   FPathSize := 32;
   FPathPadding := 2;
   FFill := TD2Brush.Create(d2BrushSolid, $FFFFFFFF);
   FFill.OnChanged := DoPathChanged;
   FStroke := TD2Brush.Create(d2BrushSolid, $FF000000);
   FStroke.SolidColor := $FF000000;
   FStroke.OnChanged := DoPathChanged;
   FStrokeThickness := 1;
   Width := 50;
   Height := 60;
End;

Destructor TD2PathButton.Destroy;
Begin
   FFill.Free;
   FStroke.Free;
   FPath.Free;
   Inherited;
End;

Procedure TD2PathButton.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('path');
   If (T <> nil) And (T Is TD2Path) Then
   Begin
      TD2Path(T).Data.Assign(FPath);
      TD2Path(T).Fill.Assign(FFill);
      TD2Path(T).Stroke.Assign(FStroke);
      TD2Path(T).StrokeThickness := FStrokeThickness;
      TD2Path(T).StrokeCap := FStrokeCap;
      TD2Path(T).StrokeJoin := FStrokeJoin;
      TD2Path(T).StrokeDash := FStrokeDash;
   End;
   T := FindResource('pathowner');
   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      TD2VisualObject(T).Padding.Rect := d2Rect(FPathPadding, FPathPadding, FPathPadding, FPathPadding);
      If (Length(FPath.PathData) = 0) Then
      Begin
         TD2VisualObject(T).Align := vaNone;
      End Else
         Case FPathLayout Of
            d2GlyphLeft:
            Begin
               TD2VisualObject(T).Align := vaLeft;
               TD2VisualObject(T).Width := FPathSize;
               TD2VisualObject(T).Padding.right := FPathSpacing;
            End;
            d2GlyphRight:
            Begin
               TD2VisualObject(T).Align := vaRight;
               TD2VisualObject(T).Width := FPathSize;
               TD2VisualObject(T).Padding.left := FPathSpacing;
            End;
            d2GlyphTop:
            Begin
               TD2VisualObject(T).Align := vaTop;
               TD2VisualObject(T).Height := FPathSize;
               TD2VisualObject(T).Padding.bottom := FPathSpacing;
            End;
            d2GlyphBottom:
            Begin
               TD2VisualObject(T).Align := vaBottom;
               TD2VisualObject(T).Height := FPathSize;
               TD2VisualObject(T).Padding.top := FPathSpacing;
            End;
            d2GlyphCenter:
            Begin
               TD2VisualObject(T).Align := vaCenter;
               TD2VisualObject(T).Width := FPathSize;
               TD2VisualObject(T).Height := FPathSize;
               TD2VisualObject(T).Padding.Rect := d2Rect(FPathSpacing, FPathSpacing, FPathSpacing, FPathSpacing);
            End;
         End;
   End;

   T := FindResource('text');

   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      TD2VisualObject(T).Padding.Rect := d2Rect(FPathPadding, FPathPadding, FPathPadding, FPathPadding);
   End;
End;

Procedure TD2PathButton.DoPathChanged(Sender: TObject);
Begin
   Repaint;
   ApplyStyle;
End;

Procedure TD2PathButton.SetPath(Const Value: TD2PathData);
Begin
   FPath.Assign(Value);
End;

Procedure TD2PathButton.SetPathLayout(Const Value: TD2ButtonLayout);
Begin
   If FPathLayout <> Value Then
   Begin
      FPathLayout := Value;
      ApplyStyle;
   End;
End;

Procedure TD2PathButton.SetPathPadding(Const Value: Single);
Begin
   If FPathPadding <> Value Then
   Begin
      FPathPadding := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2PathButton.SetPathSize(Const Value: Single);
Begin
   If FPathSize <> Value Then
   Begin
      FPathSize := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2PathButton.SetPathSpacing(Const Value: Single);
Begin
   If FPathSpacing <> Value Then
   Begin
      FPathSpacing := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Function TD2PathButton.isStrokeThicknessStored: Boolean;
Begin
   Result := FStrokeThickness <> 1;
End;

Procedure TD2PathButton.SetFill(Const Value: TD2Brush);
Begin
   FFill := Value;
End;

Procedure TD2PathButton.SetStroke(Const Value: TD2Brush);
Begin
   FStroke := Value;
End;

Procedure TD2PathButton.SetStrokeCap(Const Value: TD2StrokeCap);
Begin
   If FStrokeCap <> Value Then
   Begin
      FStrokeCap := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2PathButton.SetStrokeDash(Const Value: TD2StrokeDash);
Begin
   If FStrokeDash <> Value Then
   Begin
      FStrokeDash := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2PathButton.SetStrokeJoin(Const Value: TD2StrokeJoin);
Begin
   If FStrokeJoin <> Value Then
   Begin
      FStrokeJoin := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2PathButton.SetStrokeThickness(Const Value: Single);
Begin
   If FStrokeThickness <> Value Then
   Begin
      FStrokeThickness := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

//=============== TD2ToolButton ==================================

Constructor TD2ToolButton.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
   FBitmapLayout := d2GlyphTop;
End;

//=============== TD2ToolPathButton ==============================

Constructor TD2ToolPathButton.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
   FPathLayout := d2GlyphTop;
End;

//=============== TD2SpeedButton ===================================

Constructor TD2SpeedButton.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
   FRepeat := True;
   Width := 23;
   Height := 23;
   TextW := '';
End;

Destructor TD2SpeedButton.Destroy;
Begin
   Inherited;
End;

//=============== TD2ColorButton =====================================

Constructor TD2ColorButton.Create(AOwner: TComponent);
Begin
   Inherited;
   FAutoTranslate := False;
   FColor := vcBlack;
   FUseStandardDialog := True;
End;

Destructor TD2ColorButton.Destroy;
Begin
   Inherited;
End;

Procedure TD2ColorButton.ApplyStyle;
Var
   O: TD2Object;
Begin
   Inherited;
   O := FindResource('fill');

   If (O <> nil) And (O Is TD2Shape) Then
   Begin
      FFill := TD2Shape(O);
      FFill.Fill.Color := FColor;
   End;
End;

Procedure TD2ColorButton.FreeStyle;
Begin
   Inherited;
   FFill := nil;
End;

Procedure TD2ColorButton.SetColor(Const Value: String);
Begin
   FColor := Value;
   If FFill <> nil Then
      FFill.Fill.Color := FColor;

   If Not (csLoading In ComponentState) Then
      If Assigned(FOnChange) Then
         FOnChange(Self);
End;

Function SwapColor(Const C: TD2Color): TD2Color;
Begin
   Result := C;
   TD2ColorRec(Result).R := TD2ColorRec(C).B;
   TD2ColorRec(Result).B := TD2ColorRec(C).R;
End;

Procedure TD2ColorButton.Click;       // ct8888
Var
   xClrDlg: TColorDialog;
   xBrushDialog: TD2CustomBrushDialog;
Begin
   Inherited;
   If FUseStandardDialog Then
   Begin
      xClrDlg := TColorDialog.Create(nil);
      xClrDlg.Color := SwapColor(d2StrToColor(FColor)) And $FFFFFF;

      If xClrDlg.Execute Then
      Begin
         Color := d2ColorToStr($FF000000 Or SwapColor(xClrDlg.Color));
      End;

      xClrDlg.Free;
   End Else Begin
      If Addr(VarCreateABrushDialog) <> nil Then
      Begin
         xBrushDialog := VarCreateABrushDialog(nil);
         If xBrushDialog = nil Then
            exit;

         xBrushDialog.Brush.Style := d2BrushSolid;
         xBrushDialog.Brush.Color := FColor;
         xBrushDialog.ShowStyles := [d2BrushSolid];
         If xBrushDialog.Execute Then
            Color := xBrushDialog.Brush.Color;

         xBrushDialog.Free;
      End;
   End;
End;

//=============== TD2CornerButton ===============================

Constructor TD2CornerButton.Create(AOwner: TComponent);
Begin
   Inherited;
   FCorners := AllCorners;
   FxRadius := 3;
   FyRadius := 3;
   FSides := AllSides;
End;

Destructor TD2CornerButton.Destroy;
Begin
   Inherited;
End;

Procedure TD2CornerButton.ApplyStyle;
Var
   Background: TD2Object;
Begin
   Inherited;
   Background := FindResource('Background');
   If (Background <> nil) And (Background Is TD2Rectangle) Then
   Begin
      TD2Rectangle(Background).CornerType := FCornerType;
      TD2Rectangle(Background).Corners := FCorners;
      TD2Rectangle(Background).xRadius := xRadius;
      TD2Rectangle(Background).yRadius := yRadius;
      TD2Rectangle(Background).Sides := FSides;
   End;
End;

Function TD2CornerButton.IsCornersStored: Boolean;
Begin
   Result := FCorners <> AllCorners;
End;

Function TD2CornerButton.IsSidesStored: Boolean;
Begin
   Result := FSides * AllSides <> [];
End;

Procedure TD2CornerButton.SetCorners(Const Value: TD2Corners);
Begin
   If FCorners <> Value Then
   Begin
      FCorners := Value;
      ApplyStyle;
   End;
End;

Procedure TD2CornerButton.SetCornerType(Const Value: TD2CornerType);
Begin
   If FCornerType <> Value Then
   Begin
      FCornerType := Value;
      ApplyStyle;
   End;
End;

Procedure TD2CornerButton.SetxRadius(Const Value: Single);
Begin
   If FxRadius <> Value Then
   Begin
      FxRadius := Value;
      ApplyStyle;
   End;
End;

Procedure TD2CornerButton.SetyRadius(Const Value: Single);
Begin
   If FyRadius <> Value Then
   Begin
      FyRadius := Value;
      ApplyStyle;
   End;
End;

Procedure TD2CornerButton.SetSides(Const Value: TD2Sides);
Begin
   If FSides <> Value Then
   Begin
      FSides := Value;
      ApplyStyle;
   End;
End;

//=============== TD2CloseButton ======================================

Constructor TD2CloseButton.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
   CloseForm := True;
   Width := 20;
   Height := 20;
End;

Destructor TD2CloseButton.Destroy;
Begin
   Inherited;
End;

Procedure TD2CloseButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
      FPressing := True;
End;

Procedure TD2CloseButton.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
End;

Procedure TD2CloseButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   O: TComponent;
Begin
   Inherited;
   If FPressing Then
   Begin
      If FCloseForm And (Scene <> nil) Then
      Begin
         O := Scene.GetComponent;
         While O <> nil Do
         Begin
            If (O Is TCustomForm) Then
            Begin
               TCustomForm(O).Close;
               Break;
            End;
            O := O.Owner;
         End;
      End;

      FPressing := False;

      If Assigned(FOnClick) Then
         FOnClick(Self);
   End;
End;

//=============== TD2ExpanderButton =================================================

Constructor TD2ExpanderButton.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
End;

Destructor TD2ExpanderButton.Destroy;
Begin
   Inherited;
End;

//=============== TD2IPhoneButton ======================================================

Constructor TD2IPhoneButton.Create(AOwner: TComponent);
Begin
   Inherited;
   FBackground := TD2Brush.Create(d2BrushSolid, $FF808080);
   FBackground.OnChanged := BackChanged;
End;

Destructor TD2IPhoneButton.Destroy;
Begin
   FBackground.Free;
   Inherited;
End;

Procedure TD2IPhoneButton.BackChanged(Sender: TObject);
Var
   T: TD2Object;
Begin
   T := FindResource('background');
   If (T <> nil) And (T Is TD2Shape) Then
      TD2Shape(T).Fill.Assign(FBackground);
End;

Procedure TD2IPhoneButton.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('background');
   If (T <> nil) And (T Is TD2Shape) Then
      TD2Shape(T).Fill.Assign(FBackground);
End;

Procedure TD2IPhoneButton.SetBackground(Const Value: TD2Brush);
Begin
End;

//=============== TD2AngleButton =================================================

Constructor TD2AngleButton.Create(AOwner: TComponent);
Begin
   Inherited;
   Width := 30;
   Height := 30;
   FFrequency := 0;
   FTracking := True;
   AutoCapture := True;
End;

Destructor TD2AngleButton.Destroy;
Begin
   Inherited;
End;

Function TD2AngleButton.GetData: Variant;
Begin
   Result := Value;
End;

Procedure TD2AngleButton.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
   If VarIsNumeric(Value) Then
      Self.Value := Value
   Else
      Self.Value := 0;
End;

Procedure TD2AngleButton.ApplyStyle;
Begin
   Inherited;
   Tick;
   Text;
End;

Function TD2AngleButton.Tick: TD2VisualObject;
Var
   T: TD2Object;
Begin
   T := FindResource('tick');
   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      Result := TD2VisualObject(T);
      Result.RotateAngle := -FValue;
   End Else
      Result := nil;
End;

Function TD2AngleButton.Text: TD2Text;
Var
   T: TD2Object;
Begin
   T := FindResource('tracktext');
   If (T <> nil) And (T Is TD2Text) Then
   Begin
      TD2Text(T).Visible := False;
      TD2Text(T).TextW := IntToStr(Round(Value)) + System.Widechar($B0);

      If FPressing And Not FTracking Then
         TD2Text(T).Opacity := 1
      Else
         TD2Text(T).Opacity := 0;
   End;

   T := FindResource('text');

   If (T <> nil) And (T Is TD2Text) Then
   Begin
      Result := TD2Text(T);
      Result.Visible := FShowValue;
      Result.TextW := IntToStr(Round(Value)) + System.Widechar($B0);

      If Not FShowValue Then
         Result.Opacity := 0
      Else
         Result.Opacity := 1;
   End Else Begin
      Result := nil;
   End;
End;

Procedure TD2AngleButton.Paint;
Begin
   Inherited;
End;

Procedure TD2TrackBar.Loaded;
Begin
   Inherited;
End;

Procedure TD2TrackBar.SetMax(Const aValue: Single);
Begin
   If FMax <> aValue Then
   Begin
      Inherited;
   End;
End;

Procedure TD2TrackBar.SetMin(Const aValue: Single);
Begin
   Inherited;
   If FMin <> aValue Then
   Begin
      Inherited;
   End;
End;

Procedure TD2AngleButton.SetValue(Const Value: Single);
Begin
   If (FFrequency = 0) Then
   Begin
      If (FValue <> Value) Then
      Begin
         FValue := Value;
         If Tick <> nil Then
            Tick.RotateAngle := -FValue
         Else
            Repaint;
         Text;
         If Assigned(FOnChange) And (Not FPressing Or FTracking) Then
            FOnChange(Self);
      End;
   End Else Begin
      If FValue <> Round(Value / FFrequency) * FFrequency Then
      Begin
         FValue := Round(Value / FFrequency) * FFrequency;
         If Tick <> nil Then
            Tick.RotateAngle := -FValue
         Else
            Repaint;
         Text;
         If Assigned(FOnChange) And (Not FPressing Or FTracking) Then
            FOnChange(Self);
      End;
   End;
End;

Procedure TD2AngleButton.SetShowValue(Const Value: Boolean);
Begin
   If FShowValue <> Value Then
   Begin
      FShowValue := Value;
      Text;
      Repaint;
   End;
End;

Procedure TD2AngleButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      FPressing := True;
      FOldPos := d2Point(X, Y);
      FSaveValue := Value;
      Text;
   End;
End;

Procedure TD2AngleButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If FPressing Then
   Begin
      FPressing := False;
      Text;
      If Value <> FSaveValue Then
         If Assigned(FOnChange) Then
            FOnChange(Self);
   End;
End;

Procedure TD2AngleButton.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If (ssLeft In Shift) And (FPressing) Then
   Begin
      Value := d2VectorAngle(d2Vector(1, 0), d2Vector(X - (Width / 2), Y - (Height / 2)));
      FOldPos := d2Point(X, Y);
      Text;
   End;
End;

//=============== TD2BitmapStateButton =================================================

Constructor TD2BitmapStateButton.Create(AOwner: TComponent);
Begin
   Inherited;
   FBitmap := TD2Bitmap.Create(1, 1);
   FBitmap.OnChange := DoBitmapChanged;
   FBitmapHot := TD2Bitmap.Create(1, 1);
   FBitmapDown := TD2Bitmap.Create(1, 1);
   Width := 64;
   Height := 64;
End;

Destructor TD2BitmapStateButton.Destroy;
Begin
   FBitmap.Free;
   FBitmapHot.Free;
   FBitmapDown.Free;
   Inherited;
End;

Procedure TD2BitmapStateButton.ApplyStyle;
Begin
   Inherited;
End;

Procedure TD2BitmapStateButton.DoBitmapChanged(Sender: TObject);
Begin
   Repaint;
End;

Procedure TD2BitmapStateButton.SetBitmap(Const Value: TD2Bitmap);
Begin
End;

Procedure TD2BitmapStateButton.SetBitmapDown(Const Value: TD2Bitmap);
Begin
End;

Procedure TD2BitmapStateButton.SetBitmapHot(Const Value: TD2Bitmap);
Begin
End;

Procedure TD2BitmapStateButton.Paint;
Var
   scale: Single;
   R: TD2Rect;
   B: TD2Bitmap;
Begin
   If IsPressed Then
      B := FBitmapDown
   Else
   If IsMouseOver Then
      B := FBitmapHot
   Else
      B := FBitmap;

   R := d2Rect(0, 0, B.Width, B.Height);
   scale := d2FitRect(R, LocalRect);
   If scale > 1 Then
      Canvas.DrawBitmap(B, d2Rect(0, 0, B.Width, B.Height), R, AbsoluteOpacity)
   Else Begin
      R := d2Rect(0, 0, B.Width, B.Height);
      d2RectCenter(R, LocalRect);
      Canvas.DrawBitmap(B, d2Rect(0, 0, B.Width, B.Height), R, AbsoluteOpacity);
   End;
End;

Procedure TD2BitmapStateButton.StartTriggerAnimation(AInstance: TD2Object; ATrigger: String);
Begin
   Inherited;
   If Pos('IsPressed', ATrigger) > 0 Then
   Begin
      Repaint;
   End;
End;

Procedure TD2BitmapStateButton.MouseEnter;
Begin
   Inherited;
   Repaint;
End;

Procedure TD2BitmapStateButton.MouseLeave;
Begin
   Inherited;
   Repaint;
End;

//=============== TD2CheckBox ===================================

Constructor TD2CheckBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FAutoTranslate := True;
   AutoCapture := True;
   CanFocused := True;
   TextAlign := d2TextAlignNear;
   Width := 120;
   Height := 19;
End;

Destructor TD2CheckBox.Destroy;
Begin
   Inherited;
End;

Function TD2CheckBox.GetData: Variant;
Begin
   Result := IsChecked;
End;

Procedure TD2CheckBox.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
   If VarIsType(Value, varBoolean) Then
      IsChecked := Value
   Else
      IsChecked := False;
End;

Procedure TD2CheckBox.ApplyStyle;
Begin
   Inherited;
   StartTriggerAnimation(Self, 'IsChecked');
End;

Procedure TD2CheckBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      FPressing := True;
      FIsPressed := True;
      StartTriggerAnimation(Self, 'IsPressed');
   End;
End;

Procedure TD2CheckBox.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If (ssLeft In Shift) And (FPressing) Then
   Begin
      If FIsPressed <> d2PtInRect(d2Point(X, Y), LocalRect) Then
      Begin
         FIsPressed := d2PtInRect(d2Point(X, Y), LocalRect);
         StartTriggerAnimation(Self, 'IsPressed');
      End;
   End;
End;

Procedure TD2CheckBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If FPressing Then
   Begin
      FPressing := False;
      FIsPressed := False;
      If d2PtInRect(d2Point(X, Y), LocalRect) Then
      Begin
         IsChecked := Not IsChecked;
      End;
   End;
End;

Procedure TD2CheckBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
   If (Key = VK_RETURN) Or (Key = VK_SPACE) Then
   Begin
      IsChecked := Not IsChecked;
   End;
End;

Procedure TD2CheckBox.SetIsChecked(Const Value: Boolean);
Begin
   If FIsChecked <> Value Then
   Begin
      FIsChecked := Value;
      StartTriggerAnimation(Self, 'IsChecked');
      If Assigned(FOnChange) Then
      Begin
         FOnChange(Self);
      End;
   End;
End;

//=============== TD2PathCheckBox =============================================

Constructor TD2PathCheckBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FPath := TD2PathData.Create;
   FPath.OnChanged := DoPathChange;
   FPath.Data := 'M 341.236,311.738 L 309.372,342.676 L 291.667,325.488 L 291.667,304.863 L 309.372,321.997 L 341.236,291.113 Z';
End;

Destructor TD2PathCheckBox.Destroy;
Begin
   FPath.Free;
   Inherited;
End;

Procedure TD2PathCheckBox.ApplyStyle;
Var
   Checkmark: TD2Object;
Begin
   Inherited;
   Checkmark := FindResource('checkmark');
   If (Checkmark <> nil) And (Checkmark Is TD2Path) Then
   Begin
      TD2Path(Checkmark).Data.Assign(FPath);
   End;
End;

Procedure TD2PathCheckBox.DoPathChange(Sender: TObject);
Begin
   ApplyStyle;
End;

Procedure TD2PathCheckBox.SetPath(Const Value: TD2PathData);
Begin
   FPath.Assign(Value);
End;

//=============== TD2RadioButton ==========================================

Constructor TD2RadioButton.Create(AOwner: TComponent);
Begin
   Inherited;
   FAutoTranslate := True;
   AutoCapture := True;
   CanFocused := True;
   TextAlign := d2TextAlignNear;
   Width := 120;
   Height := 19;
End;

Destructor TD2RadioButton.Destroy;
Begin
   Inherited;
End;

Function TD2RadioButton.GetData: Variant;
Begin
   Result := IsChecked;
End;

Procedure TD2RadioButton.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
   If VarIsType(Value, varBoolean) Then
      IsChecked := Value
   Else
      IsChecked := False;
End;

Procedure TD2RadioButton.ApplyStyle;
Begin
   Inherited;
   StartTriggerAnimation(Self, 'IsChecked');
End;

Procedure TD2RadioButton.EnterFocus;
Begin
   Inherited;
End;

Procedure TD2RadioButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      FPressing := True;
      FIsPressed := True;
      StartTriggerAnimation(Self, 'IsPressed');
   End;
End;

Procedure TD2RadioButton.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If (ssLeft In Shift) And (FPressing) Then
   Begin
      If FIsPressed <> d2PtInRect(d2Point(X, Y), LocalRect) Then
      Begin
         FIsPressed := d2PtInRect(d2Point(X, Y), LocalRect);
         StartTriggerAnimation(Self, 'IsPressed');
      End;
   End;
End;

Procedure TD2RadioButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If FPressing Then
   Begin
      FPressing := False;
      FIsPressed := False;
      If d2PtInRect(d2Point(X, Y), LocalRect) Then
      Begin
         IsChecked := Not IsChecked;
      End;
   End;
End;

Procedure TD2RadioButton.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
   If (Key = VK_RETURN) Or (Key = VK_SPACE) Then
   Begin
      IsChecked := Not IsChecked;
   End;
End;

Procedure TD2RadioButton.SetIsChecked(Const Value: Boolean);
Var
   List: TList;
   i, c, cc: Integer;
Begin
   If FIsChecked <> Value Then
   Begin
      If Value Then
         FIsChecked := Value;

      c := 0;
      cc := 0;
      If Assigned(FScene) And (FScene.GetRoot <> nil) Then
      Begin
         List := TList.Create;
         FScene.GetRoot.AddControlsToList(List);
         For i := 0 To List.Count - 1 Do
            If (TD2Object(List[i]) Is TD2RadioButton) And (TD2Object(List[i]) <> Self) And (TD2RadioButton(List[i]).GroupName = GroupName) Then
            Begin
               If TD2RadioButton(List[i]).IsChecked Then
                  cc := cc + 1;
               If Value Then
                  TD2RadioButton(List[i]).IsChecked := False;
               c := c + 1;
            End;
         List.Free;
      End;

      If Not Value And (c = 0) Then
         Exit;
      If Not Value And (cc = 0) Then
         Exit;
      FIsChecked := Value;
      StartTriggerAnimation(Self, 'IsChecked');

      If Assigned(FOnChange) Then
      Begin
         FOnChange(Self);
      End;
   End;
End;


//=============== TD2ScrollBar =====================================

Constructor TD2ScrollBar.Create(AOwner: TComponent);
Begin
   Inherited;
   FMax := 100;
   FViewportSize := 0;
   FSmallChange := 1;
   Width := 150;
   Height := 18;
End;

Destructor TD2ScrollBar.Destroy;
Begin
   Inherited;
End;

Function TD2ScrollBar.GetData: Variant;
Begin
   Result := Value;
End;

Procedure TD2ScrollBar.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
   If VarIsNumeric(Value) Then
      Self.Value := Value
   Else
      Self.Value := Min;
End;

Function TD2ScrollBar.Track: TD2CustomTrack;
Var
   T: TD2Object;
   HT, VT: TD2CustomTrack;
Begin
   HT := nil;
   VT := nil;
   T := FindResource('htrack');
   If (T <> nil) And (T Is TD2CustomTrack) Then
   Begin
      HT := TD2CustomTrack(T);
      HT.FOrientation := d2Horizontal;
      HT.FMax := Max;
      HT.FMin := Min;
      HT.FValue := Value;
      HT.ViewportSize := ViewportSize;
      HT.Visible := Orientation = d2Horizontal;
      HT.OnChange := DoTrackChanged;
      HT.CanFocused := False;
      If HT.Visible Then
         HT.Realign;
   End;
   T := FindResource('vtrack');
   If (T <> nil) And (T Is TD2CustomTrack) Then
   Begin
      VT := TD2CustomTrack(T);
      VT.FOrientation := d2Vertical;
      VT.FMax := Max;
      VT.FMin := Min;
      VT.FValue := Value;
      VT.ViewportSize := ViewportSize;
      VT.Visible := Orientation = d2Vertical;
      VT.OnChange := DoTrackChanged;
      VT.CanFocused := False;
      If VT.Visible Then
         VT.Realign;
   End;
   If Orientation = d2Vertical Then
      Result := VT
   Else
      Result := HT;
End;

Function TD2ScrollBar.MinButton: TD2CustomButton;
Var
   T: TD2Object;
   LB, TB: TD2CustomButton;
Begin
   TB := nil;
   LB := nil;
   T := FindResource('leftbutton');
   If (T <> nil) And (T Is TD2CustomButton) Then
   Begin
      LB := TD2CustomButton(T);
      LB.OnClick := DoMinButtonClick;
      LB.Visible := Orientation = d2Horizontal;
      LB.CanFocused := False;
   End;

   T := FindResource('topbutton');
   If (T <> nil) And (T Is TD2CustomButton) Then
   Begin
      TB := TD2CustomButton(T);
      TB.OnClick := DoMinButtonClick;
      TB.Visible := Orientation = d2Vertical;
      TB.CanFocused := False;
   End;

   If Orientation = d2Vertical Then
      Result := TB
   Else
      Result := LB;
End;

Function TD2ScrollBar.MaxButton: TD2CustomButton;
Var
   T: TD2Object;
   RB, BB: TD2CustomButton;
Begin
   RB := nil;
   BB := nil;
   T := FindResource('rightbutton');
   If (T <> nil) And (T Is TD2CustomButton) Then
   Begin
      RB := TD2CustomButton(T);
      RB.OnClick := DoMaxButtonClick;
      RB.Visible := Orientation = d2Horizontal;
      RB.CanFocused := False;
   End;

   T := FindResource('bottombutton');
   If (T <> nil) And (T Is TD2CustomButton) Then
   Begin
      BB := TD2CustomButton(T);
      BB.OnClick := DoMaxButtonClick;
      BB.Visible := Orientation = d2Vertical;
      RB.CanFocused := False;
   End;

   If Orientation = d2Vertical Then
      Result := BB
   Else
      Result := RB;
End;

Procedure TD2ScrollBar.DoTrackChanged(Sender: TObject);
Begin
   Value := TD2CustomTrack(Sender).Value;
End;

Procedure TD2ScrollBar.DoMinButtonClick(Sender: TObject);
Begin
   Value := Value - SmallChange;
End;

Procedure TD2ScrollBar.DoMaxButtonClick(Sender: TObject);
Begin
   Value := Value + SmallChange;
End;

Procedure TD2ScrollBar.Realign;
Begin
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;
   Track;
   MinButton;
   MaxButton;
   FDisableAlign := False;
   Inherited;
End;

Procedure TD2ScrollBar.SetMax(Const Value: Single);
Begin
   If FMax <> Value Then
   Begin
      FMax := Value;
      If FMax < FMin Then
         FMax := FMin + 0.001;
      If FValue > FMax - FViewportSize Then
         Self.Value := FMax - FViewportSize;
      If FViewportSize > (FMax - FMin) Then
         FViewportSize := FMax - FMin;
      Realign;
   End;
End;

Procedure TD2ScrollBar.SetMin(Const Value: Single);
Begin
   If FMin <> Value Then
   Begin
      FMin := Value;
      If FValue < FMin Then
         Self.Value := FMin;
      Realign;
   End;
End;

Procedure TD2ScrollBar.SetOrientation(Const Value: TD2Orientation);
Var
   T: Single;
Begin
   If FOrientation <> Value Then
   Begin
      FOrientation := Value;

      If Assigned(FScene) And (FScene.GetDesignTime) And Not (csLoading In ComponentState) Then
      Begin
         T := Width;
         Width := Height;
         Height := T;
      End;
      Realign;
   End;
End;

Procedure TD2ScrollBar.SetValue(Const Value: Single);
Begin
   If FValue <> Value Then
   Begin
      FValue := Value;
      If FValue > FMax - FViewportSize Then
         FValue := FMax - FViewportSize;
      If FValue < FMin Then
         FValue := FMin;
      If Assigned(FBindingObjects) Then
         ToBindingObjects;
      If Assigned(FOnChange) Then
         FOnChange(Self);
      Realign;
   End;
End;

Procedure TD2ScrollBar.SetViewportSize(Const Value: Single);
Begin
   If FViewportSize <> Value Then
   Begin
      FViewportSize := Value;
      If FViewportSize > (FMax - FMin) Then
         FViewportSize := FMax - FMin;
      If FValue > FMax - FViewportSize Then
         Self.Value := FMax - FViewportSize;
      Realign;
   End;
End;

//=============== TD2SmallScrollBar =================================================

Constructor TD2SmallScrollBar.Create(AOwner: TComponent);
Begin
   Inherited;
   Height := 8;
End;

//=============== TD2ScrollArrowLeft =================================

Constructor TD2ScrollArrowLeft.Create(AOwner: TComponent);
Begin
   Inherited;
   Data.Data := 'M 1,0 L 1,1 L 0,0.500 L 1,0 Z';
   Data.Scale(100, 100);
   WrapMode := d2PathFit;
End;

//=============== TD2ScrollArrowRight =================================

Constructor TD2ScrollArrowRight.Create(AOwner: TComponent);
Begin
   Inherited;
   Data.Data := 'M 0,0 L 0,1 L 1,0.500 Z';
   Data.Scale(100, 100);
   WrapMode := d2PathFit;
End;

//=============== TD2AniIndicator =================================================

Constructor TD2AniIndicator.Create(AOwner: TComponent);
Begin
   Inherited;
   FEnabled := False;
   FLayout := TD2VisualObject.Create(Self);
   FLayout.Parent := Self;
   FLayout.Align := vaContents;
   FLayout.Locked := True;
   FLayout.Stored := False;
   FAni := TD2FloatAnimation.Create(Self);
   FAni.Parent := FLayout;
   FAni.Loop := True;
   FAni.StartValue := 0;
   FAni.StopValue := 360;
   FAni.Duration := 10;
   FAni.PropertyName := 'RotateAngle';
End;

Destructor TD2AniIndicator.Destroy;
Begin
   Inherited;
End;

Procedure TD2AniIndicator.Paint;
Var
   a: Integer;
   P, P2: TD2Point;
   wSize, eSize: Single;
   V: Single;
Begin
   If Width < Height Then
      wSize := Width / 2
   Else
      wSize := Height / 2;
   eSize := wSize / 3.7;
   wSize := wSize - eSize;

   Case FStyle Of
      d2AniIndicatorLine:
      Begin
         Canvas.Stroke.Style := d2Brushsolid;
         Canvas.StrokeThickness := eSize / 2;
         For a := 0 To 11 Do
         Begin
            P := d2Point(Width / 2 + (cos(d2DegToRad(a * 30)) * wSize), Height / 2 + (sin(d2DegToRad(a * 30)) * wSize));
            P2 := d2Point(Width / 2 + (cos(d2DegToRad(a * 30)) * (wSize / 2)), Height / 2 + (sin(d2DegToRad(a * 30)) * (wSize / 2)));
            Canvas.Fill.SolidColor := $FFBABABA;
            Canvas.Stroke.SolidColor := $FFBABABA;
            Canvas.DrawLine(P, P2, Opacity);
            If FEnabled Then
            Begin
               V := ((Trunc(FLayout.RotateAngle) + (30 - Trunc((a / 12) * 30))) Mod 30) / 30;
               If V > 1 Then
                  V := Abs(V - 2);
               V := 1 - V;
               Canvas.Stroke.SolidColor := $FF000000;
               Canvas.DrawLine(P, P2, V * Opacity);
            End;
         End;
      End;
      d2AniIndicatorCircle:
      Begin
         Canvas.Stroke.Style := d2BrushNone;
         For a := 0 To 7 Do
         Begin
            P := d2Point(Width / 2 + (cos(d2DegToRad(a * 45)) * wSize), Height / 2 + (sin(d2DegToRad(a * 45)) * wSize));
            Canvas.Fill.SolidColor := $FFBABABA;
            Canvas.FillEllipse(d2Rect(P.X - eSize, P.Y - eSize, P.X + eSize, P.Y + eSize), Opacity);
            If FEnabled Then
            Begin
               V := ((Trunc(FLayout.RotateAngle) + (30 - Trunc((a / 7) * 30))) Mod 30) / 30;
               If V > 1 Then
                  V := Abs(V - 2);
               V := 1 - V;
               Canvas.Fill.SolidColor := $FF000000;
               Canvas.FillEllipse(d2Rect(P.X - eSize, P.Y - eSize, P.X + eSize, P.Y + eSize), V * Opacity);
            End;
         End;
      End;
   End;
End;

Procedure TD2AniIndicator.SetEnabled(Const Value: Boolean);
Begin
   If FEnabled <> Value Then
   Begin
      FEnabled := Value;
      If FEnabled Then
      Begin
         FAni.Start;
      End Else
         FAni.Stop;
   End;
End;

Procedure TD2AniIndicator.SetStyle(Const Value: TD2AniIndicatorStyle);
Begin
   If FStyle <> Value Then
   Begin
      FStyle := Value;
      Repaint;
   End;
End;


//=============== TD2ProgressBar ============================================

Constructor TD2ProgressBar.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
   FMax := 100;
   Width := 100;
   Height := 20;
End;

Destructor TD2ProgressBar.Destroy;
Begin
   Inherited;
End;

Function TD2ProgressBar.GetData: Variant;
Begin
   Result := Value;
End;

Procedure TD2ProgressBar.SetData(Const Value: Variant);
Begin
   If VarIsNumeric(Value) Then
      Self.Value := Value
   Else
      Self.Value := Min;
End;

Procedure TD2ProgressBar.ApplyStyle;
Var
   I: TD2Object;
Begin
   Inherited;
   If Orientation = d2Horizontal Then
   Begin
      I := FindResource('hindicator');
      If (I <> nil) And (I Is TD2VisualObject) Then
         TD2VisualObject(I).StartTriggerAnimation(Self, 'IsVisible');
   End Else Begin
      I := FindResource('vindicator');
      If (I <> nil) And (I Is TD2VisualObject) Then
         TD2VisualObject(I).StartTriggerAnimation(Self, 'IsVisible');
   End;
   Realign;
End;

Procedure TD2ProgressBar.Realign;
Var
   hI, vI, T: TD2Object;
Begin
   If Not FDisableAlign Then
   Begin
      FDisableAlign := True;
      T := nil;
      If Orientation = d2Horizontal Then
      Begin
         T := FindResource('vtrack');
         If T <> nil Then
            T.Visual.Visible := False;
         T := FindResource('htrack');
      End Else Begin
         T := FindResource('htrack');
         If T <> nil Then
            T.Visual.Visible := False;
         T := FindResource('vtrack');
      End;
      If T = nil Then
         T := FindResource('track');
      If (T <> nil) And (T Is TD2VisualObject) And (Max > Min) Then
      Begin
         hI := FindResource('hindicator');
         vI := FindResource('vindicator');
         If Orientation = d2Horizontal Then
         Begin
            If (hI <> nil) And (hI Is TD2VisualObject) Then
            Begin
               TD2VisualObject(hI).Width := ((Value - Min) / (Max - Min)) * (TD2VisualObject(T).Width - TD2VisualObject(T).Margins.Left - TD2VisualObject(T).Margins.Right - TD2VisualObject(hI).Padding.Left - TD2VisualObject(hI).Padding.Right);
               TD2VisualObject(hI).Visible := TD2VisualObject(hI).Width > 2;
            End;
            If (vI <> nil) And (vI Is TD2VisualObject) Then
               TD2VisualObject(vI).Visible := False;
         End Else Begin
            If (vI <> nil) And (vI Is TD2VisualObject) Then
            Begin
               TD2VisualObject(vI).Height := ((Value - Min) / (Max - Min)) * (TD2VisualObject(T).Height - TD2VisualObject(T).Margins.Top - TD2VisualObject(T).Margins.Bottom - TD2VisualObject(hI).Padding.Top - TD2VisualObject(hI).Padding.Bottom);
               TD2VisualObject(vI).Visible := TD2VisualObject(vI).Height > 2;
            End;
            If (hI <> nil) And (hI Is TD2VisualObject) Then
               TD2VisualObject(hI).Visible := False;
         End;
      End;
      FDisableAlign := False;
   End;
   Inherited;
End;

Procedure TD2ProgressBar.SetMax(Const Value: Single);
Begin
   FMax := Value;
End;

Procedure TD2ProgressBar.SetMin(Const Value: Single);
Begin
   FMin := Value;
End;

Procedure TD2ProgressBar.SetOrientation(Const Value: TD2Orientation);
Var
   T: Single;
Begin
   If FOrientation <> Value Then
   Begin
      FOrientation := Value;

      If Assigned(FScene) And (FScene.GetDesignTime) And Not (csLoading In ComponentState) Then
      Begin
         T := Width;
         Width := Height;
         Height := T;
      End;
      Realign;
   End;
End;

Procedure TD2ProgressBar.SetValue(Const Value: Single);
Begin
   If FValue <> Value Then
   Begin
      FValue := Value;
      Realign;
   End;
End;


//=============== TD2Window ==========================================

Constructor TD2Window.Create(AOwner: TComponent);
Begin
   Inherited;
   FautoTranslate := True;
   FShowCloseButton := True;
   FShowSizeGrip := True;
   HitTest := False;
   TextAlign := d2TextalignNear;
   Width := 200;
   Height := 200;
End;

Procedure TD2Window.ApplyStyle;
Var
   sizeGrip, closeBtn: TD2Object;
Begin
   Inherited;
   closeBtn := FindResource('close');
   If (closeBtn <> nil) And (closeBtn Is TD2VisualObject) Then
   Begin
      TD2VisualObject(closeBtn).Visible := FShowCloseButton;
      If (closeBtn Is TD2CloseButton) And (Assigned(FOnCloseClick)) Then
      Begin
         TD2CloseButton(closeBtn).CloseForm := False;
         TD2CloseButton(closeBtn).OnClick := FOnCloseClick;
      End;
   End;
   sizeGrip := FindResource('sizegrip');
   If (sizeGrip <> nil) And (sizeGrip Is TD2VisualObject) Then
   Begin
      TD2VisualObject(sizeGrip).Visible := FShowSizeGrip;
   End;
End;

Destructor TD2Window.Destroy;
Begin
   Inherited;
End;

Procedure TD2Window.SetShowCloseButton(Const Value: Boolean);
Begin
   If FShowCloseButton <> Value Then
   Begin
      FShowCloseButton := Value;
      Resource := FResource;
   End;
End;

Procedure TD2Window.SetShowSizeGrip(Const Value: Boolean);
Begin
   If FShowSizeGrip <> Value Then
   Begin
      FShowSizeGrip := Value;
      Resource := FResource;
   End;
End;

//=============== TD2LayerWindow =================================================

Constructor TD2LayerWindow.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2LayerWindow.Destroy;
Begin
   Inherited;
End;

//=============== TD2HudWindow =================================================

Constructor TD2HudWindow.Create(AOwner: TComponent);
Begin
   Inherited;
   FFill := TD2Brush.Create(d2BrushSolid, $EA2F2F2F);
   FFill.OnChanged := DoFillChanged;
   FStroke := TD2Brush.Create(d2BrushSolid, $5B000000);
   FStroke.OnChanged := DoFillChanged;
   FStrokeThickness := 1;
   FDisableShadowOnOSX := True;
   FShowCaption := True;
   Font.Style := d2FontBold;
End;

Destructor TD2HudWindow.Destroy;
Begin
   FStroke.Free;
   FFill.Free;
   Inherited;
End;

Procedure TD2HudWindow.ApplyStyle;
Var
   {$IFDEF ORCA2D_USE_CARBON}
  shadow:TD2Object;
   {$endif}
   back: TD2Object;
Begin
   Inherited;
   {$IFDEF ORCA2D_USE_CARBON}
  shadow:=FindResource('shadow');
  if (shadow <> nil) and (shadow is TD2VisualObject) and (FDisableShadowOnOSX) then
  begin
    TD2VisualObject(shadow).visible:=false;
  end;
   {$ENDIF}
   back := FindResource('close');
   If (back <> nil) And (back Is TD2CloseButton) Then
   Begin
      If FCloseAlign = d2ButtonAlignLeft Then
         TD2CloseButton(back).Align := vaLeft
      Else
         TD2CloseButton(back).Align := vaRight;
      If TD2VisualObject(back).Visible Then
      Begin
         TD2VisualObject(back).Visible := FShowCaption;
         TD2VisualObject(back).DesignHide := Not FShowCaption;
      End;
   End;
   back := FindResource('back');
   If (back <> nil) And (back Is TD2Shape) Then
   Begin
      TD2Shape(back).Fill.Assign(FFill);
   End;
   back := FindResource('stroke');
   If (back <> nil) And (back Is TD2Shape) Then
   Begin
      TD2Shape(back).Stroke.Assign(FStroke);
      TD2Shape(back).StrokeThickness := FStrokeThickness;
      TD2Shape(back).StrokeCap := FStrokeCap;
      TD2Shape(back).StrokeDash := FStrokeDash;
      TD2Shape(back).StrokeJoin := FStrokeJoin;
      TD2VisualObject(back).Margins.Rect := d2Rect(FStrokeThickness, FStrokeThickness, FStrokeThickness, FStrokeThickness);
   End;
   back := FindResource('caption');
   If (back <> nil) And (back Is TD2VisualObject) Then
   Begin
      TD2VisualObject(back).Height := 20 + FStrokeThickness;
      TD2VisualObject(back).Padding.Rect := d2Rect(FStrokeThickness, FStrokeThickness, FStrokeThickness, 0);
      TD2VisualObject(back).Visible := FShowCaption;
      TD2VisualObject(back).DesignHide := Not FShowCaption;
   End;
   back := FindResource('text');
   If (back <> nil) And (back Is TD2VisualObject) Then
   Begin
      TD2VisualObject(back).Visible := FShowCaption;
      TD2VisualObject(back).DesignHide := Not FShowCaption;
   End;
End;

Procedure TD2HudWindow.SetDisableShadowOnOSX(Const Value: Boolean);
Begin
   If FDisableShadowOnOSX <> Value Then
   Begin
      FDisableShadowOnOSX := Value;
      Resource := FResource;
   End;
End;

Procedure TD2HudWindow.SetFill(Const Value: TD2Brush);
Begin
   FFill.Assign(Value);
End;

Procedure TD2HudWindow.DoFillChanged(Sender: TObject);
Begin
   If Not (csLoading In ComponentState) Then
      ApplyStyle;
End;

Function TD2HudWindow.isStrokeThicknessStored: Boolean;
Begin
   Result := StrokeThickness <> 1;
End;

Procedure TD2HudWindow.SetStroke(Const Value: TD2Brush);
Begin
   If FStroke <> Value Then
   Begin
      FStroke := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2HudWindow.SetStrokeCap(Const Value: TD2StrokeCap);
Begin
   If FStrokeCap <> Value Then
   Begin
      FStrokeCap := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2HudWindow.SetStrokeDash(Const Value: TD2StrokeDash);
Begin
   If FStrokeDash <> Value Then
   Begin
      FStrokeDash := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2HudWindow.SetStrokeJoin(Const Value: TD2StrokeJoin);
Begin
   If FStrokeJoin <> Value Then
   Begin
      FStrokeJoin := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2HudWindow.SetStrokeThickness(Const Value: Single);
Begin
   If FStrokeThickness <> Value Then
   Begin
      FStrokeThickness := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2HudWindow.SetCloseAlign(Const Value: TD2CloseAlign);
Begin
   If FCloseAlign <> Value Then
   Begin
      FCloseAlign := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;

Procedure TD2HudWindow.SetShowCaption(Const Value: Boolean);
Begin
   If FShowCaption <> Value Then
   Begin
      FShowCaption := Value;
      If Not (csLoading In ComponentState) Then
         ApplyStyle;
   End;
End;


//=============== TD2Frame =============================================

Constructor TD2Frame.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2Frame.Destroy;
Begin
   If Not (Assigned(FScene) And (FScene.GetDesignTime)) Then
      If (FSceneObject <> nil) And (FChildren <> nil) And (FChildren.Count > 0) Then
      Begin
         FSceneObject.AddObject(TD2Object(FChildren[0]));
         FSceneObject.FOpenInFrame := nil;
         If FSceneObject.FCloneFrame <> nil Then
            FreeAndNil(FSceneObject.FCloneFrame);
      End;
   If FBuffer <> nil Then
      FBuffer.Free;
   Inherited;
End;

Procedure TD2Frame.Loaded;
Begin
   Inherited;
End;

Procedure TD2Frame.GetChildren(Proc: TGetChildProc; Root: TComponent);
Begin
End;

Procedure TD2Frame.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FSceneObject) Then
      SceneObject := nil;
End;

Procedure TD2Frame.Paint;
Var
   Obj: TD2Object;
   CloneFrame: TForm;
   SaveR: TD2Rect;
Begin
   If FNeedClone Then
   Begin
      FNeedClone := False;
      If Not (Assigned(FScene) And (FScene.GetDesignTime)) And (FSceneObject <> nil) Then
      Begin
         If (FSceneObject <> nil) And (FSceneObject.FOpenInFrame <> nil) Then
         Begin
            Application.CreateForm(TComponentClass(FSceneObject.Owner.ClassType), CloneFrame);
            If CloneFrame <> nil Then
            Begin
               FSceneObject := TD2CustomScene(CloneFrame.FindComponent(FSceneObject.Name));
               If FSceneObject <> nil Then
               Begin
                  FSceneObject.FCloneFrame := CloneFrame;
               End;
            End Else
               FSceneObject := nil;
         End;

         If (FSceneObject <> nil) And (FSceneObject.FOpenInFrame = nil) And (FSceneObject.Root <> nil) Then
         Begin
            FSceneObject.FOpenInFrame := Self;
            Obj := FSceneObject.Root;
            Obj.Parent := Self;

            If Obj.isVisual Then
            Begin
               TD2VisualObject(Obj).Align := vaClient;
               Realign;
            End;
         End;
      End;
   End;

   If (FScene <> nil) And (Assigned(FScene) And (FScene.GetDesignTime)) And (SceneObject <> nil) And (SceneObject.Root <> nil) Then
   Begin
      Obj := SceneObject.Root;
      If Obj.isVisual Then
      Begin
         If FBuffer = nil Then
            FBuffer := TD2Bitmap.Create(Round(Width), Round(Height));

         If (FBuffer.Width <> Trunc(Width)) Or (FBuffer.Height <> Trunc(Height)) Then
            FBuffer.SetSize(Trunc(Width), Trunc(Height));

         FBuffer.Clear(0);
         SaveR := TD2VisualObject(Obj).ParentedRect;
         TD2VisualObject(Obj).SetBounds(0, 0, Width, Height);
         TD2VisualObject(Obj).PaintTo(FBuffer.Canvas, LocalRect);
         Canvas.DrawBitmap(FBuffer, d2Rect(0, 0, FBuffer.Width, FBuffer.Height),
            d2Rect(0, 0, FBuffer.Width - 1, FBuffer.Height - 1), 1);

         TD2VisualObject(Obj).SetBounds(SaveR.Left, SaveR.Top, d2RectWidth(SaveR), d2RectHeight(SaveR));
      End;
   End;
End;

Procedure TD2Frame.SetSceneObject(Const Value: TD2CustomScene);
Var
   Obj: TD2Object;
Begin
   If FSceneObject <> Value Then
   Begin
      If Assigned(FScene) And (Value = FScene.GetComponent) Then
         Exit;

      If Not (Assigned(FScene) And (FScene.GetDesignTime)) And (FSceneObject <> nil) Then
      Begin
         If (FSceneObject <> nil) And (FChildren <> nil) And (FChildren.Count > 0) Then
         Begin
            FSceneObject.AddObject(TD2Object(FChildren[0]));
            FSceneObject.FOpenInFrame := nil;
            If (FSceneObject.FCloneFrame <> nil) And Not (csDestroying In FSceneObject.ComponentState) Then
               FreeAndNil(FSceneObject.FCloneFrame);
         End;
      End;

      FSceneObject := Value;

      If Not (Assigned(FScene) And (FScene.GetDesignTime)) And (FSceneObject <> nil) Then
      Begin
         If (FSceneObject <> nil) And (FSceneObject.FOpenInFrame <> nil) Then
         Begin
            FNeedClone := True;
            Exit;
         End;

         If (FSceneObject <> nil) And (FSceneObject.FOpenInFrame = nil) And (FSceneObject.Root <> nil) Then
         Begin
            FSceneObject.FOpenInFrame := Self;
            Obj := FSceneObject.Root;
            Obj.Parent := Self;
            If Obj.isVisual Then
            Begin
               TD2VisualObject(Obj).Align := vaClient;
               Realign;
            End;
         End;
      End;
      If Assigned(FScene) And (FScene.GetDesignTime) Then
         Repaint;
   End;
End;

//=============== TD2ImageControl =================================================

Constructor TD2ImageControl.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := True;
   FEnableOpenDialog := True;
   FBitmap := TD2Bitmap.Create(1, 1);
   FBitmap.OnChange := DoBitmapChanged;
End;

Destructor TD2ImageControl.Destroy;
Begin
   FBitmap.Free;
   Inherited;
End;

Procedure TD2ImageControl.ApplyStyle;
Var
   O: TD2Object;
Begin
   Inherited;
   O := FindResource('image');
   If (O <> nil) And (O Is TD2Image) Then
   Begin
      FImage := TD2Image(O);
      FImage.Bitmap.Assign(FBitmap);
   End;
End;

Procedure TD2ImageControl.FreeStyle;
Begin
   Inherited;
   FImage := nil;
End;

Procedure TD2ImageControl.Click;
Var
   D: TOpenDialog;
Begin
   Inherited;
   If Not FEnableOpenDialog Then
      Exit;

   D := TOpenDialog.Create(nil);
   D.Filter := GvarD2DefaultFilterClass.GetFileTypes;
   If D.Execute Then
   Begin
      Bitmap.LoadFromFile(D.FileName);
   End;
   D.Free;
End;

Procedure TD2ImageControl.DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean);
Begin
   Inherited;
   // accept correct image file or TD2Image
   Accept :=
      ((Length(aData.Files) > 0) And FileExists(aData.Files[0]) And (Pos(ExtractFileExt(aData.Files[0]), GvarD2DefaultFilterClass.GetFileTypes) > 0)) Or (aData.Source Is TD2Image);
End;

Procedure TD2ImageControl.DragDrop(Const aData: TD2DragObject; Const Point: TD2Point);
Begin
   Inherited;
   If aData.Source Is TD2Image Then
   Begin
      Bitmap.Assign(TD2Image(aData.Source).Bitmap);
   End Else
   If Length(aData.Files) > 0 Then
   Begin
      Bitmap.LoadFromFile(aData.Files[0]);
   End;
End;

Procedure TD2ImageControl.SetBitmap(Const Value: TD2Bitmap);
Begin
   FBitmap.Assign(Value);
End;

Procedure TD2ImageControl.DoBitmapChanged(Sender: TObject);
Var
   R: TD2Rect;
Begin
   If FImage <> nil Then
   Begin
      R := d2Rect(0, 0, FBitmap.Width, FBitmap.Height);
      d2FitRect(R, d2Rect(0, 0, FImage.Width, FImage.Height));
      FImage.Bitmap.SetSize(round(d2RectWidth(R)), round(d2RectHeight(R)));
      If FImage.Bitmap.Canvas.BeginScene Then
      Begin
         FImage.Bitmap.Canvas.Clear(0);
         FImage.Bitmap.Canvas.DrawBitmap(FBitmap, d2Rect(0, 0, FBitmap.Width, FBitmap.Height),
            d2Rect(0, 0, FImage.Bitmap.Width, FImage.Bitmap.Height), 1);
         FImage.Bitmap.Canvas.EndScene;
      End;
      FImage.Repaint;

      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Function TD2ImageControl.GetData: Variant;
Begin
   Result := ObjectToVariant(Bitmap);
End;

Procedure TD2ImageControl.SetData(Const Value: Variant);
Begin
   If VarIsNull(Value) Then
      Bitmap.SetSize(1, 1)
   Else
   If VarIsObject(Value) Then
   Begin
      If VariantToObject(Value) Is TPersistent Then
         Bitmap.Assign(TPersistent(VariantToObject(Value)));
   End Else
   If VarIsStr(Value) Then
      Bitmap.LoadFromFile(Value);
End;


//=============== TD2ImageViewer =====================================================

Constructor TD2ImageViewer.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'framedscrollboxstyle';
   MouseTracking := True;
   MouseScaling := True;
   Cursor := crHandPoint;
   FBitmapScale := 1;
   FBack := TD2Rectangle.Create(Self);
   FBack.HitTest := False;
   FBack.Parent := Self;
   FBack.Locked := True;
   FBack.Stroke.Style := d2BrushNone;
   FBack.Stored := False;
   FBack.Visible := False;
   FImage := TD2Image.Create(Self);
   FImage.HitTest := False;
   FImage.Parent := Self;
   FImage.Locked := True;
   FImage.Stored := False;
   FImage.WrapMode := d2ImageStretch;
   FImage.Bitmap.OnChange := DoBitmapChange;
End;

Destructor TD2ImageViewer.Destroy;
Begin
   Inherited;
End;

Function TD2ImageViewer.GetBitmap: TD2Bitmap;
Begin
   Result := FImage.Bitmap;
End;

Function TD2ImageViewer.GetContentBounds: TD2Rect;
Begin
   FImage.SetBounds(0, 0, Bitmap.Width * BitmapScale, Bitmap.Height * BitmapScale);

   If (FContent <> nil) And (ContentLayout <> nil) Then
   Begin
      If FImage.Width < ContentLayout.Width Then
         FImage.Position.X := round((ContentLayout.Width - FImage.Width) / 2);
      If FImage.Height < ContentLayout.Height Then
         FImage.Position.Y := round((ContentLayout.Height - FImage.Height) / 2);
   End;
   FBack.SetBounds(FImage.Position.X, FImage.Position.Y, FImage.Width, FImage.Height);

   Result := d2UnionRect(d2Rect(0, 0, 0, 0), FImage.ParentedRect);
End;

Procedure TD2ImageViewer.MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean);
Begin
   If MouseScaling Then
   Begin
      BitmapScale := BitmapScale + (WheelDelta / 120) * 0.04;
      Handled := True;
   End;
   Inherited;
End;

Procedure TD2ImageViewer.BestFit;
Var
   R: TD2Rect;
   s: Single;
   NeedRealign: Boolean;
Begin
   If (FContent <> nil) And (ContentLayout <> nil) Then
   Begin
      NeedRealign := False;
      If (VScrollBar <> nil) And (VScrollBar.Enabled) Then
         NeedRealign := True;
      If (VScrollBar <> nil) And (VScrollBar.Enabled) Then
         NeedRealign := True;
      R := d2Rect(0, 0, Bitmap.Width, Bitmap.Height);
      s := d2FitRect(R, ContentLayout.LocalRect);
      If s >= 1 Then
         BitmapScale := 1 / s
      Else
         BitmapScale := 1;
      If NeedRealign Then
      Begin
         R := d2Rect(0, 0, Bitmap.Width, Bitmap.Height);
         s := d2FitRect(R, ContentLayout.LocalRect);
         If s >= 1 Then
            BitmapScale := 1 / s
         Else
            BitmapScale := 1;
      End;
   End;
End;

Procedure TD2ImageViewer.SetBitmap(Const Value: TD2Bitmap);
Begin
   FImage.Bitmap := Value;
End;

Procedure TD2ImageViewer.SetBitmapScale(Const Value: Single);
Begin
   If FBitmapScale <> Value Then
   Begin
      FBitmapScale := Value;
      If FBitmapScale < 0.01 Then
         FBitmapScale := 0.01;
      If FBitmapScale > 10 Then
         FBitmapScale := 10;
      Realign;
      Centre;
   End;
End;

Function TD2ImageViewer.GetBackgroundFill: TD2Brush;
Begin
   Result := FBack.Fill;
End;

Procedure TD2ImageViewer.SetBackgroundFill(Const Value: TD2Brush);
Begin
   FBack.Fill := Value;
End;

Procedure TD2ImageViewer.DoBitmapChange(Sender: TObject);
Begin
   Realign;
End;

Procedure TD2ImageViewer.SetShowBackground(Const Value: Boolean);
Begin
   If FShowBackground <> Value Then
   Begin
      FShowBackground := Value;
      FBack.Visible := FShowBackground;
   End;
End;

Function TD2ImageViewer.GetData: Variant;
Begin
   Result := ObjectToVariant(Bitmap);
End;

Procedure TD2ImageViewer.SetData(Const Value: Variant);
Begin
   If VarIsNull(Value) Then
      Bitmap.SetSize(1, 1)
   Else
   If VarIsObject(Value) Then
   Begin
      If VariantToObject(Value) Is TPersistent Then
         Bitmap.Assign(TPersistent(VariantToObject(Value)));
   End Else
      Bitmap.LoadFromFile(Value);
End;


//=============== TD2Image ===========================================

Constructor TD2Image.Create(AOwner: TComponent);
Begin
   Inherited;
   FBitmap := TD2Bitmap.Create(0, 0);
   FBitmap.OnThreadLoaded := DoBitmapLoaded;
   FBitmap.OnBitmapDestroy := DoBitmapDestroy;
   FBitmap.OnChange := DoBitmapChanged;
   FBitmapMargins := TD2Bounds.Create(d2Rect(0, 0, 0, 0));
   FWrapMode := d2ImageFit;
End;

Destructor TD2Image.Destroy;
Begin
   FreeAndNil(FBitmapMargins);
   FBitmap.Free;
   If FBuffer <> nil Then
      FreeAndNil(FBuffer);
   Inherited;
End;

Function TD2Image.GetData: Variant;
Begin
   Result := ObjectToVariant(FBitmap);
End;

Procedure TD2Image.SetData(Const Value: Variant);
Begin
   If VarIsObject(Value) Then
   Begin
      If VariantToObject(Value) Is TPersistent Then
         FBitmap.Assign(TPersistent(VariantToObject(Value)));
   End Else
      FBitmap.LoadFromFile(Value);
End;

Procedure TD2Image.DoBitmapLoaded(Sender: TObject);
Begin
   If Assigned(FOnBitmapLoaded) Then
      FOnBitmapLoaded(Self);
   If Not (csDestroying In ComponentState) Then
      Repaint;
End;

Procedure TD2Image.DoBitmapDestroy(Sender: TObject);
Begin
End;

Procedure TD2Image.DoBitmapChanged(Sender: TObject);
Begin
   FUpdateEffects := True;
   Repaint;
End;

Procedure TD2Image.Loaded;
Begin
   Inherited;
End;

Procedure TD2Image.Realign;
Begin
   Inherited;
End;

Procedure TD2Image.Paint;
Var
   R: TD2Rect;
   State: Integer;
   i, j: Integer;
   B: TD2Bitmap;
Begin
   If Assigned(Scene) And Scene.GetDesignTime And Not Locked And Not FInPaintTo Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      Canvas.StrokeDash := d2DashSolid;
   End;

   B := FBitmap;
   If FBitmap.ResourceBitmap <> nil Then
      B := FBitmap.ResourceBitmap;
   If B.IsEmpty Then
      Exit;

   If Not FBitmapMargins.MarginEmpty Then
   Begin
      R := d2Rect(0, 0, FBitmapMargins.Left, FBitmapMargins.Top);
      Canvas.DrawBitmap(B, d2Rect(0, 0, FBitmapMargins.Left, FBitmapMargins.Top), R, AbsoluteOpacity, True);

      R := d2Rect(FBitmapMargins.Left, 0, Width - FBitmapMargins.Right, FBitmapMargins.Top);
      Canvas.DrawBitmap(B, d2Rect(FBitmapMargins.Left, 0, B.Width - FBitmapMargins.Right, FBitmapMargins.Top), R, AbsoluteOpacity, True);

      R := d2Rect(Width - FBitmapMargins.Right, 0, Width, FBitmapMargins.Top);
      Canvas.DrawBitmap(B, d2Rect(B.Width - FBitmapMargins.Right, 0, B.Width, FBitmapMargins.Top), R, AbsoluteOpacity, True);

      R := d2Rect(0, FBitmapMargins.Top, FBitmapMargins.Left, Height - FBitmapMargins.Bottom);
      Canvas.DrawBitmap(B, d2Rect(0, FBitmapMargins.Top, FBitmapMargins.Left, B.Height - FBitmapMargins.Bottom), R, AbsoluteOpacity, True);

      R := d2Rect(FBitmapMargins.Left, FBitmapMargins.Top, Width - FBitmapMargins.Right, Height - FBitmapMargins.Bottom);
      Canvas.DrawBitmap(B, d2Rect(FBitmapMargins.Left, FBitmapMargins.Top, B.Width - FBitmapMargins.Right, B.Height - FBitmapMargins.Bottom), R, AbsoluteOpacity, True);

      R := d2Rect(Width - FBitmapMargins.Right, FBitmapMargins.Top, Width, Height - FBitmapMargins.Bottom);
      Canvas.DrawBitmap(B, d2Rect(B.Width - FBitmapMargins.Right, FBitmapMargins.Top, B.Width, B.Height - FBitmapMargins.Bottom), R, AbsoluteOpacity, True);

      R := d2Rect(0, Height - FBitmapMargins.Bottom, FBitmapMargins.Left, Height);
      Canvas.DrawBitmap(B, d2Rect(0, B.Height - FBitmapMargins.Bottom, FBitmapMargins.Left, B.Height), R, AbsoluteOpacity, True);

      R := d2Rect(FBitmapMargins.Left, Height - FBitmapMargins.Bottom, Width - FBitmapMargins.Right, Height);
      Canvas.DrawBitmap(B, d2Rect(FBitmapMargins.Left, B.Height - FBitmapMargins.Bottom, B.Width - FBitmapMargins.Right, B.Height), R, AbsoluteOpacity, True);

      R := d2Rect(Width - FBitmapMargins.Right, Height - FBitmapMargins.Bottom, Width, Height);
      Canvas.DrawBitmap(B, d2Rect(B.Width - FBitmapMargins.Right, B.Height - FBitmapMargins.Bottom, B.Width, B.Height), R, AbsoluteOpacity, True);
   End Else Begin
      Case FWrapMode Of
         d2ImageOriginal:
         Begin
            State := Canvas.SaveCanvas;
            Canvas.IntersectClipRect(LocalRect);
            R := d2Rect(0, 0, B.Width, B.Height);
            Canvas.DrawBitmap(B, d2Rect(0, 0, B.Width, B.Height), R, AbsoluteOpacity, True);
            Canvas.RestoreCanvas(State);
         End;
         d2ImageFit:
         Begin
            R := d2Rect(0, 0, B.Width, B.Height);
            d2FitRect(R, LocalRect);
            Canvas.DrawBitmap(B, d2Rect(0, 0, B.Width, B.Height), R, AbsoluteOpacity, DisableInterpolation);
         End;
         d2ImageStretch:
         Begin
            R := LocalRect;
            Canvas.DrawBitmap(B, d2Rect(0, 0, B.Width, B.Height), R, AbsoluteOpacity, DisableInterpolation);
         End;
         d2ImageTile:
         Begin
            State := Canvas.SaveCanvas;
            Canvas.IntersectClipRect(LocalRect);

            For i := 0 To round(Width / B.Width) Do
               For j := 0 To round(Height / B.Height) Do
               Begin
                  R := d2Rect(0, 0, B.Width, B.Height);
                  d2OffsetRect(R, i * B.Width, j * B.Height);
                  Canvas.DrawBitmap(B, d2Rect(0, 0, B.Width, B.Height), R, AbsoluteOpacity, True);
               End;

            Canvas.RestoreCanvas(State);
         End;
      End;
   End;
End;

Procedure TD2Image.SetBitmap(Const Value: TD2Bitmap);
Begin
   FBitmap.Assign(Value);
   Repaint;
End;

Procedure TD2Image.SetWrapMode(Const Value: TD2ImageWrap);
Begin
   If FWrapMode <> Value Then
   Begin
      FWrapMode := Value;
      repaint;
   End;
End;


//=========== TD2Designer ===============================================

Constructor TD2Designer.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2Designer.Destroy;
Begin
   Inherited;
End;

Procedure TD2Designer.CallDesignSelect(AObject: TObject);
Var
   i: Integer;
Begin
   If FScenes <> nil Then
      For i := 0 To FScenes.Count - 1 Do
      Begin
         Id2Scene(FScenes[i]).DoDesignSelect(AObject);
         Id2Scene(FScenes[i]).AddUpdateRect(d2Rect(0, 0, 10000, 10000));
      End;
End;

Function TD2Designer.AddMethod(aMethodName: String): TMethod;
Begin
   Result.Data := nil;
   Result.Code := nil;
End;

Function TD2Designer.GetMethodName(Method: TMethod): String;
Begin
   Result := '';
End;

Procedure TD2Designer.AddObject(APersistent: TPersistent);
Begin
   //......
End;

Procedure TD2Designer.Add2DObject(AObject: TD2Object);
Begin
   //......
End;

Procedure TD2Designer.Delete2DObject(AObject: TD2Object);
Begin
   //......
End;

Procedure TD2Designer.AddScene(Scene: Id2Scene);
Begin
   If Scene = nil Then
      exit;
   If FScenes = nil Then
      FScenes := TList.Create;

   If FScenes.IndexOf(Pointer(Scene)) < 0 Then
      FScenes.Add(Pointer(Scene));
End;

Procedure TD2Designer.RemoveScene(Scene: Id2Scene);
Var
   xi: Integer;
Begin
   If Scene = nil Then
      exit;
   If FScenes = nil Then
      exit;

   xi := FScenes.IndexOf(Pointer(Scene));

   If xi > -1 Then
      FScenes.Remove(Pointer(Scene));

   If FScenes.Count = 0 Then
      FreeAndNil(FScenes);
End;

Procedure TD2Designer.EditStyle(Res: TD2Resources; Const ASelected: String);
Begin
End;

//=============== TD2DesignFrame ==================================

Constructor TD2DesignFrame.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2DesignFrame.Destroy;
Begin
   Inherited;
End;

Procedure TD2DesignFrame.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
End;

Procedure TD2DesignFrame.MouseLeave;
Begin
   Inherited;
End;

Procedure TD2DesignFrame.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
End;

Procedure TD2DesignFrame.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
End;

Procedure TD2DesignFrame.Paint;
Begin
End;

//=============== TD2Effect ====================================

Constructor TD2Effect.Create(AOwner: TComponent);
Begin
   Inherited;
   FEnabled := True;
End;

Destructor TD2Effect.Destroy;
Begin
   Inherited;
End;

Function TD2Effect.GetOffset: TD2Point;
Begin
End;

Function TD2Effect.GetRect(Const ARect: TD2Rect): TD2Rect;
Begin
   Result := ARect;
End;

Procedure TD2Effect.ApplyTrigger(AInstance: TD2Object; ATrigger: String);
Var
   StartValue: Boolean;
   Line, Setter, Prop, Value: Ansistring;
Begin
   If FTrigger = '' Then
      Exit;
   If AInstance = nil Then
      Exit;
   If Pos(LowerCase(ATrigger), LowerCase(FTrigger)) = 0 Then
      Exit;

   Line := FTrigger;
   Setter := d2GetToken(Line, ';');
   StartValue := False;
   While Setter <> '' Do
   Begin
      Prop := d2GetToken(Setter, '=');
      Value := Setter;
      If GetPropInfo(AInstance, Prop, [tkBool]) <> nil Then
      Begin
         StartValue := False;
         If (CompareText(Value, 'true') = 0) And (GetOrdProp(AInstance, Prop) > 0) Then
            StartValue := True;
         If (CompareText(Value, 'false') = 0) And (GetOrdProp(AInstance, Prop) = 0) Then
            StartValue := True;
      End;
      Setter := d2GetToken(Line, ';');
   End;
   Enabled := StartValue;
End;

Procedure TD2Effect.UpdateParentEffects;
Var
   SaveEnabled: Boolean;
Begin
   If Not (csLoading In ComponentState) Then
      If (Parent <> nil) And (Parent.isVisual) Then
      Begin
         TD2VisualObject(Parent).UpdateEffects;
         TD2VisualObject(Parent).FRecalcUpdateRect := True;

         SaveEnabled := FEnabled;
         FEnabled := True;
         TD2VisualObject(Parent).Repaint;
         FEnabled := SaveEnabled;
      End;
End;

Procedure TD2Effect.ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single);
Begin
End;

Procedure TD2Effect.SetEnabled(Const Value: Boolean);
Begin
   If FEnabled <> Value Then
   Begin
      FEnabled := Value;
      If FEnabled Then
      Begin
         If (Parent <> nil) And (Parent.isVisual) Then
            TD2VisualObject(Parent).RecalcHasEffect;
      End;
      UpdateParentEffects;
   End;
End;


Procedure Blur(Const Canvas: TD2Canvas; Const Bitmap: TD2Bitmap; Const Radius: Integer; UseAlpha: Boolean = True);
Var
   pix: PD2ColorArray;
   w, h, wm, hm, wh, vdiv: Integer;
   rsum, gsum, bsum, asum, x, y, i, yp, yi, yw: Integer;
   P: Cardinal;
   divsum: Integer;
   stackpointer, stackstart: Integer;
   sir: PD2ColorRec;
   rbs, r1, routsum, goutsum, boutsum, aoutsum, rinsum, ginsum, binsum, ainsum: Integer;
   dv: PIntArray;
   vmin: PIntArray;
   r, g, b, a: PIntArray;
   stack: PD2ColorArray;
Begin
   If (radius < 1) Then
      Exit;

   pix := Bitmap.Scanline[0];

   w := Bitmap.Width;
   h := Bitmap.Height;
   wm := w - 1;
   hm := h - 1;
   wh := w * h;
   vdiv := radius + radius + 1;

   GetMem(r, wh * SizeOf(Integer));
   GetMem(g, wh * SizeOf(Integer));
   GetMem(b, wh * SizeOf(Integer));
   GetMem(a, wh * SizeOf(Integer));
   GetMem(vmin, max(w, h) * SizeOf(Integer));
   divsum := (vdiv + 1) Shr 1;
   divsum := divsum * divsum;
   GetMem(dv, 256 * divsum * SizeOf(Integer));
   For i := 0 To 256 * divsum - 1 Do
      dv[i] := (i Div divsum);

   yw := 0;
   yi := 0;

   GetMem(stack, vdiv * SizeOf(TD2Color));

   r1 := radius + 1;

   For y := 0 To h - 1 Do
   Begin
      rinsum := 0;
      ginsum := 0;
      binsum := 0;
      ainsum := 0;
      routsum := 0;
      goutsum := 0;
      boutsum := 0;
      aoutsum := 0;
      rsum := 0;
      gsum := 0;
      bsum := 0;
      asum := 0;
      For i := -radius To radius Do
      Begin
         p := pix[yi + min(wm, max(i, 0))];
         sir := @stack[i + radius];
         sir.Color := p;
         rbs := r1 - abs(i);
         rsum := rsum + (sir.r * rbs);
         gsum := gsum + (sir.g * rbs);
         bsum := bsum + (sir.b * rbs);
         If UseAlpha Then
            asum := asum + (sir.a * rbs);
         If (i > 0) Then
         Begin
            rinsum := rinsum + sir.r;
            ginsum := ginsum + sir.g;
            binsum := binsum + sir.b;
            If UseAlpha Then
               ainsum := ainsum + sir.a;
         End Else Begin
            routsum := routsum + sir.r;
            goutsum := goutsum + sir.g;
            boutsum := boutsum + sir.b;
            If UseAlpha Then
               aoutsum := aoutsum + sir.a;
         End;
      End;
      stackpointer := radius;

      For x := 0 To w - 1 Do
      Begin
         r[yi] := dv[rsum];
         g[yi] := dv[gsum];
         b[yi] := dv[bsum];
         If UseAlpha Then
            a[yi] := dv[asum];

         rsum := rsum - routsum;
         gsum := gsum - goutsum;
         bsum := bsum - boutsum;
         If UseAlpha Then
            asum := asum - aoutsum;

         stackstart := stackpointer - radius + vdiv;
         sir := @stack[stackstart Mod vdiv];

         routsum := routsum - sir.r;
         goutsum := goutsum - sir.g;
         boutsum := boutsum - sir.b;
         If UseAlpha Then
            aoutsum := aoutsum - sir.a;

         If (y = 0) Then
         Begin
            vmin[x] := min(x + radius + 1, wm);
         End;
         p := pix[yw + vmin[x]];
         sir.color := p;

         rinsum := rinsum + sir.r;
         ginsum := ginsum + sir.g;
         binsum := binsum + sir.b;
         If UseAlpha Then
            ainsum := ainsum + sir.a;

         rsum := rsum + rinsum;
         gsum := gsum + ginsum;
         bsum := bsum + binsum;
         If UseAlpha Then
            asum := asum + ainsum;

         stackpointer := (stackpointer + 1) Mod vdiv;
         sir := @stack[(stackpointer) Mod vdiv];

         routsum := routsum + sir.r;
         goutsum := goutsum + sir.g;
         boutsum := boutsum + sir.b;
         If UseAlpha Then
            aoutsum := aoutsum + sir.a;

         rinsum := rinsum - sir.r;
         ginsum := ginsum - sir.g;
         binsum := binsum - sir.b;
         If UseAlpha Then
            ainsum := ainsum - sir.a;

         yi := yi + 1;
      End;
      yw := yw + w;
   End;

   For x := 0 To w - 1 Do
   Begin
      rinsum := 0;
      ginsum := 0;
      binsum := 0;
      ainsum := 0;
      routsum := 0;
      goutsum := 0;
      boutsum := 0;
      aoutsum := 0;
      rsum := 0;
      gsum := 0;
      bsum := 0;
      asum := 0;
      yp := -radius * w;
      For i := -radius To radius Do
      Begin
         yi := max(0, yp) + x;

         sir := @stack[i + radius];

         sir.r := r[yi];
         sir.g := g[yi];
         sir.b := b[yi];
         If UseAlpha Then
            sir.a := a[yi];

         rbs := r1 - abs(i);

         rsum := rsum + (r[yi] * rbs);
         gsum := gsum + (g[yi] * rbs);
         bsum := bsum + (b[yi] * rbs);
         If UseAlpha Then
            asum := asum + (a[yi] * rbs);

         If (i > 0) Then
         Begin
            rinsum := rinsum + sir.r;
            ginsum := ginsum + sir.g;
            binsum := binsum + sir.b;
            If UseAlpha Then
               ainsum := ainsum + sir.a;
         End Else Begin
            routsum := routsum + sir.r;
            goutsum := goutsum + sir.g;
            boutsum := boutsum + sir.b;
            If UseAlpha Then
               aoutsum := aoutsum + sir.a;
         End;

         If (i < hm) Then
         Begin
            yp := yp + w;
         End;
      End;
      yi := x;
      stackpointer := radius;
      For y := 0 To h - 1 Do
      Begin
         pix[yi] := (dv[asum] Shl 24) Or (dv[rsum] Shl 16) Or (dv[gsum] Shl 8) Or dv[bsum];

         rsum := rsum - routsum;
         gsum := gsum - goutsum;
         bsum := bsum - boutsum;
         If UseAlpha Then
            asum := asum - aoutsum;

         stackstart := stackpointer - radius + vdiv;
         sir := @stack[stackstart Mod vdiv];

         routsum := routsum - sir.r;
         goutsum := goutsum - sir.g;
         boutsum := boutsum - sir.b;
         If UseAlpha Then
            aoutsum := aoutsum - sir.a;

         If (x = 0) Then
         Begin
            vmin[y] := min(y + r1, hm) * w;
         End;
         p := x + vmin[y];

         sir.r := r[p];
         sir.g := g[p];
         sir.b := b[p];
         If UseAlpha Then
            sir.a := a[p];

         rinsum := rinsum + sir.r;
         ginsum := ginsum + sir.g;
         binsum := binsum + sir.b;
         If UseAlpha Then
            ainsum := ainsum + sir.a;

         rsum := rsum + rinsum;
         gsum := gsum + ginsum;
         bsum := bsum + binsum;
         If UseAlpha Then
            asum := asum + ainsum;

         stackpointer := (stackpointer + 1) Mod vdiv;
         sir := @stack[stackpointer];

         routsum := routsum + sir.r;
         goutsum := goutsum + sir.g;
         boutsum := boutsum + sir.b;
         If UseAlpha Then
            aoutsum := aoutsum + sir.a;

         rinsum := rinsum - sir.r;
         ginsum := ginsum - sir.g;
         binsum := binsum - sir.b;
         If UseAlpha Then
            ainsum := ainsum - sir.a;

         yi := yi + w;
      End;
   End;
   FreeMem(stack, vdiv * SizeOf(TD2Color));
   FreeMem(dv, 256 * divsum * SizeOf(Integer));
   FreeMem(vmin, max(w, h) * SizeOf(Integer));
   FreeMem(a, wh * SizeOf(Integer));
   FreeMem(r, wh * SizeOf(Integer));
   FreeMem(g, wh * SizeOf(Integer));
   FreeMem(b, wh * SizeOf(Integer));
End;

//=============== TD2ShadowEffect =============================================

Constructor TD2ShadowEffect.Create(AOwner: TComponent);
Begin
   Inherited;
   //  DisablePaint:=true;
   FShadowColor := $FF000000;
   FDirection := 45;
   FOpacity := 0.6;
   FSoftness := 0.3;
   FDistance := 3;
End;

Destructor TD2ShadowEffect.Destroy;
Begin
   Inherited;
End;

Function TD2ShadowEffect.GetOffset: TD2Point;
Var
   S, C: Single;
Begin
   d2SinCos(d2DegToRad(FDirection), S, C);
   Result := d2Point(Trunc(FSoftness * 20), Trunc(FSoftness * 20));
End;

Function TD2ShadowEffect.GetRect(Const ARect: TD2Rect): TD2Rect;
Var
   S, C: Single;
Begin
   Result := ARect;
   d2InflateRect(Result, Trunc(FSoftness * 20), Trunc(FSoftness * 20));
   d2SinCos(d2DegToRad(FDirection), S, C);
   d2OffsetRect(Result, FDistance * C, FDistance * S);
End;

Function TD2ShadowEffect.GetShadowColor: String;
Begin
   Result := d2ColorToStr(FShadowColor);
End;

Procedure TD2ShadowEffect.ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single);
Var
   Mask: PByteArray;
   Sn, Cs: Single;
   Off: TD2Point;
Begin
   Mask := aVisual.CreateMask;
   aVisual.FillColor(d2Opacity(d2CorrectColor(FShadowColor), FOpacity));
   Blur(Canvas, aVisual, Trunc(5 + FSoftness * 10));

   d2SinCos(d2DegToRad(FDirection), Sn, Cs);
   Off := d2Point(FDistance * Cs * aData, FDistance * Sn * aData);
   aVisual.ApplyMask(Mask, -round(Off.X), -round(Off.Y));

   FreeMem(Mask, aVisual.Width * aVisual.Height);
End;

Procedure TD2ShadowEffect.SetDirection(Const Value: Single);
Begin
   If FDirection <> Value Then
   Begin
      FDirection := Value;
      UpdateParentEffects;
   End;
End;

Procedure TD2ShadowEffect.SetDistance(Const Value: Single);
Begin
   If FDistance <> Value Then
   Begin
      FDistance := Value;
      UpdateParentEffects;
   End;
End;

Procedure TD2ShadowEffect.SetOpacity(Const Value: Single);
Begin
   If FOpacity <> Value Then
   Begin
      FOpacity := Value;
      If FOpacity < 0 Then
         FOpacity := 0;
      If FOpacity > 1 Then
         FOpacity := 1;
      UpdateParentEffects;
   End;
End;

Procedure TD2ShadowEffect.SetShadowColor(Const Value: String);
Begin
   If FShadowColor <> d2StrToColor(Value) Then
   Begin
      FShadowColor := d2StrToColor(Value);
      UpdateParentEffects;
   End;
End;

Procedure TD2ShadowEffect.SetSoftness(Const Value: Single);
Begin
   If FSoftness <> Value Then
   Begin
      FSoftness := Value;
      If FSoftness < 0 Then
         FSoftness := 0;
      If FSoftness > 3 Then
         FSoftness := 3;
      UpdateParentEffects;
   End;
End;

//=============== TD2BlurEffect =========================================

Constructor TD2BlurEffect.Create(AOwner: TComponent);
Begin
   Inherited;
   DisablePaint := True;
   FSoftness := 0.3;
End;

Destructor TD2BlurEffect.Destroy;
Begin
   Inherited;
End;

Function TD2BlurEffect.GetOffset: TD2Point;
Begin
   Result := d2Point(Trunc(FSoftness * 20), Trunc(FSoftness * 20));
End;

Function TD2BlurEffect.GetRect(Const ARect: TD2Rect): TD2Rect;
Begin
   Result := ARect;
   d2InflateRect(Result, Trunc(FSoftness * 20), Trunc(FSoftness * 20));
End;

Procedure TD2BlurEffect.ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single);
Begin
   If FSoftness > 0 Then
      Blur(Canvas, aVisual, Trunc(FSoftness * 15));
End;

Procedure TD2BlurEffect.SetSoftness(Const Value: Single);
Begin
   If FSoftness <> Value Then
   Begin
      FSoftness := Value;
      If FSoftness < 0 Then
         FSoftness := 0;
      If FSoftness > 9 Then
         FSoftness := 9;
      UpdateParentEffects;
   End;
End;

//=============== TD2GlowEffect ===========================================================

Constructor TD2GlowEffect.Create(AOwner: TComponent);
Begin
   Inherited;
   //  DisablePaint:=true;
   FOpacity := 0.9;
   FGlowColor := $FFFFD700;
   FSoftness := 0.4;
End;

Destructor TD2GlowEffect.Destroy;
Begin
   Inherited;
End;

Function TD2GlowEffect.GetGlowColor: String;
Begin
   Result := d2ColorToStr(FGlowColor);
End;

Function TD2GlowEffect.GetOffset: TD2Point;
Begin
   Result := d2Point(4 + Trunc(FSoftness * 23), 4 + Trunc(FSoftness * 23));
End;

Function TD2GlowEffect.GetRect(Const ARect: TD2Rect): TD2Rect;
Begin
   Result := ARect;
   d2InflateRect(Result, 4 + Trunc(FSoftness * 23), 4 + Trunc(FSoftness * 23));
End;

Procedure TD2GlowEffect.ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single);
Var
   Mask: PByteArray;
Begin
   Mask := aVisual.CreateMask;
   aVisual.FillColor(d2Opacity(d2CorrectColor(FGlowColor), FOpacity));
   Blur(Canvas, aVisual, Trunc(5 + FSoftness * 10));

   aVisual.ApplyMask(Mask);
   FreeMem(Mask, aVisual.Width * aVisual.Height);
End;

Procedure TD2GlowEffect.SetGlowColor(Const Value: String);
Begin
   If FGlowColor <> d2StrToColor(Value) Then
   Begin
      FGlowColor := d2StrToColor(Value);
      UpdateParentEffects;
   End;
End;

Procedure TD2GlowEffect.SetOpacity(Const Value: Single);
Begin
   If FOpacity <> Value Then
   Begin
      FOpacity := Value;
      If FOpacity < 0 Then
         FOpacity := 0;
      If FOpacity > 1 Then
         FOpacity := 1;
      UpdateParentEffects;
   End;
End;

Procedure TD2GlowEffect.SetSoftness(Const Value: Single);
Begin
   If FSoftness <> Value Then
   Begin
      FSoftness := Value;
      If FSoftness < 0 Then
         FSoftness := 0;
      If FSoftness > 9 Then
         FSoftness := 9;
      UpdateParentEffects;
   End;
End;

//=============== TD2InnerGlowEffect =======================================================

Constructor TD2InnerGlowEffect.Create(AOwner: TComponent);
Begin
   Inherited;
   //  DisablePaint:=true;
   AfterPaint := True;
End;

Destructor TD2InnerGlowEffect.Destroy;
Begin
   Inherited;
End;

Function TD2InnerGlowEffect.GetOffset: TD2Point;
Begin
   Result := d2Point(Trunc(FSoftness * 20), Trunc(FSoftness * 20));
End;

Function TD2InnerGlowEffect.GetRect(Const ARect: TD2Rect): TD2Rect;
Begin
   Result := ARect;
   d2InflateRect(Result, Trunc(FSoftness * 20), Trunc(FSoftness * 20));
End;

Procedure TD2InnerGlowEffect.ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single);
Var
   Mask: PByteArray;
Begin
   aVisual.InvertAlpha;
   Mask := aVisual.CreateMask;
   aVisual.FillColor(d2Opacity(d2CorrectColor(FGlowColor), FOpacity));

   Blur(Canvas, aVisual, Trunc(5 + FSoftness * 10));
   aVisual.ApplyMask(Mask);
   FreeMem(Mask, aVisual.Width * aVisual.Height);
End;

//=============== TD2BevelEffect ========================================================

Constructor TD2BevelEffect.Create(AOwner: TComponent);
Begin
   Inherited;
   DisablePaint := True;
   FDirection := 45;
   FSize := 10;
End;

Destructor TD2BevelEffect.Destroy;
Begin
   Inherited;
End;

Function TD2BevelEffect.GetOffset: TD2Point;
Begin
   Result := d2Point(5, 5);
End;

Function TD2BevelEffect.GetRect(Const ARect: TD2Rect): TD2Rect;
Begin
   Result := ARect;
   d2InflateRect(Result, 5, 5);
End;

Function VectorAngleCosine(Const V1, V2: TD2Vector): Single;
Var
   dot, len1, len2: Single;
Begin
   len1 := sqrt((v1.x * v1.x) + (v1.y * v1.y) + (v1.w * v1.w));
   len2 := sqrt((v2.x * v2.x) + (v2.y * v2.y) + (v2.w * v2.w));
   dot := (V1.V[0] * V2.V[0] + V1.V[1] * V2.V[1] + V1.V[2] * V2.V[2]);
   Result := len1 * len2;
   If Abs(Result) > 1e-40 Then
      Result := dot / Result
   Else
      Result := 1;
End;

Procedure TD2BevelEffect.ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single);
Var
   DestBits, Bits: PD2ColorRecArray;
   i, j: Integer;
   a, h0, h1, h2, h3: Single;
   alpha: Byte;
   light, n, v, b: TD2Vector;
   LightMap: TD2Bitmap;
Begin
   If FSize = 0 Then
      Exit;

   DestBits := PD2ColorRecArray(aVisual.Scanline[0]);
   LightMap := TD2Bitmap.Create(aVisual.Width, aVisual.Height);
   Bits := PD2ColorRecArray(LightMap.Scanline[0]);

   System.Move(DestBits^, Bits^, aVisual.Width * aVisual.Height * 4);

   Blur(Canvas, LightMap, FSize);

   a := DegToRad(FDirection);
   light.x := cos(a);
   light.y := 0;
   light.w := sin(a);

   For j := aVisual.Height - 2 Downto 0 Do
   Begin
      For i := aVisual.Width - 2 Downto 0 Do
      Begin
         {$ifdef FPC_BIG_ENDIAN}
      alpha:=DestBits[(i) + ((j) * aVisual.Width)].Color and $FF;
         {$else}
         alpha := DestBits[(i) + ((j) * aVisual.Width)].A;
         {$endif}
         If alpha > 0 Then
         Begin
            {$ifdef FPC_BIG_ENDIAN}
        h0:=(Bits[i + ((j + 1) * aVisual.Width)].Color and $FF) / $FF;
        h1:=(Bits[(i + 1) + ((j + 1) * aVisual.Width)].Color and $FF) / $FF;
        h2:=(Bits[(i + 1) + (j * aVisual.Width)].Color and $FF) / $FF;
        h3:=(Bits[(i + 1) + ((j + 1) * aVisual.Width)].Color and $FF) / $FF;
            {$else}
            h0 := (Bits[i + ((j + 1) * aVisual.Width)].Color And $FF000000 Shr 24) / $FF;
            h1 := (Bits[(i + 1) + ((j + 1) * aVisual.Width)].Color And $FF000000 Shr 24) / $FF;
            h2 := (Bits[(i + 1) + (j * aVisual.Width)].Color And $FF000000 Shr 24) / $FF;
            h3 := (Bits[(i + 1) + ((j + 1) * aVisual.Width)].Color And $FF000000 Shr 24) / $FF;
            {$endif}

            v.x := 1.0;
            v.y := h2 - h3;
            v.w := 0;

            b.x := 0;
            b.y := h0 - h3;
            b.w := 1;

            n.x := b.y * v.w - v.y * b.w;
            n.y := v.x * b.w - b.x * v.w;
            n.w := b.x * v.y - v.x * b.y;

            n.x := n.x / b.w;
            n.y := n.y / b.w;
            n.w := n.w / b.w;

            a := VectorAngleCosine(light, n) * FSize;


            {$ifdef FPC_BIG_ENDIAN}
        color:=DestBits[(i) + ((j) * aVisual.Width)].Color;
        ReverseBytes(@color,4);
        DestBits[(i) + ((j) * aVisual.Width)].Color:=d2PremultyAlpha(d2Opacity(d2ChangeHSL(color,0,0,a * 0.4),alpha / $FF));
        ReverseBytes(@DestBits[(i) + ((j) * aVisual.Width)],4);
            {$else}
            DestBits[(i) + ((j) * aVisual.Width)].Color := d2PremultyAlpha(d2Opacity(d2ChangeHSL(DestBits[(i) + ((j) * aVisual.Width)].Color, 0, 0, a * 0.4), alpha / $FF));
            {$endif}
         End;
      End;
   End;
   LightMap.Free;
End;

Procedure TD2BevelEffect.SetDirection(Const Value: Single);
Begin
   If FDirection <> Value Then
   Begin
      FDirection := Value;
      UpdateParentEffects;
   End;
End;

Procedure TD2BevelEffect.SetSize(Const Value: Integer);
Begin
   If FSize <> Value Then
   Begin
      FSize := Value;
      UpdateParentEffects;
   End;
End;

//=============== TD2ReflectionEffect ======================================

Constructor TD2ReflectionEffect.Create(AOwner: TComponent);
Begin
   Inherited;
   //  DisablePaint:=true;
   FOffset := 0;
   FLength := 0.5;
   FOpacity := 0.5;
End;

Destructor TD2ReflectionEffect.Destroy;
Begin
   Inherited;
End;

Function TD2ReflectionEffect.GetOffset: TD2Point;
Begin
   Result := d2Point(2, 2);
End;

Function TD2ReflectionEffect.GetRect(Const ARect: TD2Rect): TD2Rect;
Begin
   Result := ARect;
   d2InflateRect(Result, 2, 2);
   d2OffsetRect(Result, 0, d2RectHeight(ARect) + FOffset);
End;

Procedure TD2ReflectionEffect.ProcessEffect(Canvas: TD2Canvas; Const aVisual: TD2Bitmap; Const aData: Single);
Var
   Bits: PD2ColorRecArray;
   c, a, i, j: Integer;
   alpha: Byte;
Begin
   Bits := PD2ColorRecArray(aVisual.Scanline[0]);
   c := aVisual.Height - 1;
   a := round(c * FLength);

   aVisual.FlipHorizontal;

   For j := 0 To c Do
      If j > (c - a) Then
         For i := 0 To aVisual.Width - 1 Do
         Begin
            {$ifdef FPC_BIG_ENDIAN}
        alpha:=Bits[i + (aVisual.Height - 1 - j) * aVisual.Width].Color and $FF;
            {$else}
            alpha := Bits[i + (aVisual.Height - 1 - j) * aVisual.Width].A;
            {$endif}
            If alpha > 0 Then
            Begin
               {$ifdef FPC_BIG_ENDIAN}
          color:=Bits[i + (aVisual.Height - 1 - j) * aVisual.Width].Color;
          ReverseBytes(@color,4);
          TD2ColorRec(color).A:=Trunc(((j - (c - a)) / a) * FOpacity * alpha);
          Bits[i + (aVisual.Height - 1 - j) * aVisual.Width].Color:=d2PremultyAlpha(color);
          ReverseBytes(@Bits[i + (aVisual.Height - 1 - j) * aVisual.Width],4);
               {$else}
               Bits[i + (aVisual.Height - 1 - j) * aVisual.Width].A := Trunc(((j - (c - a)) / a) * FOpacity * alpha);
               Bits[i + (aVisual.Height - 1 - j) * aVisual.Width].Color := d2PremultyAlpha(Bits[i + (aVisual.Height - 1 - j) * aVisual.Width].Color);
               {$endif}
            End;
         End Else
         d2FillLongword(@Bits[(aVisual.Height - 1 - j) * aVisual.Width], aVisual.Width, $0);
End;

Procedure TD2ReflectionEffect.SetLength(Const Value: Single);
Begin
   If FLength <> Value Then
   Begin
      FLength := Value;
      If FLength < 0.1 Then
         FLength := 0.1;
      If FLength > 1 Then
         FLength := 1;
      UpdateParentEffects;
   End;
End;

Procedure TD2ReflectionEffect.SetOffset(Const Value: Integer);
Begin
   If FOffset <> Value Then
   Begin
      FOffset := Value;
      UpdateParentEffects;
   End;
End;

Procedure TD2ReflectionEffect.SetOpacity(Const Value: Single);
Begin
   If FOpacity <> Value Then
   Begin
      FOpacity := Value;
      UpdateParentEffects;
   End;
End;

//=============== TD2Calendar ===========================================================

Constructor TD2Calendar.Create(AOwner: TComponent);
Var
   i: Integer;
   L: TD2VisualObject;
   AYear, AMonth, ADay: Word;
Begin
   Inherited;
   FDateTime := Now;
   DecodeDate(FDateTime, AYear, AMonth, ADay);
   FFirstDayOfWeek := d2LocaleDefault;
   Width := 180;
   Height := 160;

   L := TD2Layout.Create(Self);
   With L Do
   Begin
      Parent := Self;
      Locked := True;
      Stored := False;
      Height := 19;
      Align := vaMostTop;
      Padding.Bottom := 2;

      FPrev := TD2Button.Create(Self);
      With FPrev Do
      Begin
         Parent := L;
         Width := 19;
         Locked := True;
         Stored := False;
         Align := vaLeft;
         Padding.Right := 2;
         Resource := 'transparentcirclebuttonstyle';
         OnClick := DoPrevClick;
         RepeatClick := True;

         With TD2ScrollArrowLeft.Create(Self) Do
         Begin
            Parent := FPrev;
            Width := 5;
            Height := 5;
            Stroke.Style := d2BrushNone;
            Align := vaCenter;
            HitTest := False;
            Stored := False;
            Locked := True;
         End;
      End;

      FToday := TD2Button.Create(Self);
      With FToday Do
      Begin
         Parent := L;
         Width := 19;
         Locked := True;
         Stored := False;
         Align := vaLeft;
         Position.X := 30;
         Padding.Right := 2;
         Resource := 'transparentcirclebuttonstyle';
         OnClick := DoTodayClick;
         RepeatClick := True;

         With TD2Ellipse.Create(Self) Do
         Begin
            Parent := FToday;
            Width := 5;
            Height := 5;
            Stroke.Style := d2BrushNone;
            Align := vaCenter;
            HitTest := False;
            Stored := False;
            Locked := True;
         End;
      End;

      FNext := TD2Button.Create(Self);
      With FNext Do
      Begin
         Parent := L;
         Width := 19;
         Locked := True;
         Stored := False;
         Position.X := 50;
         Align := vaLeft;
         Padding.Right := 2;
         Resource := 'transparentcirclebuttonstyle';
         RepeatClick := True;
         OnClick := DoNextClick;
         With TD2ScrollArrowRight.Create(Self) Do
         Begin
            Parent := FNext;
            Width := 5;
            Height := 5;
            Stroke.Style := d2BrushNone;
            Align := vaCenter;
            HitTest := False;
            Stored := False;
            Locked := True;
         End;
      End;

      FMonths := TD2PopupBox.Create(Self);
      With FMonths Do
      Begin
         Parent := L;
         Align := vaClient;
         Locked := True;
         Stored := False;
         DisableFocusEffect := True;
         Padding.Left := 5;
         Padding.Right := 5;
         Resource := 'labelstyle';
         For i := 1 To 12 Do
            Items.Add(LongMonthNames[i]);
         Font.Style := d2FontBold;
         TextAlign := d2TextAlignFar;
         ItemIndex := AMonth - 1;
         OnChange := DoMonthChange;
      End;

      FYears := TD2PopupBox.Create(Self);
      With FYears Do
      Begin
         Parent := L;
         Width := 40;
         Align := vaRight;
         Locked := True;
         Stored := False;
         DisableFocusEffect := True;
         Resource := 'labelstyle';
         For i := 1 To 10 Do
            Items.Add(IntToStr(AYear - i));
         Items.Add(IntToStr(AYear));
         For i := 1 To 10 Do
            Items.Add(IntToStr(AYear + i));
         Font.Style := d2FontBold;
         TextAlign := d2TextAlignNear;
         ItemIndex := 10;
         OnChange := DoYearChange;
      End;
   End;

   FWeek := TD2GridLayout.Create(Self);
   With FWeek Do
   Begin
      Parent := Self;
      Locked := True;
      Stored := False;
      Height := 19;
      Position.Y := 20;
      ItemHeight := 19;
      Align := vaTop;
      Padding.Bottom := 2;
      For i := 0 To 6 Do
         With TD2Label.Create(Self) Do
         Begin
            Parent := FWeek;
            Locked := True;
            Stored := False;
            TextAlign := d2TextAlignCenter;
            WordWrap := False;
         End;
      ItemWidth := Width / 7;
   End;

   FWeeks := TD2GridLayout.Create(Self);
   With FWeeks Do
   Begin
      Parent := Self;
      Locked := True;
      Stored := False;
      Width := 23;
      Align := vaMostLeft;
      Padding.Top := 19 + 2;
      ItemHeight := 19;
      ItemWidth := Width;
      Visible := False;
      For i := 0 To 5 Do
         With TD2Label.Create(Self) Do
         Begin
            Parent := FWeeks;
            Locked := True;
            Stored := False;
            TextAlign := d2TextAlignCenter;
            WordWrap := False;
         End;
   End;

   FDays := TD2ListBox.Create(Self);
   With FDays Do
   Begin
      Parent := Self;
      Locked := True;
      Stored := False;
      Position.Y := 50;
      Height := 19 * 6;
      Align := vaTop;
      Columns := 7;
      ItemHeight := 19;
      AlternatingRowBackground := True;
      HideSelectionUnfocused := False;
      ShowScrollBars := False;
      OnChange := DoDayChange;
      Resource := 'transparentlistboxstyle';
      For i := 1 To 6 * 7 Do
         With TD2ListBoxItem.Create(Self) Do
         Begin
            Parent := FDays;
            Locked := True;
            Stored := False;
            TextAlign := d2TextAlignFar;
            WordWrap := False;
         End;
   End;
   FillList;
End;

Destructor TD2Calendar.Destroy;
Begin
   Inherited;
End;

Procedure TD2Calendar.FillList;
Var
   i: Integer;
   AYear, PreMonth, AMonth, ADay: Word;
   Date: TDate;
   First: PtrInt;
   A: Array[0..1] Of Char;
   Item: TD2ListBoxItem;
Begin
   FDisableDayChange := FDisableDayChange + 1;
   Try

      If FFirstDayOfWeek = d2LocaleDefault Then
      Begin
         {$IFDEF ORCA2D_OS_WINDOWS}
      GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_IFIRSTDAYOFWEEK,A,SizeOf(A));
         {$ENDIF}
         FFirstDayOfWeekNum := Ord(A[0]) - Ord('0');
      End Else
         FFirstDayOfWeekNum := Ord(FFirstDayOfWeek);
      FFirstDayOfWeekNum := (8 + FFirstDayOfWeekNum) Mod 7;

      For i := 0 To 6 Do
         TD2Label(FWeek.Children[i]).TextW := UTF8Encode(ShortDayNames[1 + ((7 + i + FFirstDayOfWeekNum) Mod 7)]);

      DecodeDate(FDateTime, AYear, AMonth, ADay);
      PreMonth := AMonth - 1;
      If PreMonth < 1 Then
         PreMonth := 12;
      Date := EncodeDate(AYear, AMonth, 1);
      First := DayOfWeek(Date);
      If First - FFirstDayOfWeekNum < 3 Then
         First := First + 7;
      If FDays.Count - (First + MonthDays[IsLeapYear(AYear), AMonth] - FFirstDayOfWeekNum) < 3 Then
         First := First - 7;
      FDays.Tag := First;
      Date := IncMonth(Date, 1);

      For i := 1 To First Do
      Begin
         Item := FDays.Items[i - 1];
         If Item = nil Then
            Continue;
         Item.Opacity := 0.3;
         Item.TextW := IntToStr(MonthDays[IsLeapYear(AYear), PreMonth] - First + i + 1 + FFirstDayOfWeekNum);
      End;

      For i := 1 To MonthDays[IsLeapYear(AYear), AMonth] Do
      Begin
         Item := FDays.Items[First + i - 2 - FFirstDayOfWeekNum];
         If Item = nil Then
            Continue;
         Item.Opacity := 1;
         Item.TextW := IntToStr(i);
      End;

      For i := First + MonthDays[IsLeapYear(AYear), AMonth] To FDays.Count + FFirstDayOfWeekNum Do
      Begin
         Item := FDays.Items[i - 1 - FFirstDayOfWeekNum];
         If Item = nil Then
            Continue;
         Item.Opacity := 0.3;
         Item.TextW := IntToStr(i - First - MonthDays[IsLeapYear(AYear), AMonth] + 1);
      End;


      If FWeekNumbers Then
      Begin
         FWeeks.Visible := True;
         DecodeDate(FDateTime, AYear, AMonth, ADay);
         Date := EncodeDate(AYear, AMonth, 1);
         For i := 0 To 5 Do
            If WeekOfYear(Date) + i = 0 Then
               TD2Label(FWeeks.Children[i]).TextW := IntToStr(52)
            Else
            If WeekOfYear(Date) = 0 Then
               TD2Label(FWeeks.Children[i]).TextW := IntToStr(i)
            Else
            If WeekOfYear(Date) + i > 52 Then
               TD2Label(FWeeks.Children[i]).TextW := IntToStr(WeekOfYear(Date) + i - 52)
            Else
               TD2Label(FWeeks.Children[i]).TextW := IntToStr(WeekOfYear(Date) + i);
      End Else
         FWeeks.Visible := False;

      FDays.ItemIndex := First + ADay - 2 - FFirstDayOfWeekNum;

      FMonths.ItemIndex := AMonth - 1;

      FYears.Items.Clear;

      For i := 10 Downto 1 Do
         FYears.Items.Add(IntToStr(AYear - i));

      FYears.Items.Add(IntToStr(AYear));
      For i := 1 To 10 Do
         FYears.Items.Add(IntToStr(AYear + i));

      FYears.TextW := IntToStr(AYear);
   Finally
      FDisableDayChange := FDisableDayChange - 1;
   End;
End;

Function TD2Calendar.GetData: Variant;
Begin
   Result := VarFromDateTime(FDateTime);
End;

Procedure TD2Calendar.SetData(Const Value: Variant);
Var
   D: TDateTime;
Begin
   If VarIsType(Value, varDate) Then
      Date := VarToDateTime(Value)
   Else
   If VarIsStr(Value) And TryStrToDateTime(Ansistring(Value), D) Then
      Date := D;
End;

Procedure TD2Calendar.DoDayChange(Sender: TObject);
Var
   AYear, AMonth, ADay: Word;
Begin
   DecodeDate(FDateTime, AYear, AMonth, ADay);
   If FDays.ItemIndex - FDays.Tag + 2 + FFirstDayOfWeekNum < 1 Then
   Begin
   End Else
   If FDays.ItemIndex - FDays.Tag + 2 + FFirstDayOfWeekNum > MonthDays[IsLeapYear(AYear), AMonth] Then
   Begin
   End Else
      DateTime := EncodeDate(AYear, AMonth, FDays.ItemIndex - FDays.Tag + 2 + FFirstDayOfWeekNum);
   If Assigned(FOnDayChange) And (FDisableDayChange = 0) Then
      FOnDayChange(Self);
End;

Procedure TD2Calendar.DoTodayClick(Sender: TObject);
Begin
   Date := Now;
   If Assigned(FOnDayChange) And (FDisableDayChange = 0) Then
      FOnDayChange(Self);
End;

Procedure TD2Calendar.DoNextClick(Sender: TObject);
Begin
   Date := IncMonth(Date, 1);
End;

Procedure TD2Calendar.DoPrevClick(Sender: TObject);
Begin
   Date := IncMonth(Date, -1);
End;

Procedure TD2Calendar.DoMonthChange(Sender: TObject);
Var
   AYear, AMonth, ADay: Word;
Begin
   DecodeDate(FDateTime, AYear, AMonth, ADay);
   DateTime := EncodeDate(AYear, FMonths.ItemIndex + 1, ADay);
End;

Procedure TD2Calendar.DoYearChange(Sender: TObject);
Var
   AYear, AMonth, ADay: Word;
Begin
   DecodeDate(FDateTime, AYear, AMonth, ADay);
   DateTime := EncodeDate(StrToInt(FYears.TextW), AMonth, ADay);
End;

Procedure TD2Calendar.Realign;
Begin
   Inherited;
   If FWeek <> nil Then
      FWeek.ItemWidth := FWeek.Width / 7 - 0.1;
End;

Function TD2Calendar.GetDate: TDate;
Begin
   Result := TDate(FDateTime);
End;

Procedure TD2Calendar.SetDate(Value: TDate);
Begin
   FDisableDayChange := FDisableDayChange + 1;
   ReplaceTime(TDateTime(Value), FDateTime);
   Try
      SetDateTime(Value);
   Except
      SetDateTime(FDateTime);
      Raise;
   End;
   FDisableDayChange := FDisableDayChange - 1;
End;

Procedure TD2Calendar.SetDateTime(Const Value: TDateTime);
Begin
   If FDateTime <> Value Then
   Begin
      FDateTime := Value;
      FillList;
      If Assigned(FBindingObjects) Then
         ToBindingObjects;
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Procedure TD2Calendar.SetFirstDayOfWeek(Const Value: TD2CalDayOfWeek);
Begin
   If FFirstDayOfWeek <> Value Then
   Begin
      FFirstDayOfWeek := Value;
      FillList;
   End;
End;

Procedure TD2Calendar.SetTodayDefault(Const Value: Boolean);
Begin
   FTodayDefault := Value;
   If FTodayDefault Then
      Date := Now;
End;

Procedure TD2Calendar.SetWeekNumbers(Const Value: Boolean);
Begin
   If FWeekNumbers <> Value Then
   Begin
      FWeekNumbers := Value;
      FillList;
   End;
End;

Procedure TD2Calendar.MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean);
Begin
   Inherited;
   If Not Handled Then
   Begin
      If WheelDelta > 0 Then
         Date := IncMonth(Date, -1)
      Else
         Date := IncMonth(Date, 1);
      Handled := True;
   End;
End;

//=============== TD2CompoundAngleBar ===============================================

Constructor TD2CompoundAngleBar.Create(AOwner: TComponent);
Var
   C: TD2Control;
Begin
   Inherited;
   FDecimalDigits := 2;

   Width := 200;
   Height := 20;
   FTextLabel := TD2Label.Create(Self);
   With FTextLabel Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      WordWrap := False;
      Name := 'TextLabel';
      TextW := 'Caption';
      Width := 70;
      TextAlign := d2TextAlignFar;
      Padding.right := 5;
      SetSubComponent(True);
   End;
   C := TD2Control.Create(Self);
   With C Do
   Begin
      Width := 40;
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaClient;
   End;
   FAngleBar := TD2AngleButton.Create(Self);
   With FAngleBar Do
   Begin
      Parent := C;
      Stored := False;
      Locked := True;
      Align := vaFit;
      Name := 'AngleBar';
      SetSubComponent(True);
      OnChange := DoChange;
   End;
   C := TD2Control.Create(Self);
   With C Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaRight;
   End;
   FValueLabel := TD2ValueLabel.Create(Self);
   With FValueLabel Do
   Begin
      Parent := C;
      Stored := False;
      Locked := True;
      Align := vaVertCenter;
      Name := 'ValueLabel';
      TextW := '0';
      Padding.left := 5;
      SetSubComponent(True);
   End;
End;

Procedure TD2CompoundAngleBar.UpdateLabel;
Begin
   FValueLabel.TextW := IntToStr(Trunc(Value)) + '°';
End;

Procedure TD2CompoundAngleBar.DoChange(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Sender);
   UpdateLabel;
End;

Function TD2CompoundAngleBar.GetValue: Single;
Begin
   Result := FAngleBar.Value;
End;

Procedure TD2CompoundAngleBar.SetValue(Const Value: Single);
Begin
   FAngleBar.Value := Value;
   UpdateLabel;
End;

//=============== TD2CompoundTextBox =============================================

Constructor TD2CompoundTextBox.Create(AOwner: TComponent);
Var
   C: TD2Control;
Begin
   Inherited;
   Width := 200;
   Height := 21;
   FTextLabel := TD2Label.Create(Self);
   With FTextLabel Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      Name := 'TextLabel';
      TextW := 'Caption';
      Width := 70;
      TextAlign := d2TextAlignFar;
      WordWrap := False;
      Padding.right := 5;
      SetSubComponent(True);
   End;
   C := TD2Control.Create(Self);
   With C Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaClient;
   End;
   FTextBox := TD2TextBox.Create(Self);
   With FTextBox Do
   Begin
      Parent := C;
      Stored := False;
      Locked := True;
      Align := vaVertCenter;
      Name := 'TextBox';
      SetSubComponent(True);
      OnChange := DoChange;
   End;
End;

Procedure TD2CompoundTextBox.DoChange(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Sender);
End;

Function TD2CompoundTextBox.GetTextW: WideString;
Begin
   Result := FTextBox.TextW;
End;

Procedure TD2CompoundTextBox.SetTextW(Const Value: WideString);
Begin
   FTextBox.TextW := Value;
End;

Function TD2CompoundTextBox.GetText: String;
Begin
   Result := UTF8Encode(GetText);
End;

Procedure TD2CompoundTextBox.SetText(Const Value: String);
Var
   ss: WideString;
Begin
   ss := UTF8Decode(Value);
   SetTextW(ss);
End;

//=============== TD2CompoundMemo ============================================

Constructor TD2CompoundMemo.Create(AOwner: TComponent);
Begin
   Inherited;
   Width := 200;
   Height := 60;
   FTextLabel := TD2Label.Create(Self);
   With FTextLabel Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      Name := 'TextLabel';
      TextW := 'Caption';
      Width := 70;
      TextAlign := d2TextAlignFar;
      WordWrap := False;
      Padding.right := 5;
      SetSubComponent(True);
   End;
   FMemo := TD2Memo.Create(Self);
   With FMemo Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaClient;
      Name := 'Memo';
      SetSubComponent(True);
      OnChange := DoChange;
   End;
End;

Procedure TD2CompoundMemo.DoChange(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Sender);
End;

Procedure TD2CompoundMemo.SetTextW(Const Value: WideString);
Begin
   If FMemo = nil Then
      Exit;
   FMemo.TextW := Value;
End;

Function TD2CompoundMemo.GetTextW: WideString;
Begin
   Result := '';
   If FMemo = nil Then
      Exit;
   Result := FMemo.TextW;
End;

Procedure TD2CompoundMemo.SetText(Const Value: String);
Var
   ss: WideString;
Begin
   If FMemo = nil Then
      Exit;
   ss := UTF8Decode(Value);
   FMemo.TextW := ss;
End;

Function TD2CompoundMemo.GetText: String;
Begin
   If FMemo = nil Then
      Exit;
   Result := UTF8Encode(FMemo.TextW);
End;

//=============== TD2CompoundNumberBox ============================================

Constructor TD2CompoundNumberBox.Create(AOwner: TComponent);
Var
   C: TD2Control;
Begin
   Inherited;
   Width := 200;
   Height := 21;
   FTextLabel := TD2Label.Create(Self);
   With FTextLabel Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      Name := 'TextLabel';
      TextW := 'Caption';
      WordWrap := False;
      Width := 70;
      TextAlign := d2TextAlignFar;
      Padding.right := 5;
      SetSubComponent(True);
   End;
   C := TD2Control.Create(Self);
   With C Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaClient;
   End;
   FNumberBox := TD2NumberBox.Create(Self);
   With FNumberBox Do
   Begin
      Parent := C;
      Stored := False;
      Locked := True;
      Align := vaVertCenter;
      Name := 'NumberBox';
      SetSubComponent(True);
      OnChange := DoChange;
   End;
End;

Procedure TD2CompoundNumberBox.DoChange(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Sender);
End;

Function TD2CompoundNumberBox.GetValue: Single;
Begin
   Result := FNumberBox.Value;
End;

Procedure TD2CompoundNumberBox.SetValue(Const Value: Single);
Begin
   FNumberBox.Value := Value;
End;

//=============== TD2CompoundPopupBox =================================================

Constructor TD2CompoundPopupBox.Create(AOwner: TComponent);
Var
   C: TD2Control;
Begin
   Inherited;
   Width := 200;
   Height := 21;
   FTextLabel := TD2Label.Create(Self);
   With FTextLabel Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      Name := 'TextLabel';
      TextW := 'Caption';
      Width := 70;
      TextAlign := d2TextAlignFar;
      WordWrap := False;
      Padding.right := 5;
      SetSubComponent(True);
   End;
   C := TD2Control.Create(Self);
   With C Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaClient;
   End;
   FPopupBox := TD2PopupBox.Create(Self);
   With FPopupBox Do
   Begin
      Parent := C;
      Stored := False;
      Locked := True;
      Align := vaVertCenter;
      Name := 'PopupBox';
      SetSubComponent(True);
      OnChange := DoChange;
   End;
End;

Procedure TD2CompoundPopupBox.DoChange(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Sender);
End;

Function TD2CompoundPopupBox.GetItemIndex: Integer;
Begin
   Result := FPopupBox.ItemIndex;
End;

Procedure TD2CompoundPopupBox.SetItemIndex(Const Value: Integer);
Begin
   FPopupBox.ItemIndex := Value;
End;

//=============== TD2CompoundColorButton ======================================================

Constructor TD2CompoundColorButton.Create(AOwner: TComponent);
Var
   C: TD2Control;
Begin
   Inherited;
   Width := 200;
   Height := 21;
   FTextLabel := TD2Label.Create(Self);
   With FTextLabel Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      Name := 'TextLabel';
      TextW := 'Caption';
      Width := 70;
      TextAlign := d2TextAlignFar;
      WordWrap := False;
      Padding.right := 5;
      SetSubComponent(True);
   End;
   C := TD2Control.Create(Self);
   With C Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      Width := 60;
   End;
   FColorButton := TD2ColorButton.Create(Self);
   With FColorButton Do
   Begin
      Parent := C;
      Stored := False;
      Locked := True;
      Align := vaVertCenter;
      Name := 'ColorButton';
      SetSubComponent(True);
      OnChange := DoChange;
   End;
End;

Procedure TD2CompoundColorButton.DoChange(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Sender);
End;

Function TD2CompoundColorButton.GetValue: String;
Begin
   Result := FColorButton.Color;
End;

Procedure TD2CompoundColorButton.SetValue(Const Value: String);
Begin
   FColorButton.Color := Value;
End;

//=============== TD2CompoundImage ===================================================

Constructor TD2CompoundImage.Create(AOwner: TComponent);
Begin
   Inherited;
   Width := 200;
   Height := 60;
   FTextLabel := TD2Label.Create(Self);
   With FTextLabel Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaLeft;
      Name := 'TextLabel';
      TextW := 'Caption';
      Width := 70;
      TextAlign := d2TextAlignFar;
      WordWrap := False;
      Padding.right := 5;
      SetSubComponent(True);
   End;
   FImage := TD2ImageControl.Create(Self);
   With FImage Do
   Begin
      Parent := Self;
      Stored := False;
      Locked := True;
      Align := vaClient;
      Name := 'Image';
      SetSubComponent(True);
      OnChange := DoChange;
   End;
End;

Procedure TD2CompoundImage.DoChange(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Sender);
End;


//=============== TD2HeaderItem ==================================================

Constructor TD2HeaderItem.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
   TextAlign := d2TextAlignNear;
   DragMode := d2DragAutomatic;
   FSplitter := TD2VisualObject.Create(Self);
   FSplitter.Parent := Self;
   FSplitter.Width := 3;
   FSplitter.Align := vaRight;
   FSplitter.Locked := True;
   FSplitter.Stored := False;
   FSplitter.HitTest := True;
   FSplitter.AutoCapture := True;
   FSplitter.Cursor := crHSplit;
   FSplitter.OnMouseMove := DoSplitterMouseMove;
   FLeftSplitter := TD2VisualObject.Create(Self);
   FLeftSplitter.Parent := Self;
   FLeftSplitter.Width := 3;
   FLeftSplitter.Align := vaLeft;
   FLeftSplitter.Locked := True;
   FLeftSplitter.Stored := False;
   FLeftSplitter.HitTest := True;
   FLeftSplitter.AutoCapture := True;
   FLeftSplitter.Cursor := crHSplit;
   FLeftSplitter.OnMouseMove := DoLeftSplitterMouseMove;
End;

Procedure TD2HeaderItem.DoSplitterMouseMove(Sender: TObject; Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   NewSize: Single;
Begin
   If TD2HackVisualObject(FSplitter).FPressed Then
   Begin
      NewSize := AbsoluteToLocal(FSplitter.LocalToAbsolute(d2Point(X, Y))).X;
      If NewSize < 0 Then
         NewSize := 0;

      If (Parent <> nil) And (Parent Is TD2Header) Then
      Begin
         If Assigned(TD2Header(Parent).OnResizeItem) Then
            TD2Header(Parent).OnResizeItem(Self, NewSize);
      End;
      Width := NewSize;
      If (Parent <> nil) And (Parent Is TD2Header) Then
         TD2Header(Parent).Realign;
   End;
End;

Procedure TD2HeaderItem.DoLeftSplitterMouseMove(Sender: TObject; Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   P: TD2Point;
   LeftItem: TD2HeaderItem;
Begin
   If (Index > 0) And TD2HackVisualObject(FLeftSplitter).FPressed Then
   Begin
      If (Parent <> nil) And (Parent Is TD2Header) Then
         LeftItem := TD2HeaderItem(TD2Header(Parent).Children[Index - 1]);
      If LeftItem = nil Then
         Exit;

      P := FLeftSplitter.LocalToAbsolute(d2Point(X, Y));

      P := LeftItem.FSplitter.AbsoluteToLocal(P);
      TD2HackVisualObject(LeftItem.FSplitter).FPressed := True;
      LeftItem.DoSplitterMouseMove(Sender, Shift, P.X, P.Y, 0, 0);
      TD2HackVisualObject(LeftItem.FSplitter).FPressed := False;
   End;
End;

Procedure TD2HeaderItem.DragDrop(Const aData: TD2DragObject; Const Point: TD2Point);
Var
   NewIndex, OldIndex: Integer;
Begin
   Inherited;
   NewIndex := Index;
   OldIndex := TD2Object(aData.Source).Index;
   TD2Object(aData.Source).Index := Index;
   If (Header <> nil) And Assigned(Header.OnRealignItem) Then
      Header.OnRealignItem(TD2Object(aData.Source), OldIndex, NewIndex);
End;

Procedure TD2HeaderItem.DragEnd;
Begin
   Inherited;
End;

Procedure TD2HeaderItem.DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean);
Begin
   Accept := (aData.Source Is TD2HeaderItem) And (TD2HeaderItem(aData.Source).Header = Header) And (DragMode <> d2DragManual);
End;

Function TD2HeaderItem.Header: TD2Header;
Begin
   If (Parent <> nil) And (Parent Is TD2Header) Then
      Result := TD2Header(Parent)
   Else
      Result := nil;
End;

//=============== TD2Header ===================================================

Constructor TD2Header.Create(AOwner: TComponent);
Begin
   Inherited;
   ClipChildren := True;
   FSides := AllSides;
   FLastItem := TD2HeaderItem.Create(Self);
   FLastItem.Parent := Self;
   FLastItem.Stored := False;
   FLastItem.Locked := True;
   FLastItem.Width := 50;
   FLastItem.DragMode := d2DragManual;
End;

Function TD2Header.GetItem(Const aIndex: Integer): TD2HeaderItem;
Begin
   Result := TD2HeaderItem(Children[aIndex]);
End;

Function TD2Header.ItemClass: String;
Begin
   Result := 'TD2HeaderItem';
End;

Procedure TD2Header.Paint;
Var
   R: TD2Rect;
Begin
   Inherited Paint;
   If Assigned(Scene) And Scene.GetDesignTime And Not Locked And Not FInPaintTo Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      Canvas.StrokeDash := d2DashSolid;
   End;
End;

Procedure TD2Header.Realign;
Var
   R: TD2Rect;

   Procedure DoPosition(Control: TD2VisualObject);
   Begin
      If Not Control.Visible Then
         Exit;
      With R Do
      Begin
         Control.SetBounds(Left, Top, Control.Width, Height);
         If Control <> FLastItem Then
            Left := Left + Control.Width;
      End;
   End;

   Procedure DoAlign;
   Var
      I: Integer;
   Begin
      For I := 0 To ChildrenCount - 1 Do
      Begin
         If Not (TD2VisualObject(Children[I]) Is TD2HeaderItem) Then
            Continue;
         DoPosition(TD2VisualObject(Children[I]));

         TD2HeaderItem(Children[I]).Sides := FSides;

         TD2HeaderItem(Children[I]).xRadius := Radius;
         TD2HeaderItem(Children[I]).yRadius := Radius;
         If I = 0 Then
         Begin
            TD2HeaderItem(Children[I]).Corners := [d2CornerTopLeft];
            TD2HeaderItem(Children[I]).Sides := TD2HeaderItem(Children[I]).Sides + (FSides * [d2SideLeft]) - [d2SideRight];
         End Else
         If Children[I] = FLastItem Then
         Begin
            TD2HeaderItem(Children[I]).Corners := [d2CornerTopRight];
            TD2HeaderItem(Children[I]).Sides := TD2HeaderItem(Children[I]).Sides + [d2SideLeft];
         End Else Begin
            TD2HeaderItem(Children[I]).Corners := [];
            TD2HeaderItem(Children[I]).Sides := TD2HeaderItem(Children[I]).Sides + [d2SideLeft] - [d2SideRight];
         End;
      End;
   End;

Begin
   Inherited;
   If csDestroying In ComponentState Then
      Exit;
   If FDisableAlign Then
      Exit;
   If ChildrenCount = 0 Then
      Exit;
   FDisableAlign := True;
   Try
      FChildren.Remove(FLastItem);
      FChildren.Add(FLastItem);

      R := d2Rect(FOffset, 0, FWidth, FHeight);
      R := Margins.MarginRect(R);
      DoAlign;

      If R.Left > R.Right Then
         FLastItem.Width := 0
      Else
         FLastItem.Width := R.Right - R.Left;
   Finally
      FDisableAlign := False;
   End;
End;

Procedure TD2Header.SetRadius(Const Value: Single);
Begin
   If FRadius <> Value Then
   Begin
      FRadius := Value;
      Realign;
   End;
End;

Procedure TD2Header.SetSides(Const Value: TD2Sides);
Begin
   If FSides <> Value Then
   Begin
      FSides := Value;
      Realign;
   End;
End;

//=============== TD2Column =========================================

Constructor TD2Column.Create(AOwner: TComponent);
Begin
   Inherited;
   FAutoTranslate := True;
   Width := 100;
   HitTest := False;
   CanFocused := False;
End;

Function TD2Column.CreateCellControl: TD2Control;
Begin
   Result := TD2TextCell.Create(Self);
   TD2TextCell(Result).OnChange := DoTextChanged;
End;

Procedure TD2Column.DoTextChanged(Sender: TObject);
Begin
   If Grid = nil Then
      Exit;
   If FUpdateColumn Then
      Exit;
   If FDisableChange Then
      Exit;
   With d2StringToPoint(TD2Object(Sender).TagString) Do
   Begin
      Grid.SetValue(trunc(x), trunc(y), TD2Control(Sender).Data);
      If Assigned(Grid.FOnEdititingDone) Then
         Grid.FOnEdititingDone(Grid, trunc(x), trunc(y));
   End;
End;

Procedure TD2Column.DoCanFocused(Sender: TObject; Var ACanFocused: Boolean);
Begin
   If Grid = nil Then
      Exit;
   ACanFocused := Grid.CanEditModify;
   If ACanFocused And ReadOnly Then
      ACanFocused := False;
End;

Procedure TD2Column.DoEnterFocus(Sender: TObject);
Begin
   If Grid = nil Then
      Exit;
   Grid.ColumnIndex := Index;
   FSaveData := TD2Object(Sender).Data;
End;

Procedure TD2Column.DoKeyDown(Sender: TObject; Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   If (KeyChar <> #0) And (Grid <> nil) And Not Grid.CanEditAcceptKey(KeyChar) Then
      KeyChar := #0;
   If (Key = VK_RETURN) And (Grid <> nil) Then
      Grid.SetFocus;
   If (Key = VK_ESCAPE) And (Grid <> nil) Then
   Begin
      FDisableChange := True;
      Try
         TD2Object(Sender).Data := FSaveData;
      Finally
         FDisableChange := False;
      End;
      Grid.Reset;
   End;
   If (Key = VK_UP) Or (Key = VK_DOWN) And (Grid <> nil) Then
   Begin
      Grid.SetFocus;
      Grid.KeyDown(Key, Keychar, Shift);
   End;
End;

Function TD2Column.Grid: TD2CustomGrid;
Var
   P: TD2Object;
Begin
   P := Parent;
   While (P <> nil) Do
   Begin
      If P Is TD2CustomGrid Then
      Begin
         Result := TD2CustomGrid(P);
         Exit;
      End;
      P := P.Parent;
   End;
   Result := nil;
End;

Procedure TD2Column.ClearColumn;
Var
   i: Integer;
Begin
   For i := 0 To High(FCellControls) Do
      FCellControls[i].Free;
   SetLength(FCellControls, 0);
End;

Function TD2Column.CellControlByPoint(X, Y: Single): TD2Control;
Var
   i: Integer;
   P: TD2Point;
Begin
   Result := nil;
   If Grid = nil Then
      Exit;

   P := Grid.LocalToAbsolute(d2Point(X, Y));
   For i := 0 To High(FCellControls) Do
      If FCellControls[i].Visible And (FCellControls[i].pointInObject(P.X, P.Y)) Then
      Begin
         Result := FCellControls[i];
         Exit;
      End;
End;

Function TD2Column.CellControlByRow(Row: Integer): TD2Control;
Var
   i: Integer;
Begin
   Result := nil;
   If Grid = nil Then
      Exit;

   For i := 0 To High(FCellControls) Do
      If (FCellControls[i].Visible) And (trunc(d2StringToPoint(FCellControls[i].TagString).Y) = Row) Then
      Begin
         Result := FCellControls[i];
         Exit;
      End;
End;

Procedure TD2Column.UpdateColumn;
Var
   i, C: Integer;
   V: Variant;
Begin
   If Grid = nil Then
      Exit;
   FUpdateColumn := True;
   Try

      If Length(FCellControls) < Min(Grid.RowCount, Grid.VisibleRows) Then
      Begin
         C := High(FCellControls);
         SetLength(FCellControls, Min(Grid.RowCount, Grid.VisibleRows));
         For i := C + 1 To Min(Grid.RowCount, Grid.VisibleRows) - 1 Do
         Begin
            FCellControls[i] := CreateCellControl;
            FCellControls[i].Parent := Self;
            FCellControls[i].HitTest := False;
            FCellControls[i].Visible := False;
            FCellControls[i].Locked := True;
            FCellControls[i].Stored := False;
            FCellControls[i].OnCanFocused := DoCanFocused;
            FCellControls[i].OnEnterFocus := DoEnterFocus;
            FCellControls[i].OnKeyDown := DoKeyDown;
         End;
      End;

      If Length(FCellControls) > Min(Grid.RowCount, Grid.VisibleRows) Then
      Begin
         For i := Min(Grid.RowCount, Grid.VisibleRows) To High(FCellControls) Do
            FCellControls[i].Visible := False;
      End;

      For i := 0 To Min(Grid.RowCount, Grid.VisibleRows) - 1 Do
      Begin
         If Grid.TopRow + i >= Grid.RowCount Then
            Break;

         V := Grid.GetValue(Index, Grid.TopRow + i);

         FCellControls[i].Visible := True;
         FCellControls[i].SetBounds(0, i * Grid.RowHeight, Width, Grid.RowHeight);
         FCellControls[i].TagString := d2PointToString(d2Point(Index, Grid.TopRow + i));
         FCellControls[i].Data := V;
      End;
      UpdateSelected;
   Finally
      FUpdateColumn := False;
   End;
End;

Procedure TD2Column.UpdateSelected;
Var
   i: Integer;
Begin
   If Grid = nil Then
      Exit;
   If Grid.ReadOnly Then
      Exit;
   If ReadOnly Then
      Exit;
   For i := 0 To Min(Grid.RowCount, Grid.VisibleRows) - 1 Do
   Begin
      If Grid.IsSelected(Grid.TopRow + i) And (Grid.ColumnIndex = Index) Then
      Begin
         FCellControls[i].CanFocused := True;
         FCellControls[i].HitTest := True;
      End Else Begin
         FCellControls[i].CanFocused := False;
         FCellControls[i].HitTest := False;
         If FCellControls[i].IsFocused Then
            Grid.SetFocus;
      End;
   End;
End;

Procedure TD2Column.SetHeader(Const Value: WideString);
Begin
   If FHeader <> Value Then
   Begin
      FHeader := Value;
      If (Grid <> nil) And (Grid.FHeader <> nil) Then
         Grid.FHeader.Items[Index].TextW := FHeader;
   End;
End;

//=============== TD2CheckColumn ================================================

Constructor TD2CheckColumn.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Function TD2CheckColumn.CreateCellControl: TD2Control;
Begin
   Result := TD2CheckCell.Create(Self);
   TD2CheckCell(Result).OnChange := DoCheckChanged;
End;

Procedure TD2CheckColumn.DoCheckChanged(Sender: TObject);
Begin
   If Grid = nil Then
      Exit;
   If FUpdateColumn Then
      Exit;
   With d2StringToPoint(TD2Object(Sender).TagString) Do
   Begin
      Grid.SetValue(trunc(x), trunc(y), TD2Control(Sender).Data);
      If Assigned(Grid.FOnEdititingDone) Then
         Grid.FOnEdititingDone(Grid, trunc(x), trunc(y));
   End;
End;

//=============== TD2ProgressColumn ============================================

Constructor TD2ProgressColumn.Create(AOwner: TComponent);
Begin
   Inherited;
   FMax := 100;
End;

Function TD2ProgressColumn.CreateCellControl: TD2Control;
Begin
   Result := TD2ProgressCell.Create(Self);
   TD2ProgressCell(Result).Min := FMin;
   TD2ProgressCell(Result).Max := FMax;
End;

//=============== TD2PopupColumn ==============================================

Constructor TD2PopupColumn.Create(AOwner: TComponent);
Begin
   Inherited;
   FItems := TD2WideStringList.Create;
End;

Destructor TD2PopupColumn.Destroy;
Begin
   FItems.Free;
   Inherited;
End;

Function TD2PopupColumn.CreateCellControl: TD2Control;
Begin
   Result := TD2PopupCell.Create(Self);
   TD2PopupCell(Result).Items.Assign(FItems);
End;

Procedure TD2PopupColumn.SetItems(Const Value: TD2WideStrings);
Begin
   FItems.Assign(Value);
End;

//=============== TD2ImageColumn =============================================

Constructor TD2ImageColumn.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Function TD2ImageColumn.CreateCellControl: TD2Control;
Begin
   Result := TD2ImageCell.Create(Self);
   TD2ImageCell(Result).EnableOpenDialog := False;
End;

//=============== TD2CustomGrid ==============================================

Constructor TD2CustomGrid.Create(AOwner: TComponent);
Begin
   Inherited;
   FOddFill := TD2Brush.Create(d2BrushSolid, $20000000);
   FLineFill := TD2Brush.Create(d2BrushSolid, $FF202020);
   FShowSelectedCell := True;
   FShowVertLines := True;
   FShowHorzLines := True;
   FShowHeader := True;
   FRowHeight := 21;
   FRowCount := 100;
   CanFocused := True;
   AutoCapture := True;
   Width := 100;
   Height := 100;
   FItemHeight := 0;
End;

Destructor TD2CustomGrid.Destroy;
Begin
   If FSelections <> nil Then
      FSelections.Free;
   FLineFill.Free;
   FOddFill.Free;
   Inherited;
End;

Function TD2CustomGrid.ItemClass: String;
Begin
   Result := 'TD2Column;TD2CheckColumn;TD2ProgressColumn;TD2PopupColumn;TD2ImageColumn';
End;

Procedure TD2CustomGrid.FreeStyle;
Begin
   Inherited;
   FSelection := nil;
   FFocus := nil;
   FHeader := nil;
End;

Procedure TD2CustomGrid.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('content');
   If (T <> nil) And (T.IsVisual) Then
   Begin
      TD2VisualObject(T).OnBeforePaint := DoContentPaint;
      TD2VisualObject(T).OnPaint := DoContentPaint2;
   End;
   T := FindResource('header');
   If (T <> nil) And (T Is TD2Header) Then
   Begin
      FHeader := TD2Header(T);
      FHeader.OnRealignItem := DoRealignItem;
      FHeader.OnResizeItem := DoResizeItem;
      FHeader.Visible := FShowHeader;
   End;
   T := FindResource('selection');
   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      FSelection := TD2VisualObject(T);
      FSelection.Visible := False;
   End;
   T := FindResource('focus');
   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      FFocus := TD2VisualObject(T);
      FFocus.Visible := False;
   End;
   T := FindResource('AlternatingRowBackground');
   If (T <> nil) And (T Is TD2BrushObject) Then
   Begin
      FOddFill.Assign(TD2BrushObject(T).Brush);
   End;
   T := FindResource('LineFill');
   If (T <> nil) And (T Is TD2BrushObject) Then
   Begin
      FLineFill.Assign(TD2BrushObject(T).Brush);
   End;
   UpdateColumns;
   UpdateHeader;
   UpdateSelection;
End;

Procedure TD2CustomGrid.UpdateSelection;
Var
   i: Integer;
   P: TD2Point;
   R: TD2Rect;
   SelRects: Array Of TD2Rect;
   Clone: TD2VisualObject;
   Vis: Boolean;
Begin
   If FSelection = nil Then
      Exit;
   If ColumnCount = 0 Then
      Exit;
   If RowCount = 0 Then
   Begin
      If FSelections <> nil Then
         For i := 0 To FSelections.Count - 1 Do
            TD2VisualObject(FSelections[i]).Visible := False;
      If FFocus <> nil Then
         FFocus.Visible := False;
      Exit;
   End;

   Vis := True;
   SetLength(SelRects, 0);
   For i := 0 To RowCount - 1 Do
   Begin
      If IsSelected(i) Then
      Begin
         P := d2Point(0, i * FRowHeight);
         P := FContent.LocalToAbsolute(P);
         If (FSelection.Parent <> nil) And FSelection.Parent.IsVisual Then
            P := FSelection.Parent.Visual.AbsoluteToLocal(P);
         If FContent.Width < ClientWidth Then
            R := d2Rect(P.X, P.Y, P.X + ClientWidth, P.Y + FRowHeight)
         Else
            R := d2Rect(P.X, P.Y, P.X + FContent.Width, P.Y + FRowHeight);
         If (Length(SelRects) > 0) And (i > 0) And (IsSelected(i - 1)) Then
            SelRects[High(SelRects)] := d2UnionRect(R, SelRects[High(SelRects)])
         Else Begin
            SetLength(SelRects, Length(SelRects) + 1);
            SelRects[High(SelRects)] := R;
         End;
      End;
   End;

   If FSelections = nil Then
      FSelections := TList.Create;

   If FSelections.Count < Length(SelRects) Then
      For i := FSelections.Count To Length(SelRects) - 1 Do
      Begin
         Clone := TD2VisualObject(FSelection.Clone(Self));
         Clone.ResourceName := '';
         FSelections.Add(Clone);
         Clone.Parent := FSelection.Parent;
         Clone.Stored := False;
      End;

   If Length(SelRects) < FSelections.Count Then
      For i := Length(SelRects) To FSelections.Count - 1 Do
      Begin
         TD2VisualObject(FSelections[i]).Visible := False;
      End;

   For i := 0 To High(SelRects) Do
   Begin
      TD2VisualObject(FSelections[i]).Visible := Vis;
      If Vis Then
      Begin
         With SelRects[i] Do
            TD2VisualObject(FSelections[i]).SetBounds(Left, Top, Right - Left, Bottom - Top);
         If (FFocus <> nil) And (FShowSelectedCell) Then
         Begin
            FFocus.Visible := True;
            FFocus.BringToFront;
            P := d2Point(Columns[ColumnIndex].Position.X, 0);
            P := FContent.LocalToAbsolute(P);
            If (FSelection.Parent <> nil) And FSelection.Parent.IsVisual Then
               P := FSelection.Parent.Visual.AbsoluteToLocal(P);
            With SelRects[i] Do
               FFocus.SetBounds(P.X + FFocus.Padding.Left, SelRects[i].Top + FFocus.Padding.Top,
                  Columns[FColumnIndex].Width - (FFocus.Padding.Left + FFocus.Padding.Right),
                  SelRects[i].Bottom - SelRects[i].Top - (FFocus.Padding.Top + FFocus.Padding.Bottom));
         End;
      End;
   End;

   For i := 0 To ColumnCount - 1 Do
      Columns[i].UpdateSelected;
End;

Procedure TD2CustomGrid.DoRealignItem(Sender: TObject; OldIndex, NewIndex: Integer);
Begin
   If ColumnIndex = Columns[OldIndex].Index Then
      ColumnIndex := NewIndex;
   Columns[OldIndex].Index := NewIndex;
End;

Procedure TD2CustomGrid.DoResizeItem(Sender: TObject; Var NewSize: Single);
Begin
   If NewSize < 10 Then
      NewSize := 10;
   Columns[TD2HeaderItem(Sender).Index].Width := NewSize;
End;

Procedure TD2CustomGrid.UpdateHeader;
Var
   i: Integer;
   Item: TD2HeaderItem;
Begin
   If FHeader = nil Then
      Exit;

   FHeader.FOffset := -HScrollBar.Value;
   FHeader.FChildren.Remove(FHeader.FLastItem);
   If FHeader.ChildrenCount < ColumnCount Then
      For i := FHeader.ChildrenCount To ColumnCount - 1 Do
      Begin
         Item := TD2HeaderItem.Create(Self);
         Item.Parent := FHeader;
         Item.Locked := True;
         Item.Stored := False;
      End;
   FHeader.FChildren.Add(FHeader.FLastItem);

   For i := 0 To ColumnCount - 1 Do
   Begin
      TD2HeaderItem(FHeader.Children[i]).TextW := Columns[i].Header;
      TD2HeaderItem(FHeader.Children[i]).Width := Columns[i].Width;
   End;
   If FHeader <> nil Then
      FHeader.Realign;
End;

Procedure TD2CustomGrid.UpdateColumns;
Var
   i: Integer;
Begin
   For i := 0 To ColumnCount - 1 Do
   Begin
      Columns[i].Position.Y := (TopRow * FRowHeight);
      Columns[i].UpdateColumn;
   End;
End;

Procedure TD2CustomGrid.DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
Var
   i: Integer;
   P: TD2Point;
Begin
   If (FContent <> nil) And (ContentLayout <> nil) Then
   Begin
      If FAlternatingRowBackground Then
      Begin
         aCanvas.Fill.Assign(FOddFill);
         For i := 0 To Min(RowCount, VisibleRows) - 1 Do
         Begin
            If Odd(TopRow + i) Then
            Begin
               P := FContent.LocalToAbsolute(d2Point(0, (TopRow + i) * FRowHeight));
               P := TD2VisualObject(Sender).AbsoluteToLocal(P);
               aCanvas.FillRect(d2Rect(P.X, P.Y, P.X + FContent.Width, P.Y + FRowHeight), 0, 0, [], AbsoluteOpacity);
            End;
         End;
      End;
   End;
End;

Procedure TD2CustomGrid.DoContentPaint2(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
Var
   i: Integer;
   P: TD2Point;
   State: Integer;
Begin
   If (FContent <> nil) And (ContentLayout <> nil) And (FShowVertLines Or FShowHorzLines) Then
   Begin
      State := aCanvas.SaveCanvas;
      aCanvas.IntersectClipRect(ARect);

      aCanvas.Stroke.Assign(FLineFill);
      If FShowHorzLines Then
         For i := 0 To Min(RowCount, VisibleRows) Do
         Begin
            P := FContent.LocalToAbsolute(d2Point(0, (TopRow + i) * FRowHeight));
            P := TD2VisualObject(Sender).AbsoluteToLocal(P);
            aCanvas.DrawLine(d2Point(P.X, P.Y - 0.5), d2Point(P.X + FContent.Width, P.Y - 0.5), AbsoluteOpacity);
         End;
      If FShowVertLines Then
         For i := 0 To ColumnCount - 1 Do
         Begin
            P := Columns[i].LocalToAbsolute(d2Point(Columns[i].Width, 0));
            P := TD2VisualObject(Sender).AbsoluteToLocal(P);
            aCanvas.DrawLine(d2Point(P.X + 0.5, P.Y), d2Point(P.X + 0.5, P.Y + (Min(RowCount, VisibleRows) * FRowHeight)), AbsoluteOpacity);
         End;

      aCanvas.RestoreCanvas(State);
   End;
End;

Function TD2CustomGrid.GetContentBounds: TD2Rect;
Var
   Col, R: TD2Rect;
   i: Integer;
Begin
   Result := LocalRect;
   If FUpdating > 0 Then
      Exit;
   If ContentLayout = nil Then
      Exit;
   R := ContentLayout.LocalRect;
   If ColumnCount > 0 Then
   Begin
      R.Right := R.Left;
      R.Top := R.Top;
      For i := 0 To ColumnCount - 1 Do
      Begin
         Col := d2Rect(R.Right, R.Top, R.Right + Columns[i].Width, R.Bottom);
         R.Right := R.Right + Columns[i].Width;
         Columns[i].SetBounds(Col.Left, (TopRow * FRowHeight), Columns[i].Width, ClientHeight);
         Columns[i].UpdateColumn;
      End;
      R.Bottom := R.Top + (FRowCount * FRowHeight);
   End;
   If d2RectWidth(R) < ContentLayout.Width Then
      R.Right := R.Left + ContentLayout.Width;
   If d2RectHeight(R) < ContentLayout.Height Then
      R.Bottom := R.Top + ContentLayout.Height;
   Result := R;
   UpdateColumns;
   UpdateHeader;
   UpdateSelection;
End;

Procedure TD2CustomGrid.HScrollChange(Sender: TObject);
Begin
   Inherited;
   UpdateSelection;
   UpdateHeader;
End;

Procedure TD2CustomGrid.VScrollChange(Sender: TObject);
Begin
   Inherited;
   UpdateColumns;
   UpdateSelection;
End;

Function TD2CustomGrid.GetColumnCount: Integer;
Var
   i: Integer;
Begin
   Result := 0;
   If (FContent <> nil) And (FContent.ChildrenCount > 0) Then
      For i := 0 To FContent.ChildrenCount - 1 Do
         If FContent.Children[i] Is TD2Column Then
         Begin
            Inc(Result);
         End;
End;

Function TD2CustomGrid.RowByPoint(Const X, Y: Single): Integer;
Var
   P: TD2Point;
Begin
   P := LocalToAbsolute(d2Point(X, Y));
   If FContent <> nil Then
   Begin
      P := FContent.AbsoluteToLocal(P);
      Result := trunc(P.Y / FRowHeight);
      Exit;
   End;
   Result := -1;
End;

Function TD2CustomGrid.ColumnByIndex(Const Idx: Integer): TD2Column;
Var
   c, i: Integer;
Begin
   c := 0;
   If (FContent <> nil) And (FContent.ChildrenCount > 0) Then
      For i := 0 To FContent.ChildrenCount - 1 Do
         If FContent.Children[i] Is TD2Column Then
         Begin
            If c = Idx Then
            Begin
               Result := TD2Column(FContent.Children[i]);
               Exit;
            End;
            Inc(c);
         End;
   Result := nil;
End;

Function TD2CustomGrid.ColumnByPoint(Const X, Y: Single): TD2Column;
Var
   i: Integer;
   P: TD2Point;
Begin
   P := LocalToAbsolute(d2Point(X, Y));
   If (FContent <> nil) And (FContent.ChildrenCount > 0) Then
      For i := 0 To FContent.ChildrenCount - 1 Do
         If FContent.Children[i] Is TD2Column Then
         Begin
            If Not TD2Column(FContent.Children[i]).Visible Then
               Continue;
            If Not d2IntersectRect(TD2Column(FContent.Children[i]).UpdateRect, UpdateRect) Then
               Continue;
            If TD2Column(FContent.Children[i]).pointInObject(P.X, P.Y) Then
            Begin
               Result := TD2Column(FContent.Children[i]);
               Exit;
            End;
         End;
   Result := nil;
End;

Function TD2CustomGrid.CanEditModify: Boolean;
Begin
   Result := Not ReadOnly;
End;

Function TD2CustomGrid.CanEditAcceptKey(Key: System.Widechar): Boolean;
Begin
   Result := True;
End;

Procedure TD2CustomGrid.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
   If RowCount > 0 Then
   Begin
      If (KeyChar <> #0) And Not (ReadOnly) And Not (Columns[ColumnIndex].ReadOnly) Then
      Begin
         If (Columns[ColumnIndex] <> nil) And (Columns[ColumnIndex].CellControlByRow(Selected) <> nil) Then
         Begin
            Columns[ColumnIndex].CellControlByRow(Selected).SetFocus;
            TD2HackVisualObject(Columns[ColumnIndex].CellControlByRow(Selected)).KeyDown(Key, KeyChar, Shift);
         End;
      End;
      Case Key Of
         VK_F2:
         Begin
            If (Columns[ColumnIndex] <> nil) And (Columns[ColumnIndex].CellControlByRow(Selected) <> nil) Then
               Columns[ColumnIndex].CellControlByRow(Selected).SetFocus;
         End;
         VK_UP: If FSelected > 0 Then
               Selected := Selected - 1;
         VK_DOWN: If FSelected < FRowCount - 1 Then
               Selected := Selected + 1;
         VK_HOME:
            If ssCtrl In Shift Then
               Selected := 0
            Else
               ColumnIndex := 0;
         VK_END:
            If ssCtrl In Shift Then
               Selected := RowCount - 1
            Else
               ColumnIndex := ColumnCount - 1;
         VK_PRIOR: If FSelected > 0 Then
               Selected := Selected - Min(RowCount, VisibleRows);
         VK_NEXT: If FSelected < FRowCount - 1 Then
               Selected := Selected + Min(RowCount, VisibleRows);
         VK_LEFT: If ColumnIndex > 0 Then
               ColumnIndex := ColumnIndex - 1;
         VK_RIGHT: If ColumnIndex < ColumnCount - 1 Then
               ColumnIndex := ColumnIndex + 1;
         Else Exit;
      End;
      Key := 0;
   End;
End;

Procedure TD2CustomGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   Column: TD2Column;
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      Selected := RowByPoint(X, Y);
      Column := ColumnByPoint(X, Y);
      If Column <> nil Then
         ColumnIndex := Column.Index;
   End;
End;

Procedure TD2CustomGrid.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   Column: TD2Column;
Begin
   Inherited;
   If FPressed Then
   Begin
      Selected := RowByPoint(X, Y);
      Column := ColumnByPoint(X, Y);
      If Column <> nil Then
         ColumnIndex := Column.Index;
   End;
End;

Procedure TD2CustomGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
End;

Procedure TD2CustomGrid.AddObject(AObject: TD2Object);
Begin
   If (FContent <> nil) And ((AObject Is TD2Column) Or (AObject Is TD2SelectionItem)) Then
   Begin
      FContent.AddObject(AObject);
      If FUpdating = 0 Then
         Realign;
   End Else
      Inherited;
End;

Procedure TD2CustomGrid.RemoveObject(AObject: TD2Object);
Begin
   Inherited;
End;

Procedure TD2CustomGrid.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
End;

Function TD2CustomGrid.GetColumn(Const aIndex: Integer): TD2Column;
Begin
   Result := ColumnByIndex(aIndex);
End;

Procedure TD2CustomGrid.SetRowCount(Const Value: Integer);
Begin
   If FRowCount <> Value Then
   Begin
      FRowCount := Value;
      Realign;
      If (FSelected >= FRowCount) And (FRowCount > 0) Then
         Selected := FRowCount - 1;
   End;
End;

Procedure TD2CustomGrid.SetRowHeight(Const Value: Single);
Begin
   If FRowHeight <> Value Then
   Begin
      FRowHeight := Value;
      If FRowHeight < 5 Then
         FRowHeight := 5;
      Realign;
   End;
End;

Function TD2CustomGrid.GetTopRow: Integer;
Begin
   If VScrollBar <> nil Then
      Result := trunc(VScrollBar.Value / FRowHeight)
   Else
      Result := 0;
End;

Function TD2CustomGrid.GetVisibleRows: Integer;
Begin
   Result := trunc(ClientHeight / FRowHeight) + 2;
End;

Function TD2CustomGrid.GetValue(Col, Row: Integer): Variant;
Begin
   Result := NULL;
   If Assigned(FOnGetValue) Then
      FOnGetValue(Self, Col, Row, Result);
End;

Procedure TD2CustomGrid.SetValue(Col, Row: Integer; Const Value: Variant);
Begin
   If Assigned(FOnSetValue) Then
      FOnSetValue(Self, Col, Row, Value);
End;

Procedure TD2CustomGrid.SetSelected(Const Value: Integer);
Begin
   If FSelected <> Value Then
   Begin
      FSelected := Value;
      If FSelected < 0 Then
         FSelected := 0;
      If FSelected > FRowCount - 1 Then
         FSelected := FRowCount - 1;

      If FSelected <= TopRow Then
         VScrollBar.Value := FSelected * FRowHeight;
      If FSelected * FRowHeight > VScrollBar.Value + ClientHeight - FRowHeight Then
         VScrollBar.Value := FSelected * FRowHeight - ClientHeight + FRowHeight;
      UpdateSelection;
   End;
End;

Function TD2CustomGrid.IsSelected(Row: Integer): Boolean;
Begin
   Result := Row = FSelected;
End;

Procedure TD2CustomGrid.SetAlternatingRowBackground(Const Value: Boolean);
Begin
   If FAlternatingRowBackground <> Value Then
   Begin
      FAlternatingRowBackground := Value;
      Repaint;
   End;
End;

Procedure TD2CustomGrid.SetShowHorzLines(Const Value: Boolean);
Begin
   If FShowHorzLines <> Value Then
   Begin
      FShowHorzLines := Value;
      Repaint;
   End;
End;

Procedure TD2CustomGrid.SetShowVertLines(Const Value: Boolean);
Begin
   If FShowVertLines <> Value Then
   Begin
      FShowVertLines := Value;
      Repaint;
   End;
End;

Procedure TD2CustomGrid.SetColumnIndex(Const Value: Integer);
Begin
   If FColumnIndex <> Value Then
   Begin
      FColumnIndex := Value;
      UpdateSelection;
      If Columns[FColumnIndex].Position.X < HScrollBar.Value Then
         HScrollBar.Value := Columns[FColumnIndex].Position.X;
      If Columns[FColumnIndex].Position.X + Columns[FColumnIndex].Width > HScrollBar.Value + ClientWidth Then
         HScrollBar.Value := Columns[FColumnIndex].Position.X + Columns[FColumnIndex].Width - ClientWidth;
   End;
End;

Procedure TD2CustomGrid.SetShowHeader(Const Value: Boolean);
Begin
   If FShowHeader <> Value Then
   Begin
      FShowHeader := Value;
      If FHeader <> nil Then
         FHeader.Visible := FShowHeader;
   End;
End;

Procedure TD2CustomGrid.Reset;
Begin
   SetFocus;
End;

//=============== TD2StringColumn =======================================================

Constructor TD2StringColumn.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2StringColumn.Destroy;
Begin
   Inherited;
End;

Procedure TD2StringColumn.UpdateColumn;
Begin
   If Grid = nil Then
      Exit;
   SetLength(FCells, Grid.RowCount);
   Inherited;
End;

//=============== TD2StringGrid =========================================================

Constructor TD2StringGrid.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'gridstyle';
End;

Destructor TD2StringGrid.Destroy;
Begin
   Inherited;
End;

Function TD2StringGrid.GetValue(Col, Row: Integer): Variant;
Var
   C: TD2Column;
Begin
   C := Columns[Col];
   If C <> nil Then
   Begin
      If Length(TD2StringColumn(C).FCells) <> RowCount Then
         SetLength(TD2StringColumn(C).FCells, RowCount);
      Result := TD2StringColumn(C).FCells[Row];
   End Else
      Result := NULL;
End;

Procedure TD2StringGrid.SetValue(Col, Row: Integer; Const Value: Variant);
Var
   C: TD2Column;
Begin
   C := Columns[Col];
   If (C <> nil) Then
   Begin
      If Length(TD2StringColumn(C).FCells) <> RowCount Then
         SetLength(TD2StringColumn(C).FCells, RowCount);
      TD2StringColumn(C).FCells[Row] := VarToWideStr(Value);
   End;
End;

Function TD2StringGrid.GetCells(ACol, ARow: Integer): WideString;
Begin
   Result := GetValue(ACol, ARow);
End;

Procedure TD2StringGrid.SetCells(ACol, ARow: Integer; Const Value: WideString);
Begin
   SetValue(ACol, ARow, Value);
End;

Function TD2StringGrid.ItemClass: String;
Begin
   Result := 'TD2StringColumn';
End;

Procedure TD2CustomGrid.SetShowSelectedCell(Const Value: Boolean);
Begin
   If FShowSelectedCell <> Value Then
   Begin
      FShowSelectedCell := Value;
      UpdateSelection;
   End;
End;

//=============== TD2PlotGrid ==================================

Constructor TD2PlotGrid.Create(AOwner: TComponent);
Begin
   Inherited;
   FLineFill := TD2Brush.Create(d2BrushSolid, $FF505050);
   FLineFill.OnChanged := LineFillChanged;
   FMarks := 25;
   FFrequency := 5;
End;

Destructor TD2PlotGrid.Destroy;
Begin
   FLineFill.Free;
   Inherited;
End;

Procedure TD2PlotGrid.LineFillChanged(Sender: TObject);
Begin
   Repaint;
End;

Procedure TD2PlotGrid.Paint;
Var
   x, y: Single;
Begin
   x := 0;
   y := 0;
   Canvas.Stroke.Assign(FLineFill);
   While x < Width / 2 Do
   Begin
      If (x = 0) Then
      Begin
         Canvas.StrokeThickness := 2;
         Canvas.Stroke.SolidColor := FLineFill.SolidColor;
      End Else Begin
         If (frac(x) = 0) And (frac(x / frequency / marks) = 0) Then
            Canvas.Stroke.SolidColor := FLineFill.SolidColor
         Else
            Canvas.Stroke.SolidColor := d2Opacity(FLineFill.SolidColor, 0.4);
         Canvas.StrokeThickness := 1;
      End;

      Canvas.DrawLine(d2Point(round(Width / 2) + x + (Canvas.StrokeThickness / 2), 0), d2Point(round(Width / 2) + x + (Canvas.StrokeThickness / 2), Height), AbsoluteOpacity);
      If x <> 0 Then
         Canvas.DrawLine(d2Point(round(Width / 2) - x + (Canvas.StrokeThickness / 2), 0), d2Point(round(Width / 2) - x + (Canvas.StrokeThickness / 2), Height), AbsoluteOpacity);
      x := x + FFrequency;
   End;
   While y < Height / 2 Do
   Begin
      If (y = 0) Then
      Begin
         Canvas.StrokeThickness := 2;
         Canvas.Stroke.SolidColor := FLineFill.SolidColor;
      End Else Begin
         If (frac(y) = 0) And (frac(y / frequency / marks) = 0) Then
            Canvas.Stroke.SolidColor := FLineFill.SolidColor
         Else
            Canvas.Stroke.SolidColor := d2Opacity(FLineFill.SolidColor, 0.4);
         Canvas.StrokeThickness := 1;
      End;

      Canvas.DrawLine(d2Point(0, round(Height / 2) + y + (Canvas.StrokeThickness / 2)), d2Point(Width, round(Height / 2) + y + (Canvas.StrokeThickness / 2)), AbsoluteOpacity);
      If y <> 0 Then
         Canvas.DrawLine(d2Point(0, round(Height / 2) - y + (Canvas.StrokeThickness / 2)), d2Point(Width, round(Height / 2) - y + (Canvas.StrokeThickness / 2)), AbsoluteOpacity);
      y := y + FFrequency;
   End;
End;

Procedure TD2PlotGrid.SetFrequency(Const Value: Single);
Begin
   If FFrequency <> Value Then
   Begin
      FFrequency := Value;
      If FFrequency < 0.001 Then
         FFrequency := 0.001;
      Repaint;
   End;
End;

Procedure TD2PlotGrid.SetLineFill(Const Value: TD2Brush);
Begin
   FLineFill.Assign(Value);
End;

Procedure TD2PlotGrid.SetMarks(Const Value: Single);
Begin
   If FMarks <> Value Then
   Begin
      FMarks := Value;
      Repaint;
   End;
End;

//=============== TD2Inspector =============================================

Constructor TD2Inspector.Create(AOwner: TComponent);
Begin
   Inherited;
   FShowProperties := True;
   FShowEvents := False;
   FResource := 'treeviewstyle';
   HideSelectionUnfocused := False;
   FDisabledProperties := TStringList.Create;
   FEditButton := TD2Button.Create(Self);
   FEditButton.ResourceName := 'inspectorEditor';
   FEditButton.TagString := 'self';
   FEditButton.DisableFocusEffect := True;
   FEditButton.Visible := False;
   FEditButton.Stored := False;
   FEditButton.Parent := Self;
   FEditButton.OnClick := DoEditorChange;
   FEditButton.Visible := False;
   FEditButton.Position.X := 2000;
   FEditBox := TD2TextBox.Create(Self);
   FEditBox.ResourceName := 'inspectorEditor';
   FEditBox.TagString := 'self';
   FEditBox.DisableFocusEffect := True;
   FEditBox.Padding.Rect := d2Rect(0, 0, 0, 0);
   FEditBox.Stored := False;
   FEditBox.Parent := Self;
   FEditBox.OnChange := DoEditorChange;
   FEditBox.Visible := False;
   FEditBox.Position.X := 2000;
   FComboBox := TD2PopupBox.Create(Self);
   FComboBox.ResourceName := 'inspectorEditor';
   FComboBox.TagString := 'self';
   FComboBox.DisableFocusEffect := True;
   FComboBox.Stored := False;
   FComboBox.Parent := Self;
   FComboBox.TextAlign := d2TextAlignNear;
   FComboBox.OnChange := DoEditorChange;
   FComboBox.Visible := False;
   FcomboBox.Position.X := 2000;
End;

Destructor TD2Inspector.Destroy;
Begin
   If (FSelectedObject <> nil) And (FSelectedObject Is TD2Object) Then
      TD2Object(FSelectedObject).RemoveFreeNotify(Self);
   FDisabledProperties.Free;
   Inherited;
End;

Procedure TD2Inspector.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FSelectedObject) Then
   Begin
      SelectedObject := nil;
   End;
End;

Procedure TD2Inspector.InsAddObject(ItemRoot: TD2Object; Root: TObject);
Var
   PropList: PPropList;
   PropInfo: PPropInfo;
   i, PropCount: Integer;
   Obj: TObject;
   Item: TD2TreeViewItem;
   Value: TD2Label;
Begin
   If Root = nil Then
      Exit;

   PropCount := GetPropList(PTypeInfo(Root.ClassInfo), PropList);

   If PropCount = 0 Then
      Exit;

   Try

      For i := 0 To PropCount - 1 Do
      Begin
         With PropList[i]^ Do
         Begin
            If FDisabledProperties.IndexOf(Name) >= 0 Then
               Continue;
            PropInfo := GetPropInfo(Root, Name);
            If PropInfo = nil Then
               Continue;
            If PropInfo^.SetProc = nil Then
               Continue;

            If (PropType^.Kind In [tkMethod]) And ((Not FShowEvents) Or (Root <> FSelectedObject)) Then
               Continue;
            If Not (PropType^.Kind In [tkMethod]) And (Not FShowProperties) Then
               Continue;

            Item := TD2TreeViewItem.Create(Self);
            Item.Parent := ItemRoot;
            Item.TextW := Name;
            Item.Locked := True;
            Item.Stored := False;
            Value := TD2Label.Create(Self);
            Value.ResourceName := 'inspectorEditor';
            Value.Locked := True;
            Value.Stored := False;
            Value.Parent := Item;
            Value.SetBounds(Width / 2, 0, Width, Item.Height + 2);
            Value.TextAlign := d2TextAlignNear;
            Value.WordWrap := False;
            Item.TagObject := Root;
            Item.TagString := Name;
            Item.Tag := PtrInt(Value);
            Case PropType^.Kind Of
               tkInteger:
               Begin
                  Value.TextW := IntToStr(GetOrdProp(Root, Name));
               End;
               tkFloat:
               Begin
                  Value.TextW := FloatToStr(GetFloatProp(Root, Name));
               End;
               tkClass:
               Begin
                  Obj := GetObjectProp(Root, Name);
                  If (Obj Is TD2Object) Then
                  Begin
                     TD2Label(Value).TextW := TComponent(Obj).Name;
                     If TComponent(Obj).Name = '' Then
                        TD2Label(Value).TextW := TD2Object(Obj).ResourceName;
                  End Else
                  If Obj <> nil Then
                  Begin
                     Item.IsExpanded := False;
                     TD2Label(Value).TextW := Obj.ClassName;
                     If Not (Obj Is TD2Object) Then
                        InsAddObject(Item, Obj);
                  End;
               End;
               tkUString, tkWString:
               Begin
                  Value.TextW := GetWideStrProp(Root, Name);
               End;
               tkString, tkLString:
               Begin
                  Value.TextW := GetStrProp(Root, Name);
               End;
               tkMethod:
               Begin
                  Value.TextW := GetStrProp(Root, Name);
               End;
               tkEnumeration:
               Begin
                  Value.TextW := GetEnumProp(Root, Name);
               End;

               tkBool:
               Begin
                  Value.TextW := GetEnumProp(Root, Name);
               End;
               Else Value.TextW := GetStrProp(Root, Name);
            End;
         End;
      End;
   Finally
      FreeMem(PropList, SizeOf(PPropInfo) * PropCount);
   End;
End;

Procedure TD2Inspector.RebuildList;
Begin
   BeginUpdate;
   Try
      FEditButton.TagObject := nil;
      FEditBox.TagObject := nil;
      FComboBox.TagObject := nil;
      Clear;
      If FSelectedObject <> nil Then
         InsAddObject(Self, FSelectedObject);
   Finally
      EndUpdate;
   End;
End;

Procedure TD2Inspector.RebuildEditor;
Var
   PropInfo: PPropInfo;
   TypeData: PTypeData;
   Editor: TD2Control;
   i: Integer;
   Obj: TObject;
   M: TMethod;
Begin
   FEditButton.Visible := False;
   FEditBox.Visible := False;
   FComboBox.Visible := False;
   If (Selected = nil) Or (Selected.TagObject = nil) Then
      Exit;

   PropInfo := GetPropInfo(Selected.TagObject, Selected.TagString);
   If PropInfo = nil Then
      Exit;

   TypeData := GetTypeData(PropInfo.PropType);
   If TypeData = nil Then
      Exit;

   If FEditButton.TagObject <> nil Then
      TD2VisualObject(FEditButton.TagObject).Visible := True;
   If FEditBox.TagObject <> nil Then
      TD2VisualObject(FEditBox.TagObject).Visible := True;
   If FComboBox.TagObject <> nil Then
      TD2VisualObject(FComboBox.TagObject).Visible := True;
   With PropInfo^ Do
   Begin
      Case PropType^.Kind Of
         tkMethod:
         Begin
            M := GetMethodProp(Selected.TagObject, Selected.TagString);
            If GvarD2Designer <> nil Then
               FEditBox.TextW := GvarD2Designer.GetMethodName(M);
            FEditBox.SelectAll;
            Editor := FEditBox;
         End;
         tkClass:
         Begin
            Obj := GetObjectProp(Selected.TagObject, Selected.TagString);
            If (Obj Is TD2Gradient) Or (Obj Is TD2Brush) Or (Obj Is TD2Bitmap) Or (Obj Is TD2PathData) Or (Obj Is TD2Font) Then
            Begin
               FEditButton.TextW := 'Edit...';
               Editor := FEditButton;
            End Else
               Editor := nil;
         End;
         tkInteger:
         Begin
            FEditBox.TextW := IntToStr(GetOrdProp(Selected.TagObject, Selected.TagString));
            Editor := FEditBox;
         End;
         tkFloat:
         Begin
            FEditBox.TextW := FloatToStr(GetFloatProp(Selected.TagObject, Selected.TagString));
            Editor := FEditBox;
         End;
         tkWString:
         Begin
            FEditBox.TextW := GetWideStrProp(Selected.TagObject, Selected.TagString);

            Editor := FEditBox;
         End;
         tkString, tkLString:
         Begin
            FEditBox.TextW := GetStrProp(Selected.TagObject, Selected.TagString);
            FEditBox.SelectAll;
            Editor := FEditBox;
         End;
         tkEnumeration:
         Begin
            FComboBox.Items.Clear;
            FComboBox.ItemIndex := -1;
            For i := TypeData.MinValue To TypeData.MaxValue Do
               FComboBox.Items.Add(GetEnumName(PropInfo.PropType, i));
            FComboBox.ItemIndex := GetOrdProp(Selected.TagObject, Selected.TagString);
            Editor := FComboBox;
         End;

         tkBool:
         Begin
            FComboBox.Items.Clear;
            For i := TypeData.MinValue To TypeData.MaxValue Do
               FComboBox.Items.Add(GetEnumName(PropInfo.PropType, i));
            FComboBox.ItemIndex := GetOrdProp(Selected.TagObject, Selected.TagString);
            Editor := FComboBox;
         End;

         Else FEditBox.TextW := GetStrProp(Selected.TagObject, Selected.TagString);
            Editor := FEditBox;
      End;
   End;
   If Editor <> nil Then
   Begin
      Editor.Visible := True;
      Editor.Locked := True;
      Editor.Stored := False;
      Editor.TagObject := TObject(Selected.Tag);
      If Editor.TagObject <> nil Then
         TD2VisualObject(Editor.TagObject).Visible := False;
      UpdateEditorPos;
      If Editor.Visible Then
         Editor.SetFocus;
   End;
End;

Procedure TD2Inspector.DoEditorChange(Sender: TObject);
Var
   Obj: TObject;
   M: TMethod;
   xFontDlg: TFontDialog;
   xBmpEditor: TD2CustomBitmapEditor;
   xPInf: PPropInfo;
   xPathDataDlg: TD2CustomPathDataDesigner;
Begin
   If FUpdating > 0 Then
      Exit;

   If (Selected <> nil) And (Selected.TagObject <> nil) Then
   Begin
      //.......
      If (Sender = FEditButton) And FEditButton.Visible Then
      Begin
         Obj := GetObjectProp(Selected.TagObject, Selected.TagString);

         // TD2Font
         If Obj Is TD2Font Then
         Begin
            xFontDlg := TFontDialog.Create(nil);
            xFontDlg.Font.Assign(TD2Font(Obj));
            If xFontDlg.Execute Then
            Begin
               TD2Font(Obj).Assign(xFontDlg.Font);
            End;
            xFontDlg.Free;
         End;

         // TD2Gradient
         If Obj Is TD2Gradient Then
         Begin
            If Addr(VarShowGradientDialog) <> nil Then
               VarShowGradientDialog(TD2Gradient(Obj));
         End;

         // TD2Brush
         If Obj Is TD2Brush Then
         Begin
            If Addr(VarSelectInDesign) <> nil Then
               VarSelectInDesign(Obj, TPersistent(SelectedObject));
         End;

         //  TD2Bitmap
         If Obj Is TD2Bitmap Then
         Begin
            If Addr(VarCreateABitmapEditor) <> nil Then
            Begin
               xBmpEditor := VarCreateABitmapEditor(nil);

               If xBmpEditor <> nil Then
               Begin
                  xBmpEditor.AssignFromBitmap(TD2Bitmap(Obj));
                  If xBmpEditor.ShowModal = mrOk Then
                  Begin
                     xBmpEditor.AssignToBitmap(TD2Bitmap(Obj));
                  End;
                  xBmpEditor.Free;
               End;
            End;
         End;

         // TD2PathData
         If Obj Is TD2PathData Then
         Begin
            If Addr(VarCreateAPathDataDesigner) <> nil Then
            Begin
               xPathDataDlg := VarCreateAPathDataDesigner(nil);

               If xPathDataDlg <> nil Then
               Begin
                  xPathDataDlg.Lines.Text := TD2PathData(Obj).Data;
                  If xPathDataDlg.ShowModal = mrOk Then
                  Begin
                     TD2PathData(Obj).Data := xPathDataDlg.Lines.Text;
                  End;
                  xPathDataDlg.Free;
               End;
            End;
         End;
      End;
      //.............

      If (Sender = FEditBox) And FEditBox.Visible Then
      Begin
         xPInf := GetPropInfo(Selected.TagObject, Selected.TagString);
         If xPInf^.PropType^.Kind = tkMethod Then
         Begin
            If GvarD2Designer <> nil Then
            Begin
               M := GvarD2Designer.AddMethod(TD2TextBox(Sender).TextW);
               SetMethodProp(Selected.TagObject, xPInf, M);

               If (M.Code <> nil) And (TD2Object(Sender).TagObject <> nil) Then
                  TD2Label(TD2Object(Sender).TagObject).TextW := TD2TextBox(Sender).TextW;
            End;
         End Else Begin
            SetPropValue(Selected.TagObject, Selected.TagString, TD2TextBox(Sender).TextW);
            If TD2Object(Sender).TagObject <> nil Then
               TD2Label(TD2Object(Sender).TagObject).TextW := TD2TextBox(Sender).TextW;
         End;
      End;

      If (Sender = FComboBox) And FComboBox.Visible Then
      Begin
         SetPropValue(Selected.TagObject, Selected.TagString, TD2PopupBox(Sender).ItemIndex);
         If TD2Object(Sender).TagObject <> nil Then
            TD2Label(TD2Object(Sender).TagObject).TextW := TD2PopupBox(Sender).TextW;
      End;

      If Assigned(FOnChangeProperty) Then
         FOnChangeProperty(Self, Selected.TagString);
   End;
End;

Procedure TD2Inspector.SetSelectedObject(Const Value: TComponent);
Begin
   If FSelectedObject <> Value Then
   Begin
      If (Editor <> nil) And (Editor.Visible) And (Editor <> FEditButton) Then
         DoEditorChange(Editor);

      If (FSelectedObject <> nil) And (FSelectedObject Is TD2Object) Then
         TD2Object(FSelectedObject).RemoveFreeNotify(Self);
      FSelectedObject := Value;

      If (FSelectedObject <> nil) And (FSelectedObject Is TD2Object) Then
         TD2Object(FSelectedObject).AddFreeNotify(Self);

      RebuildList;
      RebuildEditor;
   End;
End;

Procedure TD2Inspector.SetSelected(Const Value: TD2TreeViewItem);
Begin
   If Value <> Selected Then
   Begin
      If (Editor <> nil) And (Editor.Visible) And (Editor <> FEditButton) Then
         DoEditorChange(Editor);
      Inherited;
      RebuildEditor;
   End;
End;

Procedure TD2Inspector.SetDisabledProperties(Const Value: TStrings);
Begin
   FDisabledProperties.Assign(Value);
End;

Function TD2Inspector.Editor: TD2Control;
Begin
   If FComboBox.Visible Then
      Result := FComboBox
   Else
   If FEditBox.Visible Then
      Result := FEditBox
   Else
      Result := FEditButton;
End;

Procedure TD2Inspector.VScrollChange(Sender: TObject);
Begin
   Inherited;
   UpdateEditorPos;
End;

Procedure TD2Inspector.UpdateEditorPos;
Var
   R: TD2Rect;
Begin
   If (Editor <> nil) And (Selected <> nil) And (ContentLayout <> nil) Then
   Begin
      R := GetItemRect(Selected);
      R.TopLeft := FContent.LocalToAbsolute(R.TopLeft);
      R.BottomRight := FContent.LocalToAbsolute(R.BottomRight);
      R.TopLeft := AbsoluteToLocal(R.TopLeft);
      R.BottomRight := AbsoluteToLocal(R.BottomRight);
      With R Do
      Begin
         Editor.SetBounds(Width / 2 + (Selected.Level * Selected.Height), Top + 1,
            ContentLayout.Width - (Width / 2) - FContent.Position.X + ContentLayout.Position.X - (Selected.Level * Selected.Height) - 1,
            Selected.Height - 2);
      End;
      If Editor.Position.Y < 0 Then
         Editor.Opacity := 0
      Else
      If Editor.Position.Y + Editor.Height > Height Then
         Editor.Opacity := 0
      Else
         Editor.Opacity := 1;
   End;
End;

Procedure TD2Inspector.DialogKey(Var Key: Word; Shift: TShiftState);
Begin
   Inherited;
   If (Count > 0) And (Selected <> nil) And (Scene <> nil) And (Scene.GetFocused = Editor) Then
   Begin
      Case Key Of
         VK_ADD: Selected.IsExpanded := True;
         VK_SUBTRACT: Selected.IsExpanded := False;
         VK_HOME:
            If Editor <> FEditBox Then
               Selected := ItemByGlobalIndex(0)
            Else
               Exit;
         VK_END:
            If Editor <> FEditBox Then
               Selected := ItemByGlobalIndex(GlobalCount - 1)
            Else
               Exit;
         VK_UP:
            If Selected.GlobalIndex > 0 Then
               Selected := ItemByGlobalIndex(Selected.GlobalIndex - 1);
         VK_DOWN:
            If Selected.GlobalIndex < GlobalCount - 1 Then
               Selected := ItemByGlobalIndex(Selected.GlobalIndex + 1);
         Else Exit;
      End;
      Key := 0;
   End;
End;

Procedure TD2Inspector.SetShowEvents(Const Value: Boolean);
Begin
   If FShowEvents <> Value Then
   Begin
      FShowEvents := Value;
      RebuildList;
   End;
End;

Procedure TD2Inspector.SetShowProperties(Const Value: Boolean);
Begin
   If FShowProperties <> Value Then
   Begin
      FShowProperties := Value;
      RebuildList;
   End;
End;

//=============== TD2ScrollContent ===================================

Constructor TD2ScrollContent.Create(AOwner: TComponent);
Begin
   Inherited;
   ClipChildren := True;
End;

Procedure TD2ScrollContent.Paint;
Var
   State: Cardinal;
Begin
   Inherited;
   State := Canvas.SaveCanvas;
   Canvas.SetMatrix(AbsoluteMatrix);
   Canvas.IntersectClipRect(ClipRect);

   If (Parent <> nil) And (Parent Is TD2ScrollBox) And (TD2ScrollBox(Parent).ContentLayout <> nil) Then
   Begin
      TD2ScrollBox(Parent).ContentPaint(Canvas, LocalRect);
   End;

   Canvas.RestoreCanvas(State);
End;

Procedure TD2ScrollContent.PaintChildren;
Begin
   Inherited;
End;

Function TD2ScrollContent.GetClipRect: TD2Rect;
Begin
   If (Parent <> nil) And (Parent Is TD2ScrollBox) And (TD2ScrollBox(Parent).ContentLayout <> nil) Then
   Begin
      Result := TD2ScrollBox(Parent).ContentLayout.LocalRect;
      If (TD2ScrollBox(Parent).VScrollBar <> nil) And (TD2ScrollBox(Parent).VScrollBar.Enabled) Then
         d2OffsetRect(Result, 0, TD2ScrollBox(Parent).VScrollBar.Value);
      If (TD2ScrollBox(Parent).HScrollBar <> nil) And (TD2ScrollBox(Parent).HScrollBar.Enabled) Then
         d2OffsetRect(Result, TD2ScrollBox(Parent).HScrollBar.Value, 0);
   End Else
      Result := Inherited GetClipRect;
End;

Function TD2ScrollContent.GetUpdateRect: TD2Rect;
Begin
   If FRecalcUpdateRect Then
   Begin
      If (Parent <> nil) And (Parent Is TD2ScrollBox) Then
      Begin
         If (TD2ScrollBox(Parent).ContentLayout <> nil) Then
            FUpdateRect := TD2ScrollBox(Parent).ContentLayout.UpdateRect
         Else
            FUpdateRect := TD2ScrollBox(Parent).UpdateRect;
      End;
   End;
   Result := FUpdateRect;
End;

Function TD2ScrollContent.ObjectByPoint(X, Y: Single): TD2VisualObject;
Var
   P: TD2Point;
Begin
   Result := Inherited ObjectByPoint(X, Y);
   If Result <> nil Then
   Begin
      P := AbsoluteToLocal(d2Point(X, Y));
      If Not d2PtInRect(P, ClipRect) Then
         Result := nil;
   End;
End;

Procedure TD2ScrollContent.RemoveObject(AObject: TD2Object);
Begin
   If (Parent <> nil) And (Parent Is TD2ScrollBox) Then
      TD2ScrollBox(Parent).ContentBeforeRemoveObject(AObject);

   Inherited;

   If (Parent <> nil) And (Parent Is TD2ScrollBox) Then
      TD2ScrollBox(Parent).ContentRemoveObject(AObject);
End;

Procedure TD2ScrollContent.AddObject(AObject: TD2Object);
Begin
   Inherited;
   If (Parent <> nil) And (Parent Is TD2ScrollBox) Then
      TD2ScrollBox(Parent).ContentAddObject(AObject);
End;


//=============== TD2ScrollBox ==========================================

Constructor TD2ScrollBox.Create(AOwner: TComponent);
Begin
   Inherited;
   AutoCapture := True;
   FAnimated := True;
   FAutoHide := True;
   FShowScrollBars := True;
   FContent := TD2ScrollContent.Create(Self);
   FContent.Parent := Self;
   FContent.Stored := False;
   FContent.Locked := True;
   FContent.HitTest := False;
End;

Destructor TD2ScrollBox.Destroy;
Begin
   FContent := nil;
   Inherited;
End;

Procedure TD2ScrollBox.DefineProperties(Filer: TFiler);
Begin
   Inherited;
End;

Procedure TD2ScrollBox.Loaded;
Begin
   Inherited;
   // ScrollTo(-FScrollDesign.X,-FScrollDesign.Y);
End;

Procedure TD2ScrollBox.ApplyStyle;
Var
   B: TD2Object;
Begin
   Inherited;
   B := FindResource('sizegrip');
   If (B <> nil) And (B.IsVisual) Then
      B.Visual.Visible := FShowSizeGrip;

   If FUseSmallScrollBars Then
   Begin
      B := FindResource('vsmallscrollbar');
      If (B <> nil) And (B Is TD2ScrollBar) Then
      Begin
         FVScrollBar := TD2ScrollBar(B);
         FVScrollBar.OnChange := VScrollChange;
         FVScrollBar.Locked := True;
         If FVScrollBar.Tag = 0 Then
            FVScrollBar.Tag := PtrInt(FVScrollBar.Align);
      End;

      B := FindResource('hsmallscrollbar');

      If (B <> nil) And (B Is TD2ScrollBar) Then
      Begin
         FHScrollBar := TD2ScrollBar(B);
         FHScrollBar.OnChange := HScrollChange;
         FHScrollBar.Locked := True;
         If FHScrollBar.Tag = 0 Then
            FHScrollBar.Tag := PtrInt(FHScrollBar.Align);
      End;

      B := FindResource('vscrollbar');
      If (B <> nil) And (B.IsVisual) Then
         B.Visual.Visible := False;
      B := FindResource('hscrollbar');
      If (B <> nil) And (B.IsVisual) Then
         B.Visual.Visible := False;
   End;

   If Not FUseSmallScrollBars Or ((FVScrollBar = nil) Or (FHScrollBar = nil)) Then
   Begin
      B := FindResource('vscrollbar');
      If (B <> nil) And (B Is TD2ScrollBar) Then
      Begin
         FVScrollBar := TD2ScrollBar(B);
         FVScrollBar.OnChange := VScrollChange;
         FVScrollBar.Locked := True;
         If FVScrollBar.Tag = 0 Then
            FVScrollBar.Tag := PtrInt(FVScrollBar.Align);
      End;

      B := FindResource('hscrollbar');
      If (B <> nil) And (B Is TD2ScrollBar) Then
      Begin
         FHScrollBar := TD2ScrollBar(B);
         FHScrollBar.OnChange := HScrollChange;
         FHScrollBar.Locked := True;
         If FHScrollBar.Tag = 0 Then
            FHScrollBar.Tag := PtrInt(FHScrollBar.Align);
      End;

      B := FindResource('vsmallscrollbar');
      If (B <> nil) And (B.IsVisual) Then
         B.Visual.Visible := False;

      B := FindResource('hsmallscrollbar');
      If (B <> nil) And (B.IsVisual) Then
         B.Visual.Visible := False;
   End;

   B := FindResource('content');
   If (B <> nil) And (B.IsVisual) Then
      FContentLayout := B.Visual;

   Realign;
   FVScrollAni := nil;
   FHScrollAni := nil;
End;

Procedure TD2ScrollBox.FreeStyle;
Begin
   Inherited;
   FContentLayout := nil;
   FHScrollBar := nil;
   FVScrollBar := nil;
End;

Procedure TD2ScrollBox.RealignContent(R: TD2Rect);
Begin
   If (FContent <> nil) And (ContentLayout <> nil) Then
   Begin
      FContent.SetBounds(R.Left, R.Top, d2RectWidth(R), d2RectHeight(R));
      FContent.FRecalcUpdateRect := True;
   End;
End;

Function TD2ScrollBox.GetContentBounds: TD2Rect;
Var
   i: Integer;
   R, LocalR: TD2Rect;
Begin
   Result := d2Rect(0, 0, Width, Height);
   If (FContent <> nil) And (ContentLayout <> nil) Then
   Begin
      R := ContentLayout.LocalRect;
      For i := 0 To FContent.ChildrenCount - 1 Do
         If TD2Object(FContent.Children[i]).isVisual Then
            If (TD2VisualObject(FContent.Children[i]).Visible) Then
            Begin
               LocalR := TD2VisualObject(FContent.Children[i]).ParentedRect;
               R := d2UnionRect(R, LocalR);
            End;
      Result := R;
   End;
End;

Procedure TD2ScrollBox.Realign;
//-------------------------
   Procedure IntAlign;
   Var
      R: TD2Rect;
   Begin
      R := GetContentBounds;
      If d2RectWidth(R) * d2RectHeight(R) = 0 Then
         Exit;
      d2OffsetRect(R, ContentLayout.Position.X, ContentLayout.Position.Y);
      If (HScrollBar <> nil) And (HScrollBar.Enabled) Then
         d2OffsetRect(R, -FScrollDesign.X, 0);
      If (VScrollBar <> nil) And (VScrollBar.Enabled) Then
         d2OffsetRect(R, 0, -FScrollDesign.Y);
      RealignContent(R);

      If ContentLayout.Parent <> nil Then
         ContentLayout.Parent.Visual.BeginUpdate;
      If (VScrollBar <> nil) Then
      Begin
         VScrollBar.Enabled := d2RectHeight(R) > ContentLayout.Height;
         If FAutoHide Then
            VScrollBar.Visible := VScrollBar.Enabled;
         If Not FShowScrollBars Then
         Begin
            VScrollBar.Opacity := 0;
            VScrollBar.Align := vaNone;
         End Else Begin
            VScrollBar.Opacity := 1;
            VScrollBar.Align := TD2Align(VScrollBar.Tag);
         End;
      End;
      If (HScrollBar <> nil) Then
      Begin
         HScrollBar.Enabled := d2RectWidth(R) > ContentLayout.Width;
         If FAutoHide Then
            HScrollBar.Visible := HScrollBar.Enabled;
         If Not FShowScrollBars Then
         Begin
            HScrollBar.Opacity := 0;
            HScrollBar.Align := vaNone;
         End Else Begin
            HScrollBar.Opacity := 1;
            HScrollBar.Align := TD2Align(HScrollBar.Tag);
            If (VScrollBar <> nil) And (VScrollBar.Enabled) Then
               HScrollBar.Padding.right := VScrollBar.Width;
         End;
      End;
      If ContentLayout.Parent <> nil Then
      Begin
         ContentLayout.Parent.Visual.EndUpdate;
         ContentLayout.Parent.Visual.Realign;
      End;

      If (VScrollBar <> nil) Then
      Begin
         VScrollBar.Enabled := d2RectHeight(R) > ContentLayout.Height;
         If FAutoHide Then
            VScrollBar.Visible := VScrollBar.Enabled;
         If Not FShowScrollBars Then
         Begin
            VScrollBar.Opacity := 0;
            VScrollBar.Align := vaNone;
            VScrollBar.Position.Y := Width + 100;
         End Else Begin
            VScrollBar.Opacity := 1;
            VScrollBar.HitTest := True;
            VScrollBar.Align := TD2Align(VScrollBar.Tag);
         End;
         VScrollBar.BringToFront;
         If VScrollBar.Visible And (ContentLayout <> nil) Then
         Begin
            VScrollBar.Max := d2RectHeight(R);
            VScrollBar.ViewportSize := ContentLayout.Height;
            VScrollBar.SmallChange := VScrollBar.ViewportSize / 5;
            VScrollBar.Value := FScrollDesign.Y;
         End Else Begin
            VScrollBar.Value := 0;
         End;
      End;
      If (HScrollBar <> nil) Then
      Begin
         HScrollBar.Enabled := d2RectWidth(R) > ContentLayout.Width;
         HScrollBar.Padding.right := 0;
         If FAutoHide Then
            HScrollBar.Visible := HScrollBar.Enabled;
         If Not FShowScrollBars Then
         Begin
            HScrollBar.Opacity := 0;
            HScrollBar.Align := vaNone;
            HScrollBar.Position.Y := Height + 100;
         End Else Begin
            HScrollBar.Opacity := 1;
            HScrollBar.Align := TD2Align(HScrollBar.Tag);
            If (VScrollBar <> nil) And (VScrollBar.Enabled) Then
               HScrollBar.Padding.right := VScrollBar.Width;
         End;
         HScrollBar.BringToFront;
         If HScrollBar.Visible And (ContentLayout <> nil) Then
         Begin
            HScrollBar.Max := d2RectWidth(R);
            HScrollBar.ViewportSize := ContentLayout.Width;
            HScrollBar.SmallChange := HScrollBar.ViewportSize / 5;
            HScrollBar.Value := ContentLayout.Position.X - FContent.Position.X;
         End Else
            HScrollBar.Value := 0;
      End;
   End;

   //-------------------------
Var
   R, NewR: TD2Rect;
Begin
   If csDestroying In ComponentState Then
      Exit;
   Inherited;
   If csLoading In ComponentState Then
      Exit;
   If ContentLayout = nil Then
      Exit;
   If FDisableAlign Then
      Exit;
   If FUpdating > 0 Then
      Exit;
   FDisableAlign := True;
   Try
      R := ContentLayout.LocalRect;
      IntAlign;
      NewR := ContentLayout.LocalRect;
      If (d2RectWidth(NewR) <> d2RectWidth(R)) Or (d2RectHeight(NewR) <> d2RectHeight(R)) Then
      Begin
         IntAlign;
      End;
   Finally
      FDisableAlign := False;
   End;
End;

Function TD2ScrollBox.ContentRect: TD2Rect;
Begin
   If ContentLayout <> nil Then
      Result := ContentLayout.ParentedRect
   Else
      Result := LocalRect;
End;

Function TD2ScrollBox.VScrollBarValue: Single;
Begin
   If (VScrollBar <> nil) And (VScrollBar.Visible) Then
      Result := VScrollBar.Value
   Else
      Result := 0;
End;

Function TD2ScrollBox.HScrollBarValue: Single;
Begin
   If (HScrollBar <> nil) And (HScrollBar.Visible) Then
      Result := HScrollBar.Value
   Else
      Result := 0;
End;

Procedure TD2ScrollBox.HScrollChange(Sender: TObject);
Begin
   If ContentLayout = nil Then
      Exit;
   If HScrollBar.Visible Then
      FContent.Position.X := ContentLayout.Position.X - HScrollBar.Value
   Else
      FContent.Position.X := ContentLayout.Position.X;
   FScrollDesign.X := HScrollBar.Value;
End;

Procedure TD2ScrollBox.VScrollChange(Sender: TObject);
Begin
   If ContentLayout = nil Then
      Exit;
   If VScrollBar.Visible Then
      FContent.Position.Y := ContentLayout.Position.Y - VScrollBar.Value
   Else
      FContent.Position.Y := ContentLayout.Position.Y;
   FScrollDesign.Y := VScrollBar.Value;
End;

Procedure TD2ScrollBox.CreateVScrollAni;
Begin
   If FVScrollAni = nil Then
   Begin
      FVScrollAni := TD2FloatAnimation.Create(Self);
      FVScrollAni.Parent := VScrollBar;
      FVScrollAni.AnimationType := d2AnimationOut;
      FVScrollAni.Interpolation := d2InterpolationQuadratic;
      FVScrollAni.Duration := 0.7;
      FVScrollAni.PropertyName := 'Value';
      FVScrollAni.StartFromCurrent := True;
   End;
End;

Procedure TD2ScrollBox.CreateHScrollAni;
Begin
   If FHScrollAni = nil Then
   Begin
      FHScrollAni := TD2FloatAnimation.Create(Self);
      FHScrollAni.Parent := HScrollBar;
      FHScrollAni.AnimationType := d2AnimationOut;
      FHScrollAni.Interpolation := d2InterpolationQuadratic;
      FHScrollAni.Duration := 0.7;
      FHScrollAni.PropertyName := 'Value';
      FHScrollAni.StartFromCurrent := True;
   End;
End;

Procedure TD2ScrollBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If (Button = mbLeft) And FMouseTracking Then
   Begin
      FLastDelta := d2Point(0, 0);
      FDownPos := d2Point(X, Y);
      FCurrentPos := d2Point(X, Y);
      FDown := True;
      If (FVScrollAni <> nil) And FVScrollAni.Running Then
         FVScrollAni.StopAtCurrent;
      If (FHScrollAni <> nil) And FHScrollAni.Running Then
         FHScrollAni.StopAtCurrent;
   End;
End;

Procedure TD2ScrollBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If FDown And FMouseTracking Then
   Begin
      FDown := False;

      If FAnimated And ((FLastDelta.X <> 0) Or (FLastDelta.Y <> 0)) Then
      Begin
         If (VScrollBar <> nil) And (VScrollBar.Visible) Then
         Begin
            CreateVScrollAni;
            If FVScrollAni.Running Then
               FVScrollAni.StopAtCurrent;
            FVScrollAni.StopValue := VScrollBar.Value - (FLastDelta.Y * 7);
            FVScrollAni.Start;
         End;
         If (HScrollBar <> nil) And (HScrollBar.Visible) Then
         Begin
            CreateHScrollAni;
            If FHScrollAni.Running Then
               FHScrollAni.StopAtCurrent;
            FHScrollAni.StopValue := HScrollBar.Value - (FLastDelta.X * 7);
            FHScrollAni.Start;
         End;
      End;
   End;
End;

Procedure TD2ScrollBox.MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean);
Begin
   Inherited;
   If Not Handled And Not (FDisableMouseWheel) And (VScrollBar <> nil) And (VScrollBar.Visible) Then
   Begin
      If FAnimated Then
      Begin
         CreateVScrollAni;
         If FVScrollAni.Running Then
            FVScrollAni.StopAtCurrent;
         FVScrollAni.StopValue := VScrollBar.Value - (VScrollBar.SmallChange * 3 * (WheelDelta / 120));
         FVScrollAni.Start;
      End Else
         VScrollBar.Value := VScrollBar.Value - (VScrollBar.SmallChange * 3 * (WheelDelta / 120));
      Handled := True;
   End;
   If Not Handled And Not (FDisableMouseWheel) And (HScrollBar <> nil) And (HScrollBar.Visible) Then
   Begin
      If FAnimated Then
      Begin
         CreateHScrollAni;
         If FHScrollAni.Running Then
            FHScrollAni.StopAtCurrent;
         FHScrollAni.StopValue := HScrollBar.Value - (HScrollBar.SmallChange * 3 * (WheelDelta / 120));
         FHScrollAni.Start;
      End Else
         HScrollBar.Value := HScrollBar.Value - (HScrollBar.SmallChange * 3 * (WheelDelta / 120));
      Handled := True;
   End;
End;

Procedure TD2ScrollBox.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If FDown And FMouseTracking Then
   Begin
      If (VScrollBar <> nil) And (VScrollBar.Visible) Then
      Begin
         VScrollBar.Value := VScrollBar.Value - (Y - FCurrentPos.Y);
         FLastDelta.Y := (Y - FCurrentPos.Y);
      End;
      If (HScrollBar <> nil) And (HScrollBar.Visible) Then
      Begin
         HScrollBar.Value := HScrollBar.Value - (X - FCurrentPos.X);
         FLastDelta.X := (X - FCurrentPos.X);
      End;
      FCurrentPos := d2Point(X, Y);
   End;
End;

Procedure TD2ScrollBox.AddObject(AObject: TD2Object);
Begin
   If (FContent <> nil) And (AObject.TagString <> 'self') And (AObject <> FContent) And (AObject <> FResourceLink) And Not (AObject Is TD2Effect) And Not (AObject Is TD2Animation) Then
   Begin
      FContent.AddObject(AObject);
   End Else
      Inherited;
End;

Procedure TD2ScrollBox.Centre;
Begin
   If (VScrollBar <> nil) And (VScrollBar.Visible) Then
   Begin
      VScrollBar.Value := (VScrollBar.Max - VScrollBar.ViewportSize) / 2;
   End;
   If (HScrollBar <> nil) And (HScrollBar.Visible) Then
   Begin
      HScrollBar.Value := (HScrollBar.Max - HScrollBar.ViewportSize) / 2;
   End;
End;

Procedure TD2ScrollBox.ScrollTo(Const Dx, Dy: Single);
Begin
   If (VScrollBar <> nil) And (VScrollBar.Visible) Then
      VScrollBar.Value := VScrollBar.Value - Dy;
   If (HScrollBar <> nil) And (HScrollBar.Visible) Then
      HScrollBar.Value := HScrollBar.Value - Dx;
End;

Procedure TD2ScrollBox.InViewRect(Const Rect: TD2Rect);
Begin
End;

Procedure TD2ScrollBox.SetShowScrollBars(Const Value: Boolean);
Begin
   If FShowScrollBars <> Value Then
   Begin
      FShowScrollBars := Value;
      Realign;
   End;
End;

Procedure TD2ScrollBox.SetShowSizeGrip(Const Value: Boolean);
Begin
   If FShowSizeGrip <> Value Then
   Begin
      FShowSizeGrip := Value;
      ApplyStyle;
   End;
End;

Procedure TD2ScrollBox.WriteScrollDesign(Writer: TWriter);
Begin
   Writer.WriteString(d2PointToString(FScrollDesign));
End;

Procedure TD2ScrollBox.ReadScrollDesign(Reader: TReader);
Begin
   FScrollDesign := d2StringToPoint(Reader.ReadString);
   ;
End;

Procedure TD2ScrollBox.ContentPaint(aCanvas: TD2Canvas; Const ARect: TD2Rect);
Begin
End;

Procedure TD2ScrollBox.SetUseSmallScrollBars(Const Value: Boolean);
Begin
   If FUseSmallScrollBars <> Value Then
   Begin
      FUseSmallScrollBars := Value;
      ApplyStyle;
   End;
End;

Function TD2ScrollBox.ClientHeight: Single;
Begin
   If ContentLayout <> nil Then
      Result := ContentLayout.Height
   Else
      Result := Height;
End;

Function TD2ScrollBox.ClientWidth: Single;
Begin
   If ContentLayout <> nil Then
      Result := ContentLayout.Width
   Else
      Result := Width;
End;

Procedure TD2ScrollBox.ContentAddObject(AObject: TD2Object);
Begin
End;

Procedure TD2ScrollBox.ContentRemoveObject(AObject: TD2Object);
Begin
End;

Procedure TD2ScrollBox.ContentBeforeRemoveObject(AObject: TD2Object);
Begin
End;

//=============== TD2VertScrollBox ============================================

Constructor TD2VertScrollBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'scrollboxstyle';
End;

Function TD2VertScrollBox.GetContentBounds: TD2Rect;
Begin
   If (FContent <> nil) And (ContentLayout <> nil) Then
   Begin
      FContent.Width := ContentLayout.Width;
   End;
   Result := Inherited GetContentBounds;
End;

//=============== TD2FramedVertScrollBox ========================================

Constructor TD2FramedVertScrollBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'framedscrollboxstyle';
End;

//=============== TD2Layout =============================================================

Constructor TD2Layout.Create(AOwner: TComponent);
Begin
   Inherited;
   HitTest := False;
End;

Destructor TD2Layout.Destroy;
Begin
   Inherited;
End;

Procedure TD2Layout.Paint;
Var
   R: TD2Rect;
Begin
   If Assigned(Scene) And Scene.GetDesignTime And Not Locked And Not FInPaintTo Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      Canvas.StrokeDash := d2DashSolid;
   End;
End;


//=============== TD2GridLayout =========================================================

Constructor TD2GridLayout.Create(AOwner: TComponent);
Begin
   Inherited;
   ItemHeight := 64;
   ItemWidth := 64;
End;

Procedure TD2GridLayout.Realign;
Var
   i: Integer;
   CurPos: TD2Point;
Begin
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;
   CurPos := d2Point(Margins.Left, Margins.Top);
   For i := 0 To ChildrenCount - 1 Do
      If Children[i].IsVisual Then
         With Children[i].Visual Do
         Begin
            SetBounds(CurPos.X + Padding.Left, CurPos.Y + Padding.Top, FItemWidth - Padding.Left - Padding.right,
               FItemHeight - Padding.top - Padding.bottom);
            If Orientation = d2Horizontal Then
            Begin
               CurPos.X := CurPos.X + FItemWidth;
               If CurPos.X + FItemWidth > Self.Width - Self.Margins.Left - Self.Margins.Right Then
               Begin
                  CurPos.X := Self.Margins.Left;
                  CurPos.Y := CurPos.Y + FItemHeight;
               End;
            End Else Begin
               CurPos.Y := CurPos.Y + FItemHeight;
               If CurPos.Y + FItemHeight > Self.Height - Self.Margins.Top - Self.Margins.Bottom Then
               Begin
                  CurPos.Y := Self.Margins.Top;
                  CurPos.X := CurPos.X + FItemWidth;
               End;
            End;
         End;
   FDisableAlign := False;
End;

Procedure TD2GridLayout.SetItemHeight(Const Value: Single);
Begin
   If FItemHeight <> Value Then
   Begin
      FItemHeight := Value;
      Realign;
   End;
End;

Procedure TD2GridLayout.SetItemWidth(Const Value: Single);
Begin
   If FItemWidth <> Value Then
   Begin
      FItemWidth := Value;
      Realign;
   End;
End;

Procedure TD2GridLayout.SetOrientation(Const Value: TD2Orientation);
Begin
   If FOrientation <> Value Then
   Begin
      FOrientation := Value;
      Realign;
   End;
End;

//=============== TD2ScaledLayout ================================================

Constructor TD2ScaledLayout.Create(AOwner: TComponent);
Begin
   Inherited;
   FOriginalWidth := Width;
   FOriginalHeight := Height;
End;

Destructor TD2ScaledLayout.Destroy;
Begin
   Inherited;
End;

Procedure TD2ScaledLayout.Realign;
Begin
   If (Parent <> nil) And (Parent Is TD2ScrollBox) And (TD2ScrollBox(Parent).FUpdating > 0) Then
      Exit;
   Inherited;
   If Not (Assigned(FScene) And (FScene.GetDesignTime)) Then
   Begin
      RecalcAbsolute;
      FRecalcUpdateRect := True;
   End;
End;

Function TD2ScaledLayout.GetChildrenMatrix: TD2Matrix;
Begin
   If (Assigned(FScene) And (FScene.GetDesignTime)) Then
   Begin
      OriginalHeight := Height;
      OriginalWidth := Width;
   End;
   Result := IdentityMatrix;
   Result.m11 := Width / FOriginalWidth;
   Result.m22 := Height / FOriginalHeight;
End;

Procedure TD2ScaledLayout.Paint;
Var
   R: TD2Rect;
Begin
   If Assigned(Scene) And Scene.GetDesignTime And Not Locked And Not FInPaintTo Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      Canvas.StrokeDash := d2DashSolid;
   End;
   Inherited;
End;

Procedure TD2ScaledLayout.SetOriginalHeight(Const Value: Single);
Begin
   If FOriginalHeight <> Value Then
   Begin
      FOriginalHeight := Value;
      If FOriginalHeight < 1 Then
         FOriginalHeight := 1;
      RecalcAbsolute;
   End;
End;

Procedure TD2ScaledLayout.SetOriginalWidth(Const Value: Single);
Begin
   If FOriginalWidth <> Value Then
   Begin
      FOriginalWidth := Value;
      If FOriginalWidth < 1 Then
         FOriginalWidth := 1;
      RecalcAbsolute;
   End;
End;

Procedure TD2ScaledLayout.SetHeight(Const Value: Single);
Begin
   Inherited;
   If (Assigned(FScene) And (FScene.GetDesignTime)) Then
      OriginalHeight := Height
   Else
      RecalcAbsolute;
End;

Procedure TD2ScaledLayout.SetWidth(Const Value: Single);
Begin
   Inherited;
   If (Assigned(FScene) And (FScene.GetDesignTime)) Then
      OriginalWidth := Width
   Else
      RecalcAbsolute;
End;


//=============== TD2SplitLayout ==================================================

Constructor TD2SplitLayout.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2SplitLayout.Destroy;
Begin
   Inherited;
End;

//=============== TD2Nond2Layout =================================================

Constructor TD2Nond2Layout.Create(AOwner: TComponent);
Begin
   Inherited;
   FVisibleTimer := TD2Timer.Create(Self);
   FVisibleTimer.Enabled := False;
   FVisibleTimer.Interval := 100;
   FVisibleTimer.OnTimer := DoVisibleTimer;
End;

Destructor TD2Nond2Layout.Destroy;
Begin
   FVisibleTimer.Free;
   Inherited;
End;

Procedure TD2Nond2Layout.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FControl) Then
      FControl := nil;
End;

Function TD2Nond2Layout.CheckParentVisible: Boolean;
Begin
   Result := Inherited CheckParentVisible;
   If (FControl = nil) Then
      Exit;
   If (Result <> FControl.Visible) Then
   Begin
      FControl.Visible := Result;
      FVisibleTimer.Enabled := FControl.Visible;
      If (FControl.Visible) Then
         Realign;
   End;
End;

Procedure TD2Nond2Layout.Realign;
Var
   R: TD2Rect;
Begin
   Inherited;
   If (FControl <> nil) And (FControl.Visible) Then
   Begin
      R := AbsoluteRect;
      FControl.SetBounds(round(R.Left), round(R.Top), round(d2RectWidth(R)), round(d2RectHeight(R)));
   End;
End;

Procedure TD2Nond2Layout.MatrixChanged(Sender: TObject);
Var
   R: TD2Rect;
Begin
   Inherited;
   If (FControl <> nil) And (FControl.Visible) Then
   Begin
      R := AbsoluteRect;
      FControl.SetBounds(round(R.Left), round(R.Top), round(d2RectWidth(R)), round(d2RectHeight(R)));
   End;
End;

Procedure TD2Nond2Layout.SetControl(Const Value: TControl);
Begin
   If FControl <> Value Then
   Begin
      If Assigned(FScene) And (FControl = FScene.GetComponent) Then
         Exit;
      FControl := Value;
      Realign;
   End;
End;

Procedure TD2Nond2Layout.DoVisibleTimer(Sender: TObject);
Begin
   CheckParentVisible;
End;


//=============== TD2ListBoxItem ========================================================

Constructor TD2ListBoxItem.Create(AOwner: TComponent);
Begin
   Inherited;
   Position.Point := d2Point(5000, 5000);
   TextAlign := d2TextAlignNear;
   FAutoTranslate := True;
   FText := '';
   Height := 19;
   Width := 19;
   HitTest := False;
End;

Destructor TD2ListBoxItem.Destroy;
Begin
   Inherited;
End;

Procedure TD2ListBoxItem.ApplyStyle;
Var
   B: TD2Object;
Begin
   Inherited;
   B := FindResource('check');
   If (B <> nil) And (B Is TD2CheckBox) Then
   Begin
      FCheck := TD2CheckBox(B);
      FCheck.IsChecked := IsChecked;
      FCheck.OnChange := DoCheckClick;
      If ListBox <> nil Then
         FCheck.Visible := ListBox.ShowCheckboxes
      Else
         FCheck.Visible := False;
   End;
   StartTriggerAnimation(Self, 'IsSelected');

   ApplyTriggerEffect(Self, 'IsSelected');
End;

Procedure TD2ListBoxItem.FreeStyle;
Begin
   Inherited;
   FCheck := nil;
End;

Procedure TD2ListBoxItem.DoCheckClick(Sender: TObject);
Begin
   If FCheck <> nil Then
      FIsChecked := FCheck.IsChecked;
   If ListBox <> nil Then
   Begin
      ListBox.SetFocus;
      ListBox.ItemIndex := Index;
      If Assigned(ListBox.OnChangeCheck) Then
         ListBox.OnChangeCheck(Self);
   End;
End;

Function TD2ListBoxItem.ComboBox: TD2ComboBox;
Var
   P: TD2Object;
Begin
   P := Parent;
   While (P <> nil) Do
   Begin
      If P Is TD2ComboBox Then
      Begin
         Result := TD2ComboBox(P);
         Exit;
      End;
      P := P.Parent;
   End;
   Result := nil;
End;

Function TD2ListBoxItem.ListBox: TD2ListBox;
Var
   P: TD2Object;
Begin
   P := Parent;
   While (P <> nil) Do
   Begin
      If P Is TD2ListBox Then
      Begin
         Result := TD2ListBox(P);
         Exit;
      End;
      If P Is TD2ComboBox Then
      Begin
         Result := TD2ComboBox(P).FListBox;
         Exit;
      End;
      P := P.Parent;
   End;
   Result := nil;
End;

Procedure TD2ListBoxItem.DesignSelect;
Begin
   Inherited;
   If (ComboBox <> nil) Then
   Begin
      If ComboBox.FPopup.DesignHide Then
         ComboBox.DesignClick;
   End;
End;

Procedure TD2ListBoxItem.DesignInsert;
Begin
   Inherited;
   If (ComboBox <> nil) Then
   Begin
      If ComboBox.FPopup.DesignHide Then
         ComboBox.DesignClick;
   End;
End;

Procedure TD2ListBoxItem.Paint;
Var
   R: TD2Rect;
Begin
   Inherited Paint;
   If Assigned(Scene) And Scene.GetDesignTime And Not Locked And Not FInPaintTo Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      Canvas.StrokeDash := d2DashSolid;
   End;
End;

Function TD2ListBoxItem.GetParentComponent: TComponent;
Begin
   If (ComboBox <> nil) Then
      Result := ComboBox
   Else
   If (ListBox <> nil) Then
      Result := ListBox
   Else
      Result := Inherited GetParentComponent;
End;

Function TD2ListBoxItem.EnterFocusChildren(AObject: TD2VisualObject): Boolean;
Begin
   Result := Inherited EnterFocusChildren(AObject);
   If (ListBox <> nil) Then
   Begin
      If ListBox.MultiSelect Then
         ListBox.ClearSelection;
      ListBox.ItemIndex := Index;
      Result := True;
   End;
End;

Procedure TD2ListBoxItem.UpdateCheck;
Var
   i: Integer;
Begin
   If (ListBox <> nil) And (FCheck <> nil) Then
      FCheck.Visible := ListBox.ShowCheckboxes;
   If ChildrenCount > 0 Then
      For i := 0 To ChildrenCount - 1 Do
         If Children[i] Is TD2ListboxItem Then
            TD2ListboxItem(Children[i]).UpdateCheck;
End;

Procedure TD2ListBoxItem.SetIsChecked(Const Value: Boolean);
Begin
   If FIsChecked <> Value Then
   Begin
      FIsChecked := Value;
      If FCheck <> nil Then
         FCheck.IsChecked := FIsChecked;
   End;
End;

Procedure TD2ListBoxItem.SetIsSelected(Const Value: Boolean);
Begin
   If FIsSelected <> Value Then
   Begin
      FIsSelected := Value;
      StartTriggerAnimation(Self, 'IsSelected');

      ApplyTriggerEffect(Self, 'IsSelected');
      If FIsSelected And (ListBox <> nil) And Not (Listbox.MultiSelect) Then
         Listbox.ItemIndex := Index
      Else
      If Not FIsSelected And (ListBox <> nil) And Not (Listbox.MultiSelect) And (Listbox.ItemIndex = Index) Then
         Listbox.ItemIndex := -1
      Else
      If Listbox <> nil Then
         Listbox.UpdateSelection;
   End;
End;

Procedure TD2ListBoxItem.DragEnd;
Begin
   Inherited;
   DragLeave;
   If (ListBox <> nil) Then
      ListBox.FDragItem := nil;
End;

//=============== TD2ListBox ==========================================================

Constructor TD2ListBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FOddFill := TD2Brush.Create(d2BrushSolid, $20000000);
   FColumns := 1;
   FHideSelectionUnfocused := True;
   FContent.DisableDefaultAlign := True;
   FItemIndex := -1;
   CanFocused := True;
   AutoCapture := True;
   Width := 100;
   Height := 100;
End;

Destructor TD2ListBox.Destroy;
Begin
   If FSelections <> nil Then
      FSelections.Free;
   FOddFill.Free;
   Inherited;
End;

Function TD2ListBox.ItemClass: String;
Begin
   Result := 'TD2ListBoxItem';
End;

Procedure TD2ListBox.HScrollChange(Sender: TObject);
Begin
   Inherited;
   UpdateSelection;
End;

Procedure TD2ListBox.VScrollChange(Sender: TObject);
Begin
   Inherited;
   UpdateSelection;
End;

Function CompareListItem(item1, item2: TD2Object): Integer;
Begin
   If (item1 Is TD2ListBoxItem) And (item2 Is TD2ListBoxItem) Then
   Begin
      If (TD2ListBoxItem(item1).ListBox <> nil) And Assigned(TD2ListBoxItem(item1).ListBox.OnCompare) Then
         Result := TD2ListBoxItem(item1).ListBox.OnCompare(TD2ListBoxItem(item1), TD2ListBoxItem(item2))
      Else
         Result := WideCompareText(TD2ListBoxItem(item1).TextW, TD2ListBoxItem(item2).TextW);
   End Else
      Result := 0;
End;

Procedure TD2ListBox.SortItems;
Begin
   If Not FSorted Then
      Exit;
   FContent.Sort(CompareListItem);
End;

Procedure TD2ListBox.DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
Var
   i: Integer;
   Item: TD2ListBoxItem;
   P: TD2Point;
   R: TD2Rect;
Begin
   If (FContent <> nil) And (ContentLayout <> nil) Then
   Begin
      If FAlternatingRowBackground Then
      Begin
         aCanvas.Fill.Assign(FOddFill);

         For i := 0 To (Count - 1) Div Columns Do
         Begin
            If Odd(i) Then
            Begin
               Item := ItemByIndex(i * Columns);
               P := Item.LocalToAbsolute(d2Point(0, 0));
               P := TD2VisualObject(Sender).AbsoluteToLocal(P);
               R := d2Rect(P.X, P.Y, P.X + ContentLayout.Width, P.Y + Item.Height);
               If Not d2IntersectRect(R, ARect) Then
                  Continue;
               aCanvas.FillRect(R, 0, 0, [], AbsoluteOpacity);
            End;
         End;
      End;
   End;
End;

Procedure TD2ListBox.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('content');
   If (T <> nil) And (T.IsVisual) Then
   Begin
      TD2VisualObject(T).OnBeforePaint := DoContentPaint;
   End;
   T := FindResource('selection');
   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      FSelection := TD2VisualObject(T);
      FSelection.Visible := False;
      UpdateSelection;
   End;
   T := FindResource('AlternatingRowBackground');
   If (T <> nil) And (T Is TD2BrushObject) Then
   Begin
      FOddFill.Assign(TD2BrushObject(T).Brush);
   End;
   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      TD2VisualObject(T).Visible := False;
   End;
End;

Procedure TD2ListBox.FreeStyle;
Begin
   Inherited;
   FSelection := nil;
   If FSelections <> nil Then
      FSelections.Clear;
End;

Procedure TD2ListBox.UpdateSelection;
Var
   i: Integer;
   P: TD2Point;
   R: TD2Rect;
   SelRects: Array Of TD2Rect;
   Clone: TD2VisualObject;
   Vis: Boolean;
   Item: TD2ListboxItem;
Begin
   If FSelection = nil Then
      Exit;

   Vis := True;
   SetLength(SelRects, 0);
   For i := 0 To Count - 1 Do
   Begin
      Item := Items[i];
      If (Item.IsSelected) And d2IntersectRect(Item.UpdateRect, UpdateRect) Then
      Begin
         P := Item.LocalToAbsolute(d2Point(0, 0));
         If (FSelection.Parent <> nil) And FSelection.Parent.IsVisual Then
            P := FSelection.Parent.Visual.AbsoluteToLocal(P);
         R := d2Rect(P.X, P.Y, P.X + Item.Width, P.Y + Item.Height);
         If (Length(SelRects) > 0) And (i > 0) And (Items[i - 1].IsSelected) Then
            SelRects[High(SelRects)] := d2UnionRect(R, SelRects[High(SelRects)])
         Else Begin
            SetLength(SelRects, Length(SelRects) + 1);
            SelRects[High(SelRects)] := R;
         End;
      End;
   End;

   If FSelections = nil Then
      FSelections := TList.Create;

   If FSelections.Count < Length(SelRects) Then
      For i := FSelections.Count To Length(SelRects) - 1 Do
      Begin
         Clone := TD2VisualObject(FSelection.Clone(Self));
         Clone.ResourceName := '';
         FSelections.Add(Clone);
         Clone.Parent := FSelection.Parent;
         Clone.Stored := False;
      End;

   If Length(SelRects) < FSelections.Count Then
      For i := Length(SelRects) To FSelections.Count - 1 Do
      Begin
         TD2VisualObject(FSelections[i]).Visible := False;
      End;

   If HideSelectionUnfocused And Not IsFocused Then
      Vis := False;

   For i := 0 To High(SelRects) Do
   Begin
      TD2VisualObject(FSelections[i]).Visible := Vis;
      If Vis Then
      Begin
         With SelRects[i] Do
            TD2VisualObject(FSelections[i]).SetBounds(Left, Top, Right - Left, Bottom - Top);
      End;
   End;
End;

Procedure TD2ListBox.ContentAddObject(AObject: TD2Object);
Begin
   Inherited;
   If AObject Is TD2ListBoxItem Then
      If FUpdating = 0 Then
         Realign;
End;

Procedure TD2ListBox.ContentBeforeRemoveObject(AObject: TD2Object);
Begin
   Inherited;
   If AObject Is TD2ListBoxItem Then
   Begin
      If FItemIndex > TD2ListBoxItem(AObject).Index Then
      Begin
         Dec(FItemIndex);
         UpdateSelection;
      End;
      TD2ListBoxItem(AObject).IsSelected := False;
   End;
End;

Procedure TD2ListBox.ContentRemoveObject(AObject: TD2Object);
Begin
   Inherited;
   If AObject Is TD2ListBoxItem Then
   Begin
      If FUpdating = 0 Then
         Realign;
   End;
End;

Function TD2ListBox.GetContentBounds: TD2Rect;
Var
   R: TD2Rect;
   i, j, Idx: Integer;
   RowHeight, ColWidth, CurY: Single;
Begin
   Result := LocalRect;
   If FUpdating > 0 Then
      Exit;
   If ContentLayout = nil Then
      Exit;
   R := ContentLayout.LocalRect;

   If FContent <> nil Then
   Begin
      SortItems;

      If Not MultiSelect And (Selected <> nil) Then
         Selected.IsSelected := True;

      Case FListStyle Of
         d2ListVertical:
         Begin
            If FItemWidth <> 0 Then
            Begin
               FColumns := trunc((R.Right - R.Left) / FItemWidth);
               If FColumns < 1 Then
                  FColumns := 1;
               If FContent.ChildrenCount > 0 Then
                  For i := 0 To (FContent.ChildrenCount - 1) Do
                     With TD2ListBoxItem(FContent.Children[i]) Do
                     Begin
                        If FItemHeight <> 0 Then
                           SetBounds(Position.X, Position.Y, FItemWidth, FItemHeight)
                        Else
                           SetBounds(Position.X, Position.Y, FItemWidth, Height);
                     End;
            End;
            If (FItemWidth = 0) And (FItemHeight <> 0) Then
            Begin
               If FContent.ChildrenCount > 0 Then
                  For i := 0 To (FContent.ChildrenCount - 1) Do
                     If FContent.Children[i] Is TD2ListBoxItem Then
                        With TD2ListBoxItem(FContent.Children[i]) Do
                        Begin
                           SetBounds(Position.X, Position.Y, Width, FItemHeight);
                        End;
            End;

            CurY := 0;
            If FContent.ChildrenCount > 0 Then
               For i := 0 To (FContent.ChildrenCount - 1) Div FColumns Do
               Begin
                  RowHeight := 0;
                  For j := 0 To FColumns - 1 Do
                  Begin
                     If (i * FColumns) + j > FContent.ChildrenCount - 1 Then
                        Continue;
                     If FContent.Children[(i * FColumns) + j] Is TD2ListBoxItem Then
                        With TD2ListBoxItem(FContent.Children[(i * FColumns) + j]) Do
                        Begin
                           If Height + Padding.Top + Padding.Bottom > RowHeight Then
                              RowHeight := Height + Padding.Top + Padding.Bottom;
                        End;
                  End;

                  For j := 0 To FColumns - 1 Do
                  Begin
                     If (i * FColumns) + j > FContent.ChildrenCount - 1 Then
                        Continue;
                     If FContent.Children[(i * FColumns) + j] Is TD2ListBoxItem Then
                        With TD2ListBoxItem(FContent.Children[(i * FColumns) + j]) Do
                        Begin
                           Height := RowHeight - Padding.Top - Padding.Bottom;
                        End;
                  End;
                  CurY := CurY + RowHeight;
               End;
            FContent.Height := CurY;

            CurY := 0;
            Idx := 0;
            If FContent.ChildrenCount > 0 Then
               For i := 0 To (FContent.ChildrenCount - 1) Div FColumns Do
               Begin
                  RowHeight := 0;
                  For j := 0 To FColumns - 1 Do
                  Begin
                     If (i * FColumns) + j > FContent.ChildrenCount - 1 Then
                        Continue;

                     If FItemWidth <> 0 Then
                        ColWidth := FItemWidth
                     Else
                        ColWidth := (R.Right - R.Left) / FColumns;

                     If FContent.Children[(i * FColumns) + j] Is TD2ListBoxItem Then
                        With TD2ListBoxItem(FContent.Children[(i * FColumns) + j]) Do
                        Begin
                           SetBounds(Padding.Left + (j * ColWidth), CurY + Padding.Top,
                              ColWidth - Padding.Left - Padding.Right, Height);

                           If Height + Padding.Top + Padding.Bottom > RowHeight Then
                              RowHeight := Height + Padding.Top + Padding.Bottom;
                           Inc(Idx);
                        End;
                  End;
                  CurY := CurY + RowHeight;
               End;
            If CurY > 0 Then
               R.Bottom := R.Top + CurY;
            If FItemWidth <> 0 Then
               R.Right := R.Left + (FItemWidth * FColumns);
         End;
         d2ListHorizontal:
         Begin
            If FItemHeight <> 0 Then
            Begin
               If FColumns < 1 Then
                  FColumns := 1;
               If FContent.ChildrenCount > 0 Then
                  For i := 0 To (FContent.ChildrenCount - 1) Do
                     With TD2ListBoxItem(FContent.Children[i]) Do
                     Begin
                        If FItemWidth <> 0 Then
                           SetBounds(Position.X, Position.Y, FItemWidth, FItemHeight)
                        Else
                           SetBounds(Position.X, Position.Y, Width, FItemHeight);
                     End;
            End;
            If (FItemHeight = 0) And (FItemWidth <> 0) Then
            Begin
               If FContent.ChildrenCount > 0 Then
                  For i := 0 To (FContent.ChildrenCount - 1) Do
                     With TD2ListBoxItem(FContent.Children[i]) Do
                     Begin
                        SetBounds(Position.X, Position.Y, FItemWidth, Height);
                     End;
            End;

            CurY := 0;
            If FContent.ChildrenCount > 0 Then
               For i := 0 To (FContent.ChildrenCount - 1) Div FColumns Do
               Begin
                  ColWidth := 0;
                  If FItemHeight <> 0 Then
                     RowHeight := FItemHeight
                  Else
                     RowHeight := (R.Bottom - R.Top) / FColumns;
                  For j := 0 To FColumns - 1 Do
                     If FContent.Children[(i * FColumns) + j] Is TD2ListBoxItem Then
                        With TD2ListBoxItem(FContent.Children[(i * FColumns) + j]) Do
                        Begin
                           If ColWidth < Width + Padding.Left + Padding.Right Then
                              ColWidth := Width + Padding.Left + Padding.Right;
                        End;

                  For j := 0 To FColumns - 1 Do
                     If FContent.Children[(i * FColumns) + j] Is TD2ListBoxItem Then
                        With TD2ListBoxItem(FContent.Children[(i * FColumns) + j]) Do
                        Begin
                           Width := ColWidth - (Padding.Left + Padding.Right);
                        End;

                  CurY := CurY + ColWidth;
               End;

            If FItemIndex > Count - 1 Then
               FItemIndex := Count - 1;

            CurY := 0;
            Idx := 0;
            If FContent.ChildrenCount > 0 Then
               For i := 0 To (FContent.ChildrenCount - 1) Div FColumns Do
               Begin
                  ColWidth := 0;
                  If FItemHeight <> 0 Then
                     RowHeight := FItemHeight
                  Else
                     RowHeight := (R.Bottom - R.Top) / FColumns;
                  For j := 0 To FColumns - 1 Do
                     If FContent.Children[(i * FColumns) + j] Is TD2ListBoxItem Then
                        With TD2ListBoxItem(FContent.Children[(i * FColumns) + j]) Do
                        Begin
                           If VScrollBar <> nil Then
                              SetBounds(CurY + Padding.Left - VScrollBar.Value, Padding.Top + (j * RowHeight),
                                 Width, RowHeight - Padding.Top - Padding.Bottom)
                           Else
                              SetBounds(CurY + Padding.Left, Padding.Top + (j * RowHeight),
                                 Width, RowHeight - Padding.Top - Padding.Bottom);
                           If ColWidth < Width + Padding.Left + Padding.Right Then
                              ColWidth := Width + Padding.Left + Padding.Right;
                           Inc(Idx);
                        End;
                  CurY := CurY + ColWidth;
               End;
            If CurY > 0 Then
               R.Right := R.Left + CurY;
            If FItemHeight <> 0 Then
               R.Bottom := R.Top + (FItemHeight * FColumns);
         End;
      End;
   End;
   UpdateSelection;
   Result := R;
End;

Function TD2ListBox.GetCount: Integer;
Begin
   Result := 0;
   If (FContent <> nil) Then
      Result := FContent.ChildrenCount;
End;

Function TD2ListBox.ItemByIndex(Const Idx: Integer): TD2ListBoxItem;
Begin
   If (FContent <> nil) And (FContent.ChildrenCount > 0) And (Idx >= 0) And (Idx < FContent.ChildrenCount) Then
      Result := TD2ListBoxItem(FContent.Children[Idx])
   Else
      Result := nil;
End;

Function TD2ListBox.ItemByPoint(Const X, Y: Single): TD2ListBoxItem;
Var
   i: Integer;
   P: TD2Point;
Begin
   P := LocaltoAbsolute(d2Point(X, Y));
   If (FContent <> nil) And (FContent.ChildrenCount > 0) Then
      For i := 0 To FContent.ChildrenCount - 1 Do
         If FContent.Children[i] Is TD2ListBoxItem Then
            If TD2ListBoxItem(FContent.Children[i]).pointInObject(P.X, P.Y) Then
            Begin
               Result := TD2ListBoxItem(FContent.Children[i]);
               Exit;
            End;
   Result := nil;
End;

Procedure TD2ListBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Var
   i: Integer;
Begin
   Inherited;
   If Count > 0 Then
   Begin
      If KeyChar <> #0 Then
      Begin
         For i := 0 To Count - 1 Do
            If (Items[i].TextW <> '') And (WideLowerCase(Items[i].TextW[1]) = WideLowerCase(KeyChar)) Then
            Begin
               ItemIndex := i;
               Break;
            End;
         KeyChar := #0;
      End;
      Case Key Of
         VK_HOME: ItemIndex := 0;
         VK_END: ItemIndex := Count - FColumns;
         VK_UP: If ItemIndex > 0 Then
            Begin
               ItemIndex := ItemIndex - FColumns;
               If ItemIndex < 0 Then
                  ItemIndex := 0;
            End;
         VK_DOWN:
         Begin
            If ItemIndex < Count - 1 Then
               ItemIndex := ItemIndex + FColumns;
            If ItemIndex > Count - 1 Then
               ItemIndex := Count - 1;
         End;
         VK_LEFT: If ItemIndex > 0 Then
               ItemIndex := ItemIndex - 1;
         VK_RIGHT: If ItemIndex < Count - 1 Then
               ItemIndex := ItemIndex + 1;
         Else Exit;
      End;
      Key := 0;
   End;
End;

Procedure TD2ListBox.KeyUp(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
End;

Procedure TD2ListBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   Item: TD2ListBoxItem;
Begin
   Inherited;
   If (Button = mbLeft) And Not MouseTracking Then
   Begin
      Item := ItemByPoint(X, Y);
      If Item <> nil Then
      Begin
         If Multiselect Then
         Begin
            If ssCtrl In Shift Then
               Item.IsSelected := Not Item.IsSelected
            Else
            If ssShift In Shift Then
            Begin
               SelectRange(Selected, Item);
               ItemIndex := Item.Index;
            End Else Begin
               SelectRange(Item, Item);
               ItemIndex := Item.Index;
            End;
            FFirstSelect := Item;
         End Else Begin
            If ItemIndex <> Item.Index Then
               ItemIndex := Item.Index
            Else
            If AllowDrag Then
               Scene.BeginDragObj(Selected, MakeScreenshot);
         End;
         If Assigned(Item.OnClick) Then
            Item.OnClick(Item);
      End;
      FMouseSelecting := True;
   End;
End;

Procedure TD2ListBox.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   Item: TD2ListBoxItem;
Begin
   Inherited;
   If (ssLeft In Shift) And FMouseSelecting Then
   Begin
      Item := ItemByPoint(X, Y);
      If Item <> nil Then
      Begin
         If Selected = Item Then
            Exit;
         If MultiSelect Then
         Begin
            If ssCtrl In Shift Then
               Item.IsSelected := Not Item.IsSelected
            Else
               SelectRange(FFirstSelect, Item);
            ItemIndex := Item.Index;
         End Else
            ItemIndex := Item.Index;
      End;
   End;
End;

Procedure TD2ListBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   Item: TD2ListBoxItem;
Begin
   Inherited;
   FFirstSelect := nil;
   If MouseTracking And (FLastDelta.X = 0) And (FLastDelta.Y = 0) Then
   Begin
      Item := ItemByPoint(X, Y);
      If Item <> nil Then
         ItemIndex := Item.Index;
   End;
   FMouseSelecting := False;
End;

Function TD2ListBox.GetSelected: TD2ListBoxItem;
Begin
   Result := ItemByIndex(FItemIndex);
End;

Procedure TD2ListBox.SetItemIndex(Const Value: Integer);
Var
   Item: TD2ListBoxItem;
Begin
   If FItemIndex <> Value Then
   Begin
      Item := ItemByIndex(ItemIndex);
      If (Item <> nil) And (Not MultiSelect) Then
         Item.IsSelected := False;

      FItemIndex := Value;
      If (ItemByIndex(FItemIndex) <> nil) And (FContent <> nil) And (VScrollBar <> nil) And (ContentLayout <> nil) Then
      Begin
         Item := ItemByIndex(FItemIndex);
         If FContent.Position.Y + Item.Position.Y + Item.Padding.Top + Item.Padding.Bottom + Item.Height > ContentLayout.Position.Y + ContentLayout.Height Then
            VScrollBar.Value := VScrollBar.Value + (FContent.Position.Y + Item.Position.Y + Item.Padding.Top + Item.Padding.Bottom + Item.Height - ContentLayout.Position.Y - ContentLayout.Height);
         If FContent.Position.Y + Item.Position.Y < ContentLayout.Position.Y Then
            VScrollBar.Value := VScrollBar.Value + FContent.Position.Y + Item.Position.Y - ContentLayout.Position.Y;
      End;
      If (ItemByIndex(FItemIndex) <> nil) And (FContent <> nil) And (HScrollBar <> nil) And (ContentLayout <> nil) Then
      Begin
         Item := ItemByIndex(FItemIndex);
         If FContent.Position.X + Item.Position.X + Item.Padding.Left + Item.Padding.Right + Item.Width > ContentLayout.Position.X + ContentLayout.Width Then
            HScrollBar.Value := HScrollBar.Value + (FContent.Position.X + Item.Position.X + Item.Padding.Left + Item.Padding.Right + Item.Width - ContentLayout.Position.X - ContentLayout.Width);
         If FContent.Position.X + Item.Position.X < 0 Then
            HScrollBar.Value := HScrollBar.Value + FContent.Position.X + Item.Position.X - ContentLayout.Position.X;
      End;
      If (ItemIndex >= 0) Then
      Begin
         Item := ItemByIndex(ItemIndex);
         If Item <> nil Then
            Item.IsSelected := True;
      End;
      If (FUpdating = 0) Then
      Begin
         If Assigned(FBindingObjects) Then
            ToBindingObjects;
         If Assigned(FOnChange) Then
            FOnChange(ItemByIndex(FItemIndex));
      End;
      UpdateSelection;
   End;
End;

Procedure TD2ListBox.Clear;
Var
   i: Integer;
Begin
   BeginUpdate;
   If FContent <> nil Then
      If FContent.ChildrenCount > 0 Then
         For i := FContent.ChildrenCount - 1 Downto 0 Do
            If FContent.Children[i] Is TD2ListboxItem Then
               TD2Object(FContent.Children[i]).Free;
   FScrollDesign.Y := 0;
   FScrollDesign.X := 0;
   EndUpdate;
End;

Procedure TD2ListBox.SelectRange(Item1, Item2: TD2ListBoxItem);
Var
   i: Integer;
Begin
   If Item1 = nil Then
      Exit;
   If Item2 = nil Then
      Exit;
   For i := 0 To Min(Item1.Index, Item2.Index) - 1 Do
      ItemByIndex(i).IsSelected := False;
   For i := Max(Item1.Index, Item2.Index) + 1 To Count - 1 Do
      ItemByIndex(i).IsSelected := False;
   For i := Min(Item1.Index, Item2.Index) To Max(Item1.Index, Item2.Index) Do
      ItemByIndex(i).IsSelected := True;
End;

Procedure TD2ListBox.ClearSelection;
Var
   i: Integer;
Begin
   For i := 0 To Count - 1 Do
      ItemByIndex(i).IsSelected := False;
End;

Procedure TD2ListBox.SelectAll;
Var
   i: Integer;
Begin
   For i := 0 To Count - 1 Do
      ItemByIndex(i).IsSelected := True;
End;

Function TD2ListBox.GetData: Variant;
Begin
   If Selected <> nil Then
      Result := Selected.TextW
   Else
      Result := '';
End;

Procedure TD2ListBox.SetData(Const Value: Variant);
Begin
   If Selected <> nil Then
      Selected.TextW := Value;
End;

Procedure TD2ListBox.EnterFocus;
Begin
   Inherited;
   If HideSelectionUnfocused And (Selected <> nil) Then
      UpdateSelection;
End;

Procedure TD2ListBox.KillFocus;
Begin
   Inherited;
   If HideSelectionUnfocused And (Selected <> nil) Then
      UpdateSelection;
End;

Procedure TD2ListBox.AddObject(AObject: TD2Object);
Begin
   If (FContent <> nil) And ((AObject Is TD2ListBoxItem) Or (AObject Is TD2SelectionItem)) Then
   Begin
      FContent.AddObject(AObject);
   End Else
      Inherited;
End;

Procedure TD2ListBox.SetColumns(Const Value: Integer);
Begin
   If FColumns <> Value Then
   Begin
      FColumns := Value;
      If FColumns < 1 Then
         FColumns := 1;
      Realign;
   End;
End;

Procedure TD2ListBox.SetAlternatingRowBackground(Const Value: Boolean);
Begin
   If FAlternatingRowBackground <> Value Then
   Begin
      FAlternatingRowBackground := Value;
      Repaint;
   End;
End;

Procedure TD2ListBox.SetMultiSelect(Const Value: Boolean);
Begin
   If FMultiSelect <> Value Then
   Begin
      FMultiSelect := Value;
      If Not FMultiSelect Then
         ClearSelection;
   End;
End;

Procedure TD2ListBox.SetItemHeight(Const Value: Single);
Begin
   If FItemHeight <> Value Then
   Begin
      FItemHeight := Value;
      Realign;
   End;
End;

Procedure TD2ListBox.SetItemWidth(Const Value: Single);
Begin
   If FItemWidth <> Value Then
   Begin
      FItemWidth := Value;
      Realign;
   End;
End;

Procedure TD2ListBox.SetListStyle(Const Value: TD2ListStyle);
Begin
   If FListStyle <> Value Then
   Begin
      FListStyle := Value;
      Realign;
   End;
End;

Procedure TD2ListBox.SetShowCheckboxes(Const Value: Boolean);
Var
   i: Integer;
Begin
   If FShowCheckboxes <> Value Then
   Begin
      FShowCheckboxes := Value;
      For i := 0 To Count - 1 Do
         If ItemByIndex(i) <> nil Then
            ItemByIndex(i).UpdateCheck;
   End;
End;

Function TD2ListBox.GetItem(aIndex: Integer): TD2ListBoxItem;
Begin
   Result := ItemByIndex(aIndex);
End;

Procedure TD2ListBox.SetSorted(Const Value: Boolean);
Begin
   If FSorted <> Value Then
   Begin
      FSorted := Value;
      Realign;
   End;
End;

Procedure TD2ListBox.SetAllowDrag(Const Value: Boolean);
Begin
   If FAllowDrag <> Value Then
   Begin
      FAllowDrag := Value;
      If FAllowDrag Then
         DragDisableHighlight := True;
   End;
End;

//=============== TD2StringListBox ===================================================

Procedure TD2StringListBox.ApplyStyle;
Begin
   Inherited;
End;

Constructor TD2StringListBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FFont := TD2Font.Create;
   FFont.OnChanged := FontChanged;
   FAutoTranslate := True;
   FItems := TD2WideStringList.Create;
   TD2WideStringList(FItems).OnChange := DoItemsChanged;
   FItemHeight := 19;
   FNeedResource := True;
   FResource := 'listboxstyle';
   FItemStyle := 'listboxitemstyle';
End;

Destructor TD2StringListBox.Destroy;
Begin
   FreeAndNil(FItems);
   FreeAndNil(FFont);
   Inherited;
End;

Procedure TD2StringListBox.EndUpdate;
Begin
   FUpdating := FUpdating + 1;
   Inherited EndUpdate;
   FUpdating := FUpdating - 1;
   If FUpdating = 0 Then
      RebuildList;
End;

Procedure TD2StringListBox.DoItemsChanged(Sender: TObject);
Begin
   RebuildList;
End;

Function TD2StringListBox.GetData: Variant;
Begin
   If (ItemIndex >= 0) And (ItemIndex < FItems.Count) Then
      Result := FItems[ItemIndex]
   Else
      Result := '';
End;

Procedure TD2StringListBox.SetData(Const Value: Variant);
Begin
   If (ItemIndex >= 0) And (ItemIndex < FItems.Count) Then
      FItems[ItemIndex] := Value;
End;

Procedure TD2StringListBox.RebuildList;
Var
   i, SaveIndex: Integer;
   SaveScrollDesign: TD2Point;
   Item: TD2ListBoxItem;
Begin
   If FUpdating > 0 Then
      Exit;
   If csDestroying In ComponentState Then
      Exit;
   If Items = nil Then
      Exit;

   BeginUpdate;
   SaveScrollDesign := FScrollDesign;
   SaveIndex := ItemIndex;
   Clear;
   For i := 0 To FItems.Count - 1 Do
   Begin
      Item := TD2ListBoxItem.Create(Self);
      Item.AutoTranslate := FAutoTranslate;
      Item.Parent := Self;
      Item.Height := FItemHeight;
      Item.Stored := False;
      Item.Locked := True;
      Item.TextW := FItems[i];
      Item.Font := FFont;
      Item.FResource := FItemStyle;
      Item.TextAlign := TextAlign;
   End;
   FItemIndex := SaveIndex;
   FScrollDesign := SaveScrollDesign;
   Inherited EndUpdate;
End;

Procedure TD2StringListBox.SetItems(Const Value: TD2WideStrings);
Begin
   FItems.Assign(Value);
End;

Procedure TD2StringListBox.SetTextAlign(Const Value: TD2TextAlign);
Begin
   If FTextAlign <> Value Then
   Begin
      FTextAlign := Value;
      If Not (csLoading In ComponentState) Then
         RebuildList;
   End;
End;

Procedure TD2StringListBox.SetFont(Const Value: TD2Font);
Begin
   FFont.Assign(Value);
End;

Procedure TD2StringListBox.FontChanged(Sender: TObject);
Var
   i: Integer;
Begin
   For i := 0 To Count - 1 Do
      Inherited Items[i].Font.Assign(FFont);
End;

Procedure TD2StringListBox.SortItems;
Begin
   //TD2WideStringList(FItems).Sort;
   If FSorted Then
      TD2WideStringList(FItems).Sort;
End;


//=============== TD2HorzListBox =========================================

Constructor TD2HorzListBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'listboxstyle';
   FNeedResource := True;
   FListStyle := d2ListHorizontal;
End;

//=============== TD2HudStringListBox ===================================

Constructor TD2HudStringListBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FNeedResource := True;
   FResource := 'hudlistboxstyle';
   FItemStyle := 'hudlistboxitemstyle';
End;

//=============== TD2HudListBox ========================================

Constructor TD2HudListBox.Create(AOwner: TComponent);
Begin
   Inherited;
End;

//=============== TD2HudHorzListBox ====================================

Constructor TD2HudHorzListBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FNeedResource := True;
   FResource := 'hudlistboxstyle';
End;

//=============== TD2ImageListBoxItem =====================================

Procedure TD2ImageListBoxItem.ApplyStyle;
Begin
   Inherited;
   If (ListBox Is TD2ImageListBox) Then
   Begin
      If (TextBorder <> nil) Then
         TextBorder.Visible := TD2ImageListBox(ListBox).ShowFileName;
      If (Text <> nil) Then
         Text.TextW := ExtractFileName(TagString);
   End;
   If (ListBox Is TD2HorzImageListBox) Then
   Begin
      If (TextBorder <> nil) Then
         TextBorder.Visible := TD2HorzImageListBox(ListBox).ShowFileName;
      If (Text <> nil) Then
         Text.TextW := ExtractFileName(TagString);
   End;
End;

Function TD2ImageListBoxItem.Text: TD2Text;
Begin
   Result := TD2Text(FindResource('text'));
End;

Function TD2ImageListBoxItem.TextBorder: TD2VisualObject;
Begin
   Result := TD2VisualObject(FindResource('textborder'));
End;

//=============== TD2ImageListBox ========================================

Constructor TD2ImageListBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FUseThumbnails := True;
   FItemHeight := Trunc(Width);
   If Self Is TD2HudImageListBox Then
      FResource := 'HudListBoxStyle'
   Else
      FResource := 'listboxstyle';
End;

Procedure TD2ImageListBox.AddBitmap(Const AFile: String; Const ABitmap: TD2Bitmap);
Var
   R: TD2Rect;
   Item: TD2ListBoxItem;
   Thumb: TD2Image;
Begin
   Item := TD2ImageListBoxItem.Create(Self);
   Item.Parent := Self;
   Item.Height := FItemHeight;
   Item.Stored := False;
   Thumb := TD2Image.Create(Item);
   Thumb.Parent := Item;
   If ShowFileName Then
      Thumb.Padding.Rect := d2Rect(4, 4, 4, 20)
   Else
      Thumb.Padding.Rect := d2Rect(4, 4, 4, 4);
   Thumb.Align := vaClient;
   Thumb.Stored := False;
   Thumb.Locked := True;
   Thumb.HitTest := False;

   If FUseThumbnails Then
   Begin
      R := d2Rect(0, 0, ABitmap.Width, ABitmap.Height);
      d2FitRect(R, d2Rect(0, 0, Height, Height));
      Thumb.Bitmap.SetSize(round(d2RectWidth(R)), round(d2RectHeight(R)));
      If Thumb.Bitmap.Canvas.BeginScene Then
      Begin
         Thumb.Bitmap.Canvas.DrawBitmap(ABitmap, d2Rect(0, 0, ABitmap.Width, ABitmap.Height), d2Rect(0, 0, Thumb.Bitmap.Width, Thumb.Bitmap.Height), 1);
         Thumb.Bitmap.Canvas.EndScene;
      End;
   End Else
      Thumb.Bitmap.Assign(ABitmap);

   Item.Tag := 0;

   Item.TagString := AFile;

   Item.TagObject := Thumb;
   Realign;
   If Count = 1 Then
      ItemIndex := 0;
End;

Procedure TD2ImageListBox.AddFile(Const AFile: String);
Var
   Ext: String;
   Item: TD2ListBoxItem;
   Thumb: TD2Image;
Begin
   Ext := LowerCase(ExtractFileExt(AFile));
   If Pos(Ext, GvarD2DefaultFilterClass.GetFileTypes) > 0 Then
   Begin
      Item := TD2ImageListBoxItem.Create(Self);
      Item.Parent := Self;
      Item.Height := FItemHeight;
      Item.Stored := False;
      Item.OnApplyResource := DoApplyResource;
      Thumb := TD2Image.Create(Item);
      Thumb.Parent := Item;
      If ShowFileName Then
         Thumb.Padding.Rect := d2Rect(4, 4, 4, 20)
      Else
         Thumb.Padding.Rect := d2Rect(4, 4, 4, 4);
      Thumb.Align := vaClient;
      Thumb.Stored := False;
      Thumb.Locked := True;
      Thumb.HitTest := False;

      Item.Tag := 1;

      Item.TagString := AFile;
      Item.TagObject := Thumb;
   End;
   If Count = 1 Then
      ItemIndex := 0;
End;

Procedure TD2ImageListBox.AddFolder(Const Folder: String);
Var
   Dir: String;
   SR: TSearchRec;
Begin
   Dir := Folder;
   {$IFDEF ORCA2D_OS_WINDOWS}
  if (Length(Dir) > 0) and (Dir[Length(Dir)] <> '\') then Dir:=Dir + '\';
   {$ELSE}
   If (Length(Dir) > 0) And (Dir[Length(Dir)] <> '/') Then
      Dir := Dir + '/';
   {$ENDIF}
   BeginUpdate;
   If FindFirst(Dir + '*.*', $FFFF, SR) = 0 Then
   Begin
      Repeat
         If SR.Name = '.' Then
            Continue;
         If SR.Name = '..' Then
            Continue;
         If SR.Attr And faDirectory = faDirectory Then
            Continue;
         AddFile(Dir + SR.Name);
      Until FindNext(SR) <> 0;
      FindClose(SR);
   End;
   EndUpdate;
   If Count > 0 Then
      ItemIndex := 0;
End;

Procedure TD2ImageListBox.DoApplyResource(Sender: TObject);
Begin
   If (TD2ImageListBoxItem(Sender).Tag = 1) And (TD2ImageListBoxItem(Sender).TagString <> '') Then
   Begin
      With TD2ImageThread.Create(TD2Image(TD2ImageListBoxItem(Sender).TagObject), TD2ImageListBoxItem(Sender).TagString, FUseThumbnails) Do
      Begin
         TD2ImageListBoxItem(Sender).Tag := 0;
         Resume;
      End;
   End;
End;

Function TD2ImageListBox.GetImage(aIndex: Integer): TD2Image;
Var
   Item: TD2ListBoxItem;
Begin
   Item := ItemByIndex(aIndex);
   If (Item <> nil) And (Item.TagObject <> nil) And (Item.TagObject Is TD2Image) Then
      Result := TD2Image(Item.TagObject)
   Else
      Result := nil;
End;

Function TD2ImageListBox.GetSelectedFileName: String;
Begin
   If (Selected <> nil) Then
      Result := Selected.TagString
   Else
      Result := '';
End;

Function TD2ImageListBox.GetSelectedImage: TD2Image;
Begin
   If (Selected <> nil) And (Selected.TagObject <> nil) And (Selected.TagObject Is TD2Image) Then
      Result := TD2Image(Selected.TagObject)
   Else
      Result := nil;
End;

Procedure TD2ImageListBox.SetShowFileName(Const Value: Boolean);
Begin
   If FShowFileName <> Value Then
   Begin
      FShowFileName := Value;
   End;
End;

Procedure TD2ImageListBox.SetItemHeight(Const Value: Single);
Var
   i: Integer;
Begin
   If FItemHeight <> Value Then
   Begin
      FItemHeight := Value;
      FDisableAlign := True;
      Try
         For i := 0 To Count - 1 Do
            ItemByIndex(i).Height := ItemHeight;
      Finally
         FDisableAlign := False;
         Realign;
      End;
   End;
End;

Procedure TD2ImageListBox.Clear;
Var
   i: Integer;
Begin
   For i := 0 To Count - 1 Do
      ItemByIndex(i).Tag := 0;
   Inherited;
End;

Procedure TD2ImageListBox.BeginAutoDrag;
Begin
   If SelectedImage <> nil Then
      FScene.BeginDragObj(SelectedImage, SelectedImage.Bitmap);
End;

//=============== TD2HorzImageListBox ============================================

Constructor TD2HorzImageListBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FItemHeight := 0;
   FItemWidth := Trunc(Height + 10);
   FListStyle := d2ListHorizontal;

   If Self Is TD2HudHorzImageListBox Then
      FResource := 'HudListBoxStyle'
   Else
      FResource := 'ListBoxStyle';
End;

Procedure TD2ListBox.DragDrop(Const aData: TD2DragObject; Const Point: TD2Point);
Var
   Obj: TD2ListBoxItem;
   Allow: Boolean;
Begin
   Inherited;
   If FDragItem <> nil Then
   Begin
      FDragItem.DragLeave;
      FDragItem := nil;
   End;
   With AbsoluteToLocal(Point) Do
      Obj := ItemByPoint(X, Y);
   If Obj <> nil Then
   Begin
      Allow := True;
      If Assigned(OnDragChange) Then
         OnDragChange(TD2ListBoxItem(aData.Source), Obj, Allow);
      If Allow Then
      Begin
         Exchange(TD2ListBoxItem(aData.Source), Obj);
      End;
   End;
End;

Procedure TD2ListBox.DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean);
Var
   Obj: TD2ListBoxItem;
Begin
   Inherited;
   With AbsoluteToLocal(Point) Do
      Obj := ItemByPoint(X, Y);
   If (Obj <> FDragItem) Then
   Begin
      If FDragItem <> nil Then
         FDragItem.DragLeave;
      FDragItem := Obj;
      If FDragItem <> nil Then
      Begin
         FDragItem.DragEnter(aData, Point);
         Accept := True;
      End Else
         Accept := False;
   End Else
      Accept := True;

   If FDragItem = Selected Then
      Accept := False;
End;

Procedure TD2ListBox.Exchange2d(Item1, Item2: TD2ListBoxItem);
Begin
   If Item1.Index = FItemIndex Then
      FItemIndex := Item2.Index
   Else
   If Item2.Index = FItemIndex Then
      FItemIndex := Item1.Index;
   FContent.Exchange(Item1, Item2);
End;

//=============== TD2MemoLines ==================================================

Procedure TD2MemoLines.Clear;
Begin
   FMemo.ClearLines;
   FMemo.Change;
End;

Procedure TD2MemoLines.Delete(Index: Integer);
Begin
   FMemo.DeleteLine(Index);
   FMemo.Change;
End;

Procedure TD2MemoLines.Insert(Index: Integer; Const S: WideString);
Begin
   FMemo.InsertLine(Index, S);
   FMemo.Change;
End;

Function TD2MemoLines.Get(Index: Integer): WideString;
Begin
   Result := FMemo.GetLine(Index);
End;

Function TD2MemoLines.GetCount: Integer;
Begin
   Result := FMemo.GetLineCount;
End;

Procedure TD2MemoLines.SetUpdateState(Updating: Boolean);
Begin
   Inherited;
   FMemo.SetUpdateState(Updating);
End;

Function ComposeCaretPos(ALine, APos: Integer): TCaretPosition;
Begin
   With Result Do
   Begin
      Line := ALine;
      Pos := APos;
   End;
End;

//=============== TD2Memo ===============================================

Constructor TD2Memo.Create(AOwner: TComponent);
Begin
   Inherited Create(AOwner);
   FTextAlign := d2TextAlignNear;
   FFont := TD2Font.Create;
   FFont.OnChanged := FontChanged;
   FFontFill := TD2Brush.Create(d2BrushSolid, $FF000000);
   FSelectionFill := TD2Brush.Create(d2BrushSolid, $802A8ADF);
   FLines := TD2MemoLines.Create;
   (FLines As TD2MemoLines).FMemo := Self;
   CanFocused := True;
   Cursor := crIBeam;
   FInternalMustUpdateLines := True;

   CreatePopupMenu;

   FActionStack := TEdtActionStack.Create(Self);

   FTextAlignment := taLeftJustify;
   FAutoSelect := True;
   FCharCase := d2ecNormal;
   FHideSelection := True;
   FMaxLength := 0;
   FReadOnly := False;

   FLMouseSelecting := False;
   FOldMPt := d2Point(0, 0);

   FUpdatingBool := False;


   With FCaretPosition Do
   Begin
      Line := 0;
      Pos := 0;
   End;

   FSelStart := ComposeCaretPos(0, 0);
   FSelEnd := ComposeCaretPos(0, 0);
   FSelected := False;

   FOldSelStartPos := -1;
   FOldSelEndPos := -1;
   FOldCaretPos := -1;

   AutoCapture := True;

   FWidesLineIndex := 0;
   SetLength(FTextWidth, 0);

   Width := 100;
End;

Destructor TD2Memo.Destroy;
Begin
   If FUnwrapLines <> nil Then
      FUnwrapLines.Free;
   FSelectionFill.Free;
   FFontFill.Free;
   FFont.Free;
   FActionStack.Free;
   FPopupMenu.Free;
   FLines.Free;
   Inherited;
End;

Procedure TD2Memo.EnterFocus;
Begin
   Inherited;
   FNeedChange := False;
   UpdateCaretPosition(False);
   ShowCaretProc;
   If FScene.ShowKeyboardForControl(Self) Then
   Begin
      CaretPosition := TextPosToPos(Length(TextW));
   End Else Begin
      With FCaretPosition Do
      Begin
         Line := 0;
         Pos := 0;
      End;
      If AutoSelect Then
         SelectAll;
   End;
End;

Procedure TD2Memo.Killfocus;
Begin
   FScene.HideKeyboardForControl(Self);
   Inherited;
   HideCaret;
   Change;
End;

Function TD2Memo.TextWidth(Const Str: WideString): Single;
Var
   R: TD2Rect;
Begin
   R := ContentRect;
   R.Right := 10000;
   Canvas.Font.Assign(Font);
   Canvas.MeasureText(R, R, Str, False, TextAlign, d2TextAlignCenter);
   Result := d2RectWidth(R);
End;

Function TD2Memo.GetPositionPoint(ACaretPos: TCaretPosition): TD2Point;
Var
   WholeTextWidth: Single;
   EditRectWidth: Single;
   LineText: WideString;
Begin
   Result.X := ContentRect.Left;
   Result.Y := ContentRect.Top + (GetLineHeight * ACaretPos.Line) - VScrollBarValue;
   WholeTextWidth := 0;
   If Canvas = nil Then
      Exit;

   If (ACaretPos.Line < Lines.Count) And (Lines.Count > 0) Then
   Begin
      LineText := Lines[ACaretPos.Line];

      WholeTextWidth := TextWidth(LineText);

      If ACaretPos.Pos > 0 Then
      Begin
         If ACaretPos.Pos <= Length(LineText) Then
            Result.X := Result.X + TextWidth(Copy(LineText, 1, ACaretPos.Pos))
         Else
            Result.X := Result.X + TextWidth(LineText);
      End;
   End;
   EditRectWidth := ContentRect.Right - ContentRect.Left;
   If WholeTextWidth < EditRectWidth Then
      Case FTextAlign Of
         d2TextAlignFar: Result.X := Result.X + (EditRectWidth - WholeTextWidth);
         d2TextAlignCenter: Result.X := Result.X + ((EditRectWidth - WholeTextWidth) / 2);
      End;
   Result.X := Result.X - HScrollBarValue;
End;

Function TD2Memo.GetPointPosition(Pt: TD2Point): TCaretPosition;
Var
   CurX: Double;
   TmpX, WholeTextWidth, EdiTD2RectWidth: Single;
   LineText: WideString;
   LLine: Integer;
   LPos: Integer;
   TmpPt: TD2Point;
   LEdiTD2Rect: TD2Rect;
Begin
   With Result Do
   Begin
      Line := 0;
      Pos := 0;
   End;

   If Lines.Count <= 0 Then
      Exit;

   LEdiTD2Rect := ContentRect;

   With LEdiTD2Rect, Pt Do
   Begin
      If x < Left Then
         TmpPt.x := Left
      Else
      If x > Right Then
         TmpPt.x := Right
      Else
         TmpPt.x := x;

      If y < Top Then
         TmpPt.y := Top
      Else
      If y > Bottom Then
         TmpPt.y := Bottom
      Else
         TmpPt.y := y;
   End;

   LLine := trunc((TmpPt.Y - ContentRect.Top) / GetLineHeight + trunc(VScrollBarValue / GetLineHeight));

   LPos := 0;

   If LLine > Lines.Count - 1 Then
      LLine := Lines.Count - 1;

   LineText := Lines[LLine];

   If Length(LineText) > 0 Then
   Begin
      WholeTextWidth := TextWidth(LineText);

      EdiTD2RectWidth := ContentRect.Right - ContentRect.Left;
      TmpX := TmpPt.x;
      If WholeTextWidth < EdiTD2RectWidth Then
         Case TextAlign Of
            d2TextAlignFar: TmpX := TmpPt.x - (EdiTD2RectWidth - WholeTextWidth);
            d2TextAlignCenter: TmpX := TmpPt.x - ((EdiTD2RectWidth - WholeTextWidth) / 2);
         End;

      TmpX := TmpX + HScrollBarValue;

      CurX := ContentRect.Left + TextWidth(LineText[1]) / 2;
      While (CurX < TmpX) And (LPos + 1 <= Length(LineText)) And (CurX < ContentRect.Right + HScrollBarValue) Do
      Begin
         CurX := ContentRect.Left + TextWidth(Copy(LineText, 1, LPos + 1)) + (Font.Size / 4);
         Inc(LPos);
      End;
   End;
   With Result Do
   Begin
      Line := LLine;
      Pos := LPos;
   End;
End;

Procedure TD2Memo.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Var
   TmpS: WideString;
   OldCaretPosition: TCaretPosition;
   WasSelection: Boolean;
   LTmpOptions: TInsertOptions;
Begin
   Inherited KeyDown(Key, KeyChar, Shift);
   OldCaretPosition := CaretPosition;
   If (Key = VK_RETURN) Then
   Begin
      WasSelection := SelLength > 0;
      If WasSelection Then
         DeleteFrom(GetSelBeg, SelLength, [doMoveCaret, doCanUndo]);
      If WasSelection Then
         LTmpOptions := [ioUnDoPairedWithPriv]
      Else
         LTmpOptions := [];
      TmpS := #13#10;
      InsertAfter(CaretPosition, TmpS, LTmpOptions + [ioMoveCaret, ioCanUndo]);
      SelLength := 0;
      Key := 0;
   End;
   Case Key Of
      VK_END: If ssCtrl In Shift Then
            GoToTextEnd
         Else
            GoToLineEnd;
      VK_HOME: If ssCtrl In Shift Then
            GoToTextBegin
         Else
            GoToLineBegin;
      VK_LEFT:
         If ssCtrl In Shift Then
            CaretPosition := GetPrivWordBeging(CaretPosition)
         Else
            MoveCaretLeft;
      VK_RIGHT:
         If ssCtrl In Shift Then
            CaretPosition := GetNextWordBeging(CaretPosition)
         Else
            MoveCaretRight;
      VK_UP:
         MoveCaretUp;
      VK_DOWN:
         MoveCaretDown;
      VK_PRIOR:
         MoveCaretPageUp;
      VK_NEXT:
         MoveCaretPageDown;
      VK_DELETE, 8:
         If Not ReadOnly Then
         Begin
            If SelLength <> 0 Then
            Begin
               If ssShift In Shift Then
                  CutToClipboard
               Else
                  ClearSelection;
            End Else Begin
               TmpS := TextW;
               If Key = VK_DELETE Then
                  DeleteFrom(CaretPosition, 1, [doMoveCaret, doCanUndo])
               Else
               If PosToTextPos(CaretPosition) > 0 Then
                  DeleteFrom(GetPositionShift(CaretPosition, -1), 1, [doMoveCaret, doCanUndo]);
            End;
         End;
      VK_INSERT:
         If ssCtrl In Shift Then
            CopyToClipboard
         Else
         If ssShift In Shift Then
            PasteFromClipboard;
   End;

   Case KeyChar Of
      'a', 'A':
         If Shift = [ssCtrl] Then
         Begin
            SelectAll;
            KeyChar := #0;
         End;
      'c', 'C':
         If Shift = [ssCtrl] Then
         Begin
            CopyToClipboard;
            KeyChar := #0;
         End;
      'v', 'V':
         If Shift = [ssCtrl] Then
         Begin
            PasteFromClipboard;
            KeyChar := #0;
         End;
      'x', 'X':
         If Shift = [ssCtrl] Then
         Begin
            CutToClipboard;
            KeyChar := #0;
         End;
      'z', 'Z':
         If Shift = [ssCtrl] Then
         Begin
            KeyChar := #0;
         End;
   End;

   If ((Ord(Keychar) >= 32) Or (Keychar = #13)) And Not ReadOnly Then
   Begin
      WasSelection := SelLength > 0;
      If WasSelection Then
         DeleteFrom(GetSelBeg, SelLength, [doMoveCaret, doCanUndo]);
      If WasSelection Then
         LTmpOptions := [ioUnDoPairedWithPriv]
      Else
         LTmpOptions := [];
      If Keychar <> #13 Then
      Begin
         InsertAfter(CaretPosition, KeyChar, LTmpOptions + [ioMoveCaret, ioCanUndo]);
      End Else Begin
         TmpS := #13#10;
         InsertAfter(CaretPosition, TmpS, LTmpOptions + [ioMoveCaret, ioCanUndo]);
      End;
      SelLength := 0;
      Keychar := #0;
   End;

   If Key In [VK_END, VK_HOME, VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_PRIOR, VK_NEXT] Then
   Begin
      If ssShift In Shift Then
      Begin
         If Not FSelected Then
            SelectAtPos(OldCaretPosition);
         SelectAtPos(CaretPosition);
         Repaint;
      End Else
      If FSelected Then
      Begin
         FSelected := False;
         Repaint;
      End;
   End;
   UpdateCaretPosition(True);
End;

Procedure TD2Memo.MouseDown(Button: TMouseButton; Shift: TShiftState; x, y: Single);
Begin
   Inherited;
   If (Button = mbLeft) And (ssDouble In Shift) Then
   Begin
      If d2PtInRect(d2Point(x, y), ContentRect) Then
      Begin
         FLMouseSelecting := False;
         SelectWord;
      End;
   End;
   If (Button = mbLeft) And d2PtInRect(d2Point(x, y), ContentRect) Then
   Begin
      FLMouseSelecting := True;
      CaretPosition := GetPointPosition(d2Point(x, y));
      FSelected := False;
      SelectAtPos(CaretPosition);
      Repaint;
   End;
End;

Function TD2Memo.ContentPos: TD2Point;
Var
   T: TD2Object;
Begin
   T := FindResource('content');
   If (T <> nil) And (T.IsVisual) Then
   Begin
      Result := TD2VisualObject(T).Position.Point;
   End;
End;

Procedure TD2Memo.DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
Var
   R, TmpRect: TD2Rect;
   LSelArea: TSelArea;
   CurSelRect: Integer;
   SaveIndex: Integer;
   CurLine, LEndLine: Integer;
   TmpPt: TD2Point;
   LPageSize: Single;
Begin
   With aCanvas Do
   Begin
      SaveIndex := aCanvas.SaveCanvas;
      aCanvas.IntersectClipRect(ARect);

      Font.Assign(Font);
      Fill.Assign(FFontFill);

      R := ContentRect;
      TmpRect := ARect;
      aCanvas.Font.Assign(Font);
      LPageSize := GetPageSize;


      CurLine := trunc(VScrollBarValue / GetLineHeight);
      If round(VScrollBarValue / GetLineHeight) + LPageSize - 1 < Lines.Count - 1 Then
         LEndLine := Round(round(VScrollBarValue / GetLineHeight) + LPageSize - 1)
      Else
         LEndLine := Lines.Count - 1;
      While CurLine <= LEndLine Do
      Begin
         TmpPt := GetPositionPoint(ComposeCaretPos(CurLine, 0));
         aCanvas.FillText(d2Rect(TmpPt.X - R.Left, TmpPt.Y - R.Top, $FFFF, TmpPt.Y - R.Top + (GetLineHeight * 1.25)),
            LocalRect, Lines[CurLine], False, 1, d2TextAlignNear, d2TextAlignNear);
         Inc(CurLine);
      End;

      If IsFocused Then
      Begin
         LSelArea := GetSelArea;
         If GetShowSelection Then
         Begin
            Fill.Assign(FSelectionFill);
            For CurSelRect := Low(LSelArea) To High(LSelArea) Do
            Begin
               d2IntersectRect(TmpRect, LSelArea[CurSelRect], d2Rect(0, 0, 1000, 1000));
               d2OffsetRect(TmpRect, -R.Left, -R.Top);
               FillRect(TmpRect, 0, 0, [], 1);
            End;
         End;
      End;
      aCanvas.RestoreCanvas(SaveIndex);
   End;
End;

Procedure TD2Memo.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;

   T := FindResource('content');
   If (T <> nil) And (T.IsVisual) Then
   Begin
      TD2VisualObject(T).OnPaint := DoContentPaint;
   End;

   T := FindResource('selection');
   If (T <> nil) And (T Is TD2BrushObject) Then
   Begin
      FSelectionFill.Assign(TD2BrushObject(T).Brush);
   End;

   T := FindResource('foreground');
   If (T <> nil) And (T Is TD2BrushObject) Then
      FFontFill.Assign(TD2BrushObject(T).Brush);
End;

Procedure TD2Memo.UpdateHScrlBarByCaretPos;
Var
   LEdiTD2Rect: TD2Rect;
   LCaretLinePos: Integer;
   LCaretLine: Integer;
   CurCaretX: Integer;
Begin
   If Lines.Count <= 0 Then
      Exit;
   If Canvas = nil Then
      Exit;

   LEdiTD2Rect := ContentRect;
   CurCaretX := Round(GetPositionPoint(CaretPosition).X);

   If Not ((CurCaretX < LEdiTD2Rect.Left) Or (CurCaretX > LEdiTD2Rect.Right)) Then
      Exit;

   LCaretLinePos := CaretPosition.Pos;
   LCaretLine := CaretPosition.Line;

   If FFirstVisibleChar >= (LCaretLinePos + 1) Then
   Begin
      FFirstVisibleChar := LCaretLinePos;
      If FFirstVisibleChar < 1 Then
         FFirstVisibleChar := 1;
   End Else Begin
      While (TextWidth(Copy(Lines[LCaretLine], FFirstVisibleChar, LCaretLinePos - FFirstVisibleChar + 1)) > LEdiTD2Rect.Right - LEdiTD2Rect.Left - 5) And (FFirstVisibleChar < Length(Lines[LCaretLine])) Do
         Inc(FFirstVisibleChar);
   End;
   If (HScrollBar <> nil) And (HScrollBar.Visible) Then
      HScrollBar.Value := TextWidth(Copy(Lines[LCaretLine], 1, FFirstVisibleChar - 1));
End;

Procedure TD2Memo.MouseMove(Shift: TShiftState; x, y, dx, dy: Single);
//var
//LEdiTD2Rect :TD2Rect;
Begin
   Inherited;
   FOldMPt := d2Point(x, y);

   If FLMouseSelecting Then
   Begin
 {
    LEdiTD2Rect:=ContentRect;

    if y < LEdiTD2Rect.Top then
      VScrollBar.AutoScrollUp:=true
    else
      if y > LEdiTD2Rect.Bottom then
        VScrollBar.AutoScrollDown:=true
      else begin
        VScrollBar.AutoScrollDown:=false;
        VScrollBar.AutoScrollUp:=false;
      end;
      }

      SelectAtMousePoint;
   End;
End;

Procedure TD2Memo.MouseUp(Button: TMouseButton; Shift: TShiftState; x, y: Single);
Begin
   Inherited;
   FLMouseSelecting := False;
   If SelLength = 0 Then
      FSelected := False;
End;

Procedure TD2Memo.CopyToClipboard;
Begin
   If SelText <> '' Then
      Clipbrd.Clipboard.AsText := UTF8Encode(SelText);
End;

Procedure TD2Memo.PasteFromClipboard;
Var
   WasSelection: Boolean;
   Data: THandle;
   Insertion: WideString;
Begin
   If ReadOnly Then
      Exit;
   Try

      WasSelection := SelLength > 0;
      If WasSelection Then
      Begin
         DeleteFrom(GetSelBeg, SelLength, [doMoveCaret, doCanUndo]);
         InsertAfter(GetSelBeg, UTF8Decode(ClipBoard.AsText), [ioMoveCaret, ioCanUndo, ioUndoPairedWithPriv]);
      End Else
         InsertAfter(CaretPosition, UTF8Decode(ClipBoard.AsText), [ioMoveCaret, ioCanUndo]);


      Change;
   Finally
   End;
End;

Procedure TD2Memo.CreatePopupMenu;
Var
   TmpItem: TMenuItem;
Begin
   FPopupMenu := TPopupMenu.Create(Self);

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Undo';
      OnClick := DoUndo;
   End;
   FPopupMenu.Items.Add(TmpItem);

   //  FPopupMenu.Items.NewBottomLine;

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Cut';
      OnClick := DoCut;
   End;
   FPopupMenu.Items.Add(TmpItem);

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Copy';
      OnClick := DoCopy;
   End;
   FPopupMenu.Items.Add(TmpItem);

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Paste';
      OnClick := DoPaste;
   End;
   FPopupMenu.Items.Add(TmpItem);

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Delete';
      OnClick := DoDelete;
   End;
   FPopupMenu.Items.Add(TmpItem);

   //  FPopupMenu.Items.NewBottomLine;

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Select All';
      OnClick := DoSelectAll;
   End;
   FPopupMenu.Items.Add(TmpItem);
End;

Procedure TD2Memo.DoCut(Sender: TObject);
Begin
   CutToClipboard;
End;

Procedure TD2Memo.DoCopy(Sender: TObject);
Begin
   CopyToClipboard;
End;

Procedure TD2Memo.DoDelete(Sender: TObject);
Begin
   ClearSelection;
End;

Procedure TD2Memo.DoPaste(Sender: TObject);
Begin
   PasteFromClipboard;
End;

Procedure TD2Memo.UpdatePopupMenuItems;
Var
   SelTextEmpty: Boolean;
Begin
   SelTextEmpty := SelText <> '';
   FPopupMenu.Items.Find('Undo').Enabled := FActionStack.AtLeast(1) And Not ReadOnly;
   FPopupMenu.Items.Find('Cut').Enabled := SelTextEmpty And Not ReadOnly;
   FPopupMenu.Items.Find('Copy').Enabled := SelTextEmpty;
   FPopupMenu.Items.Find('Paste').Enabled := (ClipBoard.AsText <> '') And Not ReadOnly;
   FPopupMenu.Items.Find('Delete').Enabled := SelTextEmpty And Not ReadOnly;
   FPopupMenu.Items.Find('Select All').Enabled := SelText <> TextW;
End;

Function TD2Memo.GetNextWordBeging(StartPosition: TCaretPosition): TCaretPosition;
Var
   SpaceFound, WordFound: Boolean;
   LLineText: WideString;
   CurPos: Integer;
   CurLine: Integer;
Begin
   CurPos := StartPosition.Pos;
   CurLine := StartPosition.Line;

   If StartPosition.Pos < Length(GetLine(StartPosition.Line)) Then
   Begin
      LLineText := GetLine(StartPosition.Line);

      SpaceFound := False;
      WordFound := False;
      While (CurPos + 2 <= Length(LLineText)) And ((Not ((LLineText[CurPos + 1] <> ' ') And SpaceFound)) Or Not WordFound) Do
      Begin
         If LLineText[CurPos + 1] = ' ' Then
            SpaceFound := True;
         If LLineText[CurPos + 1] <> ' ' Then
         Begin
            WordFound := True;
            SpaceFound := False;
         End;

         CurPos := CurPos + 1;
      End;
      If Not SpaceFound Then
         CurPos := CurPos + 1;
   End Else
   If StartPosition.Line < Lines.Count - 1 Then
   Begin
      CurLine := CurLine + 1;
      CurPos := 0;
   End;

   With Result Do
   Begin
      Line := CurLine;
      Pos := CurPos;
   End;
End;

Function TD2Memo.GetPrivWordBeging(StartPosition: TCaretPosition): TCaretPosition;
Var
   WordFound: Boolean;
   LLineText: WideString;
   CurPos: Integer;
   CurLine: Integer;
Begin
   Result := StartPosition;

   CurPos := StartPosition.Pos;
   CurLine := StartPosition.Line;

   If StartPosition.Pos > 0 Then
   Begin
      LLineText := GetLine(StartPosition.Line);

      WordFound := False;
      While (CurPos > 0) And ((LLineText[CurPos] <> ' ') Or Not WordFound) Do
      Begin
         If LLineText[CurPos] <> ' ' Then
            WordFound := True;
         CurPos := CurPos - 1;
      End;
   End Else
   If (StartPosition.Line - 1 >= 0) And (StartPosition.Line - 1 <= Lines.Count - 1) Then
   Begin
      CurLine := CurLine - 1;
      CurPos := Length(GetLine(CurLine));
   End;

   With Result Do
   Begin
      Line := CurLine;
      Pos := CurPos;
   End;
End;


Procedure TD2Memo.ClearSelection;
Begin
   If Not ReadOnly Then
      DeleteFrom(GetSelBeg, SelLength, [doMoveCaret, doCanUndo]);
End;

Procedure TD2Memo.CutToClipboard;
Begin
   CopyToClipboard;
   ClearSelection;
End;

Procedure TD2Memo.SelectAll;
Begin
   FSelStart := TextPosToPos(Length(FText));
   FSelEnd := ComposeCaretPos(0, 0);
   FSelected := True;
   GoToTextEnd;
   Repaint;
End;

Procedure TD2Memo.DoSelectAll(Sender: TObject);
Begin
   SelectAll;
End;

Procedure TD2Memo.DrawPasswordChar(SymbolRect: TD2Rect; Selected: Boolean);
Var
   LRect: TD2Rect;
Begin
   d2IntersectRect(LRect, SymbolRect, ContentRect);
   Canvas.Font.Assign(Font);
End;

Function TD2Memo.CanAutoSize(Var NewWidth, NewHeight: Integer): Boolean;
Begin
   Result := True;
   NewHeight := Round(GetLineHeight + ContentRect.Top * 2);
End;

Procedure TD2Memo.SelectWord;
Begin
   FSelStart := GetPrivWordBeging(CaretPosition);
   FSelEnd := GetNextWordBeging(CaretPosition);
   FSelected := True;
   Repaint;
End;

Procedure TD2Memo.Change;
Begin
   If FNeedChange Then
   Begin
      If Assigned(FBindingObjects) Then
         ToBindingObjects;
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Procedure TD2Memo.ContextMenu(Const ScreenPosition: TD2Point);
Begin
   Inherited;
   If csDesigning In ComponentState Then
      Exit;

   UpdatePopupMenuItems;
   FPopupMenu.PopupComponent := Self;
   FPopupMenu.Popup(round(ScreenPosition.X), round(ScreenPosition.Y));
End;

Procedure TD2Memo.FontChanged(Sender: TObject);
Begin
   Inherited;
   If Not (csLoading In ComponentState) Then
   Begin
      UpdateLines;
      UpdateCaretPosition(False);
      If Not FUpdatingBool Then
         Realign;
   End;
End;

Procedure TD2Memo.SetTextW(Const Value: WideString);
Begin
   If Not ValidText(Value) Then
      Exit;

   If Value <> FText Then
   Begin
      If (Value <> '') And (CharCase <> d2ecNormal) Then
         Case CharCase Of

            d2ecUpperCase: FText := WideUpperCase(Value);
            d2ecLowerCase: FText := WideUpperCase(Value);
         End Else
         FText := Value;

      If FInternalMustUpdateLines Then
      Begin
         UpdateLines;
         If Not FUpdatingBool Then
            Realign;
      End;

      If Not (csLoading In ComponentState) And Assigned(OnChangeTracking) Then
         OnChangeTracking(Self);
      FNeedChange := True;
   End;
End;

Function TD2Memo.GetTextW: WideString;
Begin
   Result := Ftext;
End;

Procedure TD2Memo.SetText(Const Value: String);
Var
   ss: WideString;
Begin
   ss := UTF8Decode(Value);
   SetTextW(ss);
End;

Function TD2Memo.GetText: String;
Begin
   Result := UTF8Encode(Ftext);
End;

Procedure TD2Memo.SetCaretPosition(Const Value: TCaretPosition);
Begin
   If Value.Line > Lines.Count - 1 Then
      FCaretPosition.Line := Lines.Count - 1
   Else
      FCaretPosition.Line := Value.Line;

   If FCaretPosition.Line < 0 Then
      FCaretPosition.Line := 0;

   If Value.Pos < 0 Then
      FCaretPosition.Pos := 0
   Else
   If Value.Pos > Length(Lines[FCaretPosition.Line]) Then
      FCaretPosition.Pos := Length(Lines[FCaretPosition.Line])
   Else
      FCaretPosition.Pos := Value.Pos;

   UpdateCaretPosition(True);
End;

Procedure TD2Memo.SetSelLength(Const Value: Integer);
Begin
   FSelEnd := TextPosToPos(PosToTextPos(FSelStart) + Value);
End;

Procedure TD2Memo.SetSelStart(Const Value: Integer);
Begin
   FSelStart := TextPosToPos(Value);
End;

Procedure TD2Memo.SetAutoSelect(Const Value: Boolean);
Begin
   If FAutoSelect <> Value Then
      FAutoSelect := Value;
End;

Function TD2Memo.GetSelStart: Integer;
Begin
   If FSelected Then
      Result := PosToTextPos(GetSelBeg)
   Else
      Result := PosToTextPos(CaretPosition);
End;

Function TD2Memo.GetSelArea: TSelArea;
Var
   BegLine, EndLine, CurLine: Integer;
   LPageSize: Single;
   SelBegLineVisible, SelEndLineVisible: Boolean;
Begin
   If Not FSelected Then
   Begin
      SetLength(Result, 0);
      Exit;
   End;

   SelBegLineVisible := True;
   SelEndLineVisible := True;

   BegLine := GetSelBeg.Line;

   If BegLine < trunc(VScrollBarValue / GetLineHeight) Then
   Begin
      BegLine := trunc(VScrollBarValue / GetLineHeight);
      SelBegLineVisible := False;
   End;

   EndLine := GetSelEnd.Line;
   LPageSize := GetPageSize;

   If EndLine > round(VScrollBarValue / GetLineHeight) + LPageSize - 1 Then
   Begin
      EndLine := Round(round(VScrollBarValue / GetLineHeight) + LPageSize - 1);
      SelEndLineVisible := False;
   End;

   If EndLine < BegLine Then
      EndLine := BegLine;

   SetLength(Result, EndLine - BegLine + 1);

   CurLine := BegLine;
   While (CurLine <= EndLine) And (CurLine < Lines.Count) Do
   Begin
      With Result[CurLine - BegLine] Do
      Begin
         Left := GetPositionPoint(ComposeCaretPos(CurLine, 0)).x;
         Right := GetPositionPoint(ComposeCaretPos(CurLine, Length(Lines[CurLine]))).x;
         Top := GetPositionPoint(ComposeCaretPos(CurLine, 0)).y;
         Bottom := GetPositionPoint(ComposeCaretPos(CurLine, 0)).y + GetLineHeight;
      End;
      Inc(CurLine);
   End;

   If EndLine - BegLine >= 0 Then
   Begin
      If SelBegLineVisible Then
         Result[0].Left := GetPositionPoint(ComposeCaretPos(BegLine, GetSelBeg.Pos)).x;
      If SelEndLineVisible Then
         Result[EndLine - BegLine].Right := GetPositionPoint(ComposeCaretPos(EndLine, GetSelEnd.Pos)).x;
   End;
End;

Function TD2Memo.GetSelLength: Integer;
Begin
   If FSelected Then
      Result := PosToTextPos(GetSelEnd) - PosToTextPos(GetSelBeg)
   Else
      Result := 0;
End;

Function TD2Memo.GetSelText: WideString;
Var
   LSelStart, LSelLength: Integer;
Begin
   If FSelected Then
   Begin
      LSelStart := SelStart;
      LSelLength := SelLength;
      Result := Copy(TextW, LSelStart + 1, LSelLength);
   End Else
      Result := '';
End;

Procedure TD2Memo.SetCharCase(Const Value: TEditCharCase);
Var
   TmpS: WideString;
Begin
   If FCharCase <> Value Then
   Begin
      FCharCase := Value;
      If TextW <> '' Then
      Begin
         TmpS := TextW;
{      case Value of
        vgecUpperCase:TextW:=WideString(CharUpper(PChar(TmpS)));
        vgecLowerCase:TextW:=WideString(CharLower(PChar(TmpS)));
      end;}
      End;
   End;
End;

Procedure TD2Memo.SetHideSelection(Const Value: Boolean);
Begin
   If FHideSelection <> Value Then
   Begin
      FHideSelection := Value;
      Repaint;
   End;
End;

Procedure TD2Memo.SetMaxLength(Const Value: Integer);
Begin
   If FMaxLength <> Value Then
   Begin
      FMaxLength := Value;
   End;
End;

Function TD2Memo.ValidText(NewText: WideString): Boolean;
Begin
   Result := True;
End;

Procedure TD2Memo.SetTextAlignment(Const Value: TAlignment);
Begin
   If FTextAlignment <> Value Then
   Begin
      FTextAlignment := Value;
      Repaint;
   End;
End;

Procedure TD2Memo.UpdateCaretPosition(UpdateScrllBars: Boolean);
Var
   TmpPt: TD2Point;
   TmpRect: TD2Rect;
Begin
   If UpdateScrllBars Then
   Begin
      UpdateVScrlBarByCaretPos;
      UpdateHScrlBarByCaretPos;
   End;
   TmpRect := ContentRect;
   TmpPt := GetPositionPoint(CaretPosition);
   TmpPt.X := TmpPt.X + HScrollBarValue - TmpRect.Left;
   TmpPt.Y := TmpPt.Y + VScrollBarValue - TmpRect.Top;
   SetCaretSize(d2Point(2, GetLineHeight));
   SetCaretPos(TmpPt);
   SetCaretColor(FFontFill.SolidColor);
End;

Function TD2Memo.GetLineRealEnd(AStartPos: TCaretPosition; PText: PWideString): TCaretPosition;
Begin
   Result.Line := AStartPos.Line;
   While (Result.Line + 1 <= Lines.Count - 1) And (GetLineInternal(Result.Line) = GetLine(Result.Line)) Do
      Result.Line := Result.Line + 1;

   If (Result.Line <= Lines.Count - 1) And (Lines.Count > 0) Then
   Begin
      Result.Pos := Length(GetLine(Result.Line)) + FLinesBegs[Result.Line] - 1;
   End Else
      Result.Pos := 0;
End;

Function TD2Memo.FillLocalLinesBegs(PText: PWideString; ABegChar, AEndChar: Integer; TmpLinesBegs: PLinesBegs): Integer;
Var
   WStartChar, WSaveChar, WCurChar: Integer;
   LCurChar, LSaveChar: Integer;
   TmpS: WideString;
   TmpSWidth, WWidth: Single;
   LTmpWidth: Single;
   LEditRectWidth: Single;
   Tok: WideString;
   LLongestLineWidth: Single;
   CurLineIsEmpty: Boolean;
   LWidth: Single;
   i: Integer;
   LLocalWidesLineWidth: Integer;
Begin
   Result := 0;
   SetLength(TmpLinesBegs^, 0);

   If PText^ = '' Then
      Exit;

   LCurChar := ABegChar;
   TmpS := '';
   LTmpWidth := 0;
   CurLineIsEmpty := True;

   With ContentRect Do
      LEditRectWidth := Right - Left;

   Result := -1;
   LLocalWidesLineWidth := -1;
   While LCurChar <= AEndChar Do
   Begin
      If (PText^[LCurChar] = #13) Or (PText^[LCurChar] = #10) Then
      Begin
         LSaveChar := LCurChar - 1;
         If (PText^[LCurChar] = #13) And (LCurChar + 1 <= Length(PText^)) Then
            If PText^[LCurChar + 1] = #10 Then
               Inc(LCurChar);

         TmpSWidth := TextWidth(TmpS);
         If LLongestLineWidth < TmpSWidth Then
         Begin
            LLongestLineWidth := TmpSWidth;
            Result := Length(TmpLinesBegs^) - 1;
         End;

         If FWordWrap And (TextWidth(TmpS) > LEditRectWidth) Then
         Begin
            WCurChar := 1;
            WStartChar := 1;
            WSaveChar := 1;
            Tok := d2WideGetToken(WCurChar, TmpS, ' ,-');
            While Tok <> '' Do
            Begin
               WWidth := TextWidth(Copy(TmpS, WStartChar, WCurChar - WStartChar));
               If WWidth > LEditRectWidth Then
               Begin
                  WStartChar := WSaveChar;
                  SetLength(TmpLinesBegs^, Length(TmpLinesBegs^) + 1);
                  TmpLinesBegs^[Length(TmpLinesBegs^) - 1] := LCurChar - Length(TmpS) + WSaveChar - 2;
               End;
               WSaveChar := WCurChar;
               Tok := d2WideGetToken(WCurChar, TmpS, ' ,-');
               If WSaveChar = WCurChar Then
                  Break; {!!! - error }
            End;
         End;

         SetLength(TmpLinesBegs^, Length(TmpLinesBegs^) + 1);
         TmpLinesBegs^[Length(TmpLinesBegs^) - 1] := LCurChar + 1;

         TmpS := '';
         LTmpWidth := 0;
         CurLineIsEmpty := True;
      End Else Begin
         CurLineIsEmpty := False;
         TmpS := TmpS + PText^[LCurChar];
      End;
      Inc(LCurChar);
   End;
   If FWordWrap And (TextWidth(TmpS) > LEditRectWidth) Then
   Begin
      WCurChar := 1;
      WStartChar := 1;
      WSaveChar := 1;
      Tok := d2WideGetToken(WCurChar, TmpS, ' ,-');
      While Tok <> '' Do
      Begin
         WWidth := TextWidth(Copy(TmpS, WStartChar, WCurChar - WStartChar));
         If WWidth > LEditRectWidth Then
         Begin
            WStartChar := WSaveChar;
            SetLength(TmpLinesBegs^, Length(TmpLinesBegs^) + 1);
            TmpLinesBegs^[Length(TmpLinesBegs^) - 1] := LCurChar - Length(TmpS) + WSaveChar - 1;
         End;
         WSaveChar := WCurChar;
         Tok := d2WideGetToken(WCurChar, TmpS, ' ,-');
         If WSaveChar = WCurChar Then
            Break; {!!! - error }
      End;
   End Else Begin
      LWidth := Canvas.TextWidth(TmpS);
      If LWidth > LLocalWidesLineWidth Then
         Result := Length(TmpLinesBegs^) - 1;
   End;
   If Length(TmpLinesBegs^) = 0 Then
      Result := 0;
End;

Procedure TD2Memo.UpdateLines;
Const
   Sep: WideString = ' ,-.;:'#65292;
Var
   WStartChar, WSaveChar, WCurChar: Integer;
   LCurChar, LSaveChar: Integer;
   TmpS: WideString;
   TmpSWidth, WWidth: Single;
   LTmpWidth: Single;
   LEditRectWidth: Single;
   Tok, LText: WideString;
   LLongestLineWidth: Single;
   CurLineIsEmpty: Boolean;
   i: Integer;
Begin
   FWidesLineIndex := 0;
   SetLength(FLinesBegs, 0);
   If TextW = '' Then
      Exit;

   SetLength(FLinesBegs, 0);
   SetLength(FLinesBegs, 1);
   With ContentRect Do
      LEditRectWidth := Right - Left;
   If Canvas = nil Then
      Exit;


   LText := TextW;
   TmpS := '';
   LCurChar := 1;
   LTmpWidth := 0;
   CurLineIsEmpty := True;
   FLinesBegs[0] := 1;
   LLongestLineWidth := 0;
   While LCurChar <= Length(LText) Do
   Begin
      If (LText[LCurChar] = #13) Or (LText[LCurChar] = #10) Then
      Begin
         LSaveChar := LCurChar - 1;
         If (LText[LCurChar] = #13) And (LCurChar + 1 <= Length(LText)) Then
            If LText[LCurChar + 1] = #10 Then
               Inc(LCurChar);

         TmpSWidth := TextWidth(TmpS);
         If LLongestLineWidth < TmpSWidth Then
         Begin
            LLongestLineWidth := TmpSWidth;
            FWidesLineIndex := Length(FLinesBegs) - 1;
         End;

         If FWordWrap And (TextWidth(TmpS) > LEditRectWidth) Then
         Begin
            WCurChar := 1;
            WStartChar := 1;
            WSaveChar := 1;
            Tok := d2WideGetToken(WCurChar, TmpS, Sep);
            While Tok <> '' Do
            Begin
               WWidth := TextWidth(Copy(TmpS, WStartChar, WCurChar - WStartChar));
               If WWidth > LEditRectWidth Then
               Begin
                  WStartChar := WSaveChar;
                  SetLength(FLinesBegs, Length(FLinesBegs) + 1);
                  FLinesBegs[Length(FLinesBegs) - 1] := LCurChar - Length(TmpS) + WSaveChar - 2;
               End;
               WSaveChar := WCurChar;
               Tok := d2WideGetToken(WCurChar, TmpS, Sep);
               If WSaveChar = WCurChar Then
                  Break;
            End;
         End;

         SetLength(FLinesBegs, Length(FLinesBegs) + 1);
         FLinesBegs[Length(FLinesBegs) - 1] := LCurChar + 1;

         TmpS := '';
         LTmpWidth := 0;
         CurLineIsEmpty := True;
      End Else Begin
         CurLineIsEmpty := False;
         TmpS := TmpS + LText[LCurChar];
      End;
      Inc(LCurChar);
   End;

   If FWordWrap And (TextWidth(TmpS) > LEditRectWidth) Then
   Begin
      WCurChar := 1;
      WStartChar := 1;
      WSaveChar := 1;
      Tok := d2WideGetToken(WCurChar, TmpS, Sep);
      While Tok <> '' Do
      Begin
         WWidth := TextWidth(Copy(TmpS, WStartChar, WCurChar - WStartChar));
         If WWidth > LEditRectWidth Then
         Begin
            WStartChar := WSaveChar;
            SetLength(FLinesBegs, Length(FLinesBegs) + 1);
            FLinesBegs[Length(FLinesBegs) - 1] := LCurChar - Length(TmpS) + WSaveChar - 1;
         End;
         WSaveChar := WCurChar;
         Tok := d2WideGetToken(WCurChar, TmpS, Sep);
         If WSaveChar = WCurChar Then
            Break;
      End;
   End Else
   If LLongestLineWidth < Canvas.TextWidth(TmpS) Then
      FWidesLineIndex := Length(FLinesBegs) - 1;
End;

Procedure TD2Memo.UpdateRngLinesBegs(PText: PWideString; AUpdBegLine, AUpdEndLine, AUpdBegChar, AUpdEndChar, ACharDelta, AOldWideslLineWidth: Integer);
Var
   LUpdEndChar, LNewWidesLineIndex, LLineDelta, i: Integer;
   LTmpLinesBegs: TLinesBegs;
Begin
   If (Length(FLinesBegs) = 0) And (PText^ <> '') Then
   Begin
      SetLength(FLinesBegs, 1);
      FLinesBegs[0] := 1;
   End;

   LUpdEndChar := AUpdEndChar + ACharDelta;
   LNewWidesLineIndex := FillLocalLinesBegs(PText, AUpdBegChar, LUpdEndChar, @LTmpLinesBegs) + AUpdBegLine;

   LLineDelta := Length(LTmpLinesBegs) - (AUpdEndLine - AUpdBegLine);

   If LLineDelta > 0 Then
   Begin
      SetLength(FLinesBegs, Length(FLinesBegs) + LLineDelta);
      For i := Length(FLinesBegs) - 1 Downto AUpdEndLine + 1 + LLineDelta Do
         FLinesBegs[i] := FLinesBegs[i - LLineDelta] + ACharDelta;
   End Else Begin
      For i := AUpdBegLine + 1 To Length(FLinesBegs) - 1 + LLineDelta Do
         FLinesBegs[i] := FLinesBegs[i - LLineDelta] + ACharDelta;
      SetLength(FLinesBegs, Length(FLinesBegs) + LLineDelta);
   End;

   For i := 0 To Length(LTmpLinesBegs) - 1 Do
      If AUpdBegLine + i + 1 <= Length(FLinesBegs) - 1 Then
         FLinesBegs[AUpdBegLine + i + 1] := LTmpLinesBegs[i];

   If FWidesLineIndex > Length(FLinesBegs) - 1 Then
      FWidesLineIndex := Round(GetWidestLine)
   Else
   If LineWidth[LNewWidesLineIndex] >= AOldWideslLineWidth Then
      FWidesLineIndex := LNewWidesLineIndex
   Else
   If Not ((FWidesLineIndex < AUpdBegLine) Or (FWidesLineIndex > AUpdEndLine)) Then
      FWidesLineIndex := GetWidestLine;

   If Not FUpdatingBool Then
      Realign;
End;

Procedure TD2Memo.InsertAfter(Const aPosition: TCaretPosition; S: WideString; Options: TInsertOptions);
Var
   LText: WideString;
   Insertion: WideString;
   LUpdBegLine, LUpdBegChar, LUpdEndLine, LUpdEndChar: Integer;
   R: Integer;
   LInsertionLength: Integer;
   LOldWideslLineWidth: Single;
Begin
   R := PosToTextPos(CaretPosition);
   LText := TextW;
   Insertion := S;
   If MaxLength > 0 Then
      Insertion := Copy(Insertion, 1, MaxLength - Length(LText));

   If ioCanUndo In Options Then
      FActionStack.FragmentInserted(PosToTextPos(aPosition), Length(S), ioUnDoPairedWithPriv In Options);

   LUpdBegLine := aPosition.Line;
   If (Length(FLinesBegs) > 0) And (aPosition.Line <= Length(FLinesBegs) - 1) Then
      LUpdBegChar := FLinesBegs[aPosition.Line]
   Else
      LUpdBegChar := 1;

   With GetLineRealEnd(aPosition, @LText) Do
   Begin
      LUpdEndLine := Line;
      LUpdEndChar := Pos;
   End;

   LInsertionLength := Length(Insertion);
   LOldWideslLineWidth := LineWidth[FWidesLineIndex];

   Insert(Insertion, LText, PosToTextPos(aPosition) + 1);
   Try
      FInternalMustUpdateLines := False;
      TextW := LText;
   Finally
      FInternalMustUpdateLines := True;
   End;

   UpdateRngLinesBegs(@LText, LUpdBegLine, LUpdEndLine,
      LUpdBegChar, LUpdEndChar, LInsertionLength, Round(LOldWideslLineWidth));

   If ioSelected In Options Then
   Begin
      FSelStart := aPosition;
      FSelEnd := GetPositionShift(aPosition, Length(Insertion));
      FSelected := True;
      CaretPosition := FSelEnd;
   End Else Begin
      If Not (csLoading In ComponentState) Then
      Begin
         CaretPosition := TextPosToPos(R + Length(Insertion));
         UpdateCaretPosition(False);
      End;
   End;

   If Not FUpdatingBool Then
      Realign;
End;

Procedure TD2Memo.DeleteFrom(Const aPosition: TCaretPosition; ALength: Integer; Options: TDeleteOptions);
Var
   LUpdBegLine, LUpdEndLine, LUpdBegChar, LUpdEndChar: Integer;
   LText: WideString;
   LTmpPos, LTmpLength: Integer;
   LOldWideslLineWidth: Integer;
Begin
   LText := TextW;

   LTmpLength := ALength;
   LTmpPos := PosToTextPos(aPosition) + 1;

   If (LTmpPos + ALength - 1 + 1 <= System.Length(LText)) And (LTmpPos + ALength - 1 >= 1) And (LText[LTmpPos + ALength - 1] = #13) And (LText[LTmpPos + ALength - 1 + 1] = #10) Then
      LTmpLength := LTmpLength + 1;

   If (LTmpPos - 1 >= 0) And (LTmpPos <= System.Length(LText)) And (LText[LTmpPos] = #10) And (LText[LTmpPos - 1] = #13) Then
   Begin
      LTmpLength := LTmpLength + 1;
      LTmpPos := LTmpPos - 1;
   End;

   If (doCanUndo In Options) And (LTmpLength > 0) Then
      FActionStack.FragmentDeleted(LTmpPos, Copy(LText, LTmpPos, LTmpLength));

   LUpdBegLine := aPosition.Line;
   If aPosition.Line <= Length(FLinesBegs) - 1 Then
      LUpdBegChar := FLinesBegs[aPosition.Line]
   Else
      LUpdBegChar := 1;

   With GetLineRealEnd(GetPositionShift(aPosition, LTmpLength - 1), @LText) Do
   Begin
      LUpdEndLine := Line;
      LUpdEndChar := Pos;
   End;

   LOldWideslLineWidth := Round(LineWidth[FWidesLineIndex]);

   Delete(LText, LTmpPos, LTmpLength);

   Try
      FInternalMustUpdateLines := False;
      TextW := LText;
   Finally
      FInternalMustUpdateLines := True;
   End;

   UpdateRngLinesBegs(@LText, LUpdBegLine, LUpdEndLine,
      LUpdBegChar, LUpdEndChar, -LTmpLength, LOldWideslLineWidth);

   If (doMoveCaret In Options) Or (SelLength <> 0) Then
   Begin
      FSelected := False;
      CaretPosition := aPosition;
   End;

   If Not FUpdatingBool Then
      Realign;
End;

Procedure TD2Memo.DoUndo(Sender: TObject);
Begin
   UnDo;
End;

Procedure TD2Memo.UnDo;
Begin
   FActionStack.RollBackAction;
End;

Function TD2Memo.GetContentBounds: TD2Rect;
Begin
   Result := Inherited GetContentBounds;
   If FWordWrap Then
   Begin
      StorePositions;
      UpdateLines;
      RestorePositions;
   End;
   If Lines.Count > 0 Then
      Result.Bottom := Result.Top + (Lines.Count * GetLineHeight);

   If Not FWordWrap And (TextWidth(Lines[FWidesLineIndex]) > (Result.Right - Result.Left)) Then
      Result.Right := Result.Left + TextWidth(Lines[FWidesLineIndex]) + 10;
   UpdateHScrlBarByCaretPos;
End;

Procedure TD2Memo.SetLines(Const Value: TD2WideStrings);
Begin
   FLines.Assign(Value);
End;

Function TD2Memo.TextPosToPos(APos: Integer): TCaretPosition;
Var
   CurRangeBeg, CurRangeEnd: Integer;
   TmpI: Integer;
Begin
   With Result Do
   Begin
      Line := 0;
      Pos := 0;
   End;

   If Lines.Count <= 0 Then
      Exit;

   CurRangeBeg := 0;
   CurRangeEnd := Length(FLinesBegs) - 1;
   Repeat
      If ((CurRangeBeg < Length(FLinesBegs) - 1) And (APos + 1 >= FLinesBegs[CurRangeBeg]) And (APos + 1 < FLinesBegs[CurRangeBeg + 1])) Or ((CurRangeBeg = Length(FLinesBegs) - 1) And (APos + 1 >= FLinesBegs[CurRangeBeg])) Then
         CurRangeEnd := CurRangeBeg
      Else Begin
         If APos + 1 < FLinesBegs[CurRangeBeg] Then
         Begin
            TmpI := CurRangeEnd - CurRangeBeg + 1;
            CurRangeEnd := CurRangeBeg;
            CurRangeBeg := CurRangeBeg - TmpI Div 2;
         End Else
         If APos + 1 >= FLinesBegs[CurRangeEnd] Then
         Begin
            TmpI := CurRangeEnd - CurRangeBeg + 1;
            CurRangeBeg := CurRangeEnd;
            CurRangeEnd := CurRangeEnd + TmpI Div 2;
         End Else
            CurRangeEnd := (CurRangeBeg + CurRangeEnd) Div 2;

         If CurRangeBeg < 0 Then
            CurRangeBeg := 0;

         If CurRangeEnd < 0 Then
            CurRangeEnd := 0;

         If CurRangeEnd > Length(FLinesBegs) - 1 Then
            CurRangeEnd := Length(FLinesBegs) - 1;

         If CurRangeBeg > Length(FLinesBegs) - 1 Then
            CurRangeBeg := Length(FLinesBegs) - 1;
      End;

   Until CurRangeBeg = CurRangeEnd;
   Result.Line := CurRangeBeg;

   If Result.Line <= Length(FLinesBegs) - 1 Then
      Result.Pos := APos - FLinesBegs[Result.Line] + 1;
End;

Procedure TD2Memo.MoveCaretLeft;
Begin
   MoveCareteBy(-1);
End;

Procedure TD2Memo.MoveCaretRight;
Begin
   MoveCareteBy(1);
End;

Procedure TD2Memo.MoveCareteBy(Delta: Integer);
Begin
   CaretPosition := GetPositionShift(CaretPosition, Delta);
End;

Procedure TD2Memo.VScrollChange(Sender: TObject);
Begin
   Inherited;
   UpdateCaretPosition(False);
End;

Function TD2Memo.GetLineHeight: Single;
Begin
   Result := round(FFont.Size * (1.25));
End;

Procedure TD2Memo.HScrlBarChange(Sender: TObject);
Begin
   Repaint;
   UpdateCaretPosition(False);
End;

Procedure TD2Memo.UpdateVScrlBarByCaretPos;
Var
   LCaretPosLine: Integer;
   LPageSize: Single;
Begin
   LCaretPosLine := CaretPosition.Line;
   LPageSize := GetPageSize;

   If (VScrollBar <> nil) And (LCaretPosLine * GetLineHeight < VScrollBarValue) Then
      VScrollBar.Value := LCaretPosLine * GetLineHeight;

   If (VScrollBar <> nil) And ((LCaretPosLine + 1) * GetLineHeight > VScrollBarValue + ContentLayout.Height) Then
      VScrollBar.Value := (LCaretPosLine - LPageSize + 1) * GetLineHeight;
End;

Procedure TD2Memo.SetWordWrap(Const Value: Boolean);
Begin
   If FWordWrap <> Value Then
   Begin
      FWordWrap := Value;
      UpdateLines;
      If Not FUpdatingBool Then
         Realign;
      UpdateCaretPosition(False);
   End;
End;

Procedure TD2Memo.GetLineBounds(LineIndex: Integer; Var LineBeg, LineLength: Integer);
Begin
   If Length(FLinesBegs) = 0 Then
   Begin
      LineBeg := 1;
      LineLength := 0;
      Exit;
   End;

   If (LineIndex <= Length(FLinesBegs) - 1) And (LineIndex >= 0) Then
   Begin
      LineBeg := FLinesBegs[LineIndex];
      If (LineIndex + 1 < Length(FLinesBegs)) Then
         LineLength := FLinesBegs[LineIndex + 1] - LineBeg
      Else
         LineLength := Length(TextW) - LineBeg + 1;
   End Else Begin
      LineBeg := 0;
      LineLength := 0;
   End;
End;

Function TD2Memo.GetLineCount: Integer;
Begin
   If TextW <> '' Then
      Result := Length(FLinesBegs)
   Else
      Result := 0;
End;

Function TD2Memo.GetLine(Const aIndex: Integer): WideString;
Begin
   Result := GetLineInternal(aIndex);
   If Length(Result) > 0 Then
      If Result[Length(Result)] = #10 Then
         Delete(Result, Length(Result), 1);
   If Length(Result) > 0 Then
      If Result[Length(Result)] = #13 Then
         Delete(Result, Length(Result), 1);
End;

Procedure TD2Memo.InsertLine(Const aIndex: Integer; Const S: WideString);
Begin
   If aIndex < GetLineCount Then
      InsertAfter(ComposeCaretPos(aIndex, 0), S + #13 + #10, [])
   Else
   If (aIndex > 0) And (GetLineCount > 0) Then
   Begin
      InsertAfter(ComposeCaretPos(aIndex - 1, Length(GetLineInternal(aIndex - 1))), #13 + #10 + S, []);
   End Else
      InsertAfter(ComposeCaretPos(aIndex, 0), S, []);
End;

Procedure TD2Memo.DeleteLine(Const aIndex: Integer);
Begin
   If aIndex = GetLineCount - 1 Then
      DeleteFrom(ComposeCaretPos(aIndex - 1, Length(GetLineInternal(aIndex - 1))){LLineBeg-1}, Length(GetLineInternal(aIndex)) + 1, [])
   Else
      DeleteFrom(ComposeCaretPos(aIndex, 0){LLineBeg}, Length(GetLineInternal(aIndex)) + 1, []);
End;

Procedure TD2Memo.ClearLines;
Begin
   TextW := '';
End;

Procedure TD2Memo.SelectAtMousePoint;
Var
   TmpPt: TD2Point;
   LEdiTD2Rect: TD2Rect;
Begin
   LEdiTD2Rect := ContentRect;
   TmpPt := FOldMPt;
   With TmpPt, LEdiTD2Rect Do
   Begin
      If y < Top Then
         y := Top
      Else
      If y > Bottom Then
         y := Bottom;

      If x < Left Then
         x := Left
      Else
      If x > Right Then
         x := Right;
   End;

   CaretPosition := GetPointPosition(TmpPt);
   SelectAtPos(CaretPosition);
   Repaint;
End;

Function TD2Memo.GetPageSize: Single;
Begin
   With ContentRect Do
      Result := (Bottom - Top) / GetLineHeight;
End;

Function TD2Memo.GetLineWidth(LineNum: Integer): Single;
Begin
   If (LineNum >= 0) And (LineNum <= Lines.Count - 1) Then
   Begin
      Result := Canvas.TextWidth(Lines[LineNum]);
   End Else
      Result := 0;
End;

Function TD2Memo.PosToTextPos(APostion: TCaretPosition): Integer;
Var
   LTmpLine: Integer;
Begin
   Result := 0;
   If TextW = '' Then
      Exit;

   With APostion Do
   Begin
      If Line <= Length(FLinesBegs) - 1 Then
         LTmpLine := Line
      Else
         LTmpLine := Length(FLinesBegs) - 1;

      If LTmpLine < 0 Then
         exit;

      Result := FLinesBegs[LTmpLine];

      If Pos <= Length(GetLineInternal(LTmpLine)) Then
         Result := Result + Pos - 1
      Else
         Result := Result + Length(GetLineInternal(LTmpLine)) - 1;
   End;
End;

Function TD2Memo.GetLineInternal(Const aIndex: Integer): WideString;
Var
   LLineBeg, LLineLength: Integer;
Begin
   GetLineBounds(aIndex, LLineBeg, LLineLength);
   Result := Copy(TextW, LLineBeg, LLineLength);
End;

Procedure TD2Memo.GoToTextBegin;
Begin
   With FCaretPosition Do
   Begin
      Line := 0;
      Pos := 0;
   End;
End;

Procedure TD2Memo.GoToTextEnd;
Begin
   With FCaretPosition Do
   Begin
      Line := Lines.Count - 1;
      If Line >= 0 Then
         Pos := Length(Lines[Line])
      Else
         Pos := 0;
   End;
End;

Procedure TD2Memo.GoToLineEnd;
Begin
   With FCaretPosition Do
   Begin
      If Line <= Lines.Count - 1 Then
         Pos := Length(GetLine(CaretPosition.Line));
   End;
End;

Procedure TD2Memo.GoToLineBegin;
Begin
   With FCaretPosition Do
   Begin
      Pos := 0;
   End;
End;

Function TD2Memo.GetSelBeg: TCaretPosition;
Begin
   If FSelStart.Line < FSelEnd.Line Then
      Result := FSelStart
   Else
   If FSelEnd.Line < FSelStart.Line Then
      Result := FSelEnd
   Else
   If FSelStart.Pos < FSelEnd.Pos Then
      Result := FSelStart
   Else
      Result := FSelEnd;
End;

Function TD2Memo.GetSelEnd: TCaretPosition;
Begin
   If FSelStart.Line > FSelEnd.Line Then
      Result := FSelStart
   Else
   If FSelEnd.Line > FSelStart.Line Then
      Result := FSelEnd
   Else
   If FSelStart.Pos > FSelEnd.Pos Then
      Result := FSelStart
   Else
      Result := FSelEnd;
End;

Procedure TD2Memo.SelectAtPos(APos: TCaretPosition);
Begin
   If Not FSelected Then
   Begin
      FSelStart := APos;
      FSelEnd := APos;
      FSelected := True;
   End Else Begin
      FSelEnd := APos;
   End;
End;

Function TD2Memo.GetPositionShift(APos: TCaretPosition; Delta: Integer): TCaretPosition;
Var
   LNewPos: TCaretPosition;
   LNewTextPos: Integer;
   i: Integer;
   CurLineText: WideString;
Begin
   LNewPos := APos;
   With LNewPos Do
      If Delta >= 14 Then
      Begin
         LNewTextPos := PosToTextPos(CaretPosition) + Delta;

         If Delta > 0 Then
         Begin
            If (LNewTextPos + 1 <= Length(TextW)) And (TextW[LNewTextPos + 1] = #10) Then
               Inc(LNewTextPos);
         End Else
         If Delta < 0 Then
         Begin
            If (LNewTextPos + 1 - 1 >= Length(TextW)) And (TextW[LNewTextPos + 1 - 1] = #10) Then
               Dec(LNewTextPos);
         End;

         LNewPos := TextPosToPos(LNewTextPos);
      End Else Begin
         CurLineText := GetLineInternal(Line);
         If Delta > 0 Then
         Begin
            i := 1;
            While i <= Delta Do
            Begin
               Pos := Pos + 1;
               If (Pos + 1 <= Length(CurLineText)) And (CurLineText[Pos + 1] = #10) Then
               Begin
                  Inc(Pos);
                  Inc(i);
               End;
               If Pos + 1 > Length(CurLineText) Then
               Begin
                  If Line + 1 <= Lines.Count - 1 Then
                  Begin
                     Line := Line + 1;
                     CurLineText := GetLineInternal(Line);
                     Pos := 0;
                  End Else
                     Pos := Length(CurLineText);
               End;
               Inc(i);
            End;
         End Else Begin
            i := 1;
            While i <= Abs(Delta) Do
            Begin
               If Pos - 1 >= 0 Then
                  Pos := Pos - 1
               Else Begin
                  If Line - 1 >= 0 Then
                  Begin
                     Line := Line - 1;
                     CurLineText := GetLineInternal(Line);
                     If CurLineText[Length(CurLineText)] = #10 Then
                        Pos := Length(CurLineText) - 2
                     Else
                        Pos := Length(CurLineText) - 1;
                  End;
               End;
               Inc(i);
            End;
         End;
      End;
   Result := LNewPos;
End;

Procedure TD2Memo.RestorePositions;
Begin
   If FOldCaretPos >= 0 Then
      CaretPosition := TextPosToPos(FOldCaretPos);
   If FSelected And (FOldSelStartPos >= 0) Then
   Begin
      FSelStart := TextPosToPos(FOldSelStartPos);
      FSelEnd := TextPosToPos(FOldSelEndPos);
      FOldSelStartPos := -1;
   End;
End;

Procedure TD2Memo.StorePositions;
Begin
   FOldCaretPos := PosToTextPos(CaretPosition);
   If FSelected Then
   Begin
      FOldSelStartPos := PosToTextPos(FSelStart);
      FOldSelEndPos := PosToTextPos(FSelEnd);
   End;
End;

Procedure TD2Memo.MoveCaretVertical(LineDelta: Integer);
Var
   NewLine, NewY, OldX: Integer;
Begin
   With FCaretPosition Do
   Begin
      NewLine := Line + LineDelta;
      If NewLine < 0 Then
         NewLine := 0
      Else
      If NewLine > Lines.Count - 1 Then
         NewLine := Lines.Count - 1;

      NewY := Round(GetPositionPoint(ComposeCaretPos(NewLine, Pos)).Y);
      OldX := Round(GetPositionPoint(CaretPosition).X);
      Line := NewLine;
      Pos := Round(GetPointPosition(d2Point(OldX, NewY)).Pos);
   End;
End;

Procedure TD2Memo.MoveCaretDown;
Begin
   MoveCaretVertical(1);
End;

Procedure TD2Memo.MoveCaretUp;
Begin
   MoveCaretVertical(-1);
End;

Procedure TD2Memo.MoveCaretPageDown;
Begin
   MoveCaretVertical(round(GetPageSize));
End;

Procedure TD2Memo.MoveCaretPageUp;
Begin
   MoveCaretVertical(-round(GetPageSize));
End;

Function TD2Memo.GetWidestLine: Integer;
Var
   i: Integer;
   LWidth, LMaxWidth: Single;
Begin
   Result := -1;
   LMaxWidth := -1;
   For i := 0 To Lines.Count - 1 Do
   Begin
      LWidth := LineWidth[i];
      If LWidth > LMaxWidth Then
      Begin
         Result := i;
         LMaxWidth := LWidth;
      End;
   End;
End;

Function TD2Memo.GetShowSelection: Boolean;
Begin
   Result := IsFocused Or Not HideSelection;
End;

Procedure TD2Memo.MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean);
Begin
   Inherited;
   If (VScrollBar <> nil) And (VScrollBar.Visible) Then
   Begin
      VScrollBar.Value := VScrollBar.Value - (WheelDelta / 30);
   End;
End;

Function TD2Memo.GetData: Variant;
Begin
   Result := TextW;
End;

Procedure TD2Memo.SetData(Const Value: Variant);
Begin
   TextW := Value;
End;

Procedure TD2Memo.SetFont(Const Value: TD2Font);
Begin
   FFont.Assign(Value);
End;

Procedure TD2Memo.SetTextAlign(Const Value: TD2TextAlign);
Begin
   FTextAlign := Value;
End;

Procedure TD2Memo.SetUpdateState(Const aUpdating: Boolean);
Begin
   FUpdatingBool := aUpdating;
   If Not aUpdating Then
      Realign;
End;

Function TD2Memo.GetUnwrapLines: TD2WideStrings;
Begin
   If FUnwrapLines = nil Then
      FUnwrapLines := TD2WideStringList.Create;
   FUnwrapLines.Text := FText;
   Result := FUnwrapLines;
End;


//=============== TD2Shape ===============================================

Constructor TD2Shape.Create(AOwner: TComponent);
Begin
   Inherited;
   FFill := TD2Brush.Create(d2BrushSolid, $FFE0E0E0);
   FFill.OnChanged := FillChanged;
   FStroke := TD2Brush.Create(d2BrushSolid, $FF000000);
   FStroke.SolidColor := $FF000000;
   FStroke.OnChanged := StrokeChanged;
   FStrokeThickness := 1;
End;

Destructor TD2Shape.Destroy;
Begin
   FStroke.Free;
   FFill.Free;
   Inherited;
End;

Function TD2Shape.GetShapeRect: TD2Rect;
Begin
   Result := LocalRect;
   If FStroke.Style <> d2BrushNone Then
      If Odd(Round(FStrokeThickness)) Then
      Begin
         d2InflateRect(Result, -(FStrokeThickness / 2), -(FStrokeThickness / 2));
      End Else
         d2InflateRect(Result, -(FStrokeThickness / 2), -(FStrokeThickness / 2));
End;

Procedure TD2Shape.FillChanged(Sender: TObject);
Begin
   If FUpdating = 0 Then
      Repaint;
   FUpdateEffects := True;
End;

Procedure TD2Shape.StrokeChanged(Sender: TObject);
Begin
   If FUpdating = 0 Then
      Repaint;
   FUpdateEffects := True;
End;

Procedure TD2Shape.BeforePaint;
Begin
   Inherited;
   Canvas.Fill.Assign(FFill);
   Canvas.Stroke.Assign(FStroke);
   Canvas.StrokeThickness := FStrokeThickness;
   Canvas.StrokeCap := FStrokeCap;
   Canvas.StrokeJoin := FStrokeJoin;
   Canvas.StrokeDash := FStrokeDash;
End;

Procedure TD2Shape.AfterPaint;
Begin
   Inherited AfterPaint;
   Canvas.StrokeDash := d2DashSolid;
   Canvas.StrokeThickness := 1;
End;

Function TD2Shape.isStrokeThicknessStored: Boolean;
Begin
   Result := StrokeThickness <> 1;
End;

Procedure TD2Shape.SetFill(Const Value: TD2Brush);
Begin
   FFill.Assign(Value);
End;

Procedure TD2Shape.SetStroke(Const Value: TD2Brush);
Begin
   FStroke.Assign(Value);
End;

Procedure TD2Shape.SetStrokeThickness(Const Value: Single);
Begin
   If FStrokeThickness <> Value Then
   Begin
      FStrokeThickness := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

Procedure TD2Shape.SetStrokeCap(Const Value: TD2StrokeCap);
Begin
   If FStrokeCap <> Value Then
   Begin
      FStrokeCap := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

Procedure TD2Shape.SetStrokeJoin(Const Value: TD2StrokeJoin);
Begin
   If FStrokeJoin <> Value Then
   Begin
      FStrokeJoin := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

Procedure TD2Shape.SetStrokeDash(Const Value: TD2StrokeDash);
Begin
   If FStrokeDash <> Value Then
   Begin
      FStrokeDash := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;


//=============== TD2Line ================================

Constructor TD2Line.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Procedure TD2Line.Paint;
Begin
   Case FLineType Of
      d2LineHorizontal: Canvas.DrawLine(GetShapeRect.TopLeft, d2Point(GetShapeRect.Right, GetShapeRect.Top), AbsoluteOpacity);
      d2LineVertical: Canvas.DrawLine(GetShapeRect.TopLeft, d2Point(GetShapeRect.Left, GetShapeRect.Bottom), AbsoluteOpacity);
      Else Canvas.DrawLine(GetShapeRect.TopLeft, GetShapeRect.BottomRight, AbsoluteOpacity);
   End;
End;

Procedure TD2Line.SetLineType(Const Value: TD2LineType);
Begin
   If FLineType <> Value Then
   Begin
      FLineType := Value;
      Repaint;
   End;
End;

//=============== TD2Ellipse =================================

Procedure TD2Ellipse.Paint;
Begin
   Canvas.FillEllipse(GetShapeRect, AbsoluteOpacity);
   Canvas.DrawEllipse(GetShapeRect, AbsoluteOpacity);
End;

Function TD2Ellipse.PointInObject(X, Y: Single): Boolean;
Var
   P: TD2Point;
Begin
   Result := False;
   P := AbsoluteToLocal(d2Point(X, Y));
   If Width * Height = 0 Then
      Exit;
   If (Sqr((P.X * 2 - Width) / Width) + Sqr((P.Y * 2 - Height) / Height) <= 1) Then
   Begin
      Result := True;
   End;
End;

//=============== TD2Circle ====================================

Procedure TD2Circle.Paint;
Var
   R: TD2Rect;
Begin
   R := d2Rect(0, 0, d2MaxFloat(Width, Height), d2MaxFloat(Width, Height));
   d2FitRect(R, GetShapeRect);
   Canvas.FillEllipse(R, AbsoluteOpacity);
   Canvas.DrawEllipse(R, AbsoluteOpacity);
End;

//=============== TD2Pie ==============================================

Constructor TD2Pie.Create(AOwner: TComponent);
Begin
   Inherited;
   FStartAngle := 0;
   FEndAngle := -90;
End;

Procedure TD2Pie.Paint;
Var
   P: TD2PathData;
Begin
   P := TD2PathData.Create;
   P.MoveTo(d2Point(Width / 2, Height / 2));
   P.AddArc(d2Point(Width / 2, Height / 2), d2Point((Width - StrokeThickness) / 2, (Height - StrokeThickness) / 2), FStartAngle, FEndAngle - FStartAngle);
   P.LineTo(d2Point(Width / 2, Height / 2));
   P.ClosePath;
   Canvas.FillPath(P, P.GetBounds, AbsoluteOpacity);
   Canvas.DrawPath(P, P.GetBounds, AbsoluteOpacity);
   P.Free;
End;

Function TD2Pie.PointInObject(X, Y: Single): Boolean;
Var
   P: TD2PathData;
Begin
   If (Canvas <> nil) Then
   Begin
      P := TD2PathData.Create;
      P.MoveTo(d2Point(Width / 2, Height / 2));
      P.AddArc(d2Point(Width / 2, Height / 2), d2Point((Width - StrokeThickness) / 2, (Height - StrokeThickness) / 2), FStartAngle, FEndAngle - FStartAngle);
      P.LineTo(d2Point(Width / 2, Height / 2));
      P.ClosePath;
      Result := Canvas.PtInPath(AbsoluteToLocal(d2Point(X, Y)), P.GetBounds, P);
      P.Free;
   End Else
      Result := Inherited PointInObject(X, Y);
End;

Procedure TD2Pie.SetEndAngle(Const Value: Single);
Begin
   If FEndAngle <> Value Then
   Begin
      FEndAngle := Value;
      Repaint;
   End;
End;

Procedure TD2Pie.SetStartAngle(Const Value: Single);
Begin
   If FStartAngle <> Value Then
   Begin
      FStartAngle := Value;
      Repaint;
   End;
End;

//=============== TD2Arc =========================================

Constructor TD2Arc.Create(AOwner: TComponent);
Begin
   Inherited;
   Fill.Style := d2BrushNone;
   Fill.DefaultStyle := d2BrushNone;
   FStartAngle := 0;
   FEndAngle := -90;
End;

Procedure TD2Arc.Paint;
Begin
   Canvas.FillArc(d2Point(Width / 2, Height / 2), d2Point((Width - StrokeThickness) / 2, ((Height - StrokeThickness) / 2)), FStartAngle, FEndAngle, AbsoluteOpacity);
   Canvas.DrawArc(d2Point(Width / 2, Height / 2), d2Point((Width - StrokeThickness) / 2, ((Height - StrokeThickness) / 2)), FStartAngle, FEndAngle, AbsoluteOpacity);
End;

Function TD2Arc.PointInObject(X, Y: Single): Boolean;
Begin
   Result := Inherited PointInObject(X, Y);
End;

Procedure TD2Arc.SetEndAngle(Const Value: Single);
Begin
   If FEndAngle <> Value Then
   Begin
      FEndAngle := Value;
      Repaint;
   End;
End;

Procedure TD2Arc.SetStartAngle(Const Value: Single);
Begin
   If FStartAngle <> Value Then
   Begin
      FStartAngle := Value;
      Repaint;
   End;
End;

//=============== TD2Rectangle =============================================

Constructor TD2Rectangle.Create(AOwner: TComponent);
Begin
   Inherited;
   FCorners := AllCorners;
   FxRadius := 0;
   FyRadius := 0;
   FSides := AllSides;
End;

Function TD2Rectangle.IsCornersStored: Boolean;
Begin
   Result := FCorners <> AllCorners;
End;

Function TD2Rectangle.IsSidesStored: Boolean;
Begin
   Result := FSides * AllSides <> [];
End;

Procedure TD2Rectangle.Paint;
Var
   R: TD2Rect;
   Off: Single;
Begin
   R := GetShapeRect;
   If Sides <> AllSides Then
   Begin
      Off := R.Left;
      If Not (d2SideTop In FSides) Then
         R.Top := R.Top - Off;
      If Not (d2SideLeft In FSides) Then
         R.Left := R.Left - Off;
      If Not (d2SideBottom In FSides) Then
         R.Bottom := R.Bottom + Off;
      If Not (d2SideRight In FSides) Then
         R.Right := R.Right + Off;
      Canvas.FillRect(R, xRadius, yRadius, FCorners, AbsoluteOpacity, CornerType);
      Canvas.DrawRectSides(GetShapeRect, xRadius, yRadius, FCorners, AbsoluteOpacity, Sides, CornerType);
   End Else Begin
      Canvas.FillRect(R, xRadius, yRadius, FCorners, AbsoluteOpacity, CornerType);
      Canvas.DrawRect(R, xRadius, yRadius, FCorners, AbsoluteOpacity, CornerType);
   End;
End;

Procedure TD2Rectangle.SetCorners(Const Value: TD2Corners);
Begin
   If FCorners <> Value Then
   Begin
      FCorners := Value;
      Repaint;
   End;
End;

Procedure TD2Rectangle.SetCornerType(Const Value: TD2CornerType);
Begin
   If FCornerType <> Value Then
   Begin
      FCornerType := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

Procedure TD2Rectangle.SetxRadius(Const Value: Single);
Begin
   If FxRadius <> Value Then
   Begin
      FxRadius := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

Procedure TD2Rectangle.SetyRadius(Const Value: Single);
Begin
   If FyRadius <> Value Then
   Begin
      FyRadius := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

Procedure TD2Rectangle.SetSides(Const Value: TD2Sides);
Begin
   If FSides <> Value Then
   Begin
      FSides := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

//=============== TD2BlurRectangle ====================================================

Constructor TD2BlurRectangle.Create(AOwner: TComponent);
Begin
   Inherited;
   Stroke.Style := d2BrushNone;
   FSoftness := 0.3;
   FRecreate := True;
End;

Destructor TD2BlurRectangle.Destroy;
Begin
   If FBuffer <> nil Then
      FreeAndNil(FBuffer);
   Inherited;
End;

Procedure TD2BlurRectangle.Paint;
Var
   R: TD2Rect;
   Radius: Integer;
Begin
   Radius := Trunc(FSoftness * 15);
   If FBuffer = nil Then
   Begin
      FBuffer := TD2Bitmap.Create(Round(Width), Round(Height));
      FRecreate := True;
   End;
   If (FBuffer.Width <> Round(Width)) Or (FBuffer.Height <> Round(Height)) Then
   Begin
      FBuffer.SetSize(Round(Width), Round(Height));
      FRecreate := True;
   End;

   If FRecreate Then
   Begin
      R := d2Rect(Radius, Radius, Width - Radius, Height - Radius);
      FBuffer.Clear(0);
      FBuffer.Canvas.Fill.Assign(Fill);
      FBuffer.Canvas.FillRect(R, xRadius, yRadius, FCorners, AbsoluteOpacity, CornerType);
      FBuffer.Canvas.Stroke.Assign(Stroke);
      FBuffer.Canvas.DrawRect(R, xRadius, yRadius, FCorners, AbsoluteOpacity, CornerType);
      Blur(nil, FBuffer, Radius);
      FRecreate := False;
   End;
   Canvas.DrawBitmap(FBuffer, LocalRect, LocalRect, AbsoluteOpacity, True);
End;

Procedure TD2BlurRectangle.SetSoftness(Const Value: Single);
Begin
   If FSoftness <> Value Then
   Begin
      FSoftness := Value;
      FRecreate := True;
      Repaint;
   End;
End;

Procedure TD2BlurRectangle.SetCorners(Const Value: TD2Corners);
Begin
   If Value <> Corners Then
   Begin
      Inherited;
      FRecreate := True;
   End;
End;

Procedure TD2BlurRectangle.SetCornerType(Const Value: TD2CornerType);
Begin
   If Value <> CornerType Then
   Begin
      Inherited;
      FRecreate := True;
   End;
End;

Procedure TD2BlurRectangle.SetxRadius(Const Value: Single);
Begin
   If Value <> xRadius Then
   Begin
      Inherited;
      FRecreate := True;
   End;
End;

Procedure TD2BlurRectangle.SetyRadius(Const Value: Single);
Begin
   If Value <> yRadius Then
   Begin
      Inherited;
      FRecreate := True;
   End;
End;

Procedure TD2BlurRectangle.FillChanged(Sender: TObject);
Begin
   Inherited;
   FRecreate := True;
End;

//=============== TD2RoundRect ========================================

Constructor TD2RoundRect.Create(AOwner: TComponent);
Begin
   Inherited;
   FCorners := AllCorners;
End;

Function TD2RoundRect.IsCornersStored: Boolean;
Begin
   Result := FCorners <> AllCorners;
End;

Procedure TD2RoundRect.Paint;
Var
   Radius: Single;
Begin
   If Height < Width Then
      Radius := d2RectHeight(GetShapeRect) / 2
   Else
      Radius := d2RectWidth(GetShapeRect) / 2;
   Canvas.FillRect(GetShapeRect, Radius, Radius, FCorners, AbsoluteOpacity);
   Canvas.DrawRect(GetShapeRect, Radius, Radius, FCorners, AbsoluteOpacity);
End;

Procedure TD2RoundRect.SetCorners(Const Value: TD2Corners);
Begin
   If FCorners <> Value Then
   Begin
      FCorners := Value;
      Repaint;
   End;
End;

//=============== TD2BlurRoundRect ==========================================

Constructor TD2BlurRoundRect.Create(AOwner: TComponent);
Begin
   Inherited;
   Stroke.Style := d2BrushNone;
   FSoftness := 0.3;
   FRecreate := True;
End;

Destructor TD2BlurRoundRect.Destroy;
Begin
   If FBuffer <> nil Then
      FreeAndNil(FBuffer);
   Inherited;
End;

Procedure TD2BlurRoundRect.Paint;
Var
   R: TD2Rect;
   Radius: Integer;
   rectRadius: Single;
Begin
   Radius := Trunc(FSoftness * 15);
   If FBuffer = nil Then
   Begin
      FBuffer := TD2Bitmap.Create(Round(Width), Round(Height));
      FRecreate := True;
   End;
   If (FBuffer.Width <> Round(Width)) Or (FBuffer.Height <> Round(Height)) Then
   Begin
      FBuffer.SetSize(Round(Width), Round(Height));
      FRecreate := True;
   End;

   If FRecreate Then
   Begin
      R := d2Rect(Radius, Radius, Width - Radius, Height - Radius);
      rectRadius := d2RectHeight(R) / 2;
      FBuffer.Clear(0);
      FBuffer.Canvas.Fill.Assign(Fill);
      FBuffer.Canvas.FillRect(R, rectRadius, rectRadius, FCorners, AbsoluteOpacity);
      FBuffer.Canvas.Stroke.Assign(Stroke);
      FBuffer.Canvas.DrawRect(R, rectRadius, rectRadius, FCorners, AbsoluteOpacity);
      Blur(nil, FBuffer, Radius);
      FRecreate := False;
   End;
   Canvas.DrawBitmap(FBuffer, LocalRect, LocalRect, AbsoluteOpacity, True);
End;

Procedure TD2BlurRoundRect.SetSoftness(Const Value: Single);
Begin
   If FSoftness <> Value Then
   Begin
      FSoftness := Value;
      FRecreate := True;
      Repaint;
   End;
End;

Procedure TD2BlurRoundRect.SetCorners(Const Value: TD2Corners);
Begin
   If Value <> Corners Then
   Begin
      Inherited;
      FRecreate := True;
   End;
End;

Procedure TD2BlurRoundRect.FillChanged(Sender: TObject);
Begin
   Inherited;
   FRecreate := True;
End;

//=============== TD2CalloutRectangle ====================================

Constructor TD2CalloutRectangle.Create(AOwner: TComponent);
Begin
   Inherited;
   FCalloutWidth := 23;
   FCalloutLength := 11;
   FPath := TD2PathData.Create;
End;

Destructor TD2CalloutRectangle.Destroy;
Begin
   FreeAndNil(FPath);
   Inherited;
End;

Procedure TD2CalloutRectangle.CreatePath;
Var
   x1, x2, y1, y2: Single;
   R: TD2Rect;
   Off: Single;
Begin
   R := GetShapeRect;
   Case CalloutPosition Of
      d2CalloutTop: R.Top := R.Top + FCalloutLength;
      d2CalloutLeft: R.Left := R.Left + FCalloutLength;
      d2CalloutBottom: R.Bottom := R.Bottom - FCalloutLength;
      d2CalloutRight: R.Right := R.Right - FCalloutLength;
   End;
   If Sides <> AllSides Then
   Begin
      Off := R.Left;
      If Not (d2SideTop In FSides) Then
         R.Top := R.Top - Off;
      If Not (d2SideLeft In FSides) Then
         R.Left := R.Left - Off;
      If Not (d2SideBottom In FSides) Then
         R.Bottom := R.Bottom + Off;
      If Not (d2SideRight In FSides) Then
         R.Right := R.Right + Off;
   End;
   x1 := xRadius;
   If d2RectWidth(R) - (x1 * 2) < 0 Then
      x1 := (xRadius * (d2RectWidth(R) / (x1 * 2)));
   x2 := x1 / 2;
   y1 := yRadius;
   If d2RectHeight(R) - (y1 * 2) < 0 Then
      y1 := (yRadius * (d2RectHeight(R) / (y1 * 2)));
   y2 := y1 / 2;
   FPath.Clear;
   FPath.MoveTo(d2Point(R.Left, R.Top + y1));
   If d2CornerTopLeft In FCorners Then
   Begin
      Case FCornerType Of
         d2CornerInnerLine:
         Begin
            FPath.LineTo(d2Point(R.Left + x2, R.Top + y1));
            FPath.LineTo(d2Point(R.Left + x1, R.Top + y2));
            FPath.LineTo(d2Point(R.Left + x1, R.Top));
         End;
         d2CornerInnerRound: FPath.CurveTo(d2Point(R.Left + x2, R.Top + y1), d2Point(R.Left + x1, R.Top + y2), d2Point(R.Left + x1, R.Top));
         d2CornerBevel: FPath.LineTo(d2Point(R.Left + x1, R.Top));
         Else FPath.CurveTo(d2Point(R.Left, R.Top + (y2)), d2Point(R.Left + x2, R.Top), d2Point(R.Left + x1, R.Top))
      End;
   End Else Begin
      If d2SideLeft In FSides Then
         FPath.LineTo(d2Point(R.Left, R.Top))
      Else
         FPath.MoveTo(d2Point(R.Left, R.Top));

      If d2SideTop In FSides Then
         FPath.LineTo(d2Point(R.Left + x1, R.Top))
      Else
         FPath.MoveTo(d2Point(R.Left + x1, R.Top));
   End;

   If Not (d2SideTop In FSides) Then
      FPath.MoveTo(d2Point(R.Right - x1, R.Top))
   Else Begin
      If (FCalloutPosition = d2CalloutTop) Then
      Begin
         If CalloutOffset = 0 Then
         Begin
            FPath.LineTo(d2Point((R.Right - R.Left) / 2 - (CalloutWidth / 2), R.Top));
            FPath.LineTo(d2Point((R.Right - R.Left) / 2, R.Top - FCalloutLength));
            FPath.LineTo(d2Point((R.Right - R.Left) / 2 + (CalloutWidth / 2), R.Top));
            FPath.LineTo(d2Point(R.Right - x1, R.Top));
         End Else
         If CalloutOffset > 0 Then
         Begin
            FPath.LineTo(d2Point(R.Left + FCalloutOffset, R.Top));
            FPath.LineTo(d2Point(R.Left + FCalloutOffset + (CalloutWidth / 2), R.Top - FCalloutLength));
            FPath.LineTo(d2Point(R.Left + FCalloutOffset + CalloutWidth, R.Top));
            FPath.LineTo(d2Point(R.Right - x1, R.Top));
         End Else Begin
            FPath.LineTo(d2Point(R.Right - Abs(FCalloutOffset) - CalloutWidth, R.Top));
            FPath.LineTo(d2Point(R.Right - Abs(FCalloutOffset) - (CallOutWidth / 2), R.Top - FCalloutLength));
            FPath.LineTo(d2Point(R.Right - Abs(FCalloutOffset), R.Top));
            FPath.LineTo(d2Point(R.Right - x1, R.Top));
         End;
      End Else
         FPath.LineTo(d2Point(R.Right - x1, R.Top));
   End;
   If d2CornerTopRight In FCorners Then
   Begin
      Case FCornerType Of
         d2CornerInnerLine:
         Begin
            FPath.LineTo(d2Point(R.Right - x1, R.Top + y2));
            FPath.LineTo(d2Point(R.Right - x2, R.Top + y1));
            FPath.LineTo(d2Point(R.Right, R.Top + y1));
         End;
         d2CornerInnerRound: FPath.CurveTo(d2Point(R.Right - x1, R.Top + y2), d2Point(R.Right - x2, R.Top + y1), d2Point(R.Right, R.Top + y1));
         d2CornerBevel: FPath.LineTo(d2Point(R.Right, R.Top + y1));
         Else FPath.CurveTo(d2Point(R.Right - x2, R.Top), d2Point(R.Right, R.Top + (y2)), d2Point(R.Right, R.Top + y1))
      End;
   End Else Begin
      If d2SideTop In FSides Then
         FPath.LineTo(d2Point(R.Right, R.Top))
      Else
         FPath.MoveTo(d2Point(R.Right, R.Top));

      If d2SideRight In FSides Then
         FPath.LineTo(d2Point(R.Right, R.Top + y1))
      Else
         FPath.MoveTo(d2Point(R.Right, R.Top + y1));
   End;

   If Not (d2SideRight In FSides) Then
      FPath.MoveTo(d2Point(R.Right, R.Bottom - y1))
   Else Begin
      If (FCalloutPosition = d2CalloutRight) Then
      Begin
         If CalloutOffset = 0 Then
         Begin
            FPath.LineTo(d2Point(R.Right, (R.Bottom - R.Top) / 2 - (CalloutWidth / 2)));
            FPath.LineTo(d2Point(R.Right + FCalloutLength, (R.Bottom - R.Top) / 2));
            FPath.LineTo(d2Point(R.Right, (R.Bottom - R.Top) / 2 + (CalloutWidth / 2)));
            FPath.LineTo(d2Point(R.Right, R.Bottom - y1));
         End Else
         If CalloutOffset > 0 Then
         Begin
            FPath.LineTo(d2Point(R.Right, R.Top + CalloutOffset));
            FPath.LineTo(d2Point(R.Right + FCalloutLength, R.Top + CalloutOffset + (CalloutWidth / 2)));
            FPath.LineTo(d2Point(R.Right, R.Top + CalloutOffset + CalloutWidth));
            FPath.LineTo(d2Point(R.Right, R.Bottom - y1));
         End Else Begin
            FPath.LineTo(d2Point(R.Right, R.Bottom + CalloutOffset));
            FPath.LineTo(d2Point(R.Right + FCalloutLength, R.Bottom + CalloutOffset + (CalloutWidth / 2)));
            FPath.LineTo(d2Point(R.Right, R.Bottom + CalloutOffset + CalloutWidth));
            FPath.LineTo(d2Point(R.Right, R.Bottom - y1));
         End;
      End Else
         FPath.LineTo(d2Point(R.Right, R.Bottom - y1));
   End;

   If d2CornerBottomRight In FCorners Then
   Begin
      Case FCornerType Of

         d2CornerBevel: FPath.LineTo(d2Point(R.Right - x1, R.Bottom));
         d2CornerInnerRound: FPath.CurveTo(d2Point(R.Right - x2, R.Bottom - y1), d2Point(R.Right - x1, R.Bottom - y2), d2Point(R.Right - x1, R.Bottom));
         d2CornerInnerLine:
         Begin
            FPath.LineTo(d2Point(R.Right - x2, R.Bottom - y1));
            FPath.LineTo(d2Point(R.Right - x1, R.Bottom - y2));
            FPath.LineTo(d2Point(R.Right - x1, R.Bottom));
         End;
         Else FPath.CurveTo(d2Point(R.Right, R.Bottom - (y2)), d2Point(R.Right - x2, R.Bottom), d2Point(R.Right - x1, R.Bottom))
      End;
   End Else Begin
      If d2SideRight In FSides Then
         FPath.LineTo(d2Point(R.Right, R.Bottom))
      Else
         FPath.MoveTo(d2Point(R.Right, R.Bottom));
      If d2SideBottom In FSides Then
         FPath.LineTo(d2Point(R.Right - x1, R.Bottom))
      Else
         FPath.MoveTo(d2Point(R.Right - x1, R.Bottom));
   End;
   If Not (d2SideBottom In FSides) Then
      FPath.MoveTo(d2Point(R.Left + x1, R.Bottom))
   Else Begin
      If (FCalloutPosition = d2CalloutBottom) Then
      Begin
         If CalloutOffset = 0 Then
         Begin
            FPath.LineTo(d2Point((R.Right - R.Left) / 2 + (CalloutWidth / 2), R.Bottom));
            FPath.LineTo(d2Point((R.Right - R.Left) / 2, R.Bottom + FCalloutLength));
            FPath.LineTo(d2Point((R.Right - R.Left) / 2 - (CalloutWidth / 2), R.Bottom));
            FPath.LineTo(d2Point(R.Left + x1, R.Bottom));
         End Else
         If CalloutOffset > 0 Then
         Begin
            FPath.LineTo(d2Point(R.Left + FCalloutOffset + CalloutWidth, R.Bottom));
            FPath.LineTo(d2Point(R.Left + FCalloutOffset + (CalloutWidth / 2), R.Bottom + FCalloutLength));
            FPath.LineTo(d2Point(R.Left + FCalloutOffset, R.Bottom));
            FPath.LineTo(d2Point(R.Left + x1, R.Bottom));
         End Else Begin
            FPath.LineTo(d2Point(R.Right - Abs(FCalloutOffset), R.Bottom));
            FPath.LineTo(d2Point(R.Right - Abs(FCalloutOffset) - (CalloutWidth / 2), R.Bottom + FCalloutLength));
            FPath.LineTo(d2Point(R.Right - Abs(FCalloutOffset) - CalloutWidth, R.Bottom));
            FPath.LineTo(d2Point(R.Left + x1, R.Bottom));
         End;
      End Else
         FPath.LineTo(d2Point(R.Left + x1, R.Bottom));
   End;
   If d2CornerBottomLeft In FCorners Then
   Begin
      Case FCornerType Of
         d2CornerInnerLine:
         Begin
            FPath.LineTo(d2Point(R.Left + x1, R.Bottom - y2));
            FPath.LineTo(d2Point(R.Left + x2, R.Bottom - y1));
            FPath.LineTo(d2Point(R.Left, R.Bottom - y1));
         End;
         d2CornerInnerRound: FPath.CurveTo(d2Point(R.Left + x1, R.Bottom - y2), d2Point(R.Left + x2, R.Bottom - y1), d2Point(R.Left, R.Bottom - y1));
         d2CornerBevel: FPath.LineTo(d2Point(R.Left, R.Bottom - y1));
         Else FPath.CurveTo(d2Point(R.Left + x2, R.Bottom), d2Point(R.Left, R.Bottom - (y2)), d2Point(R.Left, R.Bottom - y1))
      End;
   End Else Begin
      If d2SideBottom In FSides Then
         FPath.LineTo(d2Point(R.Left, R.Bottom))
      Else
         FPath.MoveTo(d2Point(R.Left, R.Bottom));
      If d2SideLeft In FSides Then
         FPath.LineTo(d2Point(R.Left, R.Bottom - y1))
      Else
         FPath.MoveTo(d2Point(R.Left, R.Bottom - y1));
   End;

   If (d2SideLeft In FSides) Then
   Begin
      If (FCalloutPosition = d2CalloutLeft) Then
      Begin
         If CalloutOffset = 0 Then
         Begin
            FPath.LineTo(d2Point(R.Left, (R.Bottom - R.Top) / 2 + (CalloutWidth / 2)));
            FPath.LineTo(d2Point(R.Left - FCalloutLength, (R.Bottom - R.Top) / 2));
            FPath.LineTo(d2Point(R.Left, (R.Bottom - R.Top) / 2 - (CalloutWidth / 2)));
            FPath.LineTo(d2Point(R.Left, R.Top + y1));
         End Else
         If CalloutOffset > 0 Then
         Begin
            FPath.LineTo(d2Point(R.Left, R.Top + CalloutOffset + CalloutWidth));
            FPath.LineTo(d2Point(R.Left - FCalloutLength, R.Top + CalloutOffset + (CalloutWidth / 2)));
            FPath.LineTo(d2Point(R.Left, R.Top + CalloutOffset));
            FPath.LineTo(d2Point(R.Left, R.Top + y1));
         End Else Begin
            FPath.LineTo(d2Point(R.Left, R.Bottom + CalloutOffset + CalloutWidth));
            FPath.LineTo(d2Point(R.Left - FCalloutLength, R.Bottom + CalloutOffset + (CalloutWidth / 2)));
            FPath.LineTo(d2Point(R.Left, R.Bottom + CalloutOffset));
            FPath.LineTo(d2Point(R.Left, R.Top + y1));
         End;
      End Else
         FPath.LineTo(d2Point(R.Left, R.Top + y1));
   End;
End;

Procedure TD2CalloutRectangle.Paint;
Var
   R: TD2Rect;
Begin
   CreatePath;
   R := FPath.GetBounds;
   Canvas.FillPath(FPath, R, AbsoluteOpacity);
   Canvas.DrawPath(FPath, R, AbsoluteOpacity);
End;

Procedure TD2CalloutRectangle.SetCalloutWidth(Const Value: Single);
Begin
   If FCalloutWidth <> Value Then
   Begin
      FCalloutWidth := Value;
      CreatePath;
      Repaint;
   End;
End;

Procedure TD2CalloutRectangle.SetCalloutLength(Const Value: Single);
Begin
   If FCalloutLength <> Value Then
   Begin
      FCalloutLength := Value;
      CreatePath;
      Repaint;
   End;
End;

Procedure TD2CalloutRectangle.SetCalloutPosition(Const Value: TD2CalloutPosition);
Begin
   If FCalloutPosition <> Value Then
   Begin
      FCalloutPosition := Value;
      CreatePath;
      Repaint;
   End;
End;

Procedure TD2CalloutRectangle.SetCalloutOffset(Const Value: Single);
Begin
   If FCalloutOffset <> Value Then
   Begin
      FCalloutOffset := Value;
      CreatePath;
      Repaint;
   End;
End;


//=============== TD2Text =====================================================

Constructor TD2Text.Create(AOwner: TComponent);
Begin
   Inherited;
   FHorzTextAlign := d2TextAlignCenter;
   FVertTextAlign := d2TextAlignCenter;
   FWordWrap := True;
   FFont := TD2Font.Create;
   FFont.OnChanged := FontChanged;
   Fill.DefaultColor := $FF000000;
   Fill.SolidColor := $FF000000;
   Stroke.DefaultStyle := d2BrushNone;
   Stroke.Style := d2BrushNone;
End;

Destructor TD2Text.Destroy;
Begin
   FFont.Free;
   Inherited;
End;

Function TD2Text.GetData: Variant;
Begin
   Result := TextW;
End;

Procedure TD2Text.SetData(Const Value: Variant);
Begin
   TextW := Value;
End;

Procedure TD2Text.FontChanged(Sender: TObject);
Begin
   FUpdateEffects := True;
   If FAutoSize Then
      AdjustSize;
   Repaint;
End;

Procedure TD2Text.Paint;
Var
   R: TD2Rect;
   M: TD2Matrix;
Begin
   Canvas.Font.Assign(FFont);
   If FStretch Then
   Begin
      R := d2Rect(0, 0, 1000, 1000);
      Canvas.MeasureText(R, R, FText, False, FHorzTextAlign, FVertTextAlign);
      d2OffsetRect(R, -R.Left, -R.Top);
      M := IdentityMatrix;
      If Not d2IsRectEmpty(R) Then
      Begin
         M.m11 := d2RectWidth(LocalRect) / d2RectWidth(R);
         M.m22 := d2RectHeight(LocalRect) / d2RectHeight(R);
      End;
      Canvas.MultyMatrix(M);
      d2InflateRect(R, Font.Size / 3, Font.Size / 3);
      Canvas.FillText(R, R, FText, False, AbsoluteOpacity, FHorzTextAlign, FVertTextAlign);
   End Else
      Canvas.FillText(LocalRect, LocalRect, FText, FWordWrap, AbsoluteOpacity, FHorzTextAlign, FVertTextAlign);
End;

Procedure TD2Text.SetWordWrap(Const Value: Boolean);
Begin
   If FWordWrap <> Value Then
   Begin
      FWordWrap := Value;
      Repaint;
   End;
End;

Procedure TD2Text.SetAutoSize(Const Value: Boolean);
Begin
   If FAutoSize <> Value Then
   Begin
      FAutoSize := Value;
      AdjustSize;
   End;
End;

Procedure TD2Text.SetStretch(Const Value: Boolean);
Begin
   If FStretch <> Value Then
   Begin
      FUpdateEffects := True;
      FStretch := Value;
      If Stretch Then
         AutoSize := False;
      Repaint;
   End;
End;

Procedure TD2Text.AdjustSize;
Var
   R: TD2Rect;
Begin
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;
   If FAutoSize And (Canvas <> nil) And (FText <> '') Then
   Begin
      Canvas.Font.Assign(FFont);
      If WordWrap Then
         R := d2Rect(0, 0, Width, 1000)
      Else
         R := d2Rect(0, 0, 1000, 1000);
      Canvas.MeasureText(R, R, FText, WordWrap, d2TextAlignNear, d2TextAlignNear);
      If Not WordWrap Then
         Width := R.Right + (Font.Size / 2);
      If VertTextAlign <> d2TextAlignCenter Then
         Height := R.Bottom;
   End;
   FDisableAlign := False;
   If FAutoSize And (Canvas <> nil) And (FText <> '') Then
      If (Parent <> nil) And (Parent.IsVisual) Then
         Parent.Visual.Realign;
End;

Procedure TD2Text.Realign;
Begin
   Inherited;
   If FAutoSize Then
      AdjustSize;
End;

Procedure TD2Text.SetFont(Const Value: TD2Font);
Begin
   FFont.Assign(Value);
   If FAutoSize Then
      AdjustSize;
End;

Procedure TD2Text.SetHorzTextAlign(Const Value: TD2TextAlign);
Begin
   If FHorzTextAlign <> Value Then
   Begin
      FHorzTextAlign := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

Procedure TD2Text.SetVertTextAlign(Const Value: TD2TextAlign);
Begin
   If FVertTextAlign <> Value Then
   Begin
      FVertTextAlign := Value;
      FUpdateEffects := True;
      Repaint;
   End;
End;

Procedure TD2Text.SetTextW(Const Value: WideString);
Begin
   If FText <> Value Then
   Begin
      FText := Value;
      FUpdateEffects := True;
      If FAutoSize Then
         AdjustSize;
      Repaint;
   End;
End;

Function TD2Text.GetTextW: WideString;
Begin
   Result := Ftext;
End;

Procedure TD2Text.SetText(Const Value: String);
Var
   ss: WideString;
Begin
   ss := UTF8Decode(Value);
   SetTextW(ss);
End;

Function TD2Text.GetText: String;
Begin
   Result := UTF8Encode(Ftext);
End;

//=============== TD2Path =====================================================

Constructor TD2CustomPath.Create(AOwner: TComponent);
Begin
   Inherited;
   FWrapMode := d2PathStretch;
   FData := TD2PathData.Create;
   FData.OnChanged := DoChanged;
End;

Destructor TD2CustomPath.Destroy;
Begin
   FreeAndNil(FData);
   Inherited;
End;

Procedure TD2CustomPath.DoChanged(Sender: TObject);
Begin
   Repaint;
End;

Function TD2CustomPath.PointInObject(X, Y: Single): Boolean;
Begin
   If Assigned(Scene) And Scene.GetDesignTime And Not FLocked And Not FInPaintTo Then
   Begin
      Result := Inherited PointInObject(X, Y);
   End Else
   If (Canvas <> nil) And (FData.ResourcePath <> nil) And (Length(FData.ResourcePath.PathData) > 1) Then
   Begin
      Result := Canvas.PtInPath(AbsoluteToLocal(d2Point(X, Y)), GetShapeRect, FData.ResourcePath);
   End Else
   If (Canvas <> nil) And (Length(FData.PathData) > 1) Then
   Begin
      Result := Canvas.PtInPath(AbsoluteToLocal(d2Point(X, Y)), GetShapeRect, FData);
   End Else
      Result := Inherited PointInObject(X, Y);
End;

Procedure TD2CustomPath.Paint;
Var
   R: TD2Rect;
   i, j: Integer;
   State: Integer;
   P: TD2PathData;
Begin
   P := FData;
   If FData.ResourcePath <> nil Then
      P := FData.ResourcePath;

   If Not P.IsEmpty Then
   Begin
      Case FWrapMode Of
         d2PathOriginal:
         Begin
            State := Canvas.SaveCanvas;
            Canvas.IntersectClipRect(LocalRect);

            R := P.GetBounds;
            d2OffsetRect(R, shapeRect.Left, shapeRect.Top);
            Canvas.FillPath(P, R, AbsoluteOpacity);
            Canvas.DrawPath(P, R, AbsoluteOpacity);

            Canvas.RestoreCanvas(State);
         End;
         d2PathFit:
         Begin
            R := P.GetBounds;
            d2FitRect(R, ShapeRect);
            Canvas.FillPath(P, R, AbsoluteOpacity);
            Canvas.DrawPath(P, R, AbsoluteOpacity);
         End;
         d2PathStretch:
         Begin
            Canvas.FillPath(P, ShapeRect, AbsoluteOpacity);
            Canvas.DrawPath(P, ShapeRect, AbsoluteOpacity);
         End;
         d2PathTile:
         Begin
            State := Canvas.SaveCanvas;
            Canvas.IntersectClipRect(LocalRect);

            R := P.GetBounds;
            For i := 0 To round(Width / d2RectWidth(R)) Do
               For j := 0 To round(Height / d2RectHeight(R)) Do
               Begin
                  R := P.GetBounds;
                  d2OffsetRect(R, shapeRect.Left, shapeRect.Top);
                  d2OffsetRect(R, i * (d2RectWidth(R) + shapeRect.Left * 2), j * (d2RectHeight(R) + shapeRect.Top * 2));
                  Canvas.FillPath(P, R, AbsoluteOpacity);
                  Canvas.DrawPath(P, R, AbsoluteOpacity);
               End;

            Canvas.RestoreCanvas(State);
         End;
      End;
   End;
   If Assigned(Scene) And Scene.GetDesignTime And Not FLocked And Not FInPaintTo Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      Canvas.StrokeDash := d2DashSolid;
   End;
End;

Procedure TD2CustomPath.SetWrapMode(Const Value: TD2PathWrap);
Begin
   If FWrapMode <> Value Then
   Begin
      FWrapMode := Value;
      Repaint;
   End;
End;

Procedure TD2CustomPath.SetData2d(Const Value: TD2PathData);
Begin
   FData.Assign(Value);
End;


//=============== TD2SelectionItem ================================

Constructor TD2SelectionItem.Create(AOwner: TComponent);
Begin
   Inherited;
   HitTest := False;
End;

Procedure TD2SelectionItem.Paint;
Begin
   Inherited;
End;

Procedure TD2SelectionItem.PaintChildren;
Begin
   If csDesigning In ComponentState Then
      Exit;
   Inherited;
End;

//=============== TD2Selection ===========================================

Constructor TD2Selection.Create(AOwner: TComponent);
Begin
   Inherited;
   AutoCapture := True;
   ParentBounds := True;
   FMinSize := 15;
   FGripSize := 3;
End;

Destructor TD2Selection.Destroy;
Begin
   Inherited;
End;

Function TD2Selection.GetAbsoluteRect: TD2Rect;
Begin
   Result := Inherited GetAbsoluteRect;
   d2InflateRect(Result, FGripSize + 4, FGripSize + 4);
End;

Procedure TD2Selection.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   R: TD2Rect;
Begin
   Inherited;
   FDownPos := d2Point(X, Y);
   If Button = mbLeft Then
   Begin
      FRatio := Width / Height;
      R := LocalRect;
      R := d2Rect(R.Left - (GripSize), R.Top - (GripSize), R.Left + (GripSize), R.Top + (GripSize));
      If d2PtInRect(FDownPos, R) Then
      Begin
         FLeftTop := True;
         Exit;
      End;
      R := LocalRect;
      R := d2Rect(R.Right - (GripSize), R.Top - (GripSize), R.Right + (GripSize), R.Top + (GripSize));
      If d2PtInRect(FDownPos, R) Then
      Begin
         FRightTop := True;
         Exit;
      End;
      R := LocalRect;
      R := d2Rect(R.Right - (GripSize), R.Bottom - (GripSize), R.Right + (GripSize), R.Bottom + (GripSize));
      If d2PtInRect(FDownPos, R) Then
      Begin
         FRightBottom := True;
         Exit;
      End;
      R := LocalRect;
      R := d2Rect(R.Left - (GripSize), R.Bottom - (GripSize), R.Left + (GripSize), R.Bottom + (GripSize));
      If d2PtInRect(FDownPos, R) Then
      Begin
         FLeftBottom := True;
         Exit;
      End;
      FMove := True;
   End;
End;

Procedure TD2Selection.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   P, OldPos, Size: TD2Point;
   S, SaveW: Single;
   R: TD2Rect;
Begin
   Inherited;
   If Shift = [] Then
   Begin
      FMovePos := d2Point(X, Y);
      P := LocalToAbsolute(FMovePos);
      If Parent <> nil Then
         P := TD2VisualObject(Parent).AbsolutetoLocal(P);
      R := LocalRect;
      R := d2Rect(R.Left - (GripSize), R.Top - (GripSize), R.Left + (GripSize), R.Top + (GripSize));
      If d2PtInRect(FMovePos, R) And Not FLeftTopHot Then
      Begin
         FLeftTopHot := True;
         Repaint;
      End Else
      If Not d2PtInRect(FMovePos, R) And FLeftTopHot Then
      Begin
         FLeftTopHot := False;
         Repaint;
      End;
      R := LocalRect;
      R := d2Rect(R.Right - (GripSize), R.Top - (GripSize), R.Right + (GripSize), R.Top + (GripSize));
      If d2PtInRect(FMovePos, R) And Not FRightTopHot Then
      Begin
         FRightTopHot := True;
         Repaint;
      End Else
      If Not d2PtInRect(FMovePos, R) And FRightTopHot Then
      Begin
         FRightTopHot := False;
         Repaint;
      End;
      R := LocalRect;
      R := d2Rect(R.Right - (GripSize), R.Bottom - (GripSize), R.Right + (GripSize), R.Bottom + (GripSize));
      If d2PtInRect(FMovePos, R) And Not FRightBottomHot Then
      Begin
         FRightBottomHot := True;
         Repaint;
      End Else
      If Not d2PtInRect(FMovePos, R) And FRightBottomHot Then
      Begin
         FRightBottomHot := False;
         Repaint;
      End;
      R := LocalRect;
      R := d2Rect(R.Left - (GripSize), R.Bottom - (GripSize), R.Left + (GripSize), R.Bottom + (GripSize));
      If d2PtInRect(FMovePos, R) And Not FLeftBottomHot Then
      Begin
         FLeftBottomHot := True;
         Repaint;
      End Else
      If Not d2PtInRect(FMovePos, R) And FLeftBottomHot Then
      Begin
         FLeftBottomHot := False;
         Repaint;
      End;
   End;
   If ssLeft In Shift Then
   Begin
      FMovePos := d2Point(X, Y);
      If FMove Then
      Begin
         Position.X := Position.X + (FMovePos.X - FDownPos.X);
         Position.Y := Position.Y + (FMovePos.Y - FDownPos.Y);
         If ParentBounds Then
         Begin
            If Position.X < 0 Then
               Position.X := 0;
            If Position.Y < 0 Then
               Position.Y := 0;
            If Position.X + Width > TD2VisualObject(Parent).Width Then
               Position.X := TD2VisualObject(Parent).Width - Width;
            If Position.Y + Height > TD2VisualObject(Parent).Height Then
               Position.Y := TD2VisualObject(Parent).Height - Height;
         End;
         If Assigned(FOnTrack) Then
            FOnTrack(Self);
         Exit;
      End;
      OldPos := Position.Point;
      P := LocalToAbsolute(FMovePos);
      If Parent <> nil Then
         P := TD2VisualObject(Parent).AbsolutetoLocal(P);
      If ParentBounds Then
      Begin
         If P.Y < 0 Then
            P.Y := 0;
         If P.X < 0 Then
            P.X := 0;
         If P.X > TD2VisualObject(Parent).Width Then
            P.X := TD2VisualObject(Parent).Width;
         If P.Y > TD2VisualObject(Parent).Height Then
            P.Y := TD2VisualObject(Parent).Height;
      End;
      If FLeftTop Then
      Begin
         Repaint;
         Size := d2Point((Position.X - (P.X + FDownPos.X)), (Position.Y - (P.Y + FDownPos.Y)));
         If Parent <> nil Then
            Size := d2PointFromVector(TD2VisualObject(Parent).LocalToAbsoluteVector(d2Vector(Size)));
         Size := d2PointFromVector(AbsoluteToLocalVector(d2Vector(Size)));

         If FProportional Then
         Begin
            Width := Width + Size.X;
            SaveW := Width;
            If Width < FMinSize Then
               Width := FMinSize;
            If Width / FRatio < FMinSize Then
               Width := Round(FMinSize * FRatio);
            Position.X := P.X + FDownPos.X - (Width - SaveW);
            Position.Y := Position.Y + (Height - Round(Width / FRatio));
            Height := Round(Width / FRatio);
         End Else Begin
            Width := Width + Size.X;
            Height := Height + Size.Y;
            Position.X := P.X + FDownPos.X;
            Position.Y := P.Y + FDownPos.Y;
            If Width < FMinSize Then
               Width := FMinSize;
            If Height < FMinSize Then
               Height := FMinSize;
         End;
         If Assigned(FOnTrack) Then
            FOnTrack(Self);
         Repaint;
      End;
      If FRightTop Then
      Begin
         Repaint;
         Size := d2Point((P.X{ + FDownPos.X}) - Position.X, (Position.Y - (P.Y + FDownPos.Y)));
         If Parent <> nil Then
            Size := d2PointFromVector(TD2VisualObject(Parent).LocalToAbsoluteVector(d2Vector(Size)));
         Size := d2PointFromVector(AbsoluteToLocalVector(d2Vector(Size)));

         Width := Size.X;
         If FProportional Then
         Begin
            If Width < FMinSize Then
               Width := FMinSize;
            If Width / FRatio < FMinSize Then
               Width := Round(FMinSize * FRatio);
            Position.Y := Position.Y + (Height - Round(Width / FRatio));
            Height := Round(Width / FRatio);
         End Else Begin
            Height := Height + Size.Y;
            Position.Y := P.Y + FDownPos.Y;
            If Width < FMinSize Then
               Width := FMinSize;
            If Height < FMinSize Then
               Height := FMinSize;
         End;
         If Assigned(FOnTrack) Then
            FOnTrack(Self);
         Repaint;
      End;
      If FRightBottom Then
      Begin
         Repaint;
         Size := d2Point((P.X{ + FDownPos.X}) - Position.X, (P.Y{ + FDownPos.Y)}) - Position.Y);
         If Parent <> nil Then
            Size := d2PointFromVector(TD2VisualObject(Parent).LocalToAbsoluteVector(d2Vector(Size)));
         Size := d2PointFromVector(AbsoluteToLocalVector(d2Vector(Size)));

         Width := Size.X;
         If FProportional Then
         Begin
            If Width < FMinSize Then
               Width := FMinSize;
            If Width / FRatio < FMinSize Then
               Width := Round(FMinSize * FRatio);
            Height := Round(Width / FRatio);
         End Else Begin
            Height := Size.Y;
            If Width < FMinSize Then
               Width := FMinSize;
            If Height < FMinSize Then
               Height := FMinSize;
         End;

         If Assigned(FOnTrack) Then
            FOnTrack(Self);
         Repaint;
      End;
      If FLeftBottom Then
      Begin
         Repaint;
         Size := d2Point((Position.X - (P.X + FDownPos.X)), (P.Y{ + FDownPos.Y)}) - Position.Y);
         If Parent <> nil Then
            Size := d2PointFromVector(TD2VisualObject(Parent).LocalToAbsoluteVector(d2Vector(Size)));
         Size := d2PointFromVector(AbsoluteToLocalVector(d2Vector(Size)));

         If FProportional Then
         Begin
            Width := Width + Size.X;
            SaveW := Width;
            If Width < FMinSize Then
               Width := FMinSize;
            If Width / FRatio < FMinSize Then
               Width := Round(FMinSize * FRatio);
            Position.X := P.X + FDownPos.X - (Width - SaveW);
            Height := Round(Width / FRatio);
         End Else Begin
            Width := Width + Size.X;
            Position.X := P.X + FDownPos.X;
            Height := Size.Y;
            If Width < FMinSize Then
               Width := FMinSize;
            If Height < FMinSize Then
               Height := FMinSize;
         End;
         If Assigned(FOnTrack) Then
            FOnTrack(Self);
         Repaint;
      End;
   End;
End;

Function TD2Selection.PointInObject(X, Y: Single): Boolean;
Var
   R: TD2Rect;
   P: TD2Point;
Begin
   Result := Inherited PointInObject(X, Y);
   If Not Result Then
   Begin
      P := AbsoluteToLocal(d2Point(X, Y));
      R := LocalRect;
      R := d2Rect(R.Left - (GripSize), R.Top - (GripSize), R.Left + (GripSize), R.Top + (GripSize));
      If d2PtInRect(P, R) Then
      Begin
         Result := True;
         Exit;
      End;
      R := LocalRect;
      R := d2Rect(R.Right - (GripSize), R.Top - (GripSize), R.Right + (GripSize), R.Top + (GripSize));
      If d2PtInRect(P, R) Then
      Begin
         Result := True;
         Exit;
      End;
      R := LocalRect;
      R := d2Rect(R.Right - (GripSize), R.Bottom - (GripSize), R.Right + (GripSize), R.Bottom + (GripSize));
      If d2PtInRect(P, R) Then
      Begin
         Result := True;
         Exit;
      End;
      R := LocalRect;
      R := d2Rect(R.Left - (GripSize), R.Bottom - (GripSize), R.Left + (GripSize), R.Bottom + (GripSize));
      If d2PtInRect(P, R) Then
      Begin
         Result := True;
         Exit;
      End;
   End;
End;

Procedure TD2Selection.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If FMove Or FLeftTop Or FLeftBottom Or FRightTop Or FRightBottom Then
   Begin
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
   FMove := False;
   FLeftTop := False;
   FLeftBottom := False;
   FRightTop := False;
   FRightBottom := False;
End;

Procedure TD2Selection.Paint;
Var
   R: TD2Rect;
Begin
   If FHideSelection Then
      Exit;
   R := LocalRect;
   d2InflateRect(R, -0.5, -0.5);
   Canvas.Fill.Style := d2BrushSolid;
   Canvas.Fill.SolidColor := $FFFFFFFF;
   Canvas.StrokeThickness := 1;
   Canvas.Stroke.Style := d2BrushSolid;
   Canvas.Stroke.SolidColor := $FF1072C5;
   Canvas.StrokeDash := d2DashDash;
   Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
   Canvas.StrokeDash := d2DashSolid;
   { angles }
   R := LocalRect;
   d2InflateRect(R, -0.5, -0.5);
   If FLeftTopHot Then
      Canvas.Fill.SolidColor := $FFFF0000
   Else
      Canvas.Fill.SolidColor := $FFFFFFFF;
   Canvas.Fillellipse(d2Rect(R.Left - (GripSize), R.Top - (GripSize), R.Left + (GripSize), R.Top + (GripSize)), AbsoluteOpacity);
   Canvas.DrawEllipse(d2Rect(R.Left - (GripSize), R.Top - (GripSize), R.Left + (GripSize), R.Top + (GripSize)), AbsoluteOpacity);
   R := LocalRect;
   If FRightTopHot Then
      Canvas.Fill.SolidColor := $FFFF0000
   Else
      Canvas.Fill.SolidColor := $FFFFFFFF;
   Canvas.Fillellipse(d2Rect(R.Right - (GripSize), R.Top - (GripSize), R.Right + (GripSize), R.Top + (GripSize)), AbsoluteOpacity);
   Canvas.DrawEllipse(d2Rect(R.Right - (GripSize), R.Top - (GripSize), R.Right + (GripSize), R.Top + (GripSize)), AbsoluteOpacity);
   R := LocalRect;
   If FLeftBottomHot Then
      Canvas.Fill.SolidColor := $FFFF0000
   Else
      Canvas.Fill.SolidColor := $FFFFFFFF;
   Canvas.Fillellipse(d2Rect(R.Left - (GripSize), R.Bottom - (GripSize), R.Left + (GripSize), R.Bottom + (GripSize)), AbsoluteOpacity);
   Canvas.DrawEllipse(d2Rect(R.Left - (GripSize), R.Bottom - (GripSize), R.Left + (GripSize), R.Bottom + (GripSize)), AbsoluteOpacity);
   R := LocalRect;
   If FRightBottomHot Then
      Canvas.Fill.SolidColor := $FFFF0000
   Else
      Canvas.Fill.SolidColor := $FFFFFFFF;
   Canvas.FillEllipse(d2Rect(R.Right - (GripSize), R.Bottom - (GripSize), R.Right + (GripSize), R.Bottom + (GripSize)), AbsoluteOpacity);
   Canvas.DrawEllipse(d2Rect(R.Right - (GripSize), R.Bottom - (GripSize), R.Right + (GripSize), R.Bottom + (GripSize)), AbsoluteOpacity);
End;

Procedure TD2Selection.MouseLeave;
Begin
   Inherited;
   FLeftTopHot := False;
   FLeftBottomHot := False;
   FRightTopHot := False;
   FRightBottomHot := False;
   Repaint;
End;

Procedure TD2Selection.SetHideSelection(Const Value: Boolean);
Begin
   If FHideSelection <> Value Then
   Begin
      FHideSelection := Value;
      Repaint;
   End;
End;

Procedure TD2Selection.SetMinSize(Const Value: Integer);
Begin
   If FMinSize <> Value Then
   Begin
      FMinSize := Value;
      If FMinSize < 1 Then
         FMinSize := 1;
   End;
End;

Procedure TD2Selection.SetGripSize(Const Value: Single);
Begin
   If FGripSize <> Value Then
   Begin
      FGripSize := Value;
      If FGripSize > 20 Then
         FGripSize := 20;
      If FGripSize < 1 Then
         FGripSize := 1;
      Repaint;
   End;
End;

//=============== TD2SelectionPoint ===============================================

Constructor TD2SelectionPoint.Create(AOwner: TComponent);
Begin
   Inherited;
   AutoCapture := True;
   DisableDesignResize := True;
   ParentBounds := True;
   FGripSize := 3;
   Width := FGripSize * 2;
   Height := FGripSize * 2;
End;

Destructor TD2SelectionPoint.Destroy;
Begin
   Inherited;
End;

Function TD2SelectionPoint.PointInObject(X, Y: Single): Boolean;
Var
   P: TD2Point;
Begin
   Result := False;
   P := AbsoluteToLocal(d2Point(X, Y));
   If (Abs(P.X) < GripSize) And (Abs(P.Y) < GripSize) Then
   Begin
      Result := True;
   End;
End;

Procedure TD2SelectionPoint.Paint;
Begin
   Inherited;
   Canvas.StrokeThickness := 1;
   Canvas.Stroke.Style := d2BrushSolid;
   Canvas.Stroke.SolidColor := $FF1072C5;
   Canvas.Fill.Style := d2BrushSolid;
   If IsMouseOver Then
      Canvas.Fill.SolidColor := $FFFF0000
   Else
      Canvas.Fill.SolidColor := $FFFFFFFF;
   If Assigned(FScene) And (FScene.GetSelected = Self) Then
      Canvas.Fill.SolidColor := $FFFF0000;

   Canvas.FillEllipse(d2Rect(-(GripSize), -(GripSize), (GripSize), (GripSize)), AbsoluteOpacity);
   Canvas.DrawEllipse(d2Rect(-(GripSize), -(GripSize), (GripSize), (GripSize)), AbsoluteOpacity);
End;

Procedure TD2SelectionPoint.SetHeight(Const Value: Single);
Begin
   Inherited SetHeight(FGripSize * 2);
End;

Procedure TD2SelectionPoint.SetWidth(Const Value: Single);
Begin
   Inherited SetWidth(FGripSize * 2);
End;

Procedure TD2SelectionPoint.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
      FPressed := True;
End;

Procedure TD2SelectionPoint.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   P: TD2Point;
Begin
   Inherited;
   If FPressed And (Parent <> nil) Then
   Begin
      P := LocalToAbsolute(d2Point(X, Y));
      P := Parent.Visual.AbsoluteToLocal(P);
      If ParentBounds Then
      Begin
         If P.X < 0 Then
            P.X := 0;
         If P.Y < 0 Then
            P.Y := 0;
         If P.X > Parent.Visual.Width Then
            P.X := Parent.Visual.Width;
         If P.Y > Parent.Visual.Height Then
            P.Y := Parent.Visual.Height;
      End;
      Position.X := P.X;
      Position.Y := P.Y;
      If Assigned(FOnTrack) Then
         FOnTrack(Self);
   End;
End;

Procedure TD2SelectionPoint.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If FPressed Then
   Begin
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
   FPressed := False;
End;

Procedure TD2SelectionPoint.MouseEnter;
Begin
   Inherited;
   Repaint;
End;

Procedure TD2SelectionPoint.MouseLeave;
Begin
   Inherited;
   Repaint;
End;

Function TD2SelectionPoint.GetUpdateRect: TD2Rect;
Begin
   Result := Inherited GetUpdateRect;
   d2InflateRect(Result, GripSize + 1, Gripsize + 1);
End;

Procedure TD2SelectionPoint.SetGripSize(Const Value: Single);
Begin
   If FGripSize <> Value Then
   Begin
      FGripSize := Value;
      If FGripSize > 20 Then
         FGripSize := 20;
      If FGripSize < 1 Then
         FGripSize := 1;
      Repaint;
   End;
End;


Function goodCompareObj(Const a1, a2: TObject): Integer;
Begin
   Result := Integer(Pointer(a1)) - Integer(Pointer(a2));
   If Result <> 0 Then
   Begin
      If Result < 0 Then
         Result := 1
      Else
         Result := -1;
      Exit;
   End;
End;

//================= TD2WideStrings =======================================

Constructor TD2WideStrings.Create;
Begin
   Inherited Create;
End;

Destructor TD2WideStrings.Destroy;
Begin
   Inherited;
End;

Function TD2WideStrings.CompareStrings(Const S1, S2: WideString): Integer;
Begin
   Result := 0;
End;

Procedure TD2WideStrings.Delete(Index: Integer);
Begin
End;

Function TD2WideStrings.Add(Const S: WideString): Integer;
Begin
   Result := GetCount;
   Insert(Result, S);
End;

Function TD2WideStrings.Items(AIndex: Integer): WideString;
Begin
   Result := Strings[AIndex];
End;

Function TD2WideStrings.AddObject(Const S: WideString; AObject: TObject): Integer;
Begin
   Result := Add(S);
   PutObject(Result, AObject);
End;

Procedure TD2WideStrings.Append(Const S: WideString);
Begin
   Add(S);
End;

Procedure TD2WideStrings.AddStrings(Strings: TStrings);
Var
   I: Integer;
Begin
   BeginUpdate;
   Try
      For I := 0 To Strings.Count - 1 Do
         AddObject(Strings[I], Strings.Objects[I]);
   Finally
      EndUpdate;
   End;
End;

Procedure TD2WideStrings.AddStrings(Strings: TD2WideStrings);
Var
   I: Integer;
Begin
   BeginUpdate;
   Try
      For I := 0 To Strings.Count - 1 Do
         AddObject(Strings[I], Strings.Objects[I]);
   Finally
      EndUpdate;
   End;
End;

Procedure TD2WideStrings.Assign(Source: TPersistent);
Var
   I: Integer;
Begin
   If Source Is TD2WideStrings Then
   Begin
      BeginUpdate;
      Try
         Clear;
         AddStrings(TD2WideStrings(Source));
      Finally
         EndUpdate;
      End;
   End Else
   If Source Is TStrings Then
   Begin
      BeginUpdate;
      Try
         Clear;
         For I := 0 To TStrings(Source).Count - 1 Do
            AddObject(TStrings(Source)[I], TStrings(Source).Objects[I]);
      Finally
         EndUpdate;
      End;
   End Else
      Inherited Assign(Source);
End;

Procedure TD2WideStrings.AssignTo(Dest: TPersistent);
Var
   I: Integer;
Begin
   If Dest Is TStrings Then
      With Dest As TStrings Do
      Begin
         BeginUpdate;
         Try
            Clear;
            For I := 0 To Self.Count - 1 Do
               AddObject(Self[I], Self.Objects[I]);
         Finally
            EndUpdate;
         End;
      End Else
   If Dest Is TD2WideStrings Then
      With Dest As TD2WideStrings Do
      Begin
         BeginUpdate;
         Try
            Clear;
            AddStrings(Self);
         Finally
            EndUpdate;
         End;
      End Else
      Inherited;
End;

Procedure TD2WideStrings.BeginUpdate;
Begin
   If FUpdateCount = 0 Then
      SetUpdateState(True);
   System.Inc(FUpdateCount);
End;

Procedure TD2WideStrings.Clear;
Begin
End;

Procedure TD2WideStrings.DefineProperties(Filer: TFiler);

   Function DoWrite: Boolean;
   Begin
      If Filer.Ancestor <> nil Then
      Begin
         Result := True;
         If Filer.Ancestor Is TD2WideStrings Then
            Result := Not Equals(TD2WideStrings(Filer.Ancestor));
      End Else
         Result := Count > 0;
   End;

Begin
   Filer.DefineProperty('strings', ReadData, WriteData, DoWrite);
End;

Procedure TD2WideStrings.EndUpdate;
Begin
   System.Dec(FUpdateCount);
   If FUpdateCount = 0 Then
      SetUpdateState(False);
End;

Function TD2WideStrings.Equals2d(Strings: TD2WideStrings): Boolean;
Var
   I, Count: Integer;
Begin
   Result := False;
   Count := GetCount;
   If Count <> Strings.GetCount Then
      Exit;
   For I := 0 To Count - 1 Do
      If Get(I) <> Strings.Get(I) Then
         Exit;
   Result := True;
End;

Procedure TD2WideStrings.Error(Const Msg: String; Data: Integer);
Begin
End;

Procedure TD2WideStrings.Exchange(Index1, Index2: Integer);
Var
   TempObject: TObject;
   TempString: WideString;
Begin
   BeginUpdate;
   Try
      TempString := Strings[Index1];
      TempObject := Objects[Index1];
      Strings[Index1] := Strings[Index2];
      Objects[Index1] := Objects[Index2];
      Strings[Index2] := TempString;
      Objects[Index2] := TempObject;
   Finally
      EndUpdate;
   End;
End;

Function TD2WideStrings.GetCapacity: Integer;
Begin
   Result := Count;
End;

Function TD2WideStrings.GetCommaText: WideString;
Var
   S: WideString;
   P: Pwidechar;
   I, Count: Integer;
Begin
   Count := GetCount;
   If (Count = 1) And (Get(0) = '') Then
      Result := ''
   Else Begin
      Result := '';
      For I := 0 To Count - 1 Do
      Begin
         S := Get(I);
         P := Pwidechar(S);
         While Not (P^ In [d2WideNull..d2WideSpace, System.Widechar('"'), System.Widechar(',')]) Do
            System.Inc(P);

         Result := Result + S + ',';
      End;
      System.Delete(Result, Length(Result), 1);
   End;
End;

Function TD2WideStrings.GetName(Index: Integer): WideString;
Var
   P: Integer;
Begin
   Result := Get(Index);
   P := Pos(WideString('='), Result);
   If P > 0 Then
      SetLength(Result, P - 1)
   Else
      Result := '';
End;

Function TD2WideStrings.GetObject(Index: Integer): TObject;
Begin
   Result := nil;
End;

Function TD2WideStrings.GetTextStr: WideString;
Var
   I, L, Size, Count: Integer;
   P: Pwidechar;
   S: WideString;
Begin
   Count := GetCount;
   Size := 0;
   For I := 0 To Count - 1 Do
      System.Inc(Size, Length(Get(I)) + 2);
   SetLength(Result, Size);
   P := Pointer(Result);
   For I := 0 To Count - 1 Do
   Begin
      S := Get(I);
      L := Length(S);
      If L <> 0 Then
      Begin
         System.Move(Pointer(S)^, P^, 2 * L);
         System.Inc(P, L);
      End;
      P^ := d2WideCarriageReturn;
      System.Inc(P);
      P^ := d2WideLineFeed;
      System.Inc(P);
   End;
End;

Function TD2WideStrings.GetValueFromIndex(Index: Integer): WideString;
Begin
   If Index >= 0 Then
      Result := Copy(Get(Index), Length(Names[Index]) + 2, System.MaxInt)
   Else
      Result := '';
End;

Procedure TD2WideStrings.SetValueFromIndex(Index: Integer; Const Value: WideString);
Begin
   If Value <> '' Then
   Begin
      If Index < 0 Then
         Index := Add('');
      Put(Index, Names[Index] + '=' + Value);
   End Else
   If Index >= 0 Then
      Delete(Index);
End;

Procedure TD2WideStrings.sort;
Begin
End;

Function TD2WideStrings.GetValue(Const Name: WideString): WideString;
Var
   I: Integer;
Begin
   I := IndexOfName(Name);
   If I >= 0 Then
      Result := Copy(Get(I), Length(Name) + 2, System.MaxInt)
   Else
      Result := '';
End;

Function TD2WideStrings.IndexOf(Const S: WideString): Integer;
Begin
   For Result := 0 To GetCount - 1 Do
   Begin
      If CompareStrings(Get(Result), S) = 0 Then
         Exit;
   End;
   Result := -1;
End;

Function TD2WideStrings.IndexOfName(Const Name: WideString): Integer;
Var
   P: Integer;
   S: WideString;
Begin
   For Result := 0 To GetCount - 1 Do
   Begin
      S := Get(Result);
      P := Pos(WideString('='), S);
      If (P > 0) And (CompareStrings(Copy(S, 1, P - 1), Name) = 0) Then
         Exit;
   End;
   Result := -1;
End;

Function TD2WideStrings.IndexOfValue(Const Name: WideString): Integer;
Var
   P: Integer;
   S: WideString;
Begin
   For Result := 0 To GetCount - 1 Do
   Begin
      S := Get(Result);
      P := Pos(WideString('='), S);
      If (P > 0) And (CompareStrings(Copy(S, P + 1, Length(S)), Name) = 0) Then
         Exit;
   End;
   Result := -1;
End;

Function TD2WideStrings.IndexOfObject(AObject: TObject): Integer;
Begin
   For Result := 0 To GetCount - 1 Do
      If GetObject(Result) = AObject Then
         Exit;
   Result := -1;
End;

Procedure TD2WideStrings.InsertObject(Index: Integer; Const S: WideString; AObject: TObject);
Begin
   Insert(Index, S);
   PutObject(Index, AObject);
End;

Procedure TD2WideStrings.LoadFromFile(Const FileName: String);
Var
   Stream: TStream;
Begin
   Try
      Stream := TFileStream.Create(FileName, fmOpenRead Or fmShareDenyNone);
      Try
         LoadFromStream(Stream);
      Finally
         Stream.Free;
      End;
   Except
      //RaiseLastWin32Error;
   End;
End;

Procedure TD2WideStrings.LoadFromStream(Stream: TStream);
Var
   Size: Integer;
   Order: System.Widechar;
   SW: WideString;
   SA: Ansistring;
Begin
   BeginUpdate;
   Try
      Size := Stream.Size - Stream.Position;
      Stream.Read(Order, 2);

      If (Order = BOM_LSB_FIRST) Or (Order = BOM_MSB_FIRST) Then
      Begin
         SetLength(SW, (Size - 2) Div 2);
         Stream.Read(Pwidechar(SW)^, Size - 2);
         SetTextStr(SW);
      End Else Begin
         Stream.Position := Stream.Position - 2;
         SetLength(SA, Size);
         Stream.Read(Pansichar(SA)^, Size);
         SetTextStr(SA);
      End;

   Finally
      EndUpdate;
   End;
End;

Procedure TD2WideStrings.Move(CurIndex, NewIndex: Integer);
Var
   TempObject: TObject;
   TempString: WideString;
Begin
   If CurIndex <> NewIndex Then
   Begin
      BeginUpdate;
      Try
         TempString := Get(CurIndex);
         TempObject := GetObject(CurIndex);
         Delete(CurIndex);
         InsertObject(NewIndex, TempString, TempObject);
      Finally
         EndUpdate;
      End;
   End;
End;

Procedure TD2WideStrings.Put(Index: Integer; Const S: WideString);
Var
   TempObject: TObject;
Begin
   TempObject := GetObject(Index);
   Delete(Index);
   InsertObject(Index, S, TempObject);
End;

Procedure TD2WideStrings.PutObject(Index: Integer; AObject: TObject);
Begin
End;

Procedure TD2WideStrings.ReadData(Reader: TReader);
Begin
   Reader.ReadListBegin;
   BeginUpdate;
   Try
      Clear;
      While Not Reader.EndOfList Do
         Add(Reader.ReadWideString);
   Finally
      EndUpdate;
   End;
   Reader.ReadListEnd;
End;

Procedure TD2WideStrings.SaveToFile(Const FileName: String);
Var
   Stream: TStream;
Begin
   Stream := TFileStream.Create(FileName, fmCreate);
   Try
      SaveToStream(Stream);
   Finally
      Stream.Free;
   End;
End;

Procedure TD2WideStrings.SaveToAnsiFile(Const FileName: String);
Var
   S: TStrings;
Begin
   S := TStringList.Create;
   Try
      S.Text := Text;
      S.SaveToFile(FileName);
   Finally
      S.Free;
   End;
End;

Procedure TD2WideStrings.SaveToStream(Stream: TStream);
Var
   SW, BOM: WideString;
Begin
   SW := GetTextStr;

   BOM := BOM_LSB_FIRST;
   Stream.WriteBuffer(Pwidechar(BOM)^, 2);

   Stream.WriteBuffer(Pwidechar(SW)^, 2 * Length(SW));
End;

Procedure TD2WideStrings.SetCapacity(NewCapacity: Integer);
Begin
End;

Procedure TD2WideStrings.SetCaseSensitive(Const Value: Boolean);
Begin
   If Value <> FCaseSensitive Then
   Begin
      FCaseSensitive := Value;
      If Sorted Then
         Sort;
   End;
End;

Procedure TD2WideStrings.SetCommaText(Const Value: WideString);
Var
   S: WideString;
   P, P1: Pwidechar;
Begin
   BeginUpdate;
   Try
      Clear;
      P := Pwidechar(Value);
      While P^ In [System.Widechar(#1)..d2WideSpace] Do
         System.Inc(P);
      While P^ <> d2WideNull Do
      Begin
         If P^ = '"' Then

         Else Begin
            P1 := P;
            While (P^ > d2WideSpace) And (P^ <> ',') Do
               System.Inc(P);
            System.SetString(S, P1, P - P1);
         End;
         Add(S);

         While P^ In [System.Widechar(#1)..d2WideSpace] Do
            System.Inc(P);
         If P^ = ',' Then
            Repeat
               System.Inc(P);
            Until Not (P^ In [System.Widechar(#1)..d2WideSpace]);
      End;
   Finally
      EndUpdate;
   End;
End;

Procedure TD2WideStrings.SetItem(index: Integer; AText: WideString);
Begin
   Strings[index] := AText;
End;

Procedure TD2WideStrings.SetSorted(Const Value: Boolean);
Begin
   If FSorted <> Value Then
   Begin
      If Value Then
         Sort;
      FSorted := Value;
   End;
End;

Procedure TD2WideStrings.SetText(Text: Pwidechar);
Begin
   SetTextStr(Text);
End;

Procedure TD2WideStrings.SetTextStr(Const Value: WideString);
Var
   Head, Tail: Pwidechar;
   S: WideString;
Begin
   BeginUpdate;
   Try
      Clear;
      Head := Pwidechar(Value);
      While Head^ <> d2WideNull Do
      Begin
         Tail := Head;
         While Not (Tail^ In [d2WideNull, d2WideLineFeed, d2WideCarriageReturn, d2WideVerticalTab, d2WideFormFeed]) And (Tail^ <> d2WideLineSeparator) And (Tail^ <> d2WideParagraphSeparator) Do
            System.Inc(Tail);
         System.SetString(S, Head, Tail - Head);
         Add(S);
         Head := Tail;
         If Head^ <> d2WideNull Then
         Begin
            System.Inc(Head);
            If (Tail^ = d2WideCarriageReturn) And (Head^ = d2WideLineFeed) Then
               System.Inc(Head);
         End;
      End;
   Finally
      EndUpdate;
   End;
End;

Procedure TD2WideStrings.SetUpdateState(Updating: Boolean);
Begin
End;

Procedure TD2WideStrings.SetValue(Const Name, Value: WideString);
Var
   I: Integer;
Begin
   I := IndexOfName(Name);
   If Value <> '' Then
   Begin
      If I < 0 Then
         I := Add('');
      Put(I, Name + '=' + Value);
   End Else
   If I >= 0 Then
      Delete(I);
End;

Procedure TD2WideStrings.WriteData(Writer: TWriter);
Var
   I: Integer;
Begin
   Writer.WriteListBegin;
   For I := 0 To Count - 1 Do
      Writer.WriteWideString(Get(I));
   Writer.WriteListEnd;
End;

//================ TD2WideStringList ================================

Destructor TD2WideStringList.Destroy;
Begin
   FOnChange := nil;
   FOnChanging := nil;
   Inherited Destroy;
   If FCount <> 0 Then
      Finalize(FList^[0], FCount);
   FCount := 0;
   SetCapacity(0);
End;

Function TD2WideStringList.Add(Const S: WideString): Integer;
Begin
   If Not Sorted Then
      Result := FCount
   Else Begin
      If sortByObject Then
      Begin
         Result := FCount;
      End Else Begin
         If Find(S, Result) Then
            Case Duplicates Of
               dupIgnore:
                  Exit;
               dupError:
                  Error('Doublicate string', 0);
            End;
      End;
   End;
   InsertItem(Result, S);
End;

Procedure TD2WideStringList.Changed;
Begin
   If (FUpdateCount = 0) And Assigned(FOnChange) Then
      FOnChange(Self);
End;

Procedure TD2WideStringList.Changing;
Begin
   If (FUpdateCount = 0) And Assigned(FOnChanging) Then
      FOnChanging(Self);
End;

Procedure TD2WideStringList.Clear;
Begin
   If FCount <> 0 Then
   Begin
      Changing;
      Finalize(FList^[0], FCount);
      FCount := 0;
      SetCapacity(0);
      Changed;
   End;
End;

Function TD2WideStringList.CompareStrings(Const S1, S2: WideString): Integer;
Begin
   If CaseSensitive Then
      Result := WideCompareStr(S1, S2)
   Else
      Result := WideCompareText(S1, S2);
End;

Procedure TD2WideStringList.Delete(Index: Integer);
Begin
   If (Index < 0) Or (Index >= FCount) Then
      Error('List index error', Index);
   Changing;
   FList[Index].FString := '';
   System.Dec(FCount);
   If Index < FCount Then
      System.Move(FList[Index + 1], FList[Index], (FCount - Index) * SizeOf(TWideStringItem));
   Changed;
End;

Procedure TD2WideStringList.Exchange(Index1, Index2: Integer);
Begin
   If (Index1 < 0) Or (Index1 >= FCount) Then
      Error('List index error', Index1);
   If (Index2 < 0) Or (Index2 >= FCount) Then
      Error('List index error', Index2);
   Changing;
   ExchangeItems(Index1, Index2);
   Changed;
End;

Procedure TD2WideStringList.ExchangeItems(Index1, Index2: Integer);
Var
   P1, P2: Pointer;
Begin
   P1 := Pointer(Flist^[Index1].FString);
   P2 := Pointer(Flist^[Index1].FObject);
   Pointer(Flist^[Index1].FString) := Pointer(Flist^[Index2].FString);
   Pointer(Flist^[Index1].FObject) := Pointer(Flist^[Index2].FObject);
   Pointer(Flist^[Index2].FString) := P1;
   Pointer(Flist^[Index2].FObject) := P2;
End;

Function TD2WideStringList.Find(Const S: WideString; Var Index: Integer): Boolean;
Var
   L, H, I, C: Integer;
Begin
   Result := False;
   L := 0;
   H := FCount - 1;
   While L <= H Do
   Begin
      I := (L + H) Shr 1;
      C := CompareStrings(FList^[I].FString, S);
      If C < 0 Then
         L := I + 1
      Else Begin
         H := I - 1;
         If C = 0 Then
         Begin
            Result := True;
            If Duplicates <> dupAccept Then
               L := I;
         End;
      End;
   End;
   Index := L;
End;

Function TD2WideStringList.FindByObject(Const S: TObject; Var Index: Integer): Boolean;
Var
   L, H, I, C: Integer;
Begin
   Result := False;
   L := 0;
   H := FCount - 1;
   While L <= H Do
   Begin
      I := (L + H) Shr 1;
      C := goodCompareObj(FList^[I].FObject, S);
      If C < 0 Then
         L := I + 1
      Else Begin
         H := I - 1;
         If C = 0 Then
         Begin
            Result := True;
            If Duplicates <> dupAccept Then
               L := I;
         End;
      End;
   End;
   Index := L;
End;

Function TD2WideStringList.Get(Index: Integer): WideString;
Begin
   If (Index < 0) Or (Index >= FCount) Then
   Begin
      Error('List index error', Index);
      Result := '';
   End Else
      Result := FList[Index].FString;
End;

Function TD2WideStringList.GetCapacity: Integer;
Begin
   Result := FCapacity;
End;

Function TD2WideStringList.GetCount: Integer;
Begin
   Result := FCount;
End;

Function TD2WideStringList.GetObject(Index: Integer): TObject;
Begin
   If (Index < 0) Or (Index >= FCount) Then
      Error('List index error', Index);
   Result := FList[Index].FObject;
End;

Procedure TD2WideStringList.Grow;
Var
   Delta: Integer;
Begin
   If FCapacity > 64 Then
      Delta := FCapacity Div 4
   Else
   If FCapacity > 8 Then
      Delta := 16
   Else
      Delta := 4;
   SetCapacity(FCapacity + Delta);
End;

Function TD2WideStringList.IndexOf(Const S: WideString): Integer;
Begin
   If Not Sorted Then
      Result := Inherited IndexOf(S)
   Else
   If Not Find(S, Result) Then
      Result := -1;
End;

Procedure TD2WideStringList.Insert(Index: Integer; Const S: WideString);
Begin
   If Sorted Then
      Error('Sorted list error', 0);
   If (Index < 0) Or (Index > FCount) Then
      Error('List index error', Index);
   InsertItem(Index, S);
End;

Procedure TD2WideStringList.InsertItem(Index: Integer; Const S: WideString);
Begin
   Changing;
   If FCount = FCapacity Then
      Grow;
   If Index < FCount Then
      System.Move(FList^[Index], FList^[Index + 1],
         (FCount - Index) * SizeOf(TStringItem));
   With FList^[Index] Do
   Begin
      Pointer(FString) := nil;
      FObject := nil;
      FString := S;
   End;
   System.Inc(FCount);
   Changed;
End;

Procedure TD2WideStringList.Put(Index: Integer; Const S: WideString);
Begin
   If Sorted Then
      Error('Sorted list error', 0);
   If (Index < 0) Or (Index >= FCount) Then
      Error('List index error', Index);
   Changing;
   FList[Index].FString := S;
   Changed;
End;

Procedure TD2WideStringList.PutObject(Index: Integer; AObject: TObject);
Begin
   If (Index < 0) Or (Index >= FCount) Then
      Error('List index error', Index);
   Changing;
   FList[Index].FObject := AObject;
   Changed;
End;

Procedure TD2WideStringList.QuickSort(L, R: Integer);
Var
   I, J: Integer;
   P: WideString;
Begin
   Repeat
      I := L;
      J := R;
      P := FList[(L + R) Shr 1].FString;
      Repeat

         While WideCompareText(FList[I].FString, P) < 0 Do
            System.Inc(I);
         While WideCompareText(FList[J].FString, P) > 0 Do
            System.Dec(J);

         If I <= J Then
         Begin
            ExchangeItems(I, J);
            System.Inc(I);
            System.Dec(J);
         End;
      Until I > J;
      If L < J Then
         QuickSort(L, J);
      L := I;
   Until I >= R;
End;

Procedure TD2WideStringList.QuickSortByObject(L, R: Integer);
Var
   I, J: Integer;
   P: TObject;
Begin
   Repeat
      I := L;
      J := R;
      P := FList[(L + R) Shr 1].FObject;
      Repeat
         While goodCompareObj(FList[I].FObject, P) < 0 Do
            System.Inc(I);
         While goodCompareObj(FList[J].FObject, P) > 0 Do
            System.Dec(J);
         If I <= J Then
         Begin
            ExchangeItems(I, J);
            System.Inc(I);
            System.Dec(J);
         End;
      Until I > J;
      If L < J Then
         QuickSortByObject(L, J);
      L := I;
   Until I >= R;
End;

Procedure TD2WideStringList.SetCapacity(NewCapacity: Integer);
Begin
   ReallocMem(FList, NewCapacity * SizeOf(TWideStringItem));
   FCapacity := NewCapacity;
End;

Procedure TD2WideStringList.SetUpdateState(Updating: Boolean);
Begin
   If Updating Then
      Changing
   Else
      Changed;
End;

Procedure TD2WideStringList.Sort;
Begin
   If Not Sorted And (Count > 1) Then
   Begin
      Changing;
      If FsortByObject Then
         QuickSortByObject(0, FCount - 1)
      Else
         QuickSort(0, FCount - 1);
      Changed;
   End;
End;


Procedure CollectLangStart;
Begin
   ResetLang;
   If varD2CollectLang = nil Then
   Begin
      varD2CollectLang := TD2WideStringList.Create;
      varD2CollectLang.Sorted := True;
      TD2WideStringList(varD2CollectLang).CaseSensitive := True;
   End;
End;

Procedure CollectLangFinish;
Begin
   If varD2CollectLang <> nil Then
      FreeAndNil(varD2CollectLang);
End;

Function CollectLangStrings: TD2WideStrings;
Begin
   Result := varD2CollectLang;
End;

Procedure CollectLangSave;
Begin
   If varD2CollectLang <> nil Then
   Begin
      varD2CollectLang.SaveToFile(ExtractFilePath(ParamStr(0)) + 'lang.lng');
   End;
End;

Procedure LoadLangFromFile(AFileName: String);
Begin
   If Not FileExists(AFileName) Then
      Exit;
   ResetLang;
   If varD2Lang = nil Then
   Begin
      varD2Lang := TD2WideStringList.Create;
      varD2Lang.Sorted := True;
      TD2WideStringList(varD2Lang).CaseSensitive := True;
   End;
   varD2Lang.LoadFromFile(AFileName);
   UpdateLang;
End;

Procedure LoadLangFromStrings(AStr: TD2WideStrings);
Begin
   If AStr = nil Then
      Exit;
   ResetLang;
   If varD2Lang = nil Then
   Begin
      varD2Lang := TD2WideStringList.Create;
      varD2Lang.Sorted := True;
      TD2WideStringList(varD2Lang).CaseSensitive := True;
   End;
   varD2Lang.Assign(AStr);
   UpdateLang;
End;

Procedure ResetLang;
Begin
   If varD2Lang <> nil Then
   Begin
      varD2NeedResetLang := True;
      Try
         UpdateLang;
      Finally
         varD2NeedResetLang := False;
         FreeAndNil(varD2Lang);
      End;
   End;
End;

Procedure UpdateLang;
Var
   i: Integer;
Begin
   If varD2SceneList <> nil Then
   Begin
      For i := 0 To varD2SceneList.Count - 1 Do
         Id2Scene(varD2SceneList[i]).UpdateResource;
   End;
End;

Function Translate(Const AText: WideString): WideString;
Begin
   If AText = '' Then
   Begin
      Result := '';
      Exit;
   End;
   If Assigned(GvarD2CustomTranslateProc) Then
   Begin
      If GvarD2CustomTranslateProc(AText) <> '' Then
         Result := GvarD2CustomTranslateProc(AText)
      Else
         Result := AText;
      Exit;
   End;
   If varD2CollectLang <> nil Then
   Begin
      varD2CollectLang.Add(AText);
   End;
   If varD2Lang <> nil Then
   Begin
      If Not varD2NeedResetLang Then
         Result := varD2Lang.Values[AText]
      Else
      If varD2Lang.IndexOfValue(AText) >= 0 Then
         Result := varD2Lang.Names[varD2Lang.IndexOfValue(AText)];
      If Result = '' Then
         Result := AText;
   End Else
      Result := AText;
End;

Function TranslateText(Const AText: WideString): WideString;
Begin
   Result := Translate(AText);
End;


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

Function d2GetToken(Var S: Ansistring; Separators: Ansistring; Stop: Ansistring = ''): Ansistring;
Var
   i, Len: Integer;
   CopyS: Ansistring;
Begin
   Result := '';
   CopyS := S;
   Len := Length(CopyS);
   For i := 1 To Len Do
   Begin
      If Pos(CopyS[i], Stop) > 0 Then
         Break;
      Delete(S, 1, 1);
      If Pos(CopyS[i], Separators) > 0 Then
      Begin
         Result := Result;
         Break;
      End;
      Result := Result + CopyS[i];
   End;
   Result := Trim(Result);
   S := Trim(S);
End;

Function d2WideGetToken(Var Pos: Integer; Const S: WideString; Separators: WideString; Stop: WideString = ''): WideString;
Var
   i, Len: Integer;
Begin
   Result := '';
   Len := Length(S);

   For i := Pos To Len Do
      If System.Pos(S[i], Separators) <= 0 Then
         Break;
   Pos := i;

   For i := Pos To Len Do
   Begin
      If System.Pos(S[i], Stop) > 0 Then
         Break;
      If System.Pos(S[i], Separators) > 0 Then
         Break;
      Result := Result + S[i];
   End;

   Pos := i;
   For i := Pos To Len Do
      If System.Pos(S[i], Separators) <= 0 Then
         Break;
   Pos := i;
End;

Procedure d2ReverseBytes(p: Pointer; Count: Integer);
Var
   p1: PChar;
   p2: PChar;
   c: Char;
Begin
   p1 := PChar(p);
   p2 := PChar(p) + Count - 1;
   While p1 < p2 Do
   Begin
      c := p1^;
      p1^ := p2^;
      p2^ := c;
      System.Inc(p1);
      System.Dec(p2);
   End;
End;

Procedure d2MoveLongword(Const Src: Pointer; Dst: Pointer; Count: Integer);
Begin
   System.Move(Src^, Dst^, Count * 4);
End;

Procedure d2FillLongword(Src: Pointer; Count: Integer; Value: Longword);
Begin
   System.FillDWord(Src^, Count, Value);
End;

Procedure d2FillAlpha(Src: Pointer; Count: Integer; Alpha: Byte);
Var
   i: Integer;
Begin
   For i := 0 To Count - 1 Do
      PD2ColorRecArray(Src)[i].A := Alpha;
End;

Procedure d2FillLongwordRect(Src: Pointer; W, H, X1, Y1, X2, Y2: Integer; Value: Longword);
Var
   j: Integer;
Begin
   If x2 > W Then
      x2 := W;
   If y2 > H Then
      y2 := H;
   If x1 > x2 Then
      x1 := x1;
   If y1 > y2 Then
      y1 := y2;
   For j := y1 To y2 - 1 Do
      d2FillLongword(@PD2ColorArray(Src)[X1 + (j * W)], X2 - X1, Value);
End;


Const
   X = 0;
   Y = 1;
   Z = 2;
   W = 3;

Function d2MaxFloat(A1, A2: Single): Single;
Begin
   If A1 > A2 Then
      Result := A1
   Else
      Result := A2;
End;

Function d2MinFloat(A1, A2: Single): Single;
Begin
   If A1 < A2 Then
      Result := A1
   Else
      Result := A2;
End;

Function d2MinMax(x, mi, ma: Single): Single;
Begin
   If (x < mi) Then
      Result := mi
   Else
   If (x > ma) Then
      Result := ma
   Else
      Result := x;
End;

Procedure d2SinCos(Const Theta: Single; Var Sin, Cos: Single);
Var
   s, c: Extended;
Begin
   Math.SinCos(Theta, s, c);
   Sin := s;
   Cos := c;
End;

Function d2RadToDeg(Const Degrees: Single): Single;
Begin
   Result := Degrees * c180divPI;
End;

Function d2DegToRad(Const Degrees: Single): Single;
Begin
   Result := Degrees * cPIdiv180;
End;

Function d2NormalizeAngle(Const angle: Single): Single;
Begin
   Result := angle - Trunc(angle * cInv360) * c360;
   If Result < -c180 Then
      Result := Result + c360;
End;

Function d2Point(Const X, Y: Single): TD2Point;
Begin
   Result.X := X;
   Result.Y := Y;
End;

Function d2MinPoint(P1, P2: TD2Point): TD2Point;
Begin
   Result := P1;
   If (P2.y < P1.y) Or ((P2.y = P1.y) And (P2.x < P1.x)) Then
      Result := P2;
End;

Function d2ScalePoint(P: TD2Point; dx, dy: Single): TD2Point;
Begin
   Result.X := P.X * dx;
   Result.Y := P.Y * dy;
End;

Function d2Rect(Const ALeft, ATop, ARight, ABottom: Single): TD2Rect;
Begin
   Result.Left := ALeft;
   Result.Top := ATop;
   Result.Right := ARight;
   Result.Bottom := ABottom;
End;

Function d2NormalizeRect(Const Pts: Array Of TD2Point): TD2Rect;
Var
   i: Integer;
Begin
   Result.Left := $F000;
   Result.Top := $F000;
   Result.Right := -$F000;
   Result.Bottom := -$F000;
   For i := 0 To High(Pts) Do
   Begin
      If Pts[i].X < Result.Left Then
         Result.Left := Pts[i].X;
      If Pts[i].Y < Result.Top Then
         Result.Top := Pts[i].Y;
      If Pts[i].X > Result.Right Then
         Result.Right := Pts[i].X;
      If Pts[i].Y > Result.Bottom Then
         Result.Bottom := Pts[i].Y;
   End;
End;

Function d2NormalizeRect2(Const ARect: TD2Rect): TD2Rect;
Begin
   With ARect Do
      Result := d2NormalizeRect([d2Point(Left, Top), d2Point(Right, Top), d2Point(Right, Bottom), d2Point(Left, Bottom)]);
End;

Procedure d2OffsetRect(Var R: TD2Rect; Const Dx, Dy: Single);
Begin
   R.Left := R.Left + Dx;
   R.Right := R.Right + Dx;
   R.Top := R.Top + Dy;
   R.Bottom := R.Bottom + Dy;
End;

Procedure d2MultiplyRect(Var R: TD2Rect; Const Dx, Dy: Single);
Begin
   R.Left := R.Left * Dx;
   R.Right := R.Right * Dx;
   R.Top := R.Top * Dy;
   R.Bottom := R.Bottom * Dy;
End;

Procedure d2InflateRect(Var R: TD2Rect; Const Dx, Dy: Single);
Begin
   R.Left := R.Left - Dx;
   R.Right := R.Right + Dx;
   R.Top := R.Top - Dy;
   R.Bottom := R.Bottom + Dy;
End;

Function d2UnionRect(Const ARect1, ARect2: TD2Rect): TD2Rect;
Begin
   Result.Left := ARect1.Left;
   If ARect2.Left < Result.Left Then
      Result.Left := ARect2.Left;

   Result.Top := ARect1.Top;
   If ARect2.Top < Result.Top Then
      Result.Top := ARect2.Top;

   Result.Right := ARect1.Right;
   If ARect2.Right > Result.Right Then
      Result.Right := ARect2.Right;

   Result.Bottom := ARect1.Bottom;
   If ARect2.Bottom > Result.Bottom Then
      Result.Bottom := ARect2.Bottom;
End;

Function d2RectWidth(Const R: TD2Rect): Single;
Begin
   Result := R.Right - R.Left;
End;

Function d2RectHeight(Const R: TD2Rect): Single;
Begin
   Result := R.Bottom - R.Top;
End;

Function d2RectCenter(Var R: TD2Rect; Bounds: TD2Rect): TD2Rect;
Begin
   d2OffsetRect(R, -R.Left, -R.Top);
   d2OffsetRect(R, round((d2RectWidth(Bounds) - d2RectWidth(R)) / 2), round((d2RectHeight(Bounds) - d2RectHeight(R)) / 2));
   d2OffsetRect(R, Bounds.Left, Bounds.Top);

   Result := R;
End;

Function d2FitRect(Var R: TD2Rect; BoundsRect: TD2Rect): Single;
Var
   ratio: Single;
Begin
   Result := 1;
   If d2RectWidth(BoundsRect) * d2RectHeight(BoundsRect) = 0 Then
      Exit;

   If (d2RectWidth(R) / d2RectWidth(BoundsRect)) > (d2RectHeight(R) / d2RectHeight(BoundsRect)) Then
      ratio := d2RectWidth(R) / d2RectWidth(BoundsRect)
   Else
      ratio := d2RectHeight(R) / d2RectHeight(BoundsRect);

   If ratio < 1 Then
   Begin
      R := d2Rect(0, 0, d2RectWidth(R), d2RectHeight(R));
   End Else Begin
      R := d2Rect(0, 0, round(d2RectWidth(R) / ratio), round(d2RectHeight(R) / ratio));
   End;

   Result := ratio;
   d2RectCenter(R, BoundsRect);
End;

Function d2IsRectEmpty(Rect: TD2Rect): Boolean;
Begin
   Result := (d2RectWidth(Rect) <= 0) Or (d2RectHeight(Rect) <= 0);
End;

Function d2IntersectRect(Const Rect1, Rect2: TD2Rect): Boolean;
Begin
   Result := (Rect1.Left <= Rect2.Right) And (Rect1.Right >= Rect2.Left) And (Rect1.Top <= Rect2.Bottom) And (Rect1.Bottom >= Rect2.Top);
End;

Function d2IntersectRect(Var DestRect: TD2Rect; Const SrcRect1, SrcRect2: TD2Rect): Boolean;
Begin
   Result := (SrcRect2.Left < SrcRect1.Right) And (SrcRect2.Right > SrcRect1.Left) And (SrcRect2.Top < SrcRect1.Bottom) And (SrcRect2.Bottom > SrcRect1.Top);

   If Result Then
   Begin
      DestRect.Left := Max(SrcRect1.Left, SrcRect2.Left);
      DestRect.Top := Max(SrcRect1.Top, SrcRect2.Top);
      DestRect.Right := Min(SrcRect1.Right, SrcRect2.Right);
      DestRect.Bottom := Min(SrcRect1.Bottom, SrcRect2.Bottom);
   End Else Begin
      DestRect.Left := 0;
      DestRect.Top := 0;
      DestRect.Right := 0;
      DestRect.Bottom := 0;
   End;
End;

Function d2PtInRect(Const P: TD2Point; Const Rect: TD2Rect): Boolean;
Begin
   Result := (P.X >= Rect.Left) And (P.X <= Rect.Right) And (P.Y >= Rect.Top) And (P.Y <= Rect.Bottom);
End;

Function d2PtInRect(Const PX, PY, RLeft, RRight, RTop, RBottom: Single): Boolean;
Begin
   Result := (PX >= RLeft) And (PX <= RRight) And (PY >= RTop) And (PY <= RBottom);
End;

Function d2RectToString(R: TD2Rect): Ansistring;
Begin
   Result := '(' + d2FloatToStr(R.Left) + ',' + d2FloatToStr(R.Top) + ',' + d2FloatToStr(R.Right) + ',' + d2FloatToStr(R.Bottom) + ')';
End;

Function d2StringToRect(S: Ansistring): TD2Rect;
Begin
   Try
      d2GetToken(S, ',()');
      Result.Left := d2StrToFloat(d2GetToken(S, ',()'));
      Result.Top := d2StrToFloat(d2GetToken(S, ',()'));
      Result.Right := d2StrToFloat(d2GetToken(S, ',()'));
      Result.Bottom := d2StrToFloat(d2GetToken(S, ',()'));
   Except
      Result := d2Rect(0, 0, 0, 0);
   End;
End;

Function d2PointFromVector(Const v: TD2Vector): TD2Point;
Begin
   Result.x := v.x;
   Result.y := v.y;
End;

Function d2PointToString(R: TD2Point): Ansistring;
Begin
   Result := '(' + d2FloatToStr(R.X) + ',' + d2FloatToStr(R.Y) + ')';
End;

Function d2StringToPoint(S: Ansistring): TD2Point;
Begin
   Try
      d2GetToken(S, ',()');
      Result.X := d2StrToFloat(d2GetToken(S, ',()'));
      Result.Y := d2StrToFloat(d2GetToken(S, ',()'));
   Except
      Result := d2Point(0, 0);
   End;
End;

Function d2MatrixMultiply(Const M1, M2: TD2Matrix): TD2Matrix;
Begin
   Result.m11 := M1.m11 * M2.m11 + M1.m12 * M2.m21 + M1.m13 * M2.m31;
   Result.m12 := M1.m11 * M2.m12 + M1.m12 * M2.m22 + M1.m13 * M2.m32;
   Result.m13 := M1.m11 * M2.m13 + M1.m12 * M2.m23 + M1.m13 * M2.m33;
   Result.m21 := M1.m21 * M2.m11 + M1.m22 * M2.m21 + M1.m23 * M2.m31;
   Result.m22 := M1.m21 * M2.m12 + M1.m22 * M2.m22 + M1.m23 * M2.m32;
   Result.m23 := M1.m21 * M2.m13 + M1.m22 * M2.m23 + M1.m23 * M2.m33;
   Result.m31 := M1.m31 * M2.m11 + M1.m32 * M2.m21 + M1.m33 * M2.m31;
   Result.m32 := M1.m31 * M2.m12 + M1.m32 * M2.m22 + M1.m33 * M2.m32;
   Result.m33 := M1.m31 * M2.m13 + M1.m32 * M2.m23 + M1.m33 * M2.m33;
End;

Function d2MatrixDeterminant(Const M: TD2Matrix): Single;
Begin
   Result := M.M[X].V[X] * (M.M[Y].V[Y] * M.M[Z].V[Z] - M.M[Z].V[Y] * M.M[Y].V[Z]) - M.M[X].V[Y] * (M.M[Y].V[X] * M.M[Z].V[Z] - M.M[Z].V[X] * M.M[Y].V[Z]) + M.M[X].V[Z] * (M.M[Y].V[X] * M.M[Z].V[Y] - M.M[Z].V[X] * M.M[Y].V[Y]);
End;

Procedure d2AdjointMatrix(Var M: TD2Matrix);
Var
   a1, a2, a3, b1, b2, b3, c1, c2, c3: Single;
Begin
   a1 := M.M[X].V[X];
   a2 := M.M[X].V[Y];
   a3 := M.M[X].V[Z];
   b1 := M.M[Y].V[X];
   b2 := M.M[Y].V[Y];
   b3 := M.M[Y].V[Z];
   c1 := M.M[Z].V[X];
   c2 := M.M[Z].V[Y];
   c3 := M.M[Z].V[Z];
   M.M[X].V[X] := (b2 * c3 - c2 * b3);
   M.M[Y].V[X] := -(b1 * c3 - c1 * b3);
   M.M[Z].V[X] := (b1 * c2 - c1 * b2);

   M.M[X].V[Y] := -(a2 * c3 - c2 * a3);
   M.M[Y].V[Y] := (a1 * c3 - c1 * a3);
   M.M[Z].V[Y] := -(a1 * c2 - c1 * a2);

   M.M[X].V[Z] := (a2 * b3 - b2 * a3);
   M.M[Y].V[Z] := -(a1 * b3 - b1 * a3);
   M.M[Z].V[Z] := (a1 * b2 - b1 * a2);
End;

Procedure d2ScaleMatrix(Var M: TD2Matrix; Const factor: Single);
Var
   i: Integer;
Begin
   For i := 0 To 2 Do
   Begin
      M.M[I].V[0] := M.M[I].V[0] * Factor;
      M.M[I].V[1] := M.M[I].V[1] * Factor;
      M.M[I].V[2] := M.M[I].V[2] * Factor;
   End;
End;

Procedure d2InvertMatrix(Var M: TD2Matrix);
Var
   det: Single;
Begin
   det := d2MatrixDeterminant(M);
   If Abs(Det) < EPSILON Then
      M := IdentityMatrix
   Else Begin
      d2AdjointMatrix(M);
      d2ScaleMatrix(M, 1 / det);
   End;
End;

Function d2Vector(Const x, y: Single; Const w: Single = 1.0): TD2Vector;
Begin
   Result.X := x;
   Result.Y := y;
   Result.W := w;
End;

Function d2Vector(Const P: TD2Point; Const w: Single = 1.0): TD2Vector;
Begin
   Result.X := P.x;
   Result.Y := P.y;
   Result.W := w;
End;

Function d2VectorTransform(Const V: TD2Vector; Const M: TD2Matrix): TD2Vector;
Begin
   Result.V[X] := V.V[X] * M.M[X].V[X] + V.V[Y] * M.M[Y].V[X] + V.V[Z] * M.M[Z].V[X];
   Result.V[Y] := V.V[X] * M.M[X].V[Y] + V.V[Y] * M.M[Y].V[Y] + V.V[Z] * M.M[Z].V[Y];
   Result.V[2] := 1.0;
End;

Function d2VectorAdd(Const v1: TD2Vector; Const v2: TD2Vector): TD2Vector;
Begin
   Result.v[0] := v1.v[0] + v2.v[0];
   Result.v[1] := v1.v[1] + v2.v[1];
   Result.W := 1.0;
End;

Function d2VectorSubtract(Const v1: TD2Vector; Const v2: TD2Vector): TD2Vector;
Begin
   Result.v[0] := v1.v[0] - v2.v[0];
   Result.v[1] := v1.v[1] - v2.v[1];
   Result.W := 1.0;
End;

Function d2VectorNorm(Const v: TD2Vector): Single;
Begin
   Result := v.V[0] * v.V[0] + v.V[1] * v.V[1];
End;

Function RSqrt(v: Single): Single;
Var
   R: Double;
Begin
   R := Abs(V);
   If (R > 0) Then
      Result := 1 / Sqrt(R)
   Else
      Result := 1;
End;

Function d2VectorNormalize(Const v: TD2Vector): TD2Vector;
Var
   invLen: Single;
Begin
   invLen := RSqrt(Abs(d2VectorNorm(v)));
   Result.v[0] := v.v[0] * invLen;
   Result.v[1] := v.v[1] * invLen;
   Result.v[2] := 0.0;
End;

Function d2VectorScale(Const v: TD2Vector; factor: Single): TD2Vector;
Begin
   Result.v[0] := v.v[0] * factor;
   Result.v[1] := v.v[1] * factor;
   Result.W := 1;
End;

Function d2VectorLength(Const v: TD2Vector): Single;
Begin
   Result := Sqrt(d2Vectornorm(v));
End;

Function d2VectorDotProduct(Const V1, V2: TD2Vector): Single;
Begin
   Result := V1.V[0] * V2.V[0] + V1.V[1] * V2.V[1];
End;

Function d2VectorAngleCosine(Const V1, V2: TD2Vector): Single;
Begin
   If (d2VectorLength(V1) <> 0) And (d2VectorLength(V2) <> 0) Then
   Begin
      Result := d2VectorDotProduct(V1, V2) / (d2VectorLength(V1) * d2VectorLength(V2));
      If Result > 1 Then
         Result := 1;
   End Else
      Result := 0;
End;

Function d2VectorCrossProductZ(Const V1, V2: TD2Vector): Single;
Begin
   Result := v1.X * v2.Y - v1.Y * v2.X;
End;

Function d2VectorCombine2(Const V1, V2: TD2Vector; Const F1, F2: Single): TD2Vector;
Begin
   Result.V[X] := (F1 * V1.V[X]) + (F2 * V2.V[X]);
   Result.V[Y] := (F1 * V1.V[Y]) + (F2 * V2.V[Y]);
   Result.W := 1.0;
End;

Function d2VectorReflect(Const V, N: TD2Vector): TD2Vector;
Begin
   Result := d2VectorCombine2(V, N, 1, -2 * d2VectorDotProduct(V, N));
End;

Function d2VectorAngle(Const V, N: TD2Vector): Single;
Begin
   If d2VectorCrossProductZ(V, N) < 0 Then
      Result := d2RadToDeg(ArcCos(d2VectorAngleCosine(V, N)))
   Else
      Result := -d2RadToDeg(ArcCos(d2VectorAngleCosine(V, N)));
End;

Function d2CreateRotationMatrix(angle: Single): TD2Matrix;
Var
   cosine, sine: Single;
Begin
   d2SinCos(angle, sine, cosine);

   Result.m11 := cosine;
   Result.m12 := sine;
   Result.m13 := 0;
   Result.m21 := -sine;
   Result.m22 := cosine;
   Result.m23 := 0;

   Result.m31 := 0;
   Result.m32 := 0;
   Result.m33 := 1;
End;

Function d2InterpolateSingle(Const start, stop, t: Single): Single;
Begin
   Result := start + (stop - start) * t;
End;

Function d2InterpolateRotation(start, stop, t: Single): Single;
Begin
   Result := d2InterpolateSingle(start, stop, t);
End;

Function d2InterpolateColor(start, stop: TD2Color; t: Single): TD2Color;
Begin
   TD2ColorRec(Result).A := TD2ColorRec(start).A + Trunc((TD2ColorRec(stop).A - TD2ColorRec(start).A) * t);
   TD2ColorRec(Result).R := TD2ColorRec(start).R + Trunc((TD2ColorRec(stop).R - TD2ColorRec(start).R) * t);
   TD2ColorRec(Result).G := TD2ColorRec(start).G + Trunc((TD2ColorRec(stop).G - TD2ColorRec(start).G) * t);
   TD2ColorRec(Result).B := TD2ColorRec(start).B + Trunc((TD2ColorRec(stop).B - TD2ColorRec(start).B) * t);
End;

Function d2AppendColor(start, stop: TD2Color): TD2Color;
Begin
   If TD2ColorRec(start).A + TD2ColorRec(stop).A < $FF Then
      TD2ColorRec(Result).A := TD2ColorRec(start).A + TD2ColorRec(stop).A
   Else
      TD2ColorRec(Result).A := $FF;
   If TD2ColorRec(start).R + TD2ColorRec(stop).R < $FF Then
      TD2ColorRec(Result).R := TD2ColorRec(start).R + TD2ColorRec(stop).R
   Else
      TD2ColorRec(Result).R := $FF;
   If TD2ColorRec(start).G + TD2ColorRec(stop).G < $FF Then
      TD2ColorRec(Result).G := TD2ColorRec(start).G + TD2ColorRec(stop).G
   Else
      TD2ColorRec(Result).G := $FF;
   If TD2ColorRec(start).B + TD2ColorRec(stop).B < $FF Then
      TD2ColorRec(Result).B := TD2ColorRec(start).B + TD2ColorRec(stop).B
   Else
      TD2ColorRec(Result).B := $FF;
End;

Function d2SubtractColor(start, stop: TD2Color): TD2Color;
Begin
   If TD2ColorRec(start).A - TD2ColorRec(stop).A < $FF Then
      TD2ColorRec(Result).A := TD2ColorRec(start).A - TD2ColorRec(stop).A
   Else
      TD2ColorRec(Result).A := $FF;
   If TD2ColorRec(start).R - TD2ColorRec(stop).R < $FF Then
      TD2ColorRec(Result).R := TD2ColorRec(start).R - TD2ColorRec(stop).R
   Else
      TD2ColorRec(Result).R := $FF;
   If TD2ColorRec(start).G - TD2ColorRec(stop).G < $FF Then
      TD2ColorRec(Result).G := TD2ColorRec(start).G - TD2ColorRec(stop).G
   Else
      TD2ColorRec(Result).G := $FF;
   If TD2ColorRec(start).B - TD2ColorRec(stop).B < $FF Then
      TD2ColorRec(Result).B := TD2ColorRec(start).B - TD2ColorRec(stop).B
   Else
      TD2ColorRec(Result).B := $FF;
End;

{ Interpolation ===============================================================}

{ interpolations }

Function d2InterpolateBack(T, B, C, D, S: Double; aType: TD2AnimationType): Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         If S = 0 Then
            S := 1.70158;
         T := T / D;
         Result := C * T * T * ((S + 1) * T - S) + B;
      End;
      d2AnimationOut:
      Begin
         If S = 0 Then
            S := 1.70158;
         T := T / D - 1;
         Result := C * (T * T * ((S + 1) * T + S) + 1) + B;
      End;
      d2AnimationInOut:
      Begin
         If S = 0 Then
            S := 1.70158;
         T := T / (D / 2);
         If T < 1 Then
         Begin
            S := S * 1.525;
            Result := C / 2 * (T * T * ((S + 1) * T - S)) + B;
         End Else Begin
            T := T - 2;
            S := S * 1.525;
            Result := C / 2 * (T * T * ((S + 1) * T + S) + 2) + B;
         End;
      End;
   End;
End;

Function d2InterpolateBounce(T, B, C, D: Double; aType: TD2AnimationType): Double;

   Function _EaseOut(T, B, C, D: Double): Double;
   Begin
      T := T / D;
      If T < 1 / 2.75 Then
      Begin
         Result := C * (7.5625 * T * T) + B;
      End Else
      If T < 2 / 2.72 Then
      Begin
         T := T - (1.5 / 2.75);
         Result := C * (7.5625 * T * T + 0.75) + B;
      End Else
      If T < 2.5 / 2.75 Then
      Begin
         T := T - (2.25 / 2.75);
         Result := C * (7.5625 * T * T + 0.9375) + B;
      End Else Begin
         T := T - (2.625 / 2.75);
         Result := C * (7.5625 * T * T + 0.984375) + B;
      End;
   End;

   Function _EaseIn(T, B, C, D: Double): Double;
   Begin
      Result := C - _EaseOut(D - T, 0, C, D) + B;
   End;

Begin
   Case aType Of
      d2AnimationIn:
      Begin
         Result := _EaseIn(T, B, C, D);
      End;
      d2AnimationOut:
      Begin
         Result := _EaseOut(T, B, C, D);
      End;
      d2AnimationInOut:
      Begin
         If T < D / 2 Then
            Result := _EaseIn(T * 2, 0, C, D) * 0.5 + B
         Else
            Result := _EaseOut(T * 2 - D, 0, C, D) * 0.5 + C * 0.5 + B;
      End;
   End;
End;

Function d2InterpolateCirc(T, B, C, D: Double; aType: TD2AnimationType): Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         T := T / D;
         Result := -C * (Sqrt(1 - T * T) - 1) + B;
      End;
      d2AnimationOut:
      Begin
         T := T / D - 1;
         Result := C * Sqrt(1 - T * T) + B;
      End;
      d2AnimationInOut:
      Begin
         T := T / (D / 2);
         If T < 1 Then
            Result := -C / 2 * (Sqrt(1 - T * T) - 1) + B
         Else Begin
            T := T - 2;
            Result := C / 2 * (Sqrt(1 - T * T) + 1) + B;
         End;
      End;
   End;
End;

Function d2InterpolateCubic(T, B, C, D: Double; aType: TD2AnimationType): Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         T := T / D;
         Result := C * T * T * T + B;
      End;
      d2AnimationOut:
      Begin
         T := T / D - 1;
         Result := C * (T * T * T + 1) + B;
      End;
      d2AnimationInOut:
      Begin
         T := T / (D / 2);
         If T < 1 Then
            Result := C / 2 * T * T * T + B
         Else Begin
            T := T - 2;
            Result := C / 2 * (T * T * T + 2) + B;
         End;
      End;
   End;
End;

Function d2InterpolateElastic(T, B, C, D, A, P: Double; aType: TD2AnimationType): Double;
Var
   S: Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         If T = 0 Then
         Begin
            Result := B;
            Exit;
         End;
         T := T / D;
         If T = 1 Then
         Begin
            Result := B + C;
            Exit;
         End;
         If P = 0 Then
            P := D * 0.3;
         If (A = 0) Or (A < Abs(C)) Then
         Begin
            A := C;
            S := P / 4;
         End Else Begin
            S := P / (2 * Pi) * ArcSin(C / A);
         End;
         T := T - 1;
         Result := -(A * Power(2, 10 * T) * Sin((T * D - S) * (2 * Pi) / P)) + B;
      End;
      d2AnimationOut:
      Begin
         If T = 0 Then
         Begin
            Result := B;
            Exit;
         End;
         T := T / D;
         If T = 1 Then
         Begin
            Result := B + C;
            Exit;
         End;
         If P = 0 Then
            P := D * 0.3;
         If (A = 0) Or (A < Abs(C)) Then
         Begin
            A := C;
            S := P / 4;
         End Else Begin
            S := P / (2 * Pi) * ArcSin(C / A);
         End;
         Result := A * Power(2, -10 * T) * Sin((T * D - S) * (2 * Pi) / P) + C + B;
      End;
      d2AnimationInOut:
      Begin
         If T = 0 Then
         Begin
            Result := B;
            Exit;
         End;
         T := T / (D / 2);
         If T = 2 Then
         Begin
            Result := B + C;
            Exit;
         End;
         If P = 0 Then
            P := D * (0.3 * 1.5);
         If (A = 0) Or (A < Abs(C)) Then
         Begin
            A := C;
            S := P / 4;
         End Else Begin
            S := P / (2 * Pi) * ArcSin(C / A);
         End;

         If T < 1 Then
         Begin
            T := T - 1;
            Result := -0.5 * (A * Power(2, 10 * T) * Sin((T * D - S) * (2 * Pi) / P)) + B;
         End Else Begin
            T := T - 1;
            Result := A * Power(2, -10 * T) * Sin((T * D - S) * (2 * Pi) / P) * 0.5 + C + B;
         End;
      End;
   End;
End;

Function d2InterpolateExpo(T, B, C, D: Double; aType: TD2AnimationType): Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         If T = 0 Then
            Result := B
         Else
            Result := C * Power(2, 10 * (T / D - 1)) + B;
      End;
      d2AnimationOut:
      Begin
         If T = D Then
            Result := B + C
         Else
            Result := C * (-Power(2, -10 * T / D) + 1) + B;
      End;
      d2AnimationInOut:
      Begin
         If T = 0 Then
         Begin
            Result := B;
            Exit;
         End;
         If T = D Then
         Begin
            Result := B + C;
            Exit;
         End;
         T := T / (D / 2);
         If T < 1 Then
            Result := C / 2 * Power(2, 10 * (T - 1)) + B
         Else Begin
            T := T - 1;
            Result := C / 2 * (-Power(2, -10 * T) + 2) + B;
         End;
      End;
   End;
End;

Function d2InterpolateLinear(T, B, C, D: Double): Double;
Begin
   Result := C * T / D + B;
End;

Function d2InterpolateQuad(T, B, C, D: Double; aType: TD2AnimationType): Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         T := T / D;
         Result := C * T * T + B;
      End;
      d2AnimationOut:
      Begin
         T := T / D;
         Result := -C * T * (T - 2) + B;
      End;
      d2AnimationInOut:
      Begin
         T := T / (D / 2);

         If T < 1 Then
            Result := C / 2 * T * T + B
         Else Begin
            T := T - 1;
            Result := -C / 2 * (T * (T - 2) - 1) + B;
         End;
      End;
   End;
End;

Function d2InterpolateQuart(T, B, C, D: Double; aType: TD2AnimationType): Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         T := T / D;
         Result := C * T * T * T * T + B;
      End;
      d2AnimationOut:
      Begin
         T := T / D - 1;
         Result := -C * (T * T * T * T - 1) + B;
      End;
      d2AnimationInOut:
      Begin
         T := T / (D / 2);
         If T < 1 Then
            Result := C / 2 * T * T * T * T + B
         Else Begin
            T := T - 2;
            Result := -C / 2 * (T * T * T * T - 2) + B;
         End;
      End;
   End;
End;

Function d2InterpolateQuint(T, B, C, D: Double; aType: TD2AnimationType): Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         T := T / D;
         Result := C * T * T * T * T * T + B;
      End;
      d2AnimationOut:
      Begin
         T := T / D - 1;
         Result := C * (T * T * T * T * T + 1) + B;
      End;
      d2AnimationInOut:
      Begin
         T := T / (D / 2);
         If T < 1 Then
            Result := C / 2 * T * T * T * T * T + B
         Else Begin
            T := T - 2;
            Result := C / 2 * (T * T * T * T * T + 2) + B;
         End;
      End;
   End;
End;

Function d2InterpolateSine(T, B, C, D: Double; aType: TD2AnimationType): Double;
Begin
   Case aType Of
      d2AnimationIn:
      Begin
         Result := -C * Cos(T / D * (Pi / 2)) + C + B;
      End;
      d2AnimationOut:
      Begin
         Result := C * Sin(T / D * (Pi / 2)) + B;
      End;
      d2AnimationInOut:
      Begin
         Result := -C / 2 * (Cos(Pi * T / D) - 1) + B;
      End;
   End;
End;


Function d2RGBtoBGR(Const C: TD2Color): TD2Color;
Begin
   Result := C;
   TD2ColorRec(Result).R := TD2ColorRec(C).B;
   TD2ColorRec(Result).B := TD2ColorRec(C).R;
End;

Function d2ColorFromVCL(Const C: TColor): TD2Color;
Begin
   Result := d2RGBtoBGR(ColorToRGB(C)) Or $FF000000;
End;

Function d2CorrectColor(Const C: TD2Color): TD2Color;
Begin
   Result := C;
   {$IFDEF ORCA2D_USE_CARBON}
  TD2ColorRec(Result).R:=TD2ColorRec(C).B;
  TD2ColorRec(Result).B:=TD2ColorRec(C).R;
   {$ENDIF}
End;

Function d2PremultyAlpha(Const C: TD2Color): TD2Color;
Begin
   If TD2ColorRec(C).A = 0 Then
      Result := 0
   Else
   If TD2ColorRec(C).A = $FF Then
      Result := C
   Else Begin
      TD2ColorRec(Result).R := trunc(TD2ColorRec(C).R * (TD2ColorRec(C).A / $FF));
      TD2ColorRec(Result).G := trunc(TD2ColorRec(C).G * (TD2ColorRec(C).A / $FF));
      TD2ColorRec(Result).B := trunc(TD2ColorRec(C).B * (TD2ColorRec(C).A / $FF));
      TD2ColorRec(Result).A := TD2ColorRec(C).A;
   End;
End;

Function d2UnpremultyAlpha(Const C: TD2Color): TD2Color;
Begin
   If TD2ColorRec(C).A = 0 Then
      Result := 0
   Else
   If TD2ColorRec(C).A = $FF Then
      Result := C
   Else Begin
      TD2ColorRec(Result).R := trunc(TD2ColorRec(C).R / (TD2ColorRec(C).A / $FF));
      TD2ColorRec(Result).G := trunc(TD2ColorRec(C).G / (TD2ColorRec(C).A / $FF));
      TD2ColorRec(Result).B := trunc(TD2ColorRec(C).B / (TD2ColorRec(C).A / $FF));
      TD2ColorRec(Result).A := TD2ColorRec(C).A;
   End;
End;

Function d2Opacity(Const C: TD2Color; Const AOpacity: Single): TD2Color;
Begin
   Result := C;
   If AOpacity < 1 Then
      TD2ColorRec(Result).A := Trunc(TD2ColorRec(C).A * AOpacity);
End;

Function d2Color(R, G, B: Byte; A: Byte = $FF): TD2Color;
Begin
   TD2ColorRec(Result).R := R;
   TD2ColorRec(Result).G := G;
   TD2ColorRec(Result).B := B;
   TD2ColorRec(Result).A := A;
End;

Function d2ChangeHSL(Const C: TD2Color; dH, dS, dL: Single): TD2Color;
Var
   H, S, L: Single;
   A: Byte;
Begin
   A := TD2ColorRec(c).A;
   d2RGBtoHSL(C, H, S, L);
   H := H + dH;
   If H < 0 Then
      H := 0;
   If H > 1 Then
      H := 1;
   S := S + dS;
   If S < 0 Then
      S := 0;
   If S > 1 Then
      S := 1;
   L := L + dL;
   If L < 0 Then
      L := 0;
   If L > 1 Then
      L := 1;
   Result := d2HSLtoRGB(H, S, L);
   TD2ColorRec(Result).A := A;
End;

Function d2ColorToStr(Value: TD2Color): String;
Begin
   Result := '#' + IntToHex(Value, 8);
End;

Function d2StrToColor(Value: String): TD2Color;
Var
   i: Integer;
Begin
   If Value = #0 Then
      Value := '$0'
   Else
   If (Value <> '') And (Value[1] = '#') Then
      Value[1] := '$'
   Else Begin
      For i := 0 To High(d2ColorIdents) Do
         If CompareText(Value, d2ColorIdents[i].Name) = 0 Then
         Begin
            Value := d2ColorIdents[i].Value;
            Value[1] := '$';
            Break;
         End;
   End;
   Try
      Result := StrToUInt(Value);  //=== ct9999 ======
   Except
   End;
End;

Function d2FloatToStr(Value: Single): String;
Var
   S: Char;
Begin
   S := DecimalSeparator;
   Try
      DecimalSeparator := '.';
      If Frac(Value) <> 0 Then
         Result := Format('%.3f', [Value])
      Else
         Result := IntToStr(Trunc(Value))
   Finally
      DecimalSeparator := S;
   End;
End;

Function d2StrToFloat(Value: String): Single;
Var
   S: Char;
Begin
   S := DecimalSeparator;
   Try
      DecimalSeparator := '.';
      Result := StrToFloat(Value);
   Finally
      DecimalSeparator := S;
   End;
End;

Function d2HSLtoRGB(H, S, L: Single): TD2Color;

   Function hue2rgb(p, q, t: Single): Single;
   Begin
      If (t < 0) Then
         t := t + 1;
      If (t > 1) Then
         t := t - 1;
      If (t < 1 / 6) Then
      Begin
         Result := p + (q - p) * 6 * t;
         Exit;
      End;
      If (t < 1 / 2) Then
      Begin
         Result := q;
         Exit;
      End;
      If (t < 2 / 3) Then
      Begin
         Result := p + (q - p) * (2 / 3 - t) * 6;
         Exit;
      End;
      Result := p;
   End;

Var
   r, g, b: Single;
   q, p: Single;
Begin
   If (s = 0) Then
   Begin
      r := L;
      g := L;
      b := L;
   End Else Begin
      If (L < 0.5) Then
         q := L * (1 + S)
      Else
         q := L + S - L * S;
      p := 2 * L - q;
      r := hue2rgb(p, q, H + 1 / 3);
      g := hue2rgb(p, q, H);
      b := hue2rgb(p, q, H - 1 / 3);
   End;
   Result := d2Color(round(r * $FF), round(g * $FF), round(b * $FF));
End;

Procedure d2RGBtoHSL(RGB: TD2Color; out H, S, L: Single);
Var
   r, g, b: Single;
   d, mx, mn: Single;
Begin
   r := TD2ColorRec(RGB).R / $FF;
   g := TD2ColorRec(RGB).G / $FF;
   b := TD2ColorRec(RGB).B / $FF;
   mx := Max(Max(R, G), B);
   mn := Min(Min(R, G), B);
   H := (mx + mn) / 2;
   L := H;
   S := H;
   If (mx = mn) Then
   Begin
      S := 0;
      H := 0;
   End Else Begin
      d := mx - mn;
      If L > 0.5 Then
         S := d / (2 - mx - mn)
      Else
         S := d / (mx + mn);
      If (mx = r) Then
         H := (g - b) / d
      Else
      If (mx = g) Then
         H := (b - r) / d + 2
      Else
         H := (r - g) / d + 4;
      H := H / 6;
      If H < 0 Then
         H := H + 1;
   End;
End;


//=============== TD2StorageItem ==============================================================}

Constructor TD2StorageItem.Create;
Begin
   Inherited Create;
End;

Destructor TD2StorageItem.Destroy;
Begin
   If FStream <> nil Then
      FStream.Free;
   Inherited;
End;

Procedure TD2StorageItem.ReadItem(R: TReader);
Var
   Count: Longint;
Begin
   FType := R.NextValue;
   Case FType Of
      vaWString: FAsString := R.ReadWideString;
      vaString, vaLString: FAsString := R.ReadString;
      vaSingle: FAsFloat := R.ReadSingle;
      vaExtended: FAsFloat := R.ReadFloat;
      vaInt8, vaInt16, vaInt32: FAsInteger := R.ReadInteger;
      vaBinary:
      Begin
         R.ReadValue;
         R.Read(Count, SizeOf(Count));
         AsStream.SetSize(Count);
         R.Read(AsStream.Memory^, Count);
      End;
      vaTrue, vaFalse: R.ReadBoolean;
   End;
End;

Procedure TD2StorageItem.WriteItem(W: TWriter);
Var
   Count: Longint;
Begin
   Case FType Of
      vaWString: W.WriteWideString(FAsString);
      vaString, vaLString: W.WriteString(FAsString);
      vaSingle, vaExtended: W.WriteFloat(FAsFloat);
      vaInt8, vaInt16, vaInt32: W.WriteInteger(FAsInteger);
      vaBinary:
      Begin
         WriteValue(W, vaBinary);
         Count := FStream.Size;
         W.Write(Count, SizeOf(Count));
         W.Write(FStream.Memory^, FStream.Size);
      End;
      vaTrue: WriteValue(W, vaTrue);
      vaFalse: WriteValue(W, vaFalse);
      Else Count := 1;
   End;
End;

Function TD2StorageItem.GetAsBool: Boolean;
Begin
   Result := FType = vaTrue;
End;

Function TD2StorageItem.GetAsStream: TMemoryStream;
Begin
   FType := vaBinary;
   If FStream = nil Then
      FStream := TMemoryStream.Create;
   Result := FStream;
End;

Procedure TD2StorageItem.SetAsFloat(Const Value: Single);
Begin
   FType := vaSingle;
   FAsFloat := Value;
End;

Procedure TD2StorageItem.SetAsString(Const Value: WideString);
Begin
   FType := vaWString;
   FAsString := Value;
End;

Procedure TD2StorageItem.SetAsInteger(Const Value: Integer);
Begin
   FType := vaInt32;
   FAsInteger := Value;
End;

Procedure TD2StorageItem.SetAsBool(Const Value: Boolean);
Begin
   If Value Then
      FType := vaTrue
   Else
      FType := vaFalse;
End;

//=============== TD2Storage =================================================

Const
   FilerSignature: Array[1..4] Of Char = 'TPF0';

Constructor TD2Storage.Create;
Begin
   Inherited;
   FItems := TD2WideStringList.Create;
End;

Destructor TD2Storage.Destroy;
Begin
   FItems.Free;
   Inherited;
End;

Function TD2Storage.GetValues(Name: String): TD2StorageItem;
Var
   Idx: Integer;
   Item: TD2StorageItem;
Begin
   Idx := FItems.IndexOf(Name);
   If Idx >= 0 Then
      Result := TD2StorageItem(FItems.Objects[Idx])
   Else Begin
      Item := TD2StorageItem.Create;
      FItems.AddObject(Name, Item);
      Result := Item;
   End;
End;

Procedure TD2Storage.SaveToStream(S: TStream);
Var
   i: Integer;
   W: TWriter;
Begin
   W := TWriter.Create(S, 1024);
   Try
      W.Write(FilerSignature, 4);
      WriteStr(W, 'Root');
      WriteStr(W, '');
      For i := 0 To FItems.Count - 1 Do
      Begin
         WriteStr(W, FItems[i]);
         TD2StorageItem(FItems.Objects[i]).WriteItem(W);
      End;
      W.WriteListEnd;
      W.WriteListEnd;
   Finally
      W.Free;
   End;
End;

Procedure TD2Storage.LoadFromStream(S: TStream);
Var
   R: TReader;
   Signature: Longint;
Begin
   R := TReader.Create(S, 1024);
   Try
      R.Read(Signature, SizeOf(Signature));
      ReadStr(R);
      ReadStr(R);
      While Not R.EndOfList Do
      Begin
         Values[ReadStr(R)].ReadItem(R);
      End;
   Finally
      R.Free;
   End;
End;

Procedure TD2Storage.LoadFromFile(FileName: String);
Var
   B, S: TStream;
Begin
   S := TFileStream.Create(FileName, fmOpenRead);
   Try
      B := TMemoryStream.Create;
      ObjectTextToBinary(S, B);
      B.Position := 0;
      LoadFromStream(B);
      B.Free;
   Finally
      S.Free;
   End;
End;

Procedure TD2Storage.SaveToFile(FileName: String);
Var
   B, S: TStream;
Begin
   S := TFileStream.Create(FileName, fmCreate);
   Try
      B := TMemoryStream.Create;
      SaveToStream(B);
      B.Position := 0;
      ObjectBinaryToText(B, S);
      B.Free;
   Finally
      S.Free;
   End;
End;

Procedure TD2Storage.SaveToBinFile(FileName: String);
Var
   B: TMemoryStream;
   i: Integer;
   W: TWriter;
Begin
   B := TMemoryStream.Create;
   W := TWriter.Create(B, 1024);
   Try

      W.Write(FilerSignature, 4);

      For i := 0 To FItems.Count - 1 Do
      Begin
         WriteStr(W, FItems[i]);
         TD2StorageItem(FItems.Objects[i]).WriteItem(W);
      End;
      // W.WriteListEnd;
      // W.WriteListEnd;

      B.SaveToFile(FileName);

   Finally
      //B.Free;
      W.Free;
   End;
End;

//=============== TD2Bounds ===============================================

Constructor TD2Bounds.Create(Const ADefaultValue: TD2Rect);
Begin
   Inherited Create;
   FDefaultValue := ADefaultValue;
   Rect := FDefaultValue;
End;

Procedure TD2Bounds.Assign(Source: TPersistent);
Begin
   If Source Is TD2Bounds Then
   Begin
      Rect := TD2Bounds(Source).Rect;
   End Else
      Inherited;
End;

Function TD2Bounds.GetRect: TD2Rect;
Begin
   Result := d2Rect(FLeft, FTop, FRight, FBottom);
End;

Function TD2Bounds.MarginRect(Const R: TD2Rect): TD2Rect;
Begin
   Result := d2Rect(R.Left + FLeft, R.Top + FTop, R.Right - FRight, R.Bottom - FBottom);
End;

Function TD2Bounds.PaddinRect(Const R: TD2Rect): TD2Rect;
Begin
   Result := d2Rect(R.Left - FLeft, R.Top - FTop, R.Right + FRight, R.Bottom + FBottom);
End;

Function TD2Bounds.Width: Single;
Begin
   Result := d2RectWidth(Rect);
End;

Function TD2Bounds.Height: Single;
Begin
   Result := d2RectHeight(Rect);
End;

Function TD2Bounds.MarginEmpty: Boolean;
Begin
   Result := (FLeft = 0) And (FTop = 0) And (FRight = 0) And (FBottom = 0);
End;

Function TD2Bounds.Empty: Boolean;
Begin
   Result := d2IsRectEmpty(Rect);
End;

Procedure TD2Bounds.SetBottom(Const Value: Single);
Begin
   If FBottom <> Value Then
   Begin
      FBottom := Value;
      If Assigned(OnChange) Then
         OnChange(Self);
   End;
End;

Procedure TD2Bounds.SetLeft(Const Value: Single);
Begin
   If FLeft <> Value Then
   Begin
      FLeft := Value;
      If Assigned(OnChange) Then
         OnChange(Self);
   End;
End;

Procedure TD2Bounds.SetRight(Const Value: Single);
Begin
   If FRight <> Value Then
   Begin
      FRight := Value;
      If Assigned(OnChange) Then
         OnChange(Self);
   End;
End;

Procedure TD2Bounds.SetTop(Const Value: Single);
Begin
   If FTop <> Value Then
   Begin
      FTop := Value;
      If Assigned(OnChange) Then
         OnChange(Self);
   End;
End;

Procedure TD2Bounds.SetRect(Const Value: TD2Rect);
Begin
   If (FLeft <> Value.Left) Or (FTop <> Value.Top) Or (FRight <> Value.Right) Or (FBottom <> Value.Bottom) Then
   Begin
      FLeft := Value.Left;
      FTop := Value.Top;
      FRight := Value.Right;
      FBottom := Value.Bottom;
      If Assigned(OnChange) Then
         OnChange(Self);
   End;
End;

Procedure TD2Bounds.DefineProperties(Filer: TFiler);
Begin
   Inherited;
   Filer.DefineProperty('Rect', ReadRect, WriteRect, (FLeft <> DefaultValue.Left) Or (FTop <> DefaultValue.Top) Or (FRight <> DefaultValue.Right) Or (FBottom <> DefaultValue.Bottom));
End;

Procedure TD2Bounds.ReadRect(Reader: TReader);
Begin
   Rect := d2StringToRect(Reader.ReadString);
End;

Procedure TD2Bounds.WriteRect(Writer: TWriter);
Begin
   Writer.WriteString(d2RectToString(Rect));
End;

//=============== TD2Position ======================================================

Constructor TD2Position.Create(Const ADefaultValue: TD2Point);
Begin
   Inherited Create;
   FDefaultValue := ADefaultValue;
   FX := FDefaultValue.X;
   FY := FDefaultValue.Y;
End;

Procedure TD2Position.Assign(Source: TPersistent);
Begin
   If Source Is TD2Position Then
   Begin
      Point := TD2Position(Source).Point;
   End Else
      Inherited;
End;

Function TD2Position.Empty: Boolean;
Begin
   Result := (FX = 0) And (FY = 0);
End;

Procedure TD2Position.DefineProperties(Filer: TFiler);
Begin
   Inherited;
   Filer.DefineProperty('Point', ReadPoint, WritePoint, (FX <> DefaultValue.X) Or (FY <> DefaultValue.Y));
End;

Procedure TD2Position.ReadPoint(Reader: TReader);
Begin
   Point := d2StringToPoint(Reader.ReadString);
End;

Procedure TD2Position.WritePoint(Writer: TWriter);
Begin
   Writer.WriteString(d2PointToString(Point));
End;

Function TD2Position.GetPoint: TD2Point;
Begin
   Result := d2Point(Fx, Fy);
End;

Procedure TD2Position.SetPoint(Const Value: TD2Point);
Begin
   Fx := Value.X;
   Fy := Value.Y;
   If Assigned(OnChange) Then
      OnChange(Self);
End;

Procedure TD2Position.SetX(Const Value: Single);
Begin
   If FX <> Value Then
   Begin
      FX := Value;
      If Assigned(OnChange) Then
         OnChange(Self);
   End;
End;

Procedure TD2Position.SetY(Const Value: Single);
Begin
   If FY <> Value Then
   Begin
      FY := Value;
      If Assigned(OnChange) Then
         OnChange(Self);
   End;
End;

//=============== TD2Transform ================================================================}

Constructor TD2Transform.Create;
Begin
   Inherited;
   FMatrix := IdentityMatrix;
   FPosition := TD2Position.Create(d2Point(0, 0));
   FPosition.OnChange := MatrixChanged;
   FScale := TD2Position.Create(d2Point(1, 1));
   FScale.OnChange := MatrixChanged;
   FSkew := TD2Position.Create(d2Point(0, 0));
   FSkew.OnChange := MatrixChanged;
   FRotateCenter := TD2Position.Create(d2Point(0.5, 0.5));
   FRotateCenter.OnChange := MatrixChanged;
End;

Destructor TD2Transform.Destroy;
Begin
   FRotateCenter.Free;
   FScale.Free;
   FSkew.Free;
   FPosition.Free;
   Inherited;
End;

Procedure TD2Transform.Assign(Source: TPersistent);
Begin
   If Source Is TD2Transform Then
   Begin
      FPosition.FX := TD2Transform(Source).Position.FX;
      FPosition.FY := TD2Transform(Source).Position.FY;
      FScale.FX := TD2Transform(Source).Scale.FX;
      FScale.FY := TD2Transform(Source).Scale.FY;
      FSkew.FX := TD2Transform(Source).Skew.FX;
      FSkew.FY := TD2Transform(Source).Skew.FY;
      FRotateCenter.FX := TD2Transform(Source).RotateCenter.FX;
      FRotateCenter.FY := TD2Transform(Source).RotateCenter.FY;
      MatrixChanged(Self);
   End Else
      Inherited;
End;

Procedure TD2Transform.MatrixChanged(Sender: TObject);
Begin
   FMatrix := IdentityMatrix;
   FMatrix.m31 := FPosition.X;
   FMatrix.m32 := FPosition.Y;
   FMatrix.m13 := FSkew.X;
   FMatrix.m23 := FSkew.Y;
   FMatrix.m11 := FScale.X;
   FMatrix.m22 := FScale.Y;
   If FRotateAngle <> 0 Then
   Begin
{    M1:=IdentityMatrix;
    M1.m31:=-FRotateCenter.X * FWidth;
    M1.m32:=-FRotateCenter.Y * FHeight;
    M2:=IdentityMatrix;
    M2.m31:=FRotateCenter.X * FWidth;
    M2.m32:=FRotateCenter.Y * FHeight;
    RotMatrix:=d2MatrixMultiply(M1,d2MatrixMultiply(d2CreateRotationMatrix(d2DegToRad(FRotateAngle)),M2));
    FMatrix:=d2MatrixMultiply(RotMatrix,FMatrix); }
      FMatrix := d2MatrixMultiply(d2CreateRotationMatrix(d2DegToRad(FRotateAngle)), FMatrix);
   End;
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;

Procedure TD2Transform.SetRotateAngle(Const Value: Single);
Begin
   If FRotateAngle <> Value Then
   Begin
      FRotateAngle := Value;
   End;
End;


Function TD2Position.GetVector: TD2Vector;
Begin
   Result := d2Vector(FX, FY);
End;

Procedure TD2Position.SetVector(Const Value: TD2Vector);
Begin
   Point := d2Point(Value.X, Value.Y);
End;

Procedure TD2Position.Reflect(Const Normal: TD2Vector);
Begin
   Vector := d2VectorReflect(Vector, Normal);
End;


//=============== TD2GradientPoint =======================================

Procedure TD2GradientPoint.Assign(Source: TPersistent);
Begin
   If Source Is TD2GradientPoint Then
   Begin
      FColor := TD2GradientPoint(Source).FColor;
      FOffset := TD2GradientPoint(Source).FOffset;
   End Else
      Inherited;
End;

Constructor TD2GradientPoint.Create(ACollection: TCollection);
Begin
   Inherited Create(ACollection);
End;

Function TD2GradientPoint.GetColor: String;
Begin
   Result := d2ColorToStr(FColor);
End;

Procedure TD2GradientPoint.SetColor(Const Value: String);
Begin
   FColor := d2StrToColor(Value);
End;

//=============== TD2GradientPoints ====================================

Function TD2GradientPoints.GetPoint(Index: Integer): TD2GradientPoint;
Begin
   Result := TD2GradientPoint(Items[Index]);
End;

//=============== TD2Gradient =======================================

Constructor TD2Gradient.Create;
Begin
   Inherited;
   FStartPosition := TD2Position.Create(d2Point(0, 0));
   FStartPosition.OnChange := PositionChanged;
   FStopPosition := TD2Position.Create(d2Point(0, 1));
   FStopPosition.OnChange := PositionChanged;
   FRadialTransform := TD2Transform.Create;
   FRadialTransform.OnChanged := PositionChanged;
   FPoints := TD2GradientPoints.Create(TD2GradientPoint);
   With TD2GradientPoint(FPoints.Add) Do
   Begin
      IntColor := $FF000000;
   End;
   With TD2GradientPoint(FPoints.Add) Do
   Begin
      IntColor := $FFFFFFFF;
      Offset := 1;
   End;
End;

Procedure TD2Gradient.Assign(Source: TPersistent);
Var
   SaveChanged: TNotifyEvent;
Begin
   If Source Is TD2Gradient Then
   Begin
      SaveChanged := FOnChanged;
      FOnChanged := nil;
      FPoints.Clear;
      FPoints.Assign(TD2Gradient(Source).FPoints);
      FStyle := TD2Gradient(Source).Style;
      If FStyle = d2LinearGradient Then
      Begin
         FStopPosition.Assign(TD2Gradient(Source).StopPosition);
         FStartPosition.Assign(TD2Gradient(Source).StartPosition);
      End Else Begin
         FRadialTransform.Assign(TD2Gradient(Source).RadialTransform);
      End;
      FOnChanged := SaveChanged;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End Else
      Inherited;
End;

Destructor TD2Gradient.Destroy;
Begin
   FStartPosition.Free;
   FStopPosition.Free;
   FRadialTransform.Free;
   FPoints.Free;
   Inherited;
End;

Procedure TD2Gradient.Change;
Begin
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;

Function TD2Gradient.InterpolateColor(Offset: Single): TD2Color;
Var
   i: Integer;
Begin
   If FPoints.Count > 1 Then
   Begin
      If Offset < 0 Then
         Offset := 0;
      If Offset > 1 Then
         Offset := 1;
      If Offset < FPoints[0].Offset Then
      Begin
         Result := Points[0].IntColor;
         Exit;
      End;
      If Offset > FPoints[FPoints.Count - 1].Offset Then
      Begin
         Result := FPoints[FPoints.Count - 1].IntColor;
         Exit;
      End;
      For i := 0 To FPoints.Count - 2 Do
      Begin
         If (Offset < Points[i].Offset) Then
            Continue;
         If Points[i + 1].Offset - Points[i].Offset <= 0 Then
            Result := Points[i].IntColor
         Else
         If (i = FPoints.Count - 2) And (Offset > Points[Points.Count - 1].Offset) Then
            Result := Points[Points.Count - 1].IntColor
         Else
            Result := d2InterpolateColor(Points[i].IntColor, Points[i + 1].IntColor, (Offset - Points[i].Offset) / (Points[i + 1].Offset - Points[i].Offset));
      End;
   End Else
      Result := 0;
End;

Procedure TD2Gradient.PositionChanged(Sender: TObject);
Begin
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;

Function TD2Gradient.isLinearStored: Boolean;
Begin
   Result := FStyle = d2LinearGradient;
End;

Function TD2Gradient.isRadialStored: Boolean;
Begin
   Result := FStyle = d2RadialGradient;
End;

Procedure TD2Gradient.SetRadialTransform(Const Value: TD2Transform);
Begin
   FRadialTransform.Assign(Value);
End;

Procedure TD2Gradient.SetStartPosition(Const Value: TD2Position);
Begin
   FStartPosition.Assign(Value);
End;

Procedure TD2Gradient.SetStopPosition(Const Value: TD2Position);
Begin
   FStopPosition.Assign(Value);
End;

Procedure TD2Gradient.SetColor(Const Value: String);
Begin
   If FPoints.Count > 0 Then
      Points[0].Color := Value;
End;

Procedure TD2Gradient.SetColor1(Const Value: String);
Begin
   If FPoints.Count > 1 Then
      Points[1].Color := Value;
End;

Procedure TD2Gradient.SetStyle(Const Value: TD2GradientStyle);
Begin
   If FStyle <> Value Then
   Begin
      FStyle := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

//=============== TD2Brush ===================================================================}

Constructor TD2Brush.Create;
Begin
   Inherited Create;
   FDefaultStyle := ADefaultStyle;
   FDefaultColor := ADefaultColor;
   FColor := ADefaultColor;
   FStyle := FDefaultStyle;
   FGradient := TD2Gradient.Create;
   FGradient.OnChanged := GradientChanged;
   FVisual := TD2Visual.Create;
   FVisual.OnChanged := VisualChanged;
   FResource := TD2BrushResource.Create;
   FResource.OnChanged := ResourceChanged;
   FBitmap := TD2BrushBitmap.Create;
   FBitmap.OnChanged := BitmapChanged;
End;

Destructor TD2Brush.Destroy;
Begin
   FBitmap.Free;
   FVisual.Free;
   FResource.Free;
   FGradient.Free;
   Inherited;
End;

Procedure TD2Brush.Assign(Source: TPersistent);
Var
   SaveChange: TNotifyEvent;
Begin
   If Source Is TD2Brush Then
   Begin
      SaveChange := FOnChanged;
      FOnChanged := nil;
      FDefaultStyle := (Source As TD2Brush).FDefaultStyle;
      FDefaultColor := (Source As TD2Brush).FDefaultColor;
      FColor := (Source As TD2Brush).SolidColor;
      FStyle := (Source As TD2Brush).Style;
      Case FStyle Of
         d2BrushGradient: FGradient.Assign((Source As TD2Brush).Gradient);
         d2BrushResource: FResource.Assign((Source As TD2Brush).Resource);
         d2BrushVisual: FVisual.Assign((Source As TD2Brush).Visual);
         d2BrushBitmap: FBitmap.Assign((Source As TD2Brush).Bitmap);
      End;
      FOnChanged := SaveChange;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End Else
      Inherited;
End;

Procedure TD2Brush.GradientChanged(Sender: TObject);
Begin
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;

Procedure TD2Brush.VisualChanged(Sender: TObject);
Begin
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;

Procedure TD2Brush.ResourceChanged(Sender: TObject);
Begin
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;

Procedure TD2Brush.BitmapChanged(Sender: TObject);
Begin
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;

Function TD2Brush.isBitmapStored: Boolean;
Begin
   Result := (FStyle = d2BrushBitmap);
End;

Function TD2Brush.isColorStored: Boolean;
Begin
   Result := (FStyle = d2BrushSolid) And (FColor <> FDefaultColor);
End;

Function TD2Brush.isGradientStored: Boolean;
Begin
   Result := FStyle = d2BrushGradient;
End;

Function TD2Brush.isStyleStored: Boolean;
Begin
   Result := FStyle <> FDefaultStyle;
End;

Function TD2Brush.isVisualStored: Boolean;
Begin
   Result := FStyle = d2BrushVisual;
End;

Function TD2Brush.isResourceStored: Boolean;
Begin
   Result := FStyle = d2BrushResource;
End;

Procedure TD2Brush.SetResource(Const Value: TD2BrushResource);
Begin
   FResource := Value;
End;

Procedure TD2Brush.SetGradient(Const Value: TD2Gradient);
Begin
   FGradient.Assign(Value);
End;

Procedure TD2Brush.SetVisual(Const Value: TD2Visual);
Begin
   FVisual := Value;
End;

Function TD2Brush.GetColor: String;
Begin
   Result := d2ColorToStr(FColor);
End;

Procedure TD2Brush.SetColor(Const Value: String);
Begin
   SolidColor := d2StrToColor(Value);
   If FStyle = d2BrushGradient Then
      FGradient.Color := Value;
End;

Function TD2Brush.GetSolidColor: TD2Color;
Begin
   Result := FColor;
   If (Style = d2BrushResource) And (Resource <> nil) And (Resource.Brush <> nil) Then
      Result := Resource.Brush.SolidColor;
End;

Procedure TD2Brush.SetSolidColor(Const Value: TD2Color);
Begin
   If FColor <> Value Then
   Begin
      FColor := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Procedure TD2Brush.SetStyle(Const Value: TD2BrushStyle);
Begin
   If FStyle <> Value Then
   Begin
      FStyle := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

//=============== TD2BrushResource ==========================================================}

Destructor TD2BrushResource.Destroy;
Begin
   If FResource <> nil Then
   Begin
      FResource.RemoveFreeNotify(Self);
      FResource := nil;
   End;
   Inherited;
End;

Procedure TD2BrushResource.Assign(Source: TPersistent);
Begin
   If Source Is TD2BrushResource Then
   Begin
      Resource := TD2BrushResource(Source).Resource;
      FResourceName := TD2BrushResource(Source).ResourceName;
   End Else
      Inherited;
End;

Procedure TD2BrushResource.SetResource(Const Value: TD2BrushObject);
Begin
   If FResource <> Value Then
   Begin
      If FResource <> nil Then
         FResource.RemoveFreeNotify(Self);
      FResource := Value;
      If FResource <> nil Then
      Begin
         FResourceName := FResource.ResourceName;
         FResource.AddFreeNotify(Self);
      End;
   End;
End;

Function TD2BrushResource.GetResourceName: String;
Begin
   Result := FResourceName;
End;

Procedure TD2BrushResource.SetResourceName(Const Value: String);
Begin
   If Value <> FResourceName Then
   Begin
      FResourceName := Value;
   End;
End;

Function TD2BrushResource.GetBrush: TD2Brush;
Var
   O: TD2Object;
Begin
   Result := nil;
   If FResource <> nil Then
   Begin
      Result := TD2BrushObject(FResource).Brush;
   End Else
   If FResourceName <> '' Then
   Begin
      O := FindResource(FResourceName);
      If O Is TD2BrushObject Then
         Resource := TD2BrushObject(O);
      If FResource <> nil Then
         Result := TD2BrushObject(FResource).Brush;
   End;
End;

//=============== TD2BrushBitmap =============================================

Constructor TD2BrushBitmap.Create;
Begin
   Inherited Create;
   FBitmap := TD2Bitmap.Create(0, 0);
End;

Destructor TD2BrushBitmap.Destroy;
Begin
   FBitmap.Free;
   Inherited;
End;

Procedure TD2BrushBitmap.Assign(Source: TPersistent);
Begin
   If Source Is TD2BrushBitmap Then
   Begin
      FWrapMode := TD2BrushBitmap(Source).FWrapMode;
      FBitmap.Assign(TD2BrushBitmap(Source).Bitmap);
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End Else
      Inherited;
End;

Procedure TD2BrushBitmap.SetWrapMode(Const Value: TD2WrapMode);
Begin
   If FWrapMode <> Value Then
   Begin
      FWrapMode := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Procedure TD2BrushBitmap.SetBitmap(Value: TD2Bitmap);
Begin
   If Value <> nil Then
      FBitmap.Assign(Value);
End;


//=============== TD2Font =====================================================================}

Constructor TD2Font.Create;
Begin
   Inherited;
   FSize := 11;
   FFamily := 'Tahoma';
   FClearType := True;
End;

Destructor TD2Font.Destroy;
Begin
   Inherited;
End;

Procedure TD2Font.Assign(Source: TPersistent);
Var
   xFont: TFont;
Begin
   If Source = nil Then
      exit;

   If Source Is TD2Font Then
   Begin
      FFamily := (Source As TD2Font).Family;
      FSize := (Source As TD2Font).Size;
      FStyle := (Source As TD2Font).Style;
      FClearType := (Source As TD2Font).ClearType;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End Else
   If Source Is TFont Then
   Begin
      xFont := TFont(Source);

      FFamily := xFont.Name;
      FSize := Abs(xFont.Height);

      If xFont.Style = [] Then
         FStyle := d2FontRegular;
      If xFont.Style = [fsBold] Then
         FStyle := d2FontBold;
      If xFont.Style = [fsItalic] Then
         FStyle := d2FontItalic;
      If xFont.Style = [fsBold, fsItalic] Then
         FStyle := d2FontBoldItalic;

      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End Else Begin
      Inherited;
   End;
End;

Procedure TD2Font.AssignTo(Dest: TPersistent);
Var
   xFont: TFont;
Begin
   If Dest = nil Then
      exit;

   If Dest Is TFont Then
   Begin
      xFont := TFont(Dest);

      xFont.Name := FFamily;
      xFont.Height := round(FSize);

      Case Style Of
         d2FontRegular: xFont.Style := [];
         d2FontBold: xFont.Style := [fsBold];
         d2FontItalic: xFont.Style := [fsItalic];
         d2FontBoldItalic: xFont.Style := [fsBold, fsItalic];
      End;
   End Else Begin
      Inherited;
   End;
End;

Function TD2Font.isSizeStored: Boolean;
Begin
   Result := FSize <> 11;
End;

Function TD2Font.isFamilyStored: Boolean;
Begin
   Result := FFamily <> 'Tahoma';
End;

Procedure TD2Font.SetFamily(Const Value: String);
Begin
   If FFamily <> Value Then
   Begin
      FFamily := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Procedure TD2Font.SetSize(Const Value: Single);
Begin
   If FSize <> Value Then
   Begin
      FSize := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Procedure TD2Font.SetClearType(Const Value: Boolean);
Begin
   If FClearType <> Value Then
   Begin
      FClearType := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Procedure TD2Font.SetStyle(Const Value: TD2FontStyle);
Begin
   If FStyle <> Value Then
   Begin
      FStyle := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Class Function TD2Filter.GetFileTypes: String;
Begin
   Result := '';
End;

Class Function TD2Filter.GetImageSize(Const AFileName: String): TD2Point;
Begin
   Result := d2Point(0, 0);
End;


//=============== TD2Bitmap ===================================================================}

Constructor TD2Bitmap.Create(Const AWidth, AHeight: Integer; Const APremulAlpha: Boolean = True);
Begin
   Inherited Create;
   FWidth := ABS(AWidth);
   FHeight := ABS(AHeight);
   Recreate;
End;

Constructor TD2Bitmap.CreateFromStream(Const AStream: TStream);
Begin
   Create(0, 0);
   LoadFromStream(AStream);
End;

Constructor TD2Bitmap.CreateFromBitmapAndMask(Const Bitmap, Mask: TD2Bitmap);
Var
   i: Integer;
Begin
   Create(Bitmap.Width, Bitmap.Height);
   If (Bitmap.Width <> Mask.Width) Or (Bitmap.Height <> Mask.Height) Then
      Exit;
   For i := 0 To FHeight * FWidth - 1 Do
   Begin
      PD2ColorRecArray(FBits)[i] := PD2ColorRecArray(Bitmap.FBits)[i];
      PD2ColorRecArray(FBits)[i].A := PD2ColorRecArray(Mask.FBits)[i].R;
   End;
End;

Destructor TD2Bitmap.Destroy;
Begin
   If Assigned(FOnDestroyHandle) Then
      FOnDestroyHandle(Self);
   If FResource <> nil Then
   Begin
      FResource.RemoveFreeNotify(Self);
      FResource := nil;
   End;
   If Assigned(FCanvas) Then
      FreeAndNil(FCanvas);
   Handle := 0;
   If FBits <> nil Then
      FreeMem(FBits, FWidth * FHeight * SizeOf(TD2Color));
   Inherited;
End;

Procedure TD2Bitmap.SetResource(Const Value: TD2BitmapObject);
Begin
   If FResource <> Value Then
   Begin
      If FResource <> nil Then
         FResource.RemoveFreeNotify(Self);
      FResource := Value;
      If FResource <> nil Then
      Begin
         FResourceName := FResource.ResourceName;
         FResource.AddFreeNotify(Self);
      End Else
         FResourceName := '';
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Function TD2Bitmap.GetResourceName: String;
Begin
   Result := FResourceName;
End;

Procedure TD2Bitmap.SetResourceName(Const Value: String);
Begin
   If Value <> FResourceName Then
   Begin
      FResourceName := Value;
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Function TD2Bitmap.GetBitmap: TD2Bitmap;
Var
   O: TD2Object;
Begin
   Result := nil;
   If FResource <> nil Then
   Begin
      Result := TD2BitmapObject(FResource).Bitmap;
   End Else
   If FResourceName <> '' Then
   Begin
      O := FindResource(FResourceName);
      If O Is TD2BitmapObject Then
         Resource := TD2BitmapObject(O);
      If FResource <> nil Then
         Result := TD2BitmapObject(FResource).Bitmap;
   End;
End;

Procedure TD2Bitmap.SetSize(Const AWidth, AHeight: Integer);
Begin
   If (FWidth <> AWidth) Or (FHeight <> AHeight) Then
   Begin
      If FBits <> nil Then
         FreeMem(FBits, FWidth * FHeight * SizeOf(TD2Color));
      FBits := nil;
      FWidth := ABS(AWidth);
      FHeight := ABS(AHeight);
      Recreate;
      BitmapChanged;
   End;
End;

Procedure TD2Bitmap.Recreate;
Begin
   If Assigned(FOnBitmapDestroy) Then
      FOnBitmapDestroy(Self);
   If Assigned(FOnDestroyHandle) Then
      FOnDestroyHandle(Self);
   FHandle := 0;
   If Assigned(FCanvas) Then
      FreeAndNil(FCanvas);
   FCanvas := nil;
   If FBits <> nil Then
      FreeMem(FBits);
   FBits := nil;

   If Not IsEmpty Then
   Begin
      GetMem(FBits, FWidth * FHeight * SizeOf(TD2Color));
      Clear(0);
   End;

   FNeedUpdate := True;
   If Assigned(FOnBitmapCreate) Then
      FOnBitmapCreate(Self);
End;

Procedure TD2Bitmap.Clear(Const AColor: TD2Color);
Begin
   If FBits <> nil Then
   Begin
      d2FillLongword(FBits, FWidth * FHeight, d2PremultyAlpha(AColor));
      BitmapChanged;
      FNeedUpdate := True;
   End;
End;

Procedure TD2Bitmap.ClearRect(Const ARect: TD2Rect; Const AColor: TD2Color);
Var
   R: TRect;
Begin
   If FBits <> nil Then
   Begin
      R := Rect(Trunc(ARect.Left), Trunc(ARect.Top), Round(ARect.Right), Round(ARect.Bottom));

      If R.Left < 0 Then
         R.Left := 0;
      If R.Top < 0 Then
         R.Top := 0;
      If R.Right > FWidth Then
         R.Right := FWidth;
      If R.Bottom > FHeight Then
         R.Bottom := FHeight;
      If R.Bottom < R.Top Then
         R.Bottom := R.Top;
      If R.Right < R.Left Then
         R.Right := R.Left;
      If (R.Right < 0) Or (R.Top < 0) Or (R.Left > FWidth) Or (R.Top > FHeight) Then
         Exit;

      d2FillLongwordRect(FBits, FWidth, FHeight, R.Left, R.Top, R.Right, R.Bottom, d2PremultyAlpha(AColor));
      FNeedUpdate := True;
   End;
End;

Function TD2Bitmap.GetPixels(x, y: Integer): TD2Color;
Begin
   If (x >= 0) And (y >= 0) And (x < FWidth) And (y < FHeight) And (FBits <> nil) Then
      Result := FBits[x + (y * FHeight)]
   Else
      Result := 0;
End;

Procedure TD2Bitmap.BitmapChanged;
Begin
   If Assigned(FOnChange) Then
      FOnChange(Self);
End;

Function TD2Bitmap.IsEmpty: Boolean;
Begin
   Result := FWidth * FHeight = 0;
End;

Procedure TD2Bitmap.DrawGraphic(Const Graphic: TGraphic; Const DstRect: TD2Rect);
Var
   Bitmap: TBitmap;
   SL: PD2ColorArray;
   i, j: Integer;
   //  xbmp:TLazIntfImage;
Begin
   Bitmap := TBitmap.Create;
   Try
      Bitmap.HandleType := bmDIB;
      Bitmap.PixelFormat := pf32bit;
      Bitmap.Width := FWidth;
      Bitmap.Height := FHeight;
      Bitmap.Canvas.Brush.Color := RGB(255, 0, 255);
      Bitmap.Canvas.StretchDraw(Rect(round(DstRect.left), round(DstRect.top), round(DstRect.right), round(DstRect.bottom)), Graphic);

      Bitmap.CreateIntfImage;

      For j := 0 To FHeight - 1 Do
      Begin
         SL := Bitmap.ScanLine[j];
         For i := 0 To FWidth - 1 Do
            If (TD2ColorRec(SL[i]).R = $FF) And (TD2ColorRec(SL[i]).G = 0) And (TD2ColorRec(SL[i]).B = $FF) Then
               Continue
            Else
               FBits[i + (j * Width)] := SL[i];
      End;
      //Bitmap.LoadFromIntfImage(xbmp);
      // xbmp.free;
   Finally
      Bitmap.Free;
   End;
End;

Procedure TD2Bitmap.Assign(Source: TPersistent);
Var
   SLine: PD2ColorArray;
   SLine24: PD2Color24Array;
   i, j: Integer;
Begin
   If Source Is TD2Bitmap Then
   Begin
      If TD2Bitmap(Source).ResourceBitmap <> nil Then
      Begin
         Resource := TD2Bitmap(Source).Resource;
         FResourceName := TD2Bitmap(Source).ResourceName;
         If Assigned(FOnChange) Then
            FOnChange(Self);
      End Else Begin
         Resource := nil;
         FResourceName := '';
         SetSize(TD2Bitmap(Source).Width, TD2Bitmap(Source).Height);
         d2MoveLongword(TD2Bitmap(Source).FBits, FBits, Width * Height);
         FNeedUpdate := True;
         If Assigned(FOnChange) Then
            FOnChange(Self);
      End;
   End Else
   If (Source Is TBitmap) And ((Source As TBitmap).PixelFormat = pf32bit) And ((Source As TBitmap).HandleType = bmDIB) Then
      With (Source As TBitmap) Do
      Begin
         Self.SetSize(Width, Height);
         For j := 0 To Height - 1 Do
         Begin
            SLine := Scanline[j];
            For i := 0 To Width - 1 Do
            Begin
               If Transparent And (SLine^[i] = ColorToRGB(TransparentColor)) Then
                  FBits[i + (j * Width)] := 0
               Else
                  FBits[i + (j * Width)] := SLine^[i];
            End;
         End;
         FNeedUpdate := True;
         If Assigned(FOnChange) Then
            FOnChange(Self);
      End Else
   If (Source Is TBitmap) And ((Source As TBitmap).PixelFormat = pf24bit) Then
      With (Source As TBitmap) Do
      Begin
         Self.SetSize(Width, Height);

         For j := 0 To Height - 1 Do
         Begin
            SLine24 := PD2Color24Array(Scanline[j]);
            For i := 0 To Width - 1 Do
            Begin
               If Transparent And (RGB(SLine24[i].R, SLine24^[i].G, SLine24^[i].B) = ColorToRGB(TransparentColor)) Then
                  FBits[i + (j * Width)] := 0
               Else
                  FBits[i + (j * Width)] := $FF000000 + RGB(SLine24^[i].R, SLine24^[i].G, SLine24^[i].B);
            End;
         End;
         FNeedUpdate := True;
         If Assigned(FOnChange) Then
            FOnChange(Self);
      End Else
   If Source Is TGraphic Then
   Begin
      SetSize(TGraphic(Source).Width, TGraphic(Source).Height);
      If FBits = nil Then
         Exit;
      DrawGraphic(TGraphic(Source), d2Rect(0, 0, FWidth, FHeight));
      d2FillAlpha(FBits, FWidth * FHeight, $FF);
      FNeedUpdate := True;
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End Else
   If Source Is TPicture Then
   Begin
      With TPicture(Source) Do
      Begin
         Self.SetSize(TPicture(Source).Graphic.Width, TPicture(Source).Graphic.Height);
         If FBits = nil Then
            Exit;
         DrawGraphic(TPicture(Source).Graphic, d2Rect(0, 0, FWidth, FHeight));
         d2FillAlpha(FBits, FWidth * FHeight, $FF);
         FNeedUpdate := True;
         If Assigned(FOnChange) Then
            FOnChange(Self);
      End;
   End Else
      Inherited;
End;

Procedure TD2Bitmap.AssignTo(Dest: TPersistent);
Var
   i: Integer;
   B: TBitmap;
   //  xbmp:TLazIntfImage;
Begin
   If Dest Is TPicture Then
   Begin
      B := TBitmap.Create;
      B.HandleType := bmDIB;
      B.PixelFormat := pf32bit;
      B.Width := FWidth;
      B.Height := FHeight;
      //  xbmp:=B.CreateIntfImage;

      For i := 0 To FHeight - 1 Do
         System.Move(Scanline[i]^, B.ScanLine[i]^, Width * 4);

      // B.LoadFromIntfImageX(xbmp);
      // xbmp.free;

      TPicture(Dest).Assign(B);
      B.Free;
   End Else
   If Dest Is TBitmap Then
   Begin
      TBitmap(Dest).HandleType := bmDIB;
      TBitmap(Dest).PixelFormat := pf32bit;
      TBitmap(Dest).Width := FWidth;
      TBitmap(Dest).Height := FHeight;
      // xbmp:=TBitmap(Dest).CreateIntfImage;

      For i := 0 To FHeight - 1 Do
         System.Move(Scanline[i]^, TBitmap(Dest).ScanLine[i]^, Width * 4);

      //  TBitmap(Dest).LoadFromIntfImage(xbmp);
      //  xbmp.free;
   End Else
      Inherited;
End;

Procedure TD2Bitmap.DefineProperties(Filer: TFiler);
Begin
   Inherited;
   Filer.DefineBinaryProperty('PNG', ReadBitmap, WriteBitmap, FWidth * FHeight > 0);
End;

Procedure TD2Bitmap.ReadBitmap(Stream: TStream);
Begin
   LoadFromStream(Stream);
End;

Procedure TD2Bitmap.WriteBitmap(Stream: TStream);
Begin
   SaveToStream(Stream);
End;

Procedure TD2Bitmap.DoLoaded(Sender: TObject);
Begin
   If Assigned(FOnThreadLoaded) Then
      FOnThreadLoaded(Self);
End;

Procedure TD2Bitmap.Rotate(Const Angle: Single);
Var
   temp: TD2Bitmap;
   M, M2: TD2Matrix;
   Pts: Array [1..4] Of TD2Point;
   R: TD2Rect;
Begin
   If Angle = 0 Then
      Exit;

   M := IdentityMatrix;
   M.m31 := -FWidth / 2;
   M.m32 := -FHeight / 2;
   M := d2MatrixMultiply(M, d2CreateRotationMatrix(d2DegToRad(Angle)));

   Pts[1] := d2PointFromVector(d2VectorTransform(d2Vector(0, 0), M));
   Pts[2] := d2PointFromVector(d2VectorTransform(d2Vector(FWidth, 0), M));
   Pts[3] := d2PointFromVector(d2VectorTransform(d2Vector(FWidth, FHeight), M));
   Pts[4] := d2PointFromVector(d2VectorTransform(d2Vector(0, FHeight), M));

   R := d2NormalizeRect(Pts);

   M2 := IdentityMatrix;
   M2.m31 := d2RectWidth(R) / 2;
   M2.m32 := d2RectHeight(R) / 2;
   M := d2MatrixMultiply(M, M2);

   temp := TD2Bitmap.Create(Trunc(d2RectWidth(R)), Trunc(d2RectHeight(R)));
   temp.Clear(0);
   temp.Canvas.BeginScene;
   temp.Canvas.SetMatrix(M);
   temp.Canvas.DrawBitmap(Self, d2Rect(0, 0, FWidth, FHeight), d2Rect(0, 0, FWidth, FHeight), 1);
   temp.Canvas.EndScene;
   Assign(temp);
   temp.Free;
   If Assigned(FOnChange) Then
      FOnChange(Self);
End;

Procedure TD2Bitmap.FlipHorizontal;
Var
   i: Integer;
   tmp: PD2ColorArray;
Begin
   GetMem(tmp, Width * 4);
   For i := 0 To (Height - 1) Div 2 Do
   Begin
      System.Move(Scanline[Height - 1 - i][0], tmp[0], Width * 4);
      System.Move(Scanline[i][0], Scanline[Height - 1 - i][0], Width * 4);
      System.Move(tmp[0], Scanline[i][0], Width * 4);
   End;
   If Assigned(FOnChange) Then
      FOnChange(Self);
   FreeMem(tmp, Width * 4);
End;

Procedure TD2Bitmap.FlipVertical;
Var
   i, j: Integer;
   tmp: TD2Color;
Begin
   For j := 0 To Height - 1 Do
      For i := 0 To (Width - 1) Div 2 Do
      Begin
         tmp := Scanline[j][Width - 1 - i];
         Scanline[j][Width - 1 - i] := Scanline[j][i];
         Scanline[j][i] := tmp;
      End;
   If Assigned(FOnChange) Then
      FOnChange(Self);
End;

Procedure TD2Bitmap.InvertAlpha;
Var
   i, j: Integer;
   Bits: PD2ColorRecArray;
Begin
   Bits := PD2ColorRecArray(Startline);
   For j := 0 To Height - 1 Do
      For i := 0 To Width - 1 Do
      Begin
         Bits[i + (j * Width)].Color := d2UnpremultyAlpha(Bits[i + (j * Width)].Color);
         TD2ColorRec(Bits[(i) + ((j) * Width)]).A := $FF - TD2ColorRec(Bits[(i) + ((j) * Width)]).A;
         Bits[i + (j * Width)].Color := d2PremultyAlpha(Bits[i + (j * Width)].Color);
      End;
   If Assigned(FOnChange) Then
      FOnChange(Self);
End;

Procedure TD2Bitmap.FillColor(Const Color: TD2Color);
Var
   Bits: PD2ColorRecArray;
   i, j: Integer;
   a: Byte;
Begin
   Bits := PD2ColorRecArray(Startline);
   For j := 0 To Height - 1 Do
   Begin
      For i := 0 To Width - 1 Do
      Begin
         {$ifdef FPC_BIG_ENDIAN}
      a:=TD2ColorRec(Bits[(i) + ((j) * Width)]).Color and $FF;
         {$else}
         a := TD2ColorRec(Bits[(i) + ((j) * Width)]).A;
         {$endif}
         If a > 0 Then
         Begin
            bits[(i) + ((j) * Width)].Color := d2PremultyAlpha(d2Opacity(Color, a / $FF));
            {$ifdef FPC_BIG_ENDIAN}
        ReverseBytes(@Bits[(i) + ((j) * Width)].Color,4);
            {$endif}
         End;
      End;
   End;
   If Assigned(FOnChange) Then
      FOnChange(Self);
   FNeedUpdate := True;
End;

Function TD2Bitmap.CreateMask: PByteArray;
Var
   a: Byte;
   Bits: PD2ColorRecArray;
   i, j: Integer;
Begin
   GetMem(Result, Width * Height);
   FillChar(Result^, Width * Height, 0);
   Bits := PD2ColorRecArray(Startline);
   For j := 0 To Height - 1 Do
   Begin
      For i := 0 To Width - 1 Do
      Begin
         {$ifdef FPC_BIG_ENDIAN}
      a:=TD2ColorRec(Bits[(i) + ((j) * Width)]).Color and $FF;
         {$else}
         a := TD2ColorRec(Bits[(i) + ((j) * Width)]).A;
         {$endif}
         If a > 0 Then
         Begin
            Result[i + (j * Width)] := a;
         End;
      End;
   End;
End;

Procedure TD2Bitmap.ApplyMask(Const Mask: PByteArray; Const DstX: Integer = 0; Const DstY: Integer = 0);
Var
   Bits: PD2ColorRecArray;
   i, j: Integer;
Begin
   Bits := PD2ColorRecArray(Startline);
   For j := 0 To Height - 1 Do
   Begin
      For i := 0 To Width - 1 Do
      Begin
         If (i - DstX < 0) Or (i - DstX > Width - 1) Or (j - DstY < 0) Or (j - DstY > Height - 1) Then
            Continue;

         If mask[i - DstX + ((j - DstY) * Width)] > 0 Then
         Begin
            Bits[i + (j * Width)].Color := d2PremultyAlpha(d2Opacity(d2UnpremultyAlpha(Bits[i + (j * Width)].Color), ($FF - mask[i - DstX + ((j - DstY) * Width)]) / $FF));
         End;
         {$ifdef FPC_BIG_ENDIAN}
      ReverseBytes(@Bits[(i) + ((j) * Width)].Color,4);
         {$endif}
      End;
   End;
   If Assigned(FOnChange) Then
      FOnChange(Self);
   FNeedUpdate := True;
End;

Function TD2Bitmap.CreateThumbnail(Const Width, Height: Integer): TD2Bitmap;
Begin
   Result := TD2Bitmap.Create(Width, Height);
   Result.Canvas.BeginScene;
   Result.Canvas.DrawThumbnail(Self, Width, Height);
   Result.Canvas.EndScene;
End;

Procedure TD2Bitmap.LoadFromFile(Const AFileName: String; Const aRotate: Single = 0);
Var
   Filter: TD2Filter;
Begin
   If Not FileExists(AFileName) Then
      Exit;
   Filter := GvarD2DefaultFilterClass.Create;
   If Filter.LoadFromFile(AFileName, aRotate, Self) Then
      If Assigned(FOnChange) Then
         FOnChange(Self);
   Filter.Free;
End;

Procedure TD2Bitmap.LoadThumbnailFromFile(Const AFileName: String; Const AFitWidth, AFitHeight: Single; Const UseEmbedded: Boolean = True);
Var
   Filter: TD2Filter;
Begin
   Filter := GvarD2DefaultFilterClass.Create;
   If Filter.LoadThumbnailFromFile(AFileName, AFitWidth, AFitHeight, UseEmbedded, Self) Then
      If Assigned(FOnChange) Then
         FOnChange(Self);
   Filter.Free;
End;

Procedure TD2Bitmap.SaveToFile(Const AFileName: String; Const Params: String = '');
Var
   Filter: TD2Filter;
Begin
   Filter := GvarD2DefaultFilterClass.Create;
   Filter.SaveToFile(AFileName, Self, Params);
   Filter.Free;
End;

Procedure TD2Bitmap.LoadFromStream(Stream: TStream);
Var
   Filter: TD2Filter;
   S: TStream;
Begin
   If Stream.Position > 0 Then
   Begin
      S := TMemoryStream.Create;
      S.CopyFrom(Stream, Stream.Size - Stream.Position);
      S.Position := 0;
      Filter := GvarD2DefaultFilterClass.Create;
      If Filter.LoadFromStream(S, Self) Then
         If Assigned(FOnChange) Then
            FOnChange(Self);
      Filter.Free;
      S.Free;
   End Else Begin
      Filter := GvarD2DefaultFilterClass.Create;
      If Filter.LoadFromStream(Stream, Self) Then
         If Assigned(FOnChange) Then
            FOnChange(Self);
      Filter.Free;
   End;
End;

Procedure TD2Bitmap.SaveToStream(Stream: TStream);
Var
   i: Integer;
   hasAlpha: Boolean;
   Filter: TD2Filter;
Begin
   hasAlpha := False;
   For i := 0 To FWidth * FHeight - 1 Do
      If FBits[i] And $FF000000 <> $FF000000 Then
      Begin
         hasAlpha := True;
         Break;
      End;

   If hasAlpha Then
   Begin
      Filter := GvarD2DefaultFilterClass.Create;
      Filter.SaveToStream(Stream, Self, 'png');
      Filter.Free;
   End Else Begin
      Filter := GvarD2DefaultFilterClass.Create;
      Filter.SaveToStream(Stream, Self, 'jpeg', 'quality=100');
      Filter.Free;
   End;
End;

Procedure TD2Bitmap.SetHeight(Const Value: Integer);
Var
   i: Integer;
Begin
   i := ABS(Value);

   If FHeight <> i Then
   Begin
      FHeight := i;
      Recreate;
   End;
End;

Procedure TD2Bitmap.SetWidth(Const Value: Integer);
Var
   i: Integer;
Begin
   i := ABS(Value);

   If FWidth <> i Then
   Begin
      FWidth := i;
      Recreate;
   End;
End;

Function TD2Bitmap.GetCanvas: TD2Canvas;
Begin
   If FCanvas = nil Then
   Begin
      FCanvas := GvarD2DefaultCanvasClass.CreateFromBitmap(Self);
      FCanvas.SetMatrix(IdentityMatrix);
   End;
   Result := FCanvas;
End;

Function TD2Bitmap.GetScanline(y: Integer): PD2ColorArray;
Begin
   Result := GvarD2DefaultCanvasClass.GetBitmapScanline(Self, y);
End;

//=============== TD2ImageThread =========================================

Constructor TD2ImageThread.Create(Const AImage: TD2Image; Const AFileName: String; Const AUseThumbnails: Boolean);
Begin
   Inherited Create(True);
   FFileName := AFileName;
   FUseThumbnails := AUseThumbnails;
   FImage := AImage;
   Priority := tpIdle;
   FreeOnTerminate := True;
End;

Destructor TD2ImageThread.Destroy;
Begin
   Inherited;
End;

Procedure TD2ImageThread.Execute;
Begin
   FTempBitmap := TD2Bitmap.Create(0, 0);
   If FUseThumbnails Then
      FTempBitmap.LoadThumbnailFromFile(FFileName, FImage.Width, FImage.Height, True)
   Else
      FTempBitmap.LoadFromFile(FFileName);
   Synchronize(Finished);
End;

Procedure TD2ImageThread.Finished;
Begin
   If FImage <> nil Then
      FImage.Bitmap.Assign(FTempBitmap);
   FTempBitmap.Free;
End;


//=============== TD2Path ===================================================

Constructor TD2PathData.Create;
Begin
   Inherited Create;
End;

Destructor TD2PathData.Destroy;
Begin
   If FResource <> nil Then
   Begin
      FResource.RemoveFreeNotify(Self);
      FResource := nil;
   End;
   Inherited;
End;

Procedure TD2PathData.Assign(Source: TPersistent);
Begin
   If Source Is TD2PathData Then
   Begin
      If TD2PathData(Source).ResourcePath <> nil Then
      Begin
         Resource := TD2PathData(Source).Resource;
         FResourceName := TD2PathData(Source).ResourceName;
         If Assigned(FOnChanged) Then
            FOnChanged(Self);
      End Else Begin
         SetLength(PathData, Length(TD2PathData(Source).PathData));
         System.Move(TD2PathData(Source).PathData[0], PathData[0], SizeOf(TD2PathPoint) * Length(PathData));
         If Assigned(FOnChanged) Then
            FOnChanged(Self);
      End;
   End Else
      Inherited;
End;

Procedure TD2PathData.SetResource(Const Value: TD2PathObject);
Begin
   If FResource <> Value Then
   Begin
      If FResource <> nil Then
         FResource.RemoveFreeNotify(Self);
      FResource := Value;
      If FResource <> nil Then
      Begin
         FResourceName := FResource.ResourceName;
         FResource.AddFreeNotify(Self);
      End Else
         FResourceName := '';
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Function TD2PathData.GetResourceName: String;
Begin
   Result := FResourceName;
End;

Procedure TD2PathData.SetResourceName(Const Value: String);
Begin
   If Value <> FResourceName Then
   Begin
      FResourceName := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Function TD2PathData.GetPath: TD2PathData;
Var
   O: TD2Object;
Begin
   Result := nil;
   If FResource <> nil Then
   Begin
      Result := TD2PathObject(FResource).Path;
   End Else
   If FResourceName <> '' Then
   Begin
      O := FindResource(FResourceName);
      If O Is TD2PathObject Then
         Resource := TD2PathObject(O);
      If FResource <> nil Then
         Result := TD2PathObject(FResource).Path;
   End;
End;

Procedure TD2PathData.DefineProperties(Filer: TFiler);
Begin
   Inherited;
   Filer.DefineBinaryProperty('Path', ReadPath, WritePath, Length(PathData) > 0);
End;

Procedure TD2PathData.ReadPath(Stream: TStream);
Var
   S: Longint;
   i: Integer;
   k: Byte;
   p: TD2Point;
Begin
   Stream.Read(S, SizeOf(S));
   {$ifdef FPC_BIG_ENDIAN}
  ReverseBytes(@S,4);
   {$endif}
   SetLength(PathData, S);
   If S > 0 Then
   Begin
      If (Stream.Size - 4) Div S = 9 Then
      Begin
         For i := 0 To S - 1 Do
         Begin
            Stream.Read(k, 1);
            Stream.Read(p, SizeOf(p));
            {$ifdef FPC_BIG_ENDIAN}
        ReverseBytes(@p.x,4);
        ReverseBytes(@p.y,4);
            {$endif}
            PathData[i].Kind := TD2PathPointKind(k);
            PathData[i].Point := p;
         End;
      End Else Begin
         Stream.Read(PathData[0], S * SizeOf(TD2PathPoint));
         {$ifdef FPC_BIG_ENDIAN}
      for i:=0 to S * 3 - 1 do
        ReverseBytes(@PD2ColorArray(PathData)[i],4);
         {$endif}
      End;
   End;
End;

Procedure TD2PathData.WritePath(Stream: TStream);
Var
   S: Longint;
Begin
   S := Length(PathData);
   Stream.Write(S, SizeOf(S));
   If S > 0 Then
      Stream.Write(PathData[0], S * SizeOf(TD2PathPoint));
End;

Function TD2PathData.LastPoint: TD2Point;
Begin
   If Length(PathData) > 0 Then
      Result := PathData[High(PathData)].Point
   Else
      Result := d2Point(0, 0);
End;

Procedure TD2PathData.MoveTo(Const P: TD2Point);
Begin
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointMoveTo;
   PathData[High(PathData)].Point := P;
   FStartPoint := PathData[High(PathData)].Point;
End;

Procedure TD2PathData.MoveToRel(Const P: TD2Point);
Begin
   With LastPoint Do
   Begin
      SetLength(PathData, Length(PathData) + 1);
      PathData[High(PathData)].Kind := d2PathPointMoveTo;
      PathData[High(PathData)].Point := d2Point(x + P.x, y + P.y);
   End;
   FStartPoint := PathData[High(PathData)].Point;
End;

Procedure TD2PathData.LineTo(Const P: TD2Point);
Begin
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointLineTo;
   PathData[High(PathData)].Point := P;
End;

Procedure TD2PathData.LineToRel(Const P: TD2Point);
Begin
   With LastPoint Do
   Begin
      SetLength(PathData, Length(PathData) + 1);
      PathData[High(PathData)].Kind := d2PathPointLineTo;
      PathData[High(PathData)].Point := d2Point(x + P.x, y + P.y);
   End;
End;

Procedure TD2PathData.HLineTo(Const x: Single);
Begin
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointLineTo;
   PathData[High(PathData)].Point := d2Point(x, PathData[High(PathData) - 1].Point.y);
End;

Procedure TD2PathData.HLineToRel(Const x: Single);
Var
   LP: TD2Point;
Begin
   LP := LastPoint;
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointLineTo;
   PathData[High(PathData)].Point := d2Point(LP.x + x, LP.y);
End;

Procedure TD2PathData.VLineTo(Const y: Single);
Begin
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointLineTo;
   PathData[High(PathData)].Point := d2Point(PathData[High(PathData) - 1].Point.x, y);
End;

Procedure TD2PathData.VLineToRel(Const y: Single);
Var
   LP: TD2Point;
Begin
   LP := LastPoint;
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointLineTo;
   PathData[High(PathData)].Point := d2Point(LP.x, LP.y + y);
End;

Procedure TD2PathData.CurveTo(Const ControlPoint1, ControlPoint2, EndPoint: TD2Point);
Begin
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointCurveTo;
   PathData[High(PathData)].Point := ControlPoint1;
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointCurveTo;
   PathData[High(PathData)].Point := ControlPoint2;
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointCurveTo;
   PathData[High(PathData)].Point := EndPoint;
End;

Procedure TD2PathData.CurveToRel(Const ControlPoint1, ControlPoint2, EndPoint: TD2Point);
Begin
   With LastPoint Do
   Begin
      SetLength(PathData, Length(PathData) + 1);
      PathData[High(PathData)].Kind := d2PathPointCurveTo;
      PathData[High(PathData)].Point := d2Point(x + ControlPoint1.x, y + ControlPoint1.y);
      SetLength(PathData, Length(PathData) + 1);
      PathData[High(PathData)].Kind := d2PathPointCurveTo;
      PathData[High(PathData)].Point := d2Point(x + ControlPoint2.x, y + ControlPoint2.y);
      ;
      SetLength(PathData, Length(PathData) + 1);
      PathData[High(PathData)].Kind := d2PathPointCurveTo;
      PathData[High(PathData)].Point := d2Point(x + EndPoint.x, y + EndPoint.y);
      ;
   End;
End;

Procedure TD2PathData.SmoothCurveTo(Const ControlPoint2, EndPoint: TD2Point);
Var
   ControlPoint1: TD2Point;
Begin
   If Length(PathData) > 2 Then
   Begin
      ControlPoint1.x := LastPoint.x + (LastPoint.x - PathData[High(PathData)].Point.x);
      ControlPoint1.y := LastPoint.y + (LastPoint.y - PathData[High(PathData)].Point.y);
   End Else
      ControlPoint1 := ControlPoint2;
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointCurveTo;
   PathData[High(PathData)].Point := ControlPoint1;
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointCurveTo;
   PathData[High(PathData)].Point := ControlPoint2;
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointCurveTo;
   PathData[High(PathData)].Point := EndPoint;
End;

Procedure TD2PathData.SmoothCurveToRel(Const ControlPoint2, EndPoint: TD2Point);
Var
   ControlPoint1: TD2Point;
Begin
   If Length(PathData) > 2 Then
   Begin
      ControlPoint1.x := LastPoint.x + (LastPoint.x - PathData[High(PathData)].Point.x);
      ControlPoint1.y := LastPoint.y + (LastPoint.y - PathData[High(PathData)].Point.y);
   End Else
      ControlPoint1 := ControlPoint2;
   With LastPoint Do
   Begin
      SetLength(PathData, Length(PathData) + 1);
      PathData[High(PathData)].Kind := d2PathPointCurveTo;
      PathData[High(PathData)].Point := d2Point(ControlPoint1.x, ControlPoint1.y);
      SetLength(PathData, Length(PathData) + 1);
      PathData[High(PathData)].Kind := d2PathPointCurveTo;
      PathData[High(PathData)].Point := d2Point(x + ControlPoint2.x, y + ControlPoint2.y);
      ;
      SetLength(PathData, Length(PathData) + 1);
      PathData[High(PathData)].Kind := d2PathPointCurveTo;
      PathData[High(PathData)].Point := d2Point(x + EndPoint.x, y + EndPoint.y);
      ;
   End;
End;

Procedure TD2PathData.ClosePath;
Begin
   SetLength(PathData, Length(PathData) + 1);
   PathData[High(PathData)].Kind := d2PathPointClose;
   PathData[High(PathData)].Point := FStartPoint;
End;

Procedure TD2PathData.Clear;
Begin
   SetLength(PathData, 0);
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;

Function TD2PathData.GetBounds: TD2Rect;
Var
   i: Integer;
Begin
   If Length(PathData) = 0 Then
   Begin
      Result := d2Rect(0, 0, 0, 0);
      Exit;
   End;
   Result := d2Rect($FFFF, $FFFF, -$FFFF, -$FFFF);
   For i := 0 To High(PathData) Do
   Begin
      If PathData[i].Kind = d2PathPointClose Then
         Continue;

      If PathData[i].Point.X < Result.Left Then
         Result.Left := PathData[i].Point.X;
      If PathData[i].Point.X > Result.Right Then
         Result.Right := PathData[i].Point.X;
      If PathData[i].Point.Y < Result.Top Then
         Result.Top := PathData[i].Point.Y;
      If PathData[i].Point.Y > Result.Bottom Then
         Result.Bottom := PathData[i].Point.Y;
   End;
   // add small amount
   If d2RectWidth(Result) = 0 Then
      Result.Right := Result.Left + 0.001;
   If d2RectHeight(Result) = 0 Then
      Result.Bottom := Result.Top + 0.001;
End;

Procedure TD2PathData.Scale(Const scaleX, scaleY: Single);
Var
   i: Integer;
Begin
   If Length(PathData) > 0 Then
   Begin
      For i := 0 To High(PathData) Do
         Case PathData[i].Kind Of
            d2PathPointMoveTo, d2PathPointLineTo, d2PathPointCurveTo:
            Begin
               PathData[i].Point.x := PathData[i].Point.x * scaleX;
               PathData[i].Point.y := PathData[i].Point.y * scaleY;
            End;
            d2PathPointClose:
            Begin
            End;
         End;
   End;
End;

Procedure TD2PathData.Offset(Const dX, dY: Single);
Var
   i: Integer;
Begin
   If Length(PathData) > 0 Then
   Begin
      For i := 0 To High(PathData) Do
         Case PathData[i].Kind Of
            d2PathPointMoveTo, d2PathPointLineTo, d2PathPointCurveTo:
            Begin
               PathData[i].Point.x := PathData[i].Point.x + dX;
               PathData[i].Point.y := PathData[i].Point.y + dY;
            End;
            d2PathPointClose:
            Begin
            End;
         End;
   End;
End;

Procedure TD2PathData.ApplyMatrix(Const M: TD2Matrix);
Var
   i: Integer;
Begin
   If Length(PathData) > 0 Then
   Begin
      For i := 0 To High(PathData) Do
         Case PathData[i].Kind Of
            d2PathPointMoveTo, d2PathPointLineTo, d2PathPointCurveTo:
            Begin
               With d2VectorTransform(d2Vector(PathData[i].Point), M) Do
                  PathData[i].Point := d2Point(x, y);
            End;
            d2PathPointClose:
            Begin
            End;
         End;
   End;
End;

Procedure TD2PathData.Flatten(Const Flatness: Single = 0.25);

   Procedure CalculateBezierCoefficients(Const Bezier: TD2CubicBezier; out ax, bx, cx, ay, by, cy: Single);
   Begin
      cx := 3.0 * (Bezier[1].x - Bezier[0].x);
      cy := 3.0 * (Bezier[1].y - Bezier[0].y);
      bx := 3.0 * (Bezier[2].x - Bezier[1].x) - cx;
      by := 3.0 * (Bezier[2].y - Bezier[1].y) - cy;
      ax := Bezier[3].x - Bezier[0].x - cx - bx;
      ay := Bezier[3].y - Bezier[0].y - cy - by;
   End;

   Function PointOnBezier(Const StartPoint: TD2Point; Const ax, bx, cx, ay, by, cy, T: Single): TD2Point;
   Var
      tSqr: Single;
      tCube: Single;
   Begin
      tSqr := t * t;
      tCube := tSqr * t;
      Result.x := (ax * tCube) + (bx * tSqr) + (cx * t) + StartPoint.x;
      Result.y := (ay * tCube) + (by * tSqr) + (cy * t) + StartPoint.y;
   End;

   Function CreateBezier(Const Bezier: TD2CubicBezier; Const PointCount: Integer): TD2Polygon;
   Var
      ax: Single;
      bx: Single;
      cx: Single;
      ay: Single;
      by: Single;
      cy: Single;
      dT: Single;
      T: Single;
      i: Integer;
   Begin
      If PointCount = 0 Then
         exit;
      dT := 1.0 / (1.0 * PointCount - 1.0);
      T := 0.0;
      SetLength(Result, PointCount);
      CalculateBezierCoefficients(Bezier, ax, bx, cx, ay, by, cy);
      For i := 0 To PointCount - 1 Do
      Begin
         Result[i] := PointOnBezier(Bezier[0], ax, bx, cx, ay, by, cy, T);
         T := T + dT;
      End;
   End;

Var
   i, j: Integer;
   BPts: TD2Polygon;
   B: TD2CubicBezier;
   Len: Single;
   SegCount: Integer;
   OldPathData: Array Of TD2PathPoint;
   CurPoint: TD2Point;
   f, s: Single;
   Bounds, R: TD2Rect;
Begin
   { scale }
   If Length(PathData) > 0 Then
   Begin
      Bounds := GetBounds;
      R := Bounds;
      d2FitRect(R, d2Rect(0, 0, 100, 100));
      s := d2MinFloat(d2RectWidth(Bounds) / 100, d2RectHeight(Bounds) / 100);
      f := Flatness * s;
      If f < 0.05 Then
         f := 0.05;

      { copy data }
      SetLength(OldPathData, Length(PathData));
      System.Move(PathData[0], OldPathData[0], Length(PathData) * SizeOf(PathData[0]));
      SetLength(PathData, 0);

      i := 0;
      While i < Length(OldPathData) Do
      Begin
         Case OldPathData[i].Kind Of
            d2PathPointMoveTo:
            Begin
               MoveTo(OldPathData[i].Point);
               CurPoint := OldPathData[i].Point;
            End;
            d2PathPointLineTo:
            Begin
               LineTo(OldPathData[i].Point);
               CurPoint := OldPathData[i].Point;
            End;
            d2PathPointCurveTo:
            Begin
               B[0] := CurPoint;
               B[1] := OldPathData[i].Point;
               Inc(i);
               B[2] := OldPathData[i].Point;
               Inc(i);
               B[3] := OldPathData[i].Point;
               Len := d2VectorLength(d2VectorSubtract(d2Vector(B[1]), d2Vector(B[3])));
               SegCount := round(Len / f);
               If SegCount < 2 Then
                  SegCount := 2;
               BPts := CreateBezier(B, SegCount);
               For j := 0 To High(BPts) Do
               Begin
                  LineTo(BPts[j]);
               End;
               CurPoint := OldPathData[i].Point;
            End;
            d2PathPointClose:
            Begin
               ClosePath;
            End;
         End;
         Inc(i);
      End;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
   End;
End;

Function TD2PathData.FlattenToPolygon(Var Polygon: TD2Polygon; Const Flatness: Single = 0.25): TD2Point;

   Procedure CalculateBezierCoefficients(Const Bezier: TD2CubicBezier; out ax, bx, cx, ay, by, cy: Single);
   Begin
      cx := 3.0 * (Bezier[1].x - Bezier[0].x);
      cy := 3.0 * (Bezier[1].y - Bezier[0].y);
      bx := 3.0 * (Bezier[2].x - Bezier[1].x) - cx;
      by := 3.0 * (Bezier[2].y - Bezier[1].y) - cy;
      ax := Bezier[3].x - Bezier[0].x - cx - bx;
      ay := Bezier[3].y - Bezier[0].y - cy - by;
   End;

   Function PointOnBezier(Const StartPoint: TD2Point; Const ax, bx, cx, ay, by, cy, T: Single): TD2Point;
   Var
      tSqr: Single;
      tCube: Single;
   Begin
      tSqr := t * t;
      tCube := tSqr * t;
      Result.x := (ax * tCube) + (bx * tSqr) + (cx * t) + StartPoint.x;
      Result.y := (ay * tCube) + (by * tSqr) + (cy * t) + StartPoint.y;
   End;

   Function CreateBezier(Const Bezier: TD2CubicBezier; Const PointCount: Integer): TD2Polygon;
   Var
      ax: Single;
      bx: Single;
      cx: Single;
      ay: Single;
      by: Single;
      cy: Single;
      dT: Single;
      T: Single;
      i: Integer;
   Begin
      If PointCount = 0 Then
         exit;
      dT := 1.0 / (1.0 * PointCount - 1.0);
      T := 0.0;
      SetLength(Result, PointCount);
      CalculateBezierCoefficients(Bezier, ax, bx, cx, ay, by, cy);
      For i := 0 To PointCount - 1 Do
      Begin
         Result[i] := PointOnBezier(Bezier[0], ax, bx, cx, ay, by, cy, T);
         T := T + dT;
      End;
   End;

Var
   i, j: Integer;
   BPts: TD2Polygon;
   B: TD2CubicBezier;
   SP, CurPoint: TD2Point;
   Len: Single;
   SegCount: Integer;
   f, s: Single;
   Bounds, R: TD2Rect;
Begin
   Result := d2Point(0, 0);
   SetLength(Polygon, 0);
   If Length(PathData) > 0 Then
   Begin
      Bounds := GetBounds;
      R := Bounds;
      d2FitRect(R, d2Rect(0, 0, 100, 100));
      s := d2MinFloat(d2RectWidth(Bounds) / 100, d2RectHeight(Bounds) / 100);
      f := Flatness * s;
      If f < 0.05 Then
         f := 0.05;

      i := 0;
      While i < Length(PathData) Do
      Begin
         Case PathData[i].Kind Of
            d2PathPointMoveTo:
            Begin
               SetLength(Polygon, Length(Polygon) + 1);
               Polygon[High(Polygon)] := PathData[i].Point;
               CurPoint := PathData[i].Point;
               SP := CurPoint;
            End;
            d2PathPointLineTo:
            Begin
               SetLength(Polygon, Length(Polygon) + 1);
               Polygon[High(Polygon)] := PathData[i].Point;
               CurPoint := PathData[i].Point;
            End;
            d2PathPointCurveTo:
            Begin
               B[0] := CurPoint;
               B[1] := PathData[i].Point;
               Inc(i);
               B[2] := PathData[i].Point;
               Inc(i);
               B[3] := PathData[i].Point;
               Len := d2VectorLength(d2VectorSubtract(d2Vector(B[1]), d2Vector(B[3])));
               SegCount := round(Len / f);
               If SegCount < 2 Then
                  SegCount := 2;
               BPts := CreateBezier(B, SegCount);
               For j := 0 To High(BPts) Do
               Begin
                  SetLength(Polygon, Length(Polygon) + 1);
                  Polygon[High(Polygon)] := BPts[j];
               End;
               CurPoint := PathData[i].Point;
            End;
            d2PathPointClose:
            Begin
               SetLength(Polygon, Length(Polygon) + 1);
               Polygon[High(Polygon)] := SP;
               SetLength(Polygon, Length(Polygon) + 1);
               Polygon[High(Polygon)] := ClosePolygon;
            End;
         End;
         Inc(i);
      End;
      With GetBounds Do
         Result := d2Point(Abs(Right - Left), Abs(Bottom - Top));
   End;
End;

Procedure TD2PathData.AddEllipse(Const ARect: TD2Rect);
Var
   cx, cy: Single;
   px, py: Single;
Begin
   cx := (ARect.Left + ARect.Right) / 2;
   cy := (ARect.Top + ARect.Bottom) / 2;
   px := CurveKappa * (d2RectWidth(ARect) / 2);
   py := CurveKappa * (d2RectHeight(ARect) / 2);
   MoveTo(d2Point(ARect.Left, cy));
   CurveTo(d2Point(ARect.Left, cy - py), d2Point(cx - px, ARect.Top), d2Point(cx, ARect.Top));
   CurveTo(d2Point(cx + px, ARect.Top), d2Point(ARect.Right, cy - py), d2Point(ARect.Right, cy));
   CurveTo(d2Point(ARect.Right, cy + py), d2Point(cx + px, ARect.Bottom), d2Point(cx, ARect.Bottom));
   CurveTo(d2Point(cx - px, ARect.Bottom), d2Point(ARect.Left, cy + py), d2Point(ARect.Left, cy));
End;

Procedure TD2PathData.AddRectangle(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const ACornerType: TD2CornerType = d2CornerRound);
Var
   R: TD2Rect;
   x1, x2, y1, y2: Single;
Begin
   R := ARect;
   x1 := xRadius;
   If d2RectWidth(R) - (x1 * 2) < 0 Then
      x1 := (xRadius * (d2RectWidth(R) / (x1 * 2)));
   x2 := x1 / 2;
   y1 := yRadius;
   If d2RectHeight(R) - (y1 * 2) < 0 Then
      y1 := (yRadius * (d2RectHeight(R) / (y1 * 2)));
   y2 := y1 / 2;

   MoveTo(d2Point(R.Left, R.Top + y1));
   If d2CornerTopLeft In ACorners Then
   Begin
      Case ACornerType Of

         d2CornerBevel: LineTo(d2Point(R.Left + x1, R.Top));
         d2CornerInnerRound: CurveTo(d2Point(R.Left + x2, R.Top + y1), d2Point(R.Left + x1, R.Top + y2),
               d2Point(R.Left + x1, R.Top));
         d2CornerInnerLine:
         Begin
            LineTo(d2Point(R.Left + x2, R.Top + y1));
            LineTo(d2Point(R.Left + x1, R.Top + y2));
            LineTo(d2Point(R.Left + x1, R.Top));
         End;
         Else CurveTo(d2Point(R.Left, R.Top + (y2)), d2Point(R.Left + x2, R.Top), d2Point(R.Left + x1, R.Top))
      End;
   End Else Begin
      LineTo(d2Point(R.Left, R.Top));
      LineTo(d2Point(R.Left + x1, R.Top));
   End;
   LineTo(d2Point(R.Right - x1, R.Top));
   If d2CornerTopRight In ACorners Then
   Begin
      Case ACornerType Of

         d2CornerBevel: LineTo(d2Point(R.Right, R.Top + y1));
         d2CornerInnerRound: CurveTo(d2Point(R.Right - x1, R.Top + y2), d2Point(R.Right - x2, R.Top + y1),
               d2Point(R.Right, R.Top + y1));
         d2CornerInnerLine:
         Begin
            LineTo(d2Point(R.Right - x1, R.Top + y2));
            LineTo(d2Point(R.Right - x2, R.Top + y1));
            LineTo(d2Point(R.Right, R.Top + y1));
         End;
         Else CurveTo(d2Point(R.Right - x2, R.Top), d2Point(R.Right, R.Top + (y2)), d2Point(R.Right, R.Top + y1))
      End;
   End Else Begin
      LineTo(d2Point(R.Right, R.Top));
      LineTo(d2Point(R.Right, R.Top + y1));
   End;
   LineTo(d2Point(R.Right, R.Bottom - y1));
   If d2CornerBottomRight In ACorners Then
   Begin
      Case ACornerType Of

         d2CornerBevel: LineTo(d2Point(R.Right - x1, R.Bottom));
         d2CornerInnerRound: CurveTo(d2Point(R.Right - x2, R.Bottom - y1), d2Point(R.Right - x1, R.Bottom - y2),
               d2Point(R.Right - x1, R.Bottom));
         d2CornerInnerLine:
         Begin
            LineTo(d2Point(R.Right - x2, R.Bottom - y1));
            LineTo(d2Point(R.Right - x1, R.Bottom - y2));
            LineTo(d2Point(R.Right - x1, R.Bottom));
         End;
         Else CurveTo(d2Point(R.Right, R.Bottom - (y2)), d2Point(R.Right - x2, R.Bottom), d2Point(R.Right - x1, R.Bottom))
      End;
   End Else Begin
      LineTo(d2Point(R.Right, R.Bottom));
      LineTo(d2Point(R.Right - x1, R.Bottom));
   End;
   LineTo(d2Point(R.Left + x1, R.Bottom));
   If d2CornerBottomLeft In ACorners Then
   Begin
      Case ACornerType Of

         d2CornerBevel: LineTo(d2Point(R.Left, R.Bottom - y1));
         d2CornerInnerRound: CurveTo(d2Point(R.Left + x1, R.Bottom - y2), d2Point(R.Left + x2, R.Bottom - y1),
               d2Point(R.Left, R.Bottom - y1));
         d2CornerInnerLine:
         Begin
            LineTo(d2Point(R.Left + x1, R.Bottom - y2));
            LineTo(d2Point(R.Left + x2, R.Bottom - y1));
            LineTo(d2Point(R.Left, R.Bottom - y1));
         End;
         Else CurveTo(d2Point(R.Left + x2, R.Bottom), d2Point(R.Left, R.Bottom - (y2)), d2Point(R.Left, R.Bottom - y1))
      End;
   End Else Begin
      LineTo(d2Point(R.Left, R.Bottom));
      LineTo(d2Point(R.Left, R.Bottom - y1));
   End;
   ClosePath;
End;

Procedure DrawArcWithBezier(Path: TD2PathData; CenterX, CenterY, RadiusX, RadiusY, StartAngle, SweepRange: Single; UseMoveTo: Boolean);
Var
   Coord: Array[0..3] Of TD2Point;
   pts: Array[0..3] Of TD2Point;
   a, b, c, x, y: Double;
   ss, cc: Double;
   i: Integer;
Begin
   If SweepRange = 0 Then
   Begin
      If UseMoveTo Then
      Begin
         If (Length(Path.PathData) = 0) Then
            Path.MoveTo(d2Point(CenterX + RadiusX * cos(StartAngle), CenterY - RadiusY * sin(StartAngle)))
         Else
            Path.LineTo(d2Point(CenterX + RadiusX * cos(StartAngle), CenterY - RadiusY * sin(StartAngle)));
      End;
      Path.LineTo(d2Point(CenterX + RadiusX * cos(StartAngle), CenterY - RadiusY * sin(StartAngle)));
      Exit;
   End;
   b := sin(SweepRange / 2);
   c := cos(SweepRange / 2);
   a := 1 - c;
   x := a * 4.0 / 3.0;
   y := b - x * c / b;
   ss := sin(StartAngle + SweepRange / 2);
   cc := cos(StartAngle + SweepRange / 2);
   Coord[0] := d2Point(c, -b);
   Coord[1] := d2Point(c + x, -y);
   Coord[2] := d2Point(c + x, y);
   Coord[3] := d2Point(c, b);
   For i := 0 To 3 Do
   Begin
      pts[i] := d2Point(CenterX + RadiusX * (Coord[i].x * cc - Coord[i].y * ss), Centery + RadiusY * (Coord[i].x * ss + Coord[i].y * cc));
   End;
   If UseMoveTo Then
   Begin
      If (Length(Path.PathData) = 0) Then
         Path.MoveTo(pts[0])
      Else
         Path.LineTo(pts[0]);
   End;
   Path.CurveTo(pts[1], pts[2], pts[3]);
End;

Procedure TD2PathData.AddArc(Const Center, Radius: TD2Point; StartAngle, SweepAngle: Single);
Const
   bezier_arc_angle_epsilon = 0.01;
Var
   UseMoveTo: Boolean;
   i: Integer;
   f: Single;
   total_sweep, local_sweep, prev_sweep: Single;
   done: Boolean;
Begin
   StartAngle := d2DegToRad(StartAngle);
   SweepAngle := d2DegToRad(SweepAngle);

   i := trunc(StartAngle / (2.0 * cPi));
   f := StartAngle - (i * 2.0 * cPi);

   StartAngle := f;

   If SweepAngle >= 2.0 * cPi Then
      SweepAngle := 2.0 * cPi;
   If SweepAngle <= -2.0 * cPi Then
      SweepAngle := -2.0 * cPi;

   If Abs(SweepAngle) < 1e-10 Then
   Begin
      exit;
   End;

   total_sweep := 0.0;

   done := False;
   UseMoveTo := True;
   Repeat
      If SweepAngle < 0.0 Then
      Begin
         prev_sweep := total_sweep;
         local_sweep := -cPi * 0.5;
         total_sweep := total_sweep - (cPi * 0.5);
         If total_sweep <= SweepAngle + bezier_arc_angle_epsilon Then
         Begin
            local_sweep := SweepAngle - prev_sweep;
            done := True;
         End;
      End Else Begin
         prev_sweep := total_sweep;
         local_sweep := cPi * 0.5;
         total_sweep := total_sweep + (pi * 0.5);
         If total_sweep >= SweepAngle - bezier_arc_angle_epsilon Then
         Begin
            local_sweep := SweepAngle - prev_sweep;
            done := True;
         End;
      End;
      DrawArcWithBezier(Self, Center.x, Center.y, Radius.x, Radius.y, StartAngle, local_sweep, UseMoveTo);
      UseMoveTo := False;
      StartAngle := StartAngle + local_sweep;
   Until done;
End;

Procedure TD2PathData.AddArcSd2Part(Const Center, Radius: TD2Point; StartAngle, SweepAngle: Single);
Const
   bezier_arc_angle_epsilon = 0.01;
Var
   UseMoveTo: Boolean;
   i: Integer;
   f: Single;
   total_sweep, local_sweep, prev_sweep: Single;
   done: Boolean;
Begin
   StartAngle := d2DegToRad(StartAngle);
   SweepAngle := d2DegToRad(SweepAngle);

   i := trunc(StartAngle / (2.0 * cPi));
   f := StartAngle - (i * 2.0 * cPi);

   StartAngle := f;

   If SweepAngle >= 2.0 * cPi Then
      SweepAngle := 2.0 * cPi;
   If SweepAngle <= -2.0 * cPi Then
      SweepAngle := -2.0 * cPi;

   If Abs(SweepAngle) < 1e-10 Then
   Begin
      exit;
   End;

   total_sweep := 0.0;

   done := False;
   UseMoveTo := False;
   Repeat
      If SweepAngle < 0.0 Then
      Begin
         prev_sweep := total_sweep;
         local_sweep := -cPi * 0.5;
         total_sweep := total_sweep - (cPi * 0.5);
         If total_sweep <= SweepAngle + bezier_arc_angle_epsilon Then
         Begin
            local_sweep := SweepAngle - prev_sweep;
            done := True;
         End;
      End Else Begin
         prev_sweep := total_sweep;
         local_sweep := cPi * 0.5;
         total_sweep := total_sweep + (pi * 0.5);
         If total_sweep >= SweepAngle - bezier_arc_angle_epsilon Then
         Begin
            local_sweep := SweepAngle - prev_sweep;
            done := True;
         End;
      End;
      DrawArcWithBezier(Self, Center.x, Center.y, Radius.x, Radius.y, StartAngle, local_sweep, UseMoveTo);
      UseMoveTo := False;
      StartAngle := StartAngle + local_sweep;
   Until done;
End;

Procedure TD2PathData.AddArcSd2(Const P1, Radius: TD2Point; Angle: Single; Const LargeFlag, SweepFlag: Boolean; Const P2: TD2Point);
Var
   i: Integer;
   v, p, n, sq, rx, ry, x0, y0, x1, y1, x2, y2, cx, cy, ux, uy, vx, vy, dx2, dy2, prx, pry, px1, py1, cx1, cy1, sx2, sy2, sign, coef, radii_check, start_angle, sweep_angle, cos_a, sin_a: Single;
   tm: TD2Matrix;
   len: Integer;
Begin
   rx := Radius.X;
   ry := Radius.Y;
   x0 := P1.X;
   y0 := P1.Y;
   x2 := P2.X;
   y2 := P2.Y;
   angle := d2DegToRad(Angle);

   If rx < 0.0 Then
      rx := -rx;

   If ry < 0.0 Then
      ry := -rx;

   dx2 := (x0 - x2) / 2.0;
   dy2 := (y0 - y2) / 2.0;

   cos_a := cos(angle);
   sin_a := sin(angle);

   x1 := cos_a * dx2 + sin_a * dy2;
   y1 := -sin_a * dx2 + cos_a * dy2;

   prx := rx * rx;
   pry := ry * ry;
   px1 := x1 * x1;
   py1 := y1 * y1;

   radii_check := px1 / prx + py1 / pry;

   If radii_check > 1.0 Then
   Begin
      rx := sqrt(radii_check) * rx;
      ry := sqrt(radii_check) * ry;
      prx := rx * rx;
      pry := ry * ry;
   End;

   If LargeFlag = SweepFlag Then
      sign := -1.0
   Else
      sign := 1.0;

   sq := (prx * pry - prx * py1 - pry * px1) / (prx * py1 + pry * px1);

   If sq < 0 Then
      coef := sign * sqrt(0)
   Else
      coef := sign * sqrt(sq);

   cx1 := coef * ((rx * y1) / ry);
   cy1 := coef * -((ry * x1) / rx);

   sx2 := (x0 + x2) / 2.0;
   sy2 := (y0 + y2) / 2.0;
   cx := sx2 + (cos_a * cx1 - sin_a * cy1);
   cy := sy2 + (sin_a * cx1 + cos_a * cy1);

   ux := (x1 - cx1) / rx;
   uy := (y1 - cy1) / ry;
   vx := (-x1 - cx1) / rx;
   vy := (-y1 - cy1) / ry;

   n := sqrt(ux * ux + uy * uy);
   p := ux;

   If uy < 0 Then
      sign := -1.0
   Else
      sign := 1.0;

   v := p / n;

   If v < -1.0 Then
      v := -1.0;

   If v > 1.0 Then
      v := 1.0;

   start_angle := sign * ArcCos(v);

   n := sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
   p := ux * vx + uy * vy;

   If ux * vy - uy * vx < 0 Then
      sign := -1.0
   Else
      sign := 1.0;

   v := p / n;

   If v < -1.0 Then
      v := -1.0;

   If v > 1.0 Then
      v := 1.0;

   sweep_angle := sign * ArcCos(v);

   If (Not SweepFlag) And (sweep_angle > 0) Then
      sweep_angle := sweep_angle - pi * 2.0
   Else
   If SweepFlag And (sweep_angle < 0) Then
      sweep_angle := sweep_angle + pi * 2.0;

   len := Length(PathData);
   AddArcSd2Part(d2Point(0, 0), d2Point(rx, ry), d2RadToDeg(start_angle), d2RadToDeg(sweep_angle));

   tm := IdentityMatrix;
   tm.m31 := cx;
   tm.m32 := cy;
   tm := d2MatrixMultiply(d2CreateRotationMatrix(Angle), tm);

   i := len;
   While i < Length(PathData) Do
   Begin
      With d2VectorTransform(d2Vector(PathData[i].Point), tm) Do
         PathData[i].Point := d2Point(x, y);
      Inc(i);
   End;
End;

Function TD2PathData.IsEmpty: Boolean;
Begin
   Result := Length(PathData) = 0;
End;

Function TD2PathData.GetPathString: Ansistring;
Var
   i: Integer;
Begin
   Result := '';
   i := 0;
   While i < Length(PathData) Do
   Begin
      Case PathData[i].Kind Of
         d2PathPointMoveTo:
            Result := Result + 'M ' + d2FloatToStr(PathData[i].Point.X) + ',' + d2FloatToStr(PathData[i].Point.Y) + ' ';
         d2PathPointLineTo:
            Result := Result + 'L ' + d2FloatToStr(PathData[i].Point.X) + ',' + d2FloatToStr(PathData[i].Point.Y) + ' ';
         d2PathPointCurveTo:
         Begin
            Result := Result + 'C ' + d2FloatToStr(PathData[i].Point.X) + ',' + d2FloatToStr(PathData[i].Point.Y) + ' ' + d2FloatToStr(PathData[i + 1].Point.X) + ',' + d2FloatToStr(PathData[i + 1].Point.Y) +
               ' ' + d2FloatToStr(PathData[i + 2].Point.X) + ',' + d2FloatToStr(PathData[i + 2].Point.Y) + ' ';
            Inc(i, 2);
         End;
         d2PathPointClose:
            Result := Result + 'Z ';
      End;
      Inc(i);
   End;
End;

Function GetTok(Const S: Ansistring; Var Pos: Integer): Ansistring;
Var
   i: Integer;
Begin
   Result := '';
   If Pos > Length(S) Then
      Exit;
   While (Pos <= Length(S)) And (S[Pos] In [' ']) Do
      Inc(Pos);
   For i := Pos To Length(S) Do
   Begin
      If System.Pos(S[i], 'zmlchvsqtaZMLCHVSQTA') = 0 Then
         Break;
      Result := Result + S[i];
   End;
   Pos := i;
End;

Function GetNum(Const S: Ansistring; Var Pos: Integer): Ansistring;
Var
   i: Integer;
Begin
   Result := '';
   If Pos > Length(S) Then
      Exit;
   While (Pos <= Length(S)) And (S[Pos] In [' ']) Do
      Inc(Pos);
   For i := Pos To Length(S) Do
   Begin
      If (S[i] = 'e') Then
      Begin
         Result := Result + S[i];
         Continue;
      End;
      If (S[i] = '-') And (Length(Result) > 0) And (Result[Length(Result)] = 'e') Then
      Begin
         Result := Result + S[i];
         Continue;
      End;
      If (System.Pos(S[i], '0123456789.') = 0) And Not ((i = Pos) And (S[i] = '-')) Then
         Break;
      Result := Result + S[i];
   End;
   While S[Pos] In [' '] Do
      Inc(Pos);
   Pos := i;
End;

Function GetPoint(Const S: Ansistring; Var Pos: Integer): TD2Point;
Var
   x, y: Ansistring;
Begin
   Result := d2Point(0, 0);
   If Pos > Length(S) Then
      Exit;
   While (Pos <= Length(S)) And (S[Pos] In [',', ' ']) Do
      Inc(Pos);
   x := GetNum(S, Pos);
   While (Pos <= Length(S)) And (S[Pos] In [',', ' ']) Do
      Inc(Pos);
   y := GetNum(S, Pos);
   While (Pos <= Length(S)) And (S[Pos] In [',', ' ']) Do
      Inc(Pos);

   Result := d2Point(d2StrToFloat(x), d2StrToFloat(y));
End;

Procedure TD2PathData.SetPathString(Const Value: Ansistring);
Const
   TokSep = ' '#10#13;
   TokStop = '0123456789-';
   NumSep = ' ,'#10#13;
   NumStop = 'zmlchvsqtaZMLCHVSQTA';
Var
   S, toks: Ansistring;
   tok: Ansichar;
   R, CP1, CP2: TD2Point;
   angle: Single;
   large, sweet: Boolean;
   lastlen, pos, i: Integer;
Begin
   For i := 1 To Length(Value) Do
   Begin
      If Value[i] In [#9, #10, #13] Then
         Continue;
      S := S + Value[i];
   End;

   SetLength(PathData, 0);
   pos := 1;
   While S <> '' Do
   Begin
      lastlen := pos;
      toks := GetTok(S, pos);
      While toks <> '' Do
      Begin
         tok := toks[1];
         Delete(toks, 1, 1);
         Try
            If (tok In ['z', 'Z']) Then
            Begin
               ClosePath;
            End;
            If (tok In ['M']) Then
            Begin
               MoveTo(GetPoint(S, pos));
               While (S <> '') And (S[pos] In ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-']) Do
               Begin
                  LineTo(GetPoint(S, pos));
               End;
            End;
            If (tok In ['m']) Then
            Begin
               MoveToRel(GetPoint(S, pos));
               While (S <> '') And (S[pos] In ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-']) Do
               Begin
                  LineToRel(GetPoint(S, pos));
               End;
            End;
            If (tok = 'L') Then
            Begin
               LineTo(GetPoint(S, pos));
               While (S <> '') And (S[pos] In ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-']) Do
               Begin
                  LineTo(GetPoint(S, pos));
               End;
            End;
            If (tok = 'l') Then
            Begin
               LineToRel(GetPoint(S, pos));
               While (S <> '') And (S[pos] In ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-']) Do
               Begin
                  LineToRel(GetPoint(S, pos));
               End;
            End;
            If (tok = 'C') Then
            Begin
               CP1 := GetPoint(S, pos);
               CP2 := GetPoint(S, pos);
               CurveTo(CP1, CP2, GetPoint(S, pos));
               While (S <> '') And (S[pos] In ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-']) Do
               Begin
                  CP1 := GetPoint(S, pos);
                  CP2 := GetPoint(S, pos);
                  CurveTo(CP1, CP2,
                     GetPoint(S, pos)
                     );
               End;
            End;
            If (tok = 'c') Then
            Begin
               CP1 := GetPoint(S, pos);
               CP2 := GetPoint(S, pos);
               CurveToRel(CP1, CP2, GetPoint(S, pos));
               While (S <> '') And (S[pos] In ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-']) Do
               Begin
                  CP1 := GetPoint(S, pos);
                  CP2 := GetPoint(S, pos);
                  CurveToRel(CP1, CP2, GetPoint(S, pos));
               End;
            End;
            If (tok = 'S') Then
            Begin
               CP2 := GetPoint(S, pos);
               SmoothCurveTo(CP2, GetPoint(S, pos));
               While (S <> '') And (S[pos] In ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-']) Do
               Begin
                  CP2 := GetPoint(S, pos);
                  SmoothCurveTo(CP2, GetPoint(S, pos));
               End;
            End;
            If (tok = 's') Then
            Begin
               CP2 := GetPoint(S, pos);
               SmoothCurveToRel(CP2, GetPoint(S, pos));
               While (S <> '') And (S[pos] In ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-']) Do
               Begin
                  CP2 := GetPoint(S, pos);
                  SmoothCurveToRel(CP2, GetPoint(S, pos));
               End;
            End;
            If (tok = 'H') Then
            Begin
               HLineTo(d2StrToFloat(GetNum(S, Pos)));
            End;
            If (tok = 'h') Then
            Begin
               HLineToRel(d2StrToFloat(GetNum(S, Pos)));
            End;
            If (tok = 'V') Then
            Begin
               VLineTo(d2StrToFloat(GetNum(S, Pos)));
            End;
            If (tok = 'v') Then
            Begin
               VLineToRel(d2StrToFloat(GetNum(S, Pos)));
            End;
            If (tok = 'Q') Then
            Begin
               GetPoint(S, pos);
               GetPoint(S, pos);
            End;
            If (tok = 'q') Then
            Begin
               GetPoint(S, pos);
               GetPoint(S, pos);
            End;
            If (tok = 'T') Then
            Begin
               GetPoint(S, pos);
            End;
            If (tok = 't') Then
            Begin
               GetPoint(S, pos);
            End;
            If (tok = 'A') Then
            Begin
               If Length(PathData) > 0 Then
                  CP1 := PathData[High(PathData)].Point
               Else
                  CP1 := d2Point(0, 0);
               R := GetPoint(S, pos);
               angle := d2StrToFloat(GetNum(S, Pos));
               With GetPoint(S, pos) Do
               Begin
                  large := X = 1;
                  sweet := Y = 1;
               End;
               CP2 := GetPoint(S, pos);
               AddArcSd2(CP1, R, angle, large, sweet, CP2);
            End;
            If (tok = 'a') Then
            Begin
               If Length(PathData) > 0 Then
                  CP1 := PathData[High(PathData)].Point
               Else
                  CP1 := d2Point(0, 0);
               R := GetPoint(S, pos);
               angle := d2StrToFloat(GetNum(S, Pos));
               With GetPoint(S, pos) Do
               Begin
                  large := X = 1;
                  sweet := Y = 1;
               End;
               CP2 := GetPoint(S, pos);
               CP2.x := CP1.x + CP2.x;
               CP2.y := CP1.y + CP2.y;
               AddArcSd2(CP1, R, angle, large, sweet, CP2);
            End;
         Except
         End;
      End;
      If lastlen = pos Then
      Begin
         Pos := 0;
         Break;
      End;
   End;
   If Assigned(FOnChanged) Then
      FOnChanged(Self);
End;


//================== Spline ======================================

Constructor TD2Spline.Create(Const Polygon: TD2Polygon);
Var
   i: Integer;
   X, Y: Array Of Single;
Begin
   Inherited Create;
   len := Length(Polygon);
   SetLength(X, len);
   SetLength(Y, len);
   For i := 0 To len - 1 Do
   Begin
      X[i] := Polygon[i].X;
      Y[i] := Polygon[i].Y;
   End;
   fnCalcHermiteFactors(X, matX);
   fnCalcHermiteFactors(Y, matY);
End;

Destructor TD2Spline.Destroy;
Begin
   Inherited;
End;

Procedure TD2Spline.SplineXY(Const t: Single; Var X, Y: Single);
Begin
   X := HermitInterpolate(MatX, t, len);
   Y := HermitInterpolate(MatY, t, len);
End;


//=============== TD2Canvas =================================

Constructor TD2Canvas.Create(Const AWidth, AHeight: Integer);
Begin
   Inherited Create;
   FStroke := TD2Brush.Create(d2BrushSolid, $FF000000);
   FFill := TD2Brush.Create(d2BrushSolid, $FFFFFFFF);
   FFont := TD2Font.Create;
   FFont.OnChanged := FontChanged;
   ResizeBuffer(AWidth, AHeight);
End;

Constructor TD2Canvas.CreateFromBitmap(Const ABitmap: TD2Bitmap);
Begin
   Inherited Create;
   FWidth := ABS(ABitmap.Width);
   FHeight := ABS(ABitmap.Height);
   FStroke := TD2Brush.Create(d2BrushSolid, $FF000000);
   FFill := TD2Brush.Create(d2BrushSolid, $FFFFFFFF);
   FFont := TD2Font.Create;
   FFont.OnChanged := FontChanged;
End;

Destructor TD2Canvas.Destroy;
Var
   i: Integer;
Begin
   If Length(FSaveData) > 0 Then
   Begin
      For i := 0 To High(FSaveData) Do
      Begin
         If FSaveData[i].Index = $FFFFFFFF Then
            Continue;
         FSaveData[i].Stroke.Free;
         FSaveData[i].Fill.Free;
         FSaveData[i].Font.Free;
      End;
      SetLength(FSaveData, 0);
   End;
   FFont.Free;
   FStroke.Free;
   FFill.Free;
   FreeBuffer;
   Inherited;
End;

Procedure TD2Canvas.AssignTo(Dest: TPersistent);
Var
   i: Integer;
Begin
   If Dest Is TD2Bitmap Then
   Begin
      TD2Bitmap(Dest).SetSize(FWidth, FHeight);
      If FBuffered Then
         For i := 0 To FHeight - 1 Do
            System.Move(PD2ColorArray(FBufferBits)[(i) * FWidth], TD2Bitmap(Dest).Scanline[i]^, FWidth * 4);
   End Else
      Inherited;
End;

Class Function TD2Canvas.GetBitmapScanline(Bitmap: TD2Bitmap; y: Integer): PD2ColorArray;
Begin
   Result := nil;
End;

Procedure TD2Canvas.SaveToStream(S: TStream);
Var
   B: TD2Bitmap;
Begin
   If FBuffered Then
   Begin
      B := TD2Bitmap.Create(FWidth, FHeight);
      d2MoveLongword(FBufferBits, B.StartLine, FWidth * FHeight);
      B.SaveToStream(S);
      B.Free;
   End;
End;

Procedure TD2Canvas.SaveToBits(Bits: Pointer);
Begin
   If FBuffered Then
      d2MoveLongword(FBufferBits, Bits, FWidth * FHeight);
End;

Function TD2Canvas.BeginScene: Boolean;
Begin
   Result := True;
End;

Procedure TD2Canvas.EndScene;
Begin
End;

Procedure TD2Canvas.SetMatrix(Const M: TD2Matrix);
Begin
   FMatrix := M;
End;

Procedure TD2Canvas.MultyMatrix(Const M: TD2Matrix);
Begin
End;

Procedure TD2Canvas.FontChanged(Sender: TObject);
Begin
End;

Function TD2Canvas.LoadFontFromStream(AStream: TStream): Boolean;
Begin
End;

Function TD2Canvas.TextHeight(Const AText: WideString): Single;
Var
   R: TD2Rect;
Begin
   R := d2Rect(0, 0, 10000, 10000);
   MeasureText(R, R, AText, False, d2TextAlignNear, d2TextAlignCenter);
   Result := d2RectHeight(R);
End;

Function TD2Canvas.TextWidth(Const AText: WideString): Single;
Var
   R: TD2Rect;
Begin
   R := d2Rect(0, 0, 10000, 20);
   MeasureText(R, R, AText, False, d2TextAlignNear, d2TextAlignCenter);
   Result := d2RectWidth(R);
End;

Procedure TD2Canvas.FillArc(Const Center, Radius: TD2Point; StartAngle, SweepAngle: Single; Const AOpacity: Single);
Var
   P: TD2PathData;
Begin
   P := TD2PathData.Create;
   P.AddArc(Center, Radius, StartAngle, SweepAngle);
   FillPath(P, P.Getbounds, AOpacity);
   P.Free;
End;

Procedure TD2Canvas.DrawArc(Const Center, Radius: TD2Point; StartAngle, SweepAngle: Single; Const AOpacity: Single);
Var
   P: TD2PathData;
Begin
   P := TD2PathData.Create;
   P.AddArc(Center, Radius, StartAngle, SweepAngle);
   DrawPath(P, P.Getbounds, AOpacity);
   P.Free;
End;

Procedure TD2Canvas.SetStrokeDash(Const Value: TD2StrokeDash);
Begin
   If Value <> FStrokeDash Then
   Begin
      FStrokeDash := Value;
      Case FStrokeDash Of
         d2DashSolid:
         Begin
            FDashOffset := 0;
            SetLength(FDash, 0);
         End;
         d2DashDash:
         Begin
            FDashOffset := 0;
            SetLength(FDash, 2);
            FDash[0] := 1 * 3;
            FDash[1] := 1;
         End;
         d2DashDot:
         Begin
            FDashOffset := 0;
            SetLength(FDash, 2);
            FDash[0] := 1;
            FDash[1] := 1;
         End;
         d2DashDashDot:
         Begin
            FDashOffset := 0;
            SetLength(FDash, 4);
            FDash[0] := 1 * 3;
            FDash[1] := 1;
            FDash[2] := 1;
            FDash[3] := 1;
         End;
         d2DashDashDotDot:
         Begin
            FDashOffset := 0;
            SetLength(FDash, 6);
            FDash[0] := 1 * 3;
            FDash[1] := 1;
            FDash[2] := 1;
            FDash[3] := 1;
            FDash[4] := 1;
            FDash[5] := 1;
         End;
         d2DashCustom: ;
         Else FDashOffset := 0;
            SetLength(FDash, 0);
      End;
   End;
End;

Procedure TD2Canvas.SetCustomDash(Dash: Array Of Single; Offset: Single);
Var
   i: Integer;
Begin
   FStrokeDash := d2DashCustom;
   SetLength(FDash, Length(Dash));
   For i := 0 To High(Dash) Do
      FDash[i] := Dash[i];
   FDashOffset := Offset;
End;

Procedure TD2Canvas.FillPolygon(Const Points: TD2Polygon; Const AOpacity: Single);
Var
   i: Integer;
   Path: TD2PathData;
Begin
   Path := TD2PathData.Create;
   SetLength(Path.PathData, Length(Points));
   For i := 0 To High(Points) Do
      With Path.PathData[i] Do
      Begin
         Kind := d2PathPointLineTo;
         If i = 0 Then
            Kind := d2PathPointMoveTo;
         If (i > 0) And (Path.PathData[i - 1].Kind = d2PathPointClose) Then
            Kind := d2PathPointMoveTo;
         If (Points[i].x = ClosePolygon.x) And (Points[i].y = ClosePolygon.y) Then
         Begin
            Kind := d2PathPointClose;
            Continue;
         End;
         If i = High(Points) Then
            Kind := d2PathPointClose;
         Point := Points[i];
      End;
   FillPath(Path, Path.GetBounds, AOpacity);
   Path.Free;
End;

Procedure TD2Canvas.DrawPolygon(Const Points: TD2Polygon; Const AOpacity: Single);
Var
   i: Integer;
   Path: TD2PathData;
Begin
   Path := TD2PathData.Create;
   SetLength(Path.PathData, Length(Points));
   For i := 0 To High(Points) Do
      With Path.PathData[i] Do
      Begin
         Kind := d2PathPointLineTo;
         If (i = 0) Then
            Kind := d2PathPointMoveTo;
         If (i > 0) And (Path.PathData[i - 1].Kind = d2PathPointClose) Then
            Kind := d2PathPointMoveTo;
         If (Points[i].x = ClosePolygon.x) And (Points[i].y = ClosePolygon.y) Then
         Begin
            Kind := d2PathPointClose;
            Continue;
         End;
         Point := Points[i];
      End;
   DrawPath(Path, Path.GetBounds, AOpacity);
   Path.Free;
End;

Procedure TD2Canvas.DrawRectSides(Const ARect: TD2Rect; Const xRadius, yRadius: Single; Const ACorners: TD2Corners; Const AOpacity: Single; Const ASides: TD2Sides; Const ACornerType: TD2CornerType = d2CornerRound);
Var
   Path: TD2PathData;
   x1, x2, y1, y2: Single;
   R: TD2Rect;
Begin
   R := ARect;
   x1 := xRadius;
   If d2RectWidth(R) - (x1 * 2) < 0 Then
      x1 := (xRadius * (d2RectWidth(R) / (x1 * 2)));
   x2 := x1 / 2;
   y1 := yRadius;
   If d2RectHeight(R) - (y1 * 2) < 0 Then
      y1 := (yRadius * (d2RectHeight(R) / (y1 * 2)));
   y2 := y1 / 2;
   Path := TD2PathData.Create;
   Path.MoveTo(d2Point(R.Left, R.Top + y1));
   If d2CornerTopLeft In ACorners Then
   Begin
      Case ACornerType Of

         d2CornerBevel: Path.LineTo(d2Point(R.Left + x1, R.Top));
         d2CornerInnerRound: Path.CurveTo(d2Point(R.Left + x2, R.Top + y1), d2Point(R.Left + x1, R.Top + y2), d2Point(R.Left + x1, R.Top));
         d2CornerInnerLine:
         Begin
            Path.LineTo(d2Point(R.Left + x2, R.Top + y1));
            Path.LineTo(d2Point(R.Left + x1, R.Top + y2));
            Path.LineTo(d2Point(R.Left + x1, R.Top));
         End;
         Else Path.CurveTo(d2Point(R.Left, R.Top + (y2)), d2Point(R.Left + x2, R.Top), d2Point(R.Left + x1, R.Top))
      End;
   End Else Begin
      If d2SideLeft In ASides Then
         Path.LineTo(d2Point(R.Left, R.Top))
      Else
         Path.MoveTo(d2Point(R.Left, R.Top));
      If d2SideTop In ASides Then
         Path.LineTo(d2Point(R.Left + x1, R.Top))
      Else
         Path.MoveTo(d2Point(R.Left + x1, R.Top));
   End;
   If Not (d2SideTop In ASides) Then
      Path.MoveTo(d2Point(R.Right - x1, R.Top))
   Else
      Path.LineTo(d2Point(R.Right - x1, R.Top));
   If d2CornerTopRight In ACorners Then
   Begin
      Case ACornerType Of

         d2CornerBevel: Path.LineTo(d2Point(R.Right, R.Top + y1));
         d2CornerInnerRound: Path.CurveTo(d2Point(R.Right - x1, R.Top + y2), d2Point(R.Right - x2, R.Top + y1), d2Point(R.Right, R.Top + y1));
         d2CornerInnerLine:
         Begin
            Path.LineTo(d2Point(R.Right - x1, R.Top + y2));
            Path.LineTo(d2Point(R.Right - x2, R.Top + y1));
            Path.LineTo(d2Point(R.Right, R.Top + y1));
         End;
         Else Path.CurveTo(d2Point(R.Right - x2, R.Top), d2Point(R.Right, R.Top + (y2)), d2Point(R.Right, R.Top + y1))
      End;
   End Else Begin
      If d2SideTop In ASides Then
         Path.LineTo(d2Point(R.Right, R.Top))
      Else
         Path.MoveTo(d2Point(R.Right, R.Top));
      If d2SideRight In ASides Then
         Path.LineTo(d2Point(R.Right, R.Top + y1))
      Else
         Path.MoveTo(d2Point(R.Right, R.Top + y1));
   End;
   If Not (d2SideRight In ASides) Then
      Path.MoveTo(d2Point(R.Right, R.Bottom - y1))
   Else
      Path.LineTo(d2Point(R.Right, R.Bottom - y1));
   If d2CornerBottomRight In ACorners Then
   Begin
      Case ACornerType Of

         d2CornerBevel: Path.LineTo(d2Point(R.Right - x1, R.Bottom));
         d2CornerInnerRound: Path.CurveTo(d2Point(R.Right - x2, R.Bottom - y1), d2Point(R.Right - x1, R.Bottom - y2), d2Point(R.Right - x1, R.Bottom));
         d2CornerInnerLine:
         Begin
            Path.LineTo(d2Point(R.Right - x2, R.Bottom - y1));
            Path.LineTo(d2Point(R.Right - x1, R.Bottom - y2));
            Path.LineTo(d2Point(R.Right - x1, R.Bottom));
         End;
         Else Path.CurveTo(d2Point(R.Right, R.Bottom - (y2)), d2Point(R.Right - x2, R.Bottom), d2Point(R.Right - x1, R.Bottom))
      End;
   End Else Begin
      If d2SideRight In ASides Then
         Path.LineTo(d2Point(R.Right, R.Bottom))
      Else
         Path.MoveTo(d2Point(R.Right, R.Bottom));
      If d2SideBottom In ASides Then
         Path.LineTo(d2Point(R.Right - x1, R.Bottom))
      Else
         Path.MoveTo(d2Point(R.Right - x1, R.Bottom));
   End;
   If Not (d2SideBottom In ASides) Then
      Path.MoveTo(d2Point(R.Left + x1, R.Bottom))
   Else
      Path.LineTo(d2Point(R.Left + x1, R.Bottom));
   If d2CornerBottomLeft In ACorners Then
   Begin
      Case ACornerType Of

         d2CornerBevel: Path.LineTo(d2Point(R.Left, R.Bottom - y1));
         d2CornerInnerRound: Path.CurveTo(d2Point(R.Left + x1, R.Bottom - y2), d2Point(R.Left + x2, R.Bottom - y1), d2Point(R.Left, R.Bottom - y1));
         d2CornerInnerLine:
         Begin
            Path.LineTo(d2Point(R.Left + x1, R.Bottom - y2));
            Path.LineTo(d2Point(R.Left + x2, R.Bottom - y1));
            Path.LineTo(d2Point(R.Left, R.Bottom - y1));
         End;
         Else Path.CurveTo(d2Point(R.Left + x2, R.Bottom), d2Point(R.Left, R.Bottom - (y2)), d2Point(R.Left, R.Bottom - y1))
      End;
   End Else Begin
      If d2SideBottom In ASides Then
         Path.LineTo(d2Point(R.Left, R.Bottom))
      Else
         Path.MoveTo(d2Point(R.Left, R.Bottom));
      If d2SideLeft In ASides Then
         Path.LineTo(d2Point(R.Left, R.Bottom - y1))
      Else
         Path.MoveTo(d2Point(R.Left, R.Bottom - y1));
   End;
   If (d2SideLeft In ASides) Then
   Begin
      Path.LineTo(d2Point(R.Left, R.Top + y1));
   End;
   DrawPath(Path, ARect, AOpacity);
   Path.Free;
End;

//=============== TD2Object =========================================

Constructor TD2Object.Create(AOwner: TComponent);
Begin
   Inherited Create(AOwner);
   AddResource(Self);
   FIndex := -1;
   FStored := True;
   FIsVisual := Self Is TD2VisualObject;
   If FIsVisual Then
      FVisual := TD2VisualObject(Self);
End;

Destructor TD2Object.Destroy;
Var
   i: Integer;
Begin
   If FNotifyList <> nil Then
   Begin
      For i := FNotifyList.Count - 1 Downto 0 Do
      Try
         If (TObject(FNotifyList[i]) Is TD2Visual) And (TD2Visual(FNotifyList[i]).VisualObject = Self) Then
            TD2Visual(FNotifyList[i]).FVisualObject := nil;
         If (TObject(FNotifyList[i]) Is TD2BrushResource) And (TD2BrushResource(FNotifyList[i]).Resource = Self) Then
            TD2BrushResource(FNotifyList[i]).FResource := nil;
         If (TObject(FNotifyList[i]) Is TD2PathData) And (TD2PathData(FNotifyList[i]).Resource = Self) Then
            TD2PathData(FNotifyList[i]).FResource := nil;
         If (TObject(FNotifyList[i]) Is TD2Bitmap) And (TD2Bitmap(FNotifyList[i]).Resource = Self) Then
            TD2Bitmap(FNotifyList[i]).FResource := nil;
         If TObject(FNotifyList[i]) Is TComponent Then
            If TObject(FNotifyList[i]) Is TComponent Then
               THackComponent(FNotifyList[i]).Notification(Self, opRemove);
      Except
      End;
      FreeAndNil(FNotifyList);
   End;
   i := varD2ResourceList.IndexOf(Self);
   If i >= 0 Then
      varD2ResourceList[i] := nil;

   If FScene <> nil Then
   Begin
      FScene.Notification(Self, opRemove);
   End;

   If FParent <> nil Then
      FParent.RemoveObject(Self)
   Else
   If FScene <> nil Then
      FScene.RemoveObject(Self);
   FScene := nil;
   DeleteChildren;
   Inherited;
End;

Procedure TD2Object.Release(Delay: Single = 0.1);
Var
   T: TTimer;
Begin
   T := TTimer.Create(Application);
   T.OnTimer := DoReleaseTimer;
   T.Interval := round(Delay * 1000);
   T.Enabled := True;
End;

Procedure TD2Object.DoReleaseTimer(Sender: TObject);
Begin
   Free;
   TTimer(Sender).Enabled := False;
End;

Function TD2Object.ItemClass: String;
Begin
   Result := '';
End;

Procedure TD2Object.AddFreeNotify(Const AObject: TObject);
Begin
   If FNotifyList = nil Then
      FNotifyList := TList.Create;
   FNotifyList.Add(AObject);
End;

Procedure TD2Object.RemoveFreeNotify(Const AObject: TObject);
Begin
   If FNotifyList <> nil Then
      FNotifyList.Remove(AObject);
End;

Procedure TD2Object.ReaderSetName(Reader: TReader; Component: TComponent; Var aName: String);
Begin
   aName := '';
End;

Procedure TD2Object.ReaderError(Reader: TReader; Const Message: String; Var Handled: Boolean);
Begin
   Handled := True;
End;

Procedure TD2Object.LoadFromStream(Const AStream: TStream);
Var
   BinStream: TStream;
Begin
   { store }
   BinStream := TMemoryStream.Create;
   Try
      ObjectTextToBinary(AStream, BinStream);
      BinStream.Position := 0;
      LoadFromBinStream(BinStream);
   Finally
      BinStream.Free;
   End;
End;

Procedure TD2Object.SaveToStream(Const Stream: TStream);
Var
   BinStream: TStream;
Begin
   { store }
   BinStream := TMemoryStream.Create;
   Try
      BinStream.WriteComponent(Self);
      BinStream.Position := 0;
      ObjectBinaryToText(BinStream, Stream);
   Finally
      BinStream.Free;
   End;
End;

Procedure TD2Object.LoadFromBinStream(Const AStream: TStream);
Var
   R: TReader;
Begin
   R := TReader.Create(AStream, 1024);
   R.OnError := ReaderError;
   Try
      R.ReadRootComponent(Self);
   Finally
      R.Free;
   End;
End;

Procedure TD2Object.SaveToBinStream(Const AStream: TStream);
Begin
   AStream.WriteComponent(Self);
End;

Procedure TD2Object.IntLoadFromBinStream(Const AStream: TStream);
Var
   R: TReader;
Begin
   R := TReader.Create(AStream, 1024);
   R.OnSetName := ReaderSetName;
   R.OnError := ReaderError;
   Try
      If Not (csDesigning In ComponentState) Then
      Begin
         SetDesigning(True, False);
         R.ReadRootComponent(Self);
         SetDesigning(False, False);
      End Else
         R.ReadRootComponent(Self);
   Finally
      R.Free;
   End;
End;

Procedure TD2Object.IntSaveToBinStream(Const AStream: TStream);
Var
   SaveName: String;
Begin
   SaveName := Name;
   Name := '';
   AStream.WriteComponent(Self);
   Name := SaveName;
End;

Function TD2Object.Clone(Const AOwner: TComponent): TD2Object;
Var
   S: TStream;
   SaveName: String;
Begin
   S := TMemoryStream.Create;
   Try

      SaveName := Name;
      Name := '';
      S.WriteComponent(Self);
      Name := SaveName;
      S.Position := 0;

      Result := TD2ObjectClass(ClassType).Create(AOwner);
      If Result <> nil Then
         Result.IntLoadFromBinStream(S);
   Finally
      S.Free;
   End;
End;

Procedure TD2Object.CloneChildFromStream(AStream: TStream);
Var
   i: Integer;
   Obj: TD2Object;
Begin
   Obj := CreateD2ObjectFromStream(Self, AStream);
   If (Obj <> nil) And (Obj.FChildren <> nil) And (Obj.FChildren.Count > 0) Then
   Begin
      DeleteChildren;

      For i := 0 To Obj.FChildren.Count - 1 Do
      Begin
         If TD2Object(Obj.FChildren[0]).isVisual Then
            TD2VisualObject(Obj.FChildren[0]).Locked := True;
         TD2Object(Obj.FChildren[0]).Stored := False;
         TD2Object(Obj.FChildren[0]).Parent := Self;
      End;

      If Obj.isVisual And (isVisual) Then
      Begin
         TD2VisualObject(Self).FLastWidth := TD2VisualObject(Obj).Width;
         TD2VisualObject(Self).FLastHeight := TD2VisualObject(Obj).Height;
         TD2VisualObject(Self).Realign;
      End;
   End;
End;

Procedure TD2VisualObject.SetLocked(Const Value: Boolean);
Begin
   FLocked := Value;
End;

Function TD2Object.GetScene: Id2Scene;
Begin
   If FScene <> nil Then
      Result := FScene
   Else
   If FParent <> nil Then
   Begin
      FScene := FParent.FScene;
      Result := FScene;
   End Else
      Result := nil;
End;

Function TD2Object.HasClipParent: TD2VisualObject;
Var
   i: Integer;
Begin
   Result := nil;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If (TD2Object(FChildren[i]).IsVisual) And (TD2VisualObject(FChildren[i]).ClipParent) Then
         Begin
            Result := TD2VisualObject(FChildren[i]);
            Exit;
         End;
End;

Function TD2VisualObject.GetEffectsRect: TD2Rect;
Var
   i: Integer;
Begin
   Result := LocalRect;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If (TD2Object(FChildren[i]) Is TD2Effect) And (TD2Effect(FChildren[i]).Enabled) Then
            Result := d2UnionRect(Result, TD2Effect(FChildren[i]).GetRect(LocalRect));
      End;
End;

Procedure TD2VisualObject.RecalcHasEffect;
Var
   i: Integer;
Begin
   FHasEffect := False;
   If FDisableEffect Then
      Exit;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If (TD2Object(FChildren[i]) Is TD2Effect) And (TD2Effect(FChildren[i]).Enabled) Then
         Begin
            If Not FHasEffect Then
            Begin
               UpdateEffects;
               Repaint;
            End;
            FHasEffect := True;
            Break;
         End;
      End;
End;

Function TD2VisualObject.HasDisablePaintEffect: Boolean;
Var
   i: Integer;
Begin
   Result := False;
   If FDisableEffect Then
      Exit;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If (TD2Object(FChildren[i]) Is TD2Effect) And (TD2Effect(FChildren[i]).Enabled) And (TD2Effect(FChildren[i]).DisablePaint) Then
         Begin
            Result := True;
            Exit;
         End;
End;

Function TD2VisualObject.HasAfterPaintEffect: Boolean;
Var
   i: Integer;
Begin
   Result := False;
   If FDisableEffect Then
      Exit;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If (TD2Object(FChildren[i]) Is TD2Effect) And (TD2Effect(FChildren[i]).Enabled) And (TD2Effect(FChildren[i]).AfterPaint) Then
         Begin
            Result := True;
            Exit;
         End;
End;

{ Property animation }

Procedure TD2Object.AnimateColor(Const APropertyName, NewValue: String; Duration: Single = 0.2; AType: TD2AnimationType = d2AnimationIn; AInterpolation: TD2InterpolationType = d2InterpolationLinear);
Var
   A: TD2ColorAnimation;
Begin
   A := TD2ColorAnimation.Create(Self);
   A.Parent := Self;
   A.AnimationType := AType;
   A.Interpolation := AInterpolation;
   A.OnFinish := DoAniFinished;
   A.Duration := Duration;
   A.PropertyName := APropertyName;
   A.StartFromCurrent := True;
   A.StopValue := NewValue;
   A.Start;
End;

Procedure TD2Object.AnimateFloat(Const APropertyName: String; Const NewValue: Single; Duration: Single = 0.2; AType: TD2AnimationType = d2AnimationIn; AInterpolation: TD2InterpolationType = d2InterpolationLinear);
Var
   A: TD2FloatAnimation;
Begin
   A := TD2FloatAnimation.Create(Self);
   A.Parent := Self;
   A.AnimationType := AType;
   A.Interpolation := AInterpolation;
   A.OnFinish := DoAniFinished;
   A.Duration := Duration;
   A.PropertyName := APropertyName;
   A.StartFromCurrent := True;
   A.StopValue := NewValue;
   A.Start;
End;

Procedure TD2Object.AnimateFloatDelay(Const APropertyName: String; Const NewValue: Single; Duration: Single = 0.2; Delay: Single = 0.0; AType: TD2AnimationType = d2AnimationIn; AInterpolation: TD2InterpolationType = d2InterpolationLinear);
Var
   A: TD2FloatAnimation;
Begin
   A := TD2FloatAnimation.Create(Self);
   A.Parent := Self;
   A.AnimationType := AType;
   A.Interpolation := AInterpolation;
   A.Delay := Delay;
   A.Duration := Duration;
   A.PropertyName := APropertyName;
   A.StartFromCurrent := True;
   A.StopValue := NewValue;
   A.Start;
End;

Procedure TD2Object.AnimateFloatWait(Const APropertyName: String; Const NewValue: Single; Duration: Single = 0.2; AType: TD2AnimationType = d2AnimationIn; AInterpolation: TD2InterpolationType = d2InterpolationLinear);
Var
   A: TD2FloatAnimation;
Begin
   A := TD2FloatAnimation.Create(Self);
   A.Parent := Self;
   A.AnimationType := AType;
   A.Interpolation := AInterpolation;
   A.Duration := Duration;
   A.PropertyName := APropertyName;
   A.StartFromCurrent := True;
   A.StopValue := NewValue;
   A.Start;
   While A.FRunning Do
   Begin
      Application.ProcessMessages;
      Sleep(0);
   End;
   A.Free;
End;

Procedure TD2Object.DoAniFinished(Sender: TObject);
Begin
   TD2Animation(Sender).Free;
End;

//====================== Animations ==================================================

Procedure TD2Object.StartAnimation(Const AName: WideString);
Var
   i: Integer;
   E: TD2Animation;
Begin
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If TD2Object(FChildren[i]) Is TD2Animation Then
            If CompareText(TD2Animation(FChildren[i]).Name, AName) = 0 Then
            Begin
               E := TD2Animation(FChildren[i]);
               E.Start;
            End;
      End;
End;

Procedure TD2Object.StopAnimation(Const AName: WideString);
Var
   i: Integer;
   E: TD2Animation;
Begin
   If FChildren <> nil Then
      For i := FChildren.Count - 1 Downto 0 Do
         If TD2Object(FChildren[i]) Is TD2Animation Then
            If CompareText(TD2Animation(FChildren[i]).Name, AName) = 0 Then
            Begin
               E := TD2Animation(FChildren[i]);
               E.Stop;
            End;
End;

Procedure TD2Object.StartTriggerAnimation(AInstance: TD2Object; ATrigger: String);
Var
   i: Integer;
Begin
   StopTriggerAnimation(AInstance);
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If TD2Object(FChildren[i]) Is TD2Animation Then
            TD2Animation(FChildren[i]).StartTrigger(AInstance, ATrigger);
         { locked objects }
         If TD2Object(FChildren[i]).isVisual And TD2VisualObject(FChildren[i]).Locked And Not TD2VisualObject(FChildren[i]).HitTest Then
         Begin
            TD2Object(FChildren[i]).StartTriggerAnimation(AInstance, ATrigger);
         End;
      End;
End;

Procedure TD2Object.StartTriggerAnimationWait(AInstance: TD2Object; ATrigger: String);
Var
   i: Integer;
Begin
   StopTriggerAnimation(AInstance);
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If TD2Object(FChildren[i]) Is TD2Animation Then
         Begin
            TD2Animation(FChildren[i]).StartTrigger(AInstance, ATrigger);
            While TD2Animation(FChildren[i]).Running Do
            Begin
               Application.ProcessMessages;
               Sleep(0);
            End;
         End;
         { locked objects }
         If TD2Object(FChildren[i]).isVisual And TD2VisualObject(FChildren[i]).Locked And Not TD2VisualObject(FChildren[i]).HitTest Then
         Begin
            TD2Object(FChildren[i]).StartTriggerAnimationWait(AInstance, ATrigger);
         End;
      End;
End;

Procedure TD2Object.StopTriggerAnimation(AInstance: TD2Object);
Var
   i: Integer;
   E: TD2Animation;
Begin
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If TD2Object(FChildren[i]) Is TD2Animation Then
            If TD2Animation(FChildren[i]).Trigger <> '' Then
            Begin
               E := TD2Animation(FChildren[i]);
               E.Stop;
            End;

         If TD2Object(FChildren[i]).isVisual And TD2VisualObject(FChildren[i]).Locked And Not TD2VisualObject(FChildren[i]).HitTest Then
         Begin
            TD2Object(FChildren[i]).StopTriggerAnimation(AInstance);
         End;
      End;
End;

Procedure TD2Object.ApplyTriggerEffect(AInstance: TD2Object; ATrigger: String);
Var
   i: Integer;
Begin
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If TD2Object(FChildren[i]) Is TD2Effect Then
            TD2Effect(FChildren[i]).ApplyTrigger(AInstance, ATrigger);

         If TD2Object(FChildren[i]).isVisual And TD2VisualObject(FChildren[i]).Locked And Not TD2VisualObject(FChildren[i]).HitTest Then
         Begin
            TD2Object(FChildren[i]).ApplyTriggerEffect(AInstance, ATrigger);
         End;
      End;
End;


Procedure TD2Object.SetNewScene(AScene: Id2Scene);
Var
   i: Integer;
Begin
   FScene := AScene;
   If (FChildren <> nil) And (FChildren.Count > 0) Then
      For i := 0 To FChildren.Count - 1 Do
         TD2Object(FChildren[i]).SetNewScene(FScene);
End;

Procedure TD2Object.ChangeParent;
Begin
End;

Procedure TD2Object.SetParent(Const Value: TD2Object);
Begin
   If Parent <> Value Then
   Begin
      If FParent <> nil Then
         FParent.RemoveObject(Self)
      Else
      If FScene <> nil Then
         FScene.RemoveObject(Self);
      If Value <> nil Then
      Begin
         Value.AddObject(Self);
      End Else
         FParent := Value;
   End;
End;

Function TD2Object.GetChild(Index: Integer): TD2Object;
Begin
   If (FChildren <> nil) And (Index < FChildren.Count) Then
      Result := TD2Object(FChildren[Index])
   Else
      Result := nil;
End;

Function TD2Object.GetChildrenCount: Integer;
Begin
   If (FChildren <> nil) Then
      Result := FChildren.Count
   Else
      Result := 0;
End;

Procedure TD2Object.SetParentComponent(Value: TComponent);
Var
   SI: Id2Scene;
Begin
   Inherited;
   If FParent <> nil Then
      FParent.RemoveObject(Self);

   If (Value <> nil) And (Value Is TD2Object) Then
   Begin
      TD2Object(Value).AddObject(Self);
   End Else
   If (THackComponent(Value).QueryInterface(Id2Scene, SI) = 0) And (Assigned(SI)) Then
   Begin
      SI.AddObject(Self);
   End;
End;

Procedure TD2Object.GetChildren(Proc: TGetChildProc; Root: TComponent);
Var
   i, j: Integer;
Begin
   Inherited;
   If (Self Is TD2Content) Then
      Exit;

   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If (TD2Object(FChildren[i]) Is TD2Content) And (TD2Content(FChildren[i]).FChildren <> nil) Then
         Begin
            For j := 0 To TD2Content(FChildren[i]).FChildren.Count - 1 Do
               If TD2Object(TD2Content(FChildren[i]).FChildren[j]).Stored Then
                  Proc(TComponent(TD2Content(FChildren[i]).FChildren[j]));
         End;
         If TD2Object(FChildren[i]).Stored Then
         Begin
            Proc(TComponent(FChildren[i]));
         End;
      End;
End;

Function TD2Object.GetParentComponent: TComponent;
Begin
   If (FParent <> nil) And (FParent Is TD2Popup) And (TD2Popup(FParent).Parent <> nil) And (TD2Popup(FParent).Parent Is TD2PopupItem) Then
      Result := TD2Content(FParent).Parent
   Else
   If (FParent <> nil) And (FParent Is TD2Content) Then
      Result := TD2Content(FParent).Parent
   Else
   If (FParent <> nil) And (FParent Is TD2ComboListBox) Then
      Result := TD2ComboListBox(FParent).Parent
   Else
      Result := FParent;
   If (Result = nil) And (FScene <> nil) Then
      Result := FScene.GetComponent;
End;

Function TD2Object.HasParent: Boolean;
Begin
   Result := True;
End;

Function TD2Object.GetData: Variant;
Begin
   Result := Name;
End;

Procedure TD2Object.SetData(Const Value: Variant);
Begin
End;

Function TD2Object.GetBinding(Index: String): Variant;
Var
   Obj: TD2Object;
Begin
   Obj := FindBinding(Index);
   If Obj <> nil Then
      Result := Obj.Data
   Else
      Result := '';
End;

Procedure TD2Object.SetBinding(Index: String; Const Value: Variant);
Var
   Obj: TD2Object;
Begin
   Obj := FindBinding(Index);
   If Obj <> nil Then
   Begin
      Try
         Obj.Data := Value;
      Except
      End;
   End;
End;

Function TD2Object.FindBinding(Const ABinding: String): TD2Object;
Var
   i: Integer;
Begin
   Result := nil;
   If CompareText(BindingName, ABinding) = 0 Then
   Begin
      Result := Self;
      Exit;
   End;
   If (FChildren <> nil) And (FChildren.Count > 0) Then
   Begin
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If CompareText(TD2Object(FChildren[i]).BindingName, ABinding) = 0 Then
         Begin
            Result := TD2Object(FChildren[i]);
            Exit;
         End;
         Result := TD2Object(FChildren[i]).FindBinding(ABinding);
         If Result <> nil Then
            Exit;
      End;
   End;
End;

Procedure TD2Object.SetBindingName(Const Value: String);
Begin
   If FBindingName <> Value Then
   Begin
      FBindingName := Value;
   End;
End;

Function TD2Object.FindResource(Const AResource: String): TD2Object;
Var
   i: Integer;
Begin
   Result := nil;
   If AResource = '' Then
      Exit;
   If (FChildren <> nil) And (FChildren.Count > 0) Then
   Begin
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If CompareText(TD2Object(FChildren[i]).ResourceName, AResource) = 0 Then
         Begin
            Result := TD2Object(FChildren[i]);
            Exit;
         End;
         If TD2Object(FChildren[i]) Is TD2Control Then
            Continue;

         Result := TD2Object(FChildren[i]).FindResource(AResource);
         If Result <> nil Then
            Exit;
      End;
   End;
End;

Procedure TD2Object.SetResourceName(Const Value: String);
Begin
   If FResourceName <> Value Then
   Begin
      FResourceName := Value;
   End;
End;

Procedure TD2Object.SetStored(Const Value: Boolean);
Var
   i: Integer;
Begin
   If FStored <> Value Then
   Begin
      FStored := Value;
      If (FChildren <> nil) And (FChildren.Count > 0) Then
      Begin
         For i := 0 To FChildren.Count - 1 Do
         Begin
            TD2Object(FChildren[i]).Stored := Value;
         End;
      End;
   End;
End;

Procedure TD2Object.UpdateResource;
Var
   i: Integer;
Begin
   If csLoading In ComponentState Then
      Exit;
   If csDestroying In ComponentState Then
      Exit;
   If (FChildren <> nil) And (FChildren.Count > 0) Then
   Begin
      For i := 0 To FChildren.Count - 1 Do
      Begin
         TD2Object(FChildren[i]).UpdateResource;
      End;
   End;
End;

Procedure TD2Object.DeleteChildren;
Var
   i: Integer;
   Child: TD2Object;
Begin
   If Assigned(FChildren) Then
   Begin
      For i := FChildren.Count - 1 Downto 0 Do
      Begin
         Child := TD2Object(FChildren[i]);
         Child.FParent := nil;
         Child.SetNewScene(nil);
         Child.Free;
      End;
      FreeAndNil(FChildren);
   End;
End;

Procedure TD2Object.AddObjectsToList(Const AList: TList);
Var
   i: Integer;
Begin
   AList.Add(Self);
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         TD2Object(FChildren[i]).AddObjectsToList(AList);
End;

Procedure TD2Object.AddControlsToList(Const AList: TList);
Var
   i: Integer;
Begin
   If (Self Is TD2Control) Then
      AList.Add(Self);
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         TD2Object(FChildren[i]).AddControlsToList(AList);
End;

Procedure TD2Object.AddObject(AObject: TD2Object);
Begin
   If AObject = nil Then
      Exit;
   If AObject.Parent <> nil Then
      AObject.Parent := nil;
   If FChildren = nil Then
   Begin
      FChildren := TList.Create;
      FChildren.Capacity := 10;
   End;
   If FChildren.IndexOf(AObject) >= 0 Then
      Exit;
   FChildren.Add(AObject);
   AObject.FParent := Self;
   AObject.SetNewScene(FScene);
   AObject.ChangeParent;
   If AObject.IsVisual And Not (csLoading In ComponentState) Then
   Begin
   End;
   If IsVisual And AObject.IsVisual And (AObject.Visual.Align <> vaNone) Then
      Self.Visual.FNeedAlign := True;
   If IsVisual And (AObject Is TD2Effect) And (TD2Effect(AObject).Enabled) Then
   Begin
      Visual.FHasEffect := True;
      Visual.UpdateEffects;
   End;
   If IsVisual And AObject.IsVisual Then
   Begin
      If Visual.TempCanvas <> nil Then
         AObject.Visual.TempCanvas := Visual.TempCanvas;
      If Self.IsVisual Then
         TD2VisualObject(AObject).FUpdating := TD2VisualObject(Self).FUpdating;
      AObject.Visual.RecalcOpacity;
      AObject.Visual.RecalcAbsolute;
      AObject.Visual.RecalcUpdateRect;
      If Visual.FHasEffect Then
      Begin
         Visual.UpdateEffects;
         Visual.Repaint;
      End;
      If AObject.isVisual And (TD2VisualObject(AObject).Align <> vaNone) Then
         TD2VisualObject(Self).Realign
      Else
         TD2VisualObject(Self).Repaint;
      If TD2VisualObject(Self).FTabList = nil Then
         TD2VisualObject(Self).FTabList := TList.Create;
      TD2VisualObject(Self).FTabList.Add(AObject);
   End;
End;

Procedure TD2Object.Sort(Compare: TD2ObjectSortCompare);
Begin
   If FChildren <> nil Then
      FChildren.Sort(TListSortCompare(Compare));
End;

Function TD2Object.GetIndex: Integer;
Begin
   If (FIndex < 0) And (FParent <> nil) Then
      FIndex := FParent.FChildren.IndexOf(Self);
   Result := FIndex;
End;

Procedure TD2Object.SetIndex(Idx: Integer);
Var
   i: Integer;
Begin
   If (Parent <> nil) And (Parent.FChildren.IndexOf(Self) >= 0) Then
   Begin
      Parent.FChildren.Remove(Self);
      Parent.FChildren.Insert(Idx, Self);

      For i := 0 To Parent.FChildren.Count - 1 Do
         TD2Object(Parent.FChildren[i]).FIndex := -1;
      If IsVisual And Not (csLoading In ComponentState) And (Parent.IsVisual) Then
         Parent.Visual.Realign;
   End;
End;

Procedure TD2Object.Exchange(AObject1, AObject2: TD2Object);
Var
   Idx: Integer;
Begin
   If (FChildren <> nil) And (AObject1.Parent = Self) And (AObject2.Parent = Self) Then
   Begin
      FChildren.Exchange(AObject1.Index, AObject2.Index);
      Idx := AObject1.FIndex;
      AObject1.FIndex := AObject2.Index;
      AObject2.FIndex := Idx;
      If IsVisual And Not (csLoading In ComponentState) And (Parent.IsVisual) Then
         Parent.Visual.Realign;
   End;
End;

Procedure TD2Object.RemoveObject(AObject: TD2Object);
Var
   i: Integer;
Begin
   If (FChildren <> nil) And (FChildren.IndexOf(AObject) < 0) Then
      Exit;
   If IsVisual And AObject.IsVisual And (TD2VisualObject(Self).FTabList <> nil) Then
   Begin
      TD2VisualObject(AObject).FUpdating := 0;
      TD2VisualObject(Self).FTabList.Remove(AObject);
   End;
   If IsVisual And AObject.IsVisual Then
      AObject.Visual.Repaint;
   AObject.FParent := nil;
   AObject.SetNewScene(nil);

   If AObject.FIndex >= 0 Then
      For i := AObject.FIndex To FChildren.Count - 1 Do
         TD2Object(FChildren[i]).FIndex := -1;

   If FChildren <> nil Then
      FChildren.Remove(AObject);
   If IsVisual Then
      Visual.RecalcHasEffect;
   If IsVisual And AObject.IsVisual Then
   Begin
      TD2VisualObject(Self).RecalcNeedAlign;
      If AObject.Visual.TempCanvas <> nil Then
         AObject.Visual.TempCanvas := nil;
   End;
End;

Procedure TD2Object.BringToFront;
Var
   i: Integer;
Begin
   If (Parent <> nil) And (Parent.FChildren <> nil) Then
   Begin
      Parent.FChildren.Remove(Self);
      Parent.FChildren.Add(Self);

      For i := 0 To Parent.FChildren.Count - 1 Do
         TD2Object(Parent.FChildren[i]).FIndex := -1;
      If isVisual Then
         TD2VisualObject(Self).Repaint;
   End;
End;

Procedure TD2Object.SendToBack;
Var
   i: Integer;
Begin
   If (Parent <> nil) And (Parent.FChildren <> nil) Then
   Begin
      Parent.FChildren.Remove(Self);
      Parent.FChildren.Insert(0, Self);

      For i := 0 To Parent.FChildren.Count - 1 Do
         TD2Object(Parent.FChildren[i]).FIndex := -1;
      If Parent.IsVisual Then
         TD2VisualObject(Parent).Realign;
   End;
End;


//=============== TD2BrushObject ==========================================

Constructor TD2BrushObject.Create(AOwner: TComponent);
Begin
   Inherited;
   FBrush := TD2Brush.Create(d2BrushSolid, $FFFFFFFF);
End;

Destructor TD2BrushObject.Destroy;
Begin
   FreeAndNil(FBrush);
   Inherited;
End;

Procedure TD2BrushObject.SetName(Const NewName: TComponentName);
Begin
   Inherited;
   If FResourceName = '' Then
      FResourceName := Name;
End;

//=============== TD2PathObject ==========================================

Constructor TD2PathObject.Create(AOwner: TComponent);
Begin
   Inherited;
   FPath := TD2PathData.Create();
End;

Destructor TD2PathObject.Destroy;
Begin
   FreeAndNil(FPath);
   Inherited;
End;

Procedure TD2PathObject.SetName(Const NewName: TComponentName);
Begin
   Inherited;
   If FResourceName = '' Then
      FResourceName := Name;
End;

//=============== TD2BitmapObject ======================================

Constructor TD2BitmapObject.Create(AOwner: TComponent);
Begin
   Inherited;
   FBitmap := TD2Bitmap.Create(1, 1);
End;

Destructor TD2BitmapObject.Destroy;
Begin
   FreeAndNil(FBitmap);
   Inherited;
End;

Procedure TD2BitmapObject.SetName(Const NewName: TComponentName);
Begin
   Inherited;
   If FResourceName = '' Then
      FResourceName := Name;
End;

//=============== TD2Visual ======================================================

Constructor TD2Visual.Create;
Begin
   Inherited;
End;

Procedure TD2Visual.Assign(Source: TPersistent);
Begin
   If Source Is TD2Visual Then
   Begin
      VisualObject := TD2Visual(Source).VisualObject;
   End Else
      Inherited;
End;

Destructor TD2Visual.Destroy;
Begin
   If FVisualObject <> nil Then
   Begin
      FVisualObject.RemoveFreeNotify(Self);
      FVisualObject := nil;
   End;
   Inherited;
End;

Procedure TD2Visual.SetVisualObject(Const Value: TD2VisualObject);
Begin
   If FVisualObject <> Value Then
   Begin
      If FVisualObject <> nil Then
         FVisualObject.RemoveFreeNotify(Self);
      FVisualObject := Value;
      If Assigned(FOnChanged) Then
         FOnChanged(Self);
      If FVisualObject <> nil Then
         FVisualObject.AddFreeNotify(Self);
   End;
End;

//=============== TD2VisualObject ========================================================

Constructor TD2VisualObject.Create(AOwner: TComponent);
Begin
   Inherited;
   FTabOrder := -1;
   FEnabled := True;
   FRecalcEnabled := True;
   FOpacity := 1;
   FLocalMatrix := IdentityMatrix;
   FPosition := TD2Position.Create(d2Point(0, 0));
   FPosition.OnChange := MatrixChanged;
   FScale := TD2Position.Create(d2Point(1, 1));
   FScale.OnChange := MatrixChanged;
   FSkew := TD2Position.Create(d2Point(0, 0));
   FSkew.OnChange := MatrixChanged;
   FRotateCenter := TD2Position.Create(d2Point(0.5, 0.5));
   FRotateCenter.OnChange := MatrixChanged;
   FMargins := TD2Bounds.Create(d2Rect(0, 0, 0, 0));
   FMargins.OnChange := MarginsChanged;
   FPadding := TD2Bounds.Create(d2Rect(0, 0, 0, 0));
   FPadding.OnChange := PaddingChanged;
   FWidth := 50;
   FLastWidth := FWidth;
   FHeight := 50;
   FLastHeight := FHeight;
   FVisible := True;
   FHitTest := True;
   FRecalcAbsolute := True;
   FRecalcOpacity := True;
   FUpdateEffects := True;
   FRecalcUpdateRect := True;
   FCanFocused := False;
   FCanClipped := True;
End;

Destructor TD2VisualObject.Destroy;
Begin
   If FTabList <> nil Then
      FreeAndNil(FTabList);
   If FEffectBitmap <> nil Then
      FreeAndNil(FEffectBitmap);
   FMargins.Free;
   FPadding.Free;
   FRotateCenter.Free;
   FScale.Free;
   FSkew.Free;
   FPosition.Free;
   Inherited;
End;

Procedure TD2VisualObject.Loaded;
Begin
   Inherited;
   FLastWidth := FWidth;
   FLastHeight := FHeight;
   MatrixChanged(Self);
   If (FChildren <> nil) And (FChildren.Count > 0) Then
      Realign;
   FixupTabList;
End;

Procedure TD2VisualObject.DeleteChildren;
Begin
   Inherited;
   If FTabList <> nil Then
      FreeAndNil(FTabList);
End;

Procedure TD2VisualObject.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   If (Operation = opRemove) And (AComponent = FPopupMenu) Then
      FPopupMenu := nil;

   If (Operation = opRemove) And (AComponent = FPopup) Then
      FPopup := nil;
End;

Procedure TD2VisualObject.MatrixChanged(Sender: TObject);
Var
   RotMatrix: TD2Matrix;
   M1, M2: TD2Matrix;
Begin
   If (FScene <> nil) And (Not FScene.GetDisableUpdate) And (Not FInPaintTo) And (FUpdating = 0) Then
      Repaint;
   FLocalMatrix := IdentityMatrix;
   FLocalMatrix.m31 := FPosition.X;
   FLocalMatrix.m32 := FPosition.Y;
   FLocalMatrix.m11 := FScale.X;
   FLocalMatrix.m22 := FScale.Y;
   If FRotateAngle <> 0 Then
   Begin
      M1 := IdentityMatrix;
      M1.m31 := -FRotateCenter.X * FWidth;
      M1.m32 := -FRotateCenter.Y * FHeight;
      M2 := IdentityMatrix;
      M2.m31 := FRotateCenter.X * FWidth;
      M2.m32 := FRotateCenter.Y * FHeight;
      RotMatrix := d2MatrixMultiply(M1, d2MatrixMultiply(d2CreateRotationMatrix(d2DegToRad(FRotateAngle)), M2));
      FLocalMatrix := d2MatrixMultiply(RotMatrix, FLocalMatrix);
   End;
   RecalcAbsolute;
   RecalcUpdateRect;
   UpdateEffects;
   If (FScene <> nil) And (Not FScene.GetDisableUpdate) And (Not FInPaintTo) And (FUpdating = 0) Then
      Repaint;
End;

Procedure TD2VisualObject.RecalcUpdateRect;
Var
   i: Integer;
Begin
   If (Parent <> nil) And (Parent.IsVisual) And Not (Parent.Visual.ClipChildren) And ((Position.X < 0) Or (Position.Y < 0) Or (Position.X + Width < Parent.Visual.Width) Or (Position.Y + Height < Parent.Visual.Height)) Then
      Parent.Visual.FRecalcUpdateRect := True;

   FRecalcUpdateRect := True;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If Not TD2Object(FChildren[i]).IsVisual Then
            Continue;
         TD2VisualObject(FChildren[i]).RecalcUpdateRect;
      End;
End;

Function TD2VisualObject.GetUpdateRect: TD2Rect;
Var
   R: TD2Rect;
   P: TD2Object;
   i: Integer;
Begin
   If FRecalcUpdateRect Then
   Begin
      FRecalcUpdateRect := False;
      FUpdating := FUpdating + 1;
      FUpdateRect := AbsoluteRect;
      If (FScene <> nil) And Not (FScene.GetDisableUpdate) Then
      Begin
         If Not (csLoading In ComponentState) Then
         Begin
            P := Parent;
            While (P <> nil) And (P.IsVisual) Do
            Begin
               If TD2VisualObject(P).ClipChildren Then
                  d2IntersectRect(FUpdateRect, FUpdateRect, TD2VisualObject(P).UpdateRect);
               P := P.Parent;
            End;

            If CanFocused And IsFocused Then
               d2InflateRect(FUpdateRect, 5, 5);
            If (FScene <> nil) And (Self = FScene.GetSelected) Then
            Begin
               d2InflateRect(FUpdateRect, (cnGripSize) + 1, (cnGripSize) + 1);
               FUpdateRect.Top := FUpdateRect.Top - cnRotSize - cnGripSize;
            End;
            If (FScene <> nil) And (FScene.GetDesignPlaceObject = Self) Then
            Begin
               d2InflateRect(FUpdateRect, 1, 1);
               FUpdateRect.Top := FUpdateRect.Top - 20;
               If d2RectWidth(FUpdateRect) < 160 Then
                  FUpdateRect.Right := FUpdateRect.Left + 160;
            End;

            If FHasEffect And Not ClipChildren Then
            Begin
               R := GetEffectsRect;
               With R Do
                  R := d2NormalizeRect([LocaltoAbsolute(d2Point(Left, Top)), LocaltoAbsolute(d2Point(Right, Top)), LocaltoAbsolute(d2Point(Right, Bottom)), LocaltoAbsolute(d2Point(Left, Bottom))]);
               FUpdateRect := d2UnionRect(FUpdateRect, R);
            End;

            If Not ClipChildren And (FChildren <> nil) Then
            Begin
               For i := 0 To FChildren.Count - 1 Do
               Begin
                  If Not TD2Object(FChildren[i]).IsVisual Then
                     Continue;
                  If Not TD2VisualObject(FChildren[i]).Visible Then
                     Continue;
                  R := TD2VisualObject(FChildren[i]).UpdateRect;
                  FUpdateRect := d2UnionRect(FUpdateRect, R);
               End;
            End;
         End;
      End;
      FUpdating := FUpdating - 1;
   End;
   Result := FUpdateRect;
End;

Function TD2VisualObject.GetChildrenRect: TD2Rect;
Var
   i: Integer;
Begin
   Result := AbsoluteRect;

   If Not ClipChildren And (FChildren <> nil) Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]).IsVisual And (TD2VisualObject(FChildren[i]).Visible) Then
            Result := d2UnionRect(Result, TD2VisualObject(FChildren[i]).GetChildrenRect);
End;

Function TD2VisualObject.GetAbsoluteWidth: Single;
Var
   V: TD2Vector;
Begin
   V := LocalToAbsoluteVector(d2Vector(Width, Height));
   Result := V.X;
End;

Function TD2VisualObject.GetAbsoluteHeight: Single;
Var
   V: TD2Vector;
Begin
   V := LocalToAbsoluteVector(d2Vector(Width, Height));
   Result := V.Y;
End;

Function TD2VisualObject.GetAbsoluteScale: TD2Point;
Var
   P: TD2Object;
Begin
   Result := Scale.Point;
   P := Parent;
   While P <> nil Do
   Begin
      If P.IsVisual Then
      Begin
         Result.X := Result.X * P.Visual.Scale.X;
         Result.Y := Result.Y * P.Visual.Scale.Y;
      End;
      P := P.Parent;
   End;
End;

Function TD2VisualObject.GetChildrenMatrix: TD2Matrix;
Begin
   Result := IdentityMatrix;
End;

Function TD2VisualObject.GetAbsoluteMatrix: TD2Matrix;
Begin
   If FRecalcAbsolute Then
   Begin
      If (FParent <> nil) And (FParent.IsVisual) Then
      Begin
         FAbsoluteMatrix := d2MatrixMultiply(d2MatrixMultiply(FLocalMatrix, FParent.Visual.GetChildrenMatrix), FParent.Visual.AbsoluteMatrix);
      End Else
         FAbsoluteMatrix := FLocalMatrix;

      Result := FAbsoluteMatrix;
      FRecalcAbsolute := False;
      If (FScene <> nil) And (Not FScene.GetDisableUpdate) And (Not FInPaintTo) And (FUpdating = 0) Then
         Repaint;
   End Else Begin
      Result := FAbsoluteMatrix;
   End;
End;

Function TD2VisualObject.GetInvertAbsoluteMatrix: TD2Matrix;
Begin
   Result := AbsoluteMatrix;
   d2InvertMatrix(Result);
End;

Procedure TD2VisualObject.RecalcAbsoluteNow;
Var
   i: Integer;
   Child: TD2VisualObject;
Begin
   AbsoluteMatrix;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If Not TD2Object(FChildren[i]).isVisual Then
            Continue;
         Child := TD2VisualObject(FChildren[i]);
         TD2VisualObject(Child).RecalcAbsoluteNow;
      End;
End;

Procedure TD2VisualObject.RecalcAbsolute;
Var
   i: Integer;
   Child: TD2VisualObject;
Begin
   FRecalcAbsolute := True;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If Not TD2Object(FChildren[i]).isVisual Then
            Continue;
         Child := TD2VisualObject(FChildren[i]);
         TD2VisualObject(Child).RecalcAbsolute;
      End;
End;

Function TD2VisualObject.AbsoluteToLocalVector(P: TD2Vector): TD2Vector;
Begin
   P.W := 0;
   Result := d2VectorTransform(P, InvertAbsoluteMatrix);
End;

Function TD2VisualObject.LocalToAbsoluteVector(P: TD2Vector): TD2Vector;
Begin
   P.W := 0;
   Result := d2VectorTransform(P, AbsoluteMatrix);
End;

Function TD2VisualObject.AbsoluteToLocal(P: TD2Point): TD2Point;
Var
   V: TD2Vector;
Begin
   V.X := P.X;
   V.Y := P.Y;
   V.W := 1;
   V := d2VectorTransform(V, InvertAbsoluteMatrix);
   Result.X := V.X;
   Result.Y := V.Y;
End;

Function TD2VisualObject.LocalToAbsolute(P: TD2Point): TD2Point;
Var
   V: TD2Vector;
Begin
   V.X := P.X;
   V.Y := P.Y;
   V.W := 1;
   V := d2VectorTransform(V, AbsoluteMatrix);
   Result := d2Point(V.X, V.Y);
End;

Function TD2VisualObject.GetAbsoluteOpacity: Single;
Begin
   If FRecalcOpacity Then
   Begin
      If (FParent <> nil) And (FParent.IsVisual) Then
         FAbsoluteOpacity := FOpacity * FParent.Visual.AbsoluteOpacity
      Else
         FAbsoluteOpacity := FOpacity;

      If Not AbsoluteEnabled And (FScene <> nil) And ((FScene.GetRoot <> Self) And (FScene.GetRoot <> Parent)) Then
         FAbsoluteOpacity := FAbsoluteOpacity * 0.8;

      Result := FAbsoluteOpacity;

      FRecalcOpacity := False;
   End Else Begin
      Result := FAbsoluteOpacity;
   End;
End;

Procedure TD2VisualObject.RecalcOpacity;
Var
   i: Integer;
   Child: TD2VisualObject;
Begin
   FRecalcOpacity := True;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If Not TD2Object(FChildren[i]).isVisual Then
            Continue;
         Child := TD2VisualObject(FChildren[i]);
         TD2VisualObject(Child).RecalcOpacity;
      End;
End;

Procedure TD2VisualObject.CreateCaret;
Var
   A: TD2FloatAnimation;
Begin
   If FCaret = nil Then
   Begin
      FCaret := TD2Rectangle.Create(Self);
      FCaret.Parent := Self;
      FCaret.Width := 3;
      FCaret.Height := 20;
      FCaret.Stored := False;
      FCaret.HitTest := False;
      FCaret.Locked := True;
      TD2Rectangle(FCaret).Fill.Color := vcBlue;
      TD2Rectangle(FCaret).Stroke.Style := d2BrushNone;
      If Assigned(FScene) And (FScene.GetAnimatedCaret) Then
      Begin
         A := TD2FloatAnimation.Create(Self);
         A.BindingName := 'caret';
         A.Parent := FCaret;
         A.StartValue := 1;
         A.Duration := 0.1;
         A.PropertyName := 'Opacity';
         A.StopValue := 0;
         A.AutoReverse := True;
         A.Loop := True;
         A.Enabled := False;
      End;
      FCaret.Visible := False;
   End;
End;

Procedure TD2VisualObject.ShowCaretProc;
Begin
   If FCaret = nil Then
      CreateCaret;

   FCaret.Visible := True;
   If (FCaret.FindBinding('caret') <> nil) And Not TD2FloatAnimation(FCaret.FindBinding('caret')).Running Then
      TD2FloatAnimation(FCaret.FindBinding('caret')).Start;
End;

Procedure TD2VisualObject.SetCaretPos(Const APoint: TD2Point);
Begin
   If FCaret = nil Then
      CreateCaret;
   FCaret.Position.Point := d2Point(round(APoint.x), round(APoint.y));
End;

Procedure TD2VisualObject.SetCaretSize(Const ASize: TD2Point);
Begin
   If FCaret = nil Then
      CreateCaret;
   FCaret.Width := ASize.X;
   FCaret.Height := ASize.Y;
End;

Procedure TD2VisualObject.SetCaretColor(Const AColor: TD2Color);
Begin
   If FCaret = nil Then
      CreateCaret;
   TD2Rectangle(FCaret).Fill.SolidColor := AColor;
End;

Procedure TD2VisualObject.HideCaret;
Begin
   If FCaret <> nil Then
      FCaret.Visible := False;
End;

Function TD2VisualObject.PointInObject(X, Y: Single): Boolean;
Var
   P: TD2Point;
Begin
   Result := False;
   P := AbsoluteToLocal(d2Point(X, Y));
   If (P.X > 0) And (P.X < Width) And (P.Y > 0) And (P.Y < Height) Then
   Begin
      Result := True;
   End;
End;

Function TD2VisualObject.CheckHitTest(Const AHitTest: Boolean): Boolean;
Begin
   Result := FHitTest;
   If ((Scene <> nil) And (Scene.GetDesignTime)) Then
      Result := True;
   If (((Scene <> nil) And (Scene.GetDesignTime))) And FLocked Then
      Result := False;
   If (((Scene <> nil) And (Scene.GetDesignTime))) And FDesignHide Then
      Result := False;
End;

Function TD2VisualObject.FindTarget(Const APoint: TD2Point; Const aData: TD2DragObject): TD2VisualObject;
Var
   i: Integer;
   Obj, NewObj: TD2VisualObject;
   Accept: Boolean;
Begin
   If Not Visible And Not (Assigned(FScene) And (FScene.GetDesignTime)) Then
   Begin
      Result := nil;
      Exit;
   End;
   If (Self Is TD2Control) And Not TD2Control(Self).AbsoluteEnabled And Not (Assigned(FScene) And (FScene.GetDesignTime)) Then
   Begin
      Result := nil;
      Exit;
   End;

   If FChildren <> nil Then
      For i := FChildren.Count - 1 Downto 0 Do
      Begin
         If Not TD2Object(FChildren[i]).IsVisual Then
            Continue;
         Obj := TD2VisualObject(FChildren[i]);
         If (Not Obj.Visible) And Not (((FScene <> nil) And FScene.GetDesignTime)) Then
            Continue;
         If FDesignHide And (((FScene <> nil) And FScene.GetDesignTime)) Then
            Continue;
         If ClipChildren And Not PointInObject(APoint.X, APoint.Y) Then
            Continue;

         NewObj := Obj.FindTarget(APoint, aData);
         If NewObj <> nil Then
         Begin
            Result := NewObj;
            Exit;
         End;
      End;

   Result := nil;
   Accept := False;
   DragOver(aData, APoint, Accept);
   If PointInObject(APoint.X, APoint.Y) And CheckHitTest(HitTest) And (Accept) Then
      Result := Self;
End;

Function TD2VisualObject.ObjectByPoint(X, Y: Single): TD2VisualObject;
Var
   i: Integer;
   Obj, NewObj: TD2VisualObject;
Begin
   If Not Visible And Not (Assigned(FScene) And (FScene.GetDesignTime)) Then
   Begin
      Result := nil;
      Exit;
   End;
   If (Self Is TD2Control) And Not TD2Control(Self).AbsoluteEnabled And Not (Assigned(FScene) And (FScene.GetDesignTime)) Then
   Begin
      Result := nil;
      Exit;
   End;
   If ClipChildren And Not PointInObject(X, Y) Then
   Begin
      Result := nil;
      Exit;
   End;
   If FChildren <> nil Then
      For i := FChildren.Count - 1 Downto 0 Do
      Begin
         If Not TD2Object(FChildren[i]).IsVisual Then
            Continue;
         Obj := TD2VisualObject(FChildren[i]);
         If (Not Obj.Visible) And Not (((FScene <> nil) And FScene.GetDesignTime)) Then
            Continue;
         If FDesignHide And (((FScene <> nil) And FScene.GetDesignTime)) Then
            Continue;

         NewObj := Obj.ObjectByPoint(X, Y);
         If NewObj <> nil Then
         Begin
            Result := NewObj;
            Exit;
         End;
      End;

   Result := nil;
   If PointInObject(X, Y) And CheckHitTest(HitTest) Then
      Result := Self;
End;

Function TD2VisualObject.GetCanvas: TD2Canvas;
Begin
   If FTempCanvas <> nil Then
      Result := FTempCanvas
   Else
   If FScene <> nil Then
      Result := FScene.GetCanvas
   Else
      Result := nil;
End;

Procedure TD2VisualObject.BeforePaint;
Begin
End;

Procedure TD2VisualObject.ApplyResource;
Begin
End;

Procedure TD2VisualObject.Paint;
Begin
End;

Procedure TD2VisualObject.AfterPaint;
Begin
End;

Procedure TD2VisualObject.SetInPaintTo(Value: Boolean);
Var
   i: Integer;
Begin
   FInPaintTo := Value;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]).isVisual Then
            TD2VisualObject(FChildren[i]).SetInPaintTo(Value);
End;

Procedure TD2VisualObject.PaintTo(Const ACanvas: TD2Canvas; Const ARect: TD2Rect; Const AParent: TD2Object = nil);
Var
   SaveIndex: Integer;
   SaveTempCanvas: TD2Canvas;
   SaveDisableAlign: Boolean;
   SavePos: TD2Point;
   SaveScale: TD2Point;
   SaveParent: TD2Object;
   SaveRotate: Single;
Begin
   If FScene = nil Then
      Exit;
   If Width * Height = 0 Then
      Exit;

   FScene.SetDisableUpdate(True);
   SaveDisableAlign := FDisableAlign;
   FDisableAlign := True;
   SetInPaintTo(True);
   Try
      SaveTempCanvas := TempCanvas;
      TempCanvas := ACanvas;
      SaveIndex := TempCanvas.SaveCanvas;

      SavePos := Position.Point;
      SaveScale := Scale.Point;
      SaveParent := FParent;
      SaveRotate := RotateAngle;
      FParent := AParent;
      FPosition.FX := ARect.Left;
      FPosition.FY := ARect.Top;
      FScale.FX := d2RectWidth(ARect) / Width;
      FScale.FY := d2RectHeight(ARect) / Height;
      FRotateAngle := 0;
      MatrixChanged(Self);

      TempCanvas.SetMatrix(AbsoluteMatrix);
      BeforePaint;
      Paint;
      AfterPaint;
      PaintChildren;

      FRotateAngle := SaveRotate;
      FPosition.FX := SavePos.X;
      FPosition.FY := SavePos.Y;
      FScale.FX := SaveScale.X;
      FScale.FY := SaveScale.Y;
      FParent := SaveParent;
      MatrixChanged(Self);
      RecalcUpdateRect;
      RecalcAbsoluteNow;
      RecalcOpacity;
      RecalcEnabled;
   Finally
      SetInPaintTo(False);
      FDisableAlign := SaveDisableAlign;
      TempCanvas.RestoreCanvas(SaveIndex);
      TempCanvas := SaveTempCanvas;
      FScene.SetDisableUpdate(False);
   End;
End;

Procedure TD2VisualObject.UpdateEffects;
Var
   P: TD2Object;
Begin
   If FHasEffect Then
   Begin
      FUpdateEffects := True;
   End;
   P := Parent;
   While P <> nil Do
   Begin
      If P.IsVisual Then
         P.Visual.UpdateEffects;
      P := P.Parent;
   End;
End;

Procedure TD2VisualObject.ApplyEffect;
Var
   i, State, State2: Integer;
   M: TD2Matrix;
   R: TD2Rect;
   Effect: TD2Effect;
   EffectRect: TD2Rect;
Begin
   If FChildren = nil Then
      Exit;
   If FScene = nil Then
      Exit;
   If FDisableEffect Then
      Exit;
   If Not FHasEffect Then
      Exit;

   State := Canvas.SaveCanvas;
   If Not FUpdateEffects Then
   Begin
      If FEffectBitmap <> nil Then
      Begin
         Canvas.SetMatrix(AbsoluteMatrix);
         For i := 0 To FChildren.Count - 1 Do
            If (TD2Object(FChildren[i]) Is TD2Effect) And (TD2Effect(FChildren[i]).Enabled) Then
            Begin
               Effect := TD2Effect(FChildren[i]);
               EffectRect := Effect.GetRect(d2Rect(0, 0, Width, Height));
               Canvas.DrawBitmap(FEffectBitmap, d2Rect(0, 0, FEffectBitmap.Width, FEffectBitmap.Height), EffectRect, AbsoluteOpacity, RotateAngle = 0);
            End;
      End;
   End Else Begin
      For i := 0 To FChildren.Count - 1 Do
         If (TD2Object(FChildren[i]) Is TD2Effect) And (TD2Effect(FChildren[i]).Enabled) Then
         Begin
            Effect := TD2Effect(FChildren[i]);
            EffectRect := Effect.GetRect(d2Rect(0, 0, Width, Height));
            With GetAbsoluteScale Do
               d2MultiplyRect(EffectRect, X, Y);

            If FEffectBitmap = nil Then
            Begin
               FEffectBitmap := TD2Bitmap.Create(trunc(d2RectWidth(EffectRect)), trunc(d2RectHeight(EffectRect)));
            End Else
            If (FEffectBitmap.Width <> trunc(d2RectWidth(EffectRect))) Or (FEffectBitmap.Height <> trunc(d2RectHeight(EffectRect))) Then
            Begin
               FEffectBitmap.SetSize(trunc(d2RectWidth(EffectRect)), trunc(d2RectHeight(EffectRect)));
            End;

            State2 := FEffectBitmap.Canvas.SaveCanvas;
            M := IdentityMatrix;
            FEffectBitmap.Canvas.BeginScene;
            FEffectBitmap.Canvas.Clear(0);
            FEffectBitmap.Canvas.SetMatrix(M);
            R := d2Rect(Effect.GetOffset.X, Effect.GetOffset.Y, (Effect.GetOffset.X + Width), (Effect.GetOffset.Y + Height));
            With GetAbsoluteScale Do
               d2MultiplyRect(R, X, Y);

            PaintTo(FEffectBitmap.Canvas, R);

            FEffectBitmap.Canvas.EndScene;
            FEffectBitmap.Canvas.RestoreCanvas(State2);

            With GetAbsoluteScale Do
            Begin
               Effect.ProcessEffect(FEffectBitmap.Canvas, FEffectBitmap, X);
               d2MultiplyRect(EffectRect, 1 / X, 1 / Y);
            End;
            Canvas.SetMatrix(AbsoluteMatrix);
            Canvas.DrawBitmap(FEffectBitmap, d2Rect(0, 0, FEffectBitmap.Width, FEffectBitmap.Height), EffectRect, AbsoluteOpacity, RotateAngle = 0);
         End;
      FUpdateEffects := False;
   End;
   Canvas.RestoreCanvas(State);
End;

Procedure TD2VisualObject.PaintChildren;
Var
   i, j: Integer;
   R: TD2Rect;
   State, State2, State3: Cardinal;
   ClipParentObject: TD2VisualObject;
   AllowPaint: Boolean;
Begin
   If FScene = nil Then
      Exit;
   If FChildren <> nil Then
   Begin
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]).IsVisual And ((TD2VisualObject(FChildren[i]).Visible) Or (Not TD2VisualObject(FChildren[i]).Visible And (Assigned(FScene) And (FScene.GetDesignTime)) And Not TD2VisualObject(FChildren[i]).Locked)) Then
            With TD2VisualObject(FChildren[i]) Do
            Begin
               If (((Scene <> nil) And (Scene.GetDesignTime))) And FDesignHide Then
                  Continue;
               If (d2RectWidth(UpdateRect) = 0) Or (d2RectHeight(UpdateRect) = 0) Then
                  Continue;

               FScene := Self.FScene;
               If Self.ClipChildren And Not d2IntersectRect(Self.UpdateRect, UpdateRect) Then
                  Continue;

               AllowPaint := False;
               If (Assigned(FScene) And (FScene.GetDesignTime)) Or FInPaintTo Then
                  AllowPaint := True;
               If Not AllowPaint Then
               Begin
                  R := d2UnionRect(GetChildrenRect, UpdateRect);
                  For j := 0 To FScene.GetUpdateRectsCount - 1 Do
                     If d2IntersectRect(FScene.GetUpdateRect(j), R) Then
                     Begin
                        AllowPaint := True;
                        Break;
                     End;
               End;

               If AllowPaint Then
               Begin
                  If Self.FClipChildren And CanClipped Then
                  Begin
                     State := Canvas.SaveCanvas;
                     Canvas.SetMatrix(Self.AbsoluteMatrix);
                     Canvas.IntersectClipRect(Self.ClipRect);
                  End Else
                     State := InvalideCanvasState;
                  Canvas.SetMatrix(AbsoluteMatrix);
                  BeforePaint;
                  If FHasEffect And Not HasAfterPaintEffect Then
                     ApplyEffect;
                  Canvas.SetMatrix(AbsoluteMatrix);
                  If Not HasDisablePaintEffect Then
                  Begin
                     ClipParentObject := HasClipParent;
                     If ClipParentObject <> nil Then
                     Begin
                        State3 := Canvas.SaveCanvas;

                        Canvas.SetMatrix(ClipParentObject.AbsoluteMatrix);
                        Canvas.ExcludeClipRect(ClipParentObject.LocalRect);
                        Canvas.SetMatrix(AbsoluteMatrix);

                        If ClipChildren Then
                        Begin
                           State2 := Canvas.SaveCanvas;
                           Canvas.SetMatrix(AbsoluteMatrix);
                           Canvas.IntersectClipRect(ClipRect);
                           If Assigned(FOnBeforePaint) Then
                           Begin
                              FOnBeforePaint(TD2VisualObject(Self.FChildren[i]), Canvas, LocalRect);
                              Canvas.SetMatrix(AbsoluteMatrix);
                           End;
                           Paint;
                           Canvas.RestoreCanvas(State2);
                        End Else Begin
                           If Assigned(FOnBeforePaint) Then
                           Begin
                              FOnBeforePaint(TD2VisualObject(Self.FChildren[i]), Canvas, LocalRect);
                              Canvas.SetMatrix(AbsoluteMatrix);
                           End;
                           Paint;
                        End;
                        Canvas.RestoreCanvas(State3);
                        PaintChildren;
                        If Assigned(FOnPaint) Then
                        Begin
                           Canvas.SetMatrix(AbsoluteMatrix);
                           FOnPaint(TD2VisualObject(Self.FChildren[i]), Canvas, LocalRect);
                        End;
                     End Else Begin
                        If ClipChildren Then
                        Begin
                           State2 := Canvas.SaveCanvas;
                           Canvas.SetMatrix(AbsoluteMatrix);
                           Canvas.IntersectClipRect(ClipRect);
                           If Assigned(FOnBeforePaint) Then
                           Begin
                              FOnBeforePaint(TD2VisualObject(Self.FChildren[i]), Canvas, LocalRect);
                              Canvas.SetMatrix(AbsoluteMatrix);
                           End;
                           Paint;
                           Canvas.RestoreCanvas(State2);
                        End Else Begin
                           If Assigned(FOnBeforePaint) Then
                           Begin
                              FOnBeforePaint(TD2VisualObject(Self.FChildren[i]), Canvas, LocalRect);
                              Canvas.SetMatrix(AbsoluteMatrix);
                           End;
                           Paint;
                        End;
                        PaintChildren;
                        If Assigned(FOnPaint) Then
                        Begin
                           Canvas.SetMatrix(AbsoluteMatrix);
                           FOnPaint(TD2VisualObject(Self.FChildren[i]), Canvas, LocalRect);
                        End;
                     End;
                  End;
                  AfterPaint;

                  If (GvarD2Designer <> nil) And (Assigned(Self.Scene)) And (Self.GetOwner <> nil) And (Self.FChildren[i] <> Scene.GetSelected) And (GvarD2Designer.IsSelected(Self.Scene.GetOwner, TD2VisualObject(Self.FChildren[i]))) Then
                  Begin
                     Canvas.SetMatrix(AbsoluteMatrix);
                     Canvas.Stroke.Style := d2BrushSolid;
                     Canvas.Stroke.SolidColor := $B200CC5A;
                     Canvas.StrokeCap := d2CapFlat;
                     Canvas.StrokeJoin := d2JoinMiter;
                     Canvas.StrokeDash := d2DashDash;
                     Canvas.StrokeThickness := 1;
                     R := d2Rect(0, 0, Width, Height);
                     d2InflateRect(R, -1, -1);
                     Canvas.DrawRect(R, 1, 1, AllCorners, 1);
                     Canvas.StrokeDash := d2DashSolid;
                  End;

                  If IsDragOver And Not DragDisableHighlight Then
                  Begin
                     Canvas.SetMatrix(AbsoluteMatrix);
                     Canvas.Stroke.Style := d2BrushSolid;
                     Canvas.Stroke.SolidColor := $B2005ACC;
                     Canvas.StrokeCap := d2CapFlat;
                     Canvas.StrokeJoin := d2JoinMiter;
                     Canvas.StrokeDash := d2DashSolid;
                     Canvas.StrokeThickness := 3;
                     R := d2Rect(0, 0, Width, Height);
                     d2InflateRect(R, -1, -1);
                     Canvas.DrawRect(R, 1, 1, AllCorners, 1);
                     Canvas.StrokeDash := d2DashSolid;
                  End;
                  If State <> InvalideCanvasState Then
                  Begin
                     Canvas.RestoreCanvas(State);
                  End;
               End;
               If HasAfterPaintEffect Then
               Begin
                  Canvas.SetMatrix(AbsoluteMatrix);
                  ApplyEffect;
               End;
               {$IFDEF BOUNDS}
          State3:=Canvas.SaveCanvas;
          Canvas.ResetClipRect;
          Canvas.SetMatrix(IdentityMatrix);
          Canvas.Stroke.Style:=d2BrushSolid;
          Canvas.Stroke.Color:='#FF00FF00';
          Canvas.StrokeThickness:=1;
          R:=AbsoluteRect;
          Canvas.DrawRect(R,0,0,Allcorners,0.5);
          Canvas.RestoreCanvas(State3);
   {$ENDIF}
            End;
   End;
   {$IFDEF DRAWFOCUSED}
  if Canfocused and IsFocused then
  begin
    State3:=Canvas.SaveCanvas;
    Canvas.ResetClipRect;
    Canvas.SetMatrix(AbsoluteMatrix);
    R:=localRect;
    Canvas.Stroke.Style:=d2Brushsolid;
    Canvas.Stroke.Color:=vcRed;
    Canvas.StrokeThickness:=1;
    Canvas.DrawRect(R,1,1,Allcorners,AbsoluteOpacity);
    Canvas.RestoreCanvas(State3);
  end;
   {$ENDIF}
End;

Function TD2VisualObject.CheckParentVisible: Boolean;
Var
   P: TD2Object;
Begin
   P := Self;
   Result := False;
   While P <> nil Do
   Begin
      If P.IsVisual And Not TD2VisualObject(P).Visible Then
         Exit;
      P := P.Parent;
   End;
   Result := True;
End;

Procedure TD2VisualObject.InvalidateRect(ARect: TD2Rect);
Begin
   If Not Visible And (FScene <> nil) And (Not FScene.GetDesignTime) Then
      Exit;
   If FScene = nil Then
      Exit;
   If (((Scene <> nil) And (Scene.GetDesignTime))) And FDesignHide Then
      Exit;
   ;
   If FScene.GetDisableUpdate Then
      Exit;
   If Not (((Scene <> nil) And (Scene.GetDesignTime))) And Not CheckParentVisible Then
      Exit;
   ;
   ARect.TopLeft := LocalToAbsolute(ARect.TopLeft);
   ARect.BottomRight := LocalToAbsolute(ARect.BottomRight);
   FScene.AddUpdateRect(ARect);
End;

Procedure TD2VisualObject.Repaint;
Begin
   If Not Visible And (FScene <> nil) And (Not FScene.GetDesignTime) Then
      Exit;
   If FScene = nil Then
      Exit;
   If (((Scene <> nil) And (Scene.GetDesignTime))) And FDesignHide Then
      Exit;
   ;
   If FScene.GetDisableUpdate Then
      Exit;
   If Not (((Scene <> nil) And (Scene.GetDesignTime))) And Not CheckParentVisible Then
      Exit;
   ;
   If FUpdating > 0 Then
      Exit;
   If HasDisablePaintEffect Then
      FUpdateEffects := True;
   FScene.AddUpdateRect(UpdateRect);
End;


Procedure TD2VisualObject.Lock;
Var
   i: Integer;
Begin
   Locked := True;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]).isVisual Then
            TD2VisualObject(FChildren[i]).Lock;
End;

Function TD2VisualObject.GetLocalRect: TD2Rect;
Begin
   Result := d2Rect(0, 0, FWidth, FHeight);
End;

Function TD2VisualObject.GetAbsoluteRect: TD2Rect;
Begin
   Result := d2NormalizeRect([LocalToAbsolute(d2Point(0, 0)), LocalToAbsolute(d2Point(Width, 0)), LocalToAbsolute(d2Point(Width, Height)), LocalToAbsolute(d2Point(0, Height))]);
End;

Function TD2VisualObject.GetClipRect: TD2Rect;
Begin
   Result := d2Rect(0, 0, Width, Height);
End;

Function TD2VisualObject.GetBoundsRect: TD2Rect;
Begin
   Result := d2Rect(0, 0, Width, Height);
End;

Function TD2VisualObject.GetParentedRect: TD2Rect;
Begin
   Result := d2Rect(0, 0, Width, Height);
   d2OffsetRect(Result, Position.X, Position.Y);
End;

Procedure TD2VisualObject.SetBoundsRect(Const Value: TD2Rect);
Var
   P: TD2Point;
Begin
   Repaint;
   FWidth := ABS(Value.Right - Value.Left);
   FHeight := ABS(Value.Bottom - Value.Top);

   P := LocalToAbsolute(d2Point(Value.Left + FRotateCenter.X * FWidth, Value.Top + FRotateCenter.Y * FHeight));
   If (Parent <> nil) And (Parent.isVisual) Then
      P := TD2VisualObject(Parent).AbsoluteToLocal(P);
   FPosition.FX := P.X - FScale.X * FRotateCenter.X * FWidth;
   FPosition.FY := P.Y - FScale.Y * FRotateCenter.Y * FHeight;
   If (FWidth < 0) Then
   Begin
      FWidth := Abs(FWidth);
      FScale.X := -FScale.X;
   End;
   If (FHeight < 0) Then
   Begin
      FHeight := Abs(FHeight);
      FScale.Y := -FScale.Y;
   End;
   MatrixChanged(Self);
   Realign;
End;

Procedure TD2VisualObject.PaddingChanged(Sender: TObject);
Begin
   If (FParent <> nil) And (FParent.isVisual) Then
      TD2VisualObject(FParent).Realign;
End;

Procedure TD2VisualObject.MarginsChanged(Sender: TObject);
Begin
   Realign;
End;

Procedure TD2VisualObject.DesignInsert;
Begin
End;

Procedure TD2VisualObject.DesignSelect;
Begin
End;

Procedure TD2VisualObject.DesignClick;
Begin
End;

Type

   TD2AlignInfo = Record
      AlignList: TList;
      ControlIndex: Integer;
      Align: TD2Align;
      Scratch: Integer;
   End;

Procedure TD2VisualObject.BeginUpdate;
Var
   i: Integer;
Begin
   FUpdating := FUpdating + 1;
   For i := 0 To ChildrenCount - 1 Do
      If TD2Object(FChildren[i]).IsVisual Then
         TD2VisualObject(FChildren[i]).BeginUpdate;
End;

Procedure TD2VisualObject.EndUpdate;
Var
   i: Integer;
Begin
   FUpdating := FUpdating - 1;
   For i := 0 To ChildrenCount - 1 Do
      If TD2Object(FChildren[i]).IsVisual Then
         TD2VisualObject(FChildren[i]).EndUpdate;
   If FUpdating = 0 Then
      Realign;
End;

Procedure TD2VisualObject.RecalcNeedAlign;
Var
   i: Integer;
Begin
   FNeedAlign := False;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
      Begin
         If Not (TD2Object(FChildren[i]).IsVisual) Then
            Continue;
         If TD2VisualObject(FChildren[i]).Align <> vaNone Then
         Begin
            FNeedAlign := True;
            Break;
         End;
      End;
End;

Procedure TD2VisualObject.Realign;
Var
   R: TD2Rect;
   AlignList: TList;

   Function InsertBefore(C1, C2: TD2VisualObject; AAlign: TD2Align): Boolean;
   Begin
      Result := False;
      Case AAlign Of
         vaTop, vaMostTop: Result := C1.Position.Y < C2.Position.Y;
         vaBottom, vaMostBottom: Result := (C1.Position.Y + C1.Height) >= (C2.Position.Y + C2.Height);
         vaLeft, vaMostLeft: Result := C1.Position.X < C2.Position.X;
         vaRight, vaMostRight: Result := (C1.Position.X + C1.Width) >= (C2.Position.X + C2.Width);
      End;
   End;

   Procedure DoPosition(Control: TD2VisualObject; AAlign: TD2Align; AlignInfo: TD2AlignInfo);
   Var
      NewLeft, NewTop, NewWidth, NewHeight: Single;
      cR, mR: TD2Rect;
      fitScale: Single;
   Begin
      With R Do
      Begin
         NewWidth := Right - Left;
         If (NewWidth < 0) Or (AAlign In [vaLeft, vaRight, vaVertical, vaMostLeft, vaMostRight, vaTopLeft, vaTopRight, vaBottomLeft, vaBottomRight]) Then
            NewWidth := Control.Width + Control.Padding.Left + Control.Padding.Right;
         NewHeight := Bottom - Top;
         If (NewHeight < 0) Or (AAlign In [vaTop, vaBottom, vaMostTop, vaMostBottom, vaHorizontal, vaTopLeft, vaTopRight, vaBottomLeft, vaBottomRight]) Then
            NewHeight := Control.Height + Control.Padding.Top + Control.Padding.Bottom;
         NewLeft := Left;
         NewTop := Top;
         If (AAlign In [vaVertical]) Then
            NewLeft := Control.Position.X + Control.Padding.Left;
         If (AAlign In [vaHorizontal]) Then
            NewTop := Control.Position.Y + Control.Padding.Top;
         Case AAlign Of
            vaTop, vaMostTop:
               Top := Top + NewHeight;
            vaBottom, vaMostBottom:
            Begin
               Bottom := Bottom - NewHeight;
               NewTop := Bottom;
            End;
            vaLeft, vaMostLeft:
               Left := Left + NewWidth;
            vaRight, vaMostRight:
            Begin
               Right := Right - NewWidth;
               NewLeft := Right;
            End;
            vaContents:
            Begin
               NewLeft := 0;
               NewTop := 0;
               NewWidth := Width;
               NewHeight := Height;
               Control.FPosition.FX := NewLeft + Control.Padding.Left;
               Control.FPosition.FY := NewTop + Control.Padding.Top;
               Control.FWidth := ABS(NewWidth - Control.Padding.Left - Control.Padding.Right);
               Control.FHeight := ABS(NewHeight - Control.Padding.Top - Control.Padding.Bottom);
               Control.MatrixChanged(Self);
               Control.Realign;
               Exit;
            End;
            vaFit, vaFitLeft, vaFitRight:
            Begin
               mR := d2Rect(Margins.Left, Margins.Top, Width - Margins.Right, Height - Margins.Bottom);
               cR := d2Rect(Control.FPosition.FX - Control.Padding.Left, Control.FPosition.FY - Control.Padding.Top, Control.FPosition.FX + Control.FWidth + Control.Padding.Right,
                  Control.FPosition.FY + Control.FHeight + Control.Padding.Bottom);
               fitScale := d2FitRect(cR, mR);
               If fitScale < 1 Then
               Begin
                  cR.Left := cR.Left / fitScale;
                  cR.Right := cR.Right / fitScale;
                  cR.Top := cR.Top / fitScale;
                  cR.Bottom := cR.Bottom / fitScale;
                  d2RectCenter(cR, mR);
                  If AAlign = vaFitLeft Then
                     d2OffsetRect(cR, mR.Left - cR.Left, 0);
                  If AAlign = vaFitRight Then
                     d2OffsetRect(cR, mR.Right - cR.Right, 0);
                  NewLeft := cR.Left;
                  NewTop := cR.Top;
                  NewWidth := cR.Right - cR.Left;
                  NewHeight := cR.Bottom - cR.Top;
               End Else Begin
                  If AAlign = vaFitLeft Then
                     d2OffsetRect(cR, mR.Left - cR.Left, 0);
                  If AAlign = vaFitRight Then
                     d2OffsetRect(cR, mR.Right - cR.Right, 0);
                  NewLeft := cR.Left;
                  NewTop := cR.Top;
                  NewWidth := cR.Right - cR.Left;
                  NewHeight := cR.Bottom - cR.Top;
               End;
               Control.FPosition.FX := NewLeft + Control.Padding.Left;
               Control.FPosition.FY := NewTop + Control.Padding.Top;
               Control.FWidth := ABS(NewWidth - Control.Padding.Left - Control.Padding.Right);
               Control.FHeight := ABS(NewHeight - Control.Padding.Top - Control.Padding.Bottom);
               Control.MatrixChanged(Self);
               Control.Realign;
               Exit;
            End;
            vaCenter:
            Begin
               NewLeft := Left + Trunc((NewWidth - (Control.Width + Control.Padding.Left + Control.Padding.Right)) / 2);
               NewWidth := (Control.Width + Control.Padding.Left + Control.Padding.Right);
               NewTop := Top + Trunc((NewHeight - (Control.Height + Control.Padding.Top + Control.Padding.Bottom)) / 2);
               NewHeight := (Control.Height + Control.Padding.Top + Control.Padding.Bottom);
            End;
            vaHorzCenter:
            Begin
               NewLeft := Left + Trunc((NewWidth - (Control.Width + Control.Padding.Left + Control.Padding.Right)) / 2);
               NewWidth := (Control.Width + Control.Padding.Left + Control.Padding.Right);
            End;
            vaVertCenter:
            Begin
               NewTop := Top + Trunc((NewHeight - (Control.Height + Control.Padding.Top + Control.Padding.Bottom)) / 2);
               NewHeight := (Control.Height + Control.Padding.Top + Control.Padding.Bottom);
            End;
            vaTopRight:
            Begin
               Control.Position.X := Control.Position.X + (FWidth - FLastWidth);
               Exit;
            End;
            vaBottomLeft:
            Begin
               Control.Position.Y := Control.FPosition.Y + (FHeight - FLastHeight);
               Exit;
            End;
            vaBottomRight:
            Begin
               Control.Position.SetPoint(d2Point(Control.Position.X + (FWidth - FLastWidth), Control.FPosition.Y + (FHeight - FLastHeight)));
               Exit;
            End;
         End;
      End;

      If (AALign = vaScale) Then
      Begin
         If (FLastWidth > 0) And (FLastHeight > 0) And (FWidth > 0) And (FHeight > 0) Then
         Begin
            Control.FPosition.FX := Control.FPosition.X * (FWidth / FLastWidth);
            Control.FPosition.FY := Control.FPosition.Y * (FHeight / FLastHeight);
            Control.FWidth := ABS(Control.FWidth * (FWidth / FLastWidth));
            Control.FHeight := ABS(Control.FHeight * (FHeight / FLastHeight));
            Control.MatrixChanged(Self);
            Control.Realign;
         End;
         Exit;
      End Else Begin
         Control.FPosition.FX := NewLeft + Control.Padding.Left;
         Control.FPosition.FY := NewTop + Control.Padding.Top;
         If (Control.FWidth <> NewWidth - Control.Padding.Left - Control.Padding.Right) Or (Control.FHeight <> NewHeight - Control.Padding.Top - Control.Padding.Bottom) Then
         Begin
            Control.FWidth := ABS(NewWidth - Control.Padding.Left - Control.Padding.Right);
            Control.FHeight := ABS(NewHeight - Control.Padding.Top - Control.Padding.Bottom);
            Control.Realign;
         End;
         Control.MatrixChanged(Self);
      End;

      If (Control.Width + Control.Padding.Left + Control.Padding.Right <> NewWidth) Or (Control.Height + Control.Padding.Top + Control.Padding.Bottom <> NewHeight) Then
         With R Do
            Case AAlign Of
               vaTop:
                  Top := Top - (NewHeight - (Control.Height + Control.Padding.Left + Control.Padding.Right));
               vaBottom:
                  Bottom := Bottom + (NewHeight - (Control.Height + Control.Padding.Top + Control.Padding.Bottom));
               vaLeft:
                  Left := Left - (NewWidth - (Control.Width + Control.Padding.Left + Control.Padding.Right));
               vaRight:
                  Right := Right + (NewWidth - (Control.Width + Control.Padding.Top + Control.Padding.Bottom));
               vaClient:
               Begin
                  Right := Right + NewWidth - (Control.Width + Control.Padding.Left + Control.Padding.Right);
                  Bottom := Bottom + NewHeight - (Control.Height + Control.Padding.Top + Control.Padding.Bottom);
               End;
            End;
   End;

   Procedure DoAlign(AAlign: TD2Align);
   Var
      I, J: Integer;
      Control: TD2VisualObject;
      AlignInfo: TD2AlignInfo;
   Begin
      AlignList.Clear;
      For I := 0 To FChildren.Count - 1 Do
      Begin
         If Not TD2Object(FChildren[i]).isVisual Then
            Continue;
         Control := TD2VisualObject(FChildren[i]);
         If (Control.Align = AAlign) And (Control.Visible) Then
         Begin
            J := 0;
            While (J < AlignList.Count) And Not InsertBefore(Control, TD2VisualObject(AlignList[J]), AAlign) Do
               Inc(J);
            AlignList.Insert(J, Control);
         End;
      End;
      For I := 0 To AlignList.Count - 1 Do
      Begin
         AlignInfo.AlignList := AlignList;
         AlignInfo.ControlIndex := I;
         AlignInfo.Align := AAlign;
         DoPosition(TD2VisualObject(AlignList[I]), AAlign, AlignInfo);
      End;
   End;

Begin
   If csDestroying In ComponentState Then
      Exit;
   If ((FWidth > -2) And (FWidth < 2)) Or ((FHeight > -2) And (FHeight < 2)) Then
      Exit;
   If FDisableDefaultAlign Then
      Exit;
   If FDisableAlign Then
      Exit;
   If FUpdating > 0 Then
      Exit;
   If csLoading In ComponentState Then
   Begin
      FLastWidth := FWidth;
      FLastHeight := FHeight;
      Exit;
   End;
   If ((FLastWidth <> FWidth) Or (FLastHeight <> FHeight)) And FHasEffect Then
   Begin
      UpdateEffects;
   End;
   If Not FNeedAlign Then
      Exit;
   If FChildren = nil Then
      Exit;
   If FChildren.Count = 0 Then
      Exit;

   FDisableAlign := True;
   Try
      R := d2Rect(0, 0, FWidth, FHeight);
      R := FMargins.MarginRect(R);
      AlignList := TList.Create;
      Try
         DoAlign(vaMostTop);
         DoAlign(vaMostBottom);
         DoAlign(vaMostLeft);
         DoAlign(vaMostRight);
         DoAlign(vaTop);
         DoAlign(vaBottom);
         DoAlign(vaLeft);
         DoAlign(vaRight);
         DoAlign(vaClient);
         DoAlign(vaHorizontal);
         DoAlign(vaVertical);
         DoAlign(vaContents);
         DoAlign(vaCenter);
         DoAlign(vaHorzCenter);
         DoAlign(vaVertCenter);
         DoAlign(vaScale);
         DoAlign(vaFit);
         DoAlign(vaFitLeft);
         DoAlign(vaFitRight);
         DoAlign(vaTopRight);
         DoAlign(vaBottomLeft);
         DoAlign(vaBottomRight);
      Finally
         AlignList.Free;
      End;
      FLastWidth := FWidth;
      FLastHeight := FHeight;
      Repaint;
   Finally
      FDisableAlign := False;
   End;
End;

Procedure TD2VisualObject.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Var
   VP: TD2Point;
Begin
   If (Key = VK_APPS) Then
   Begin
      VP := LocalToAbsolute(d2Point(Width / 2, Height / 2));
      VP := Scene.LocalToScreen(VP);
      ContextMenu(VP);
   End Else
   If Assigned(FOnKeyDown) Then
      FOnKeyDown(Self, Key, KeyChar, Shift);
End;

Procedure TD2VisualObject.KeyUp(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   If Assigned(FOnKeyUp) Then
      FOnKeyUp(Self, Key, KeyChar, Shift);
End;

Procedure TD2VisualObject.DialogKey(Var Key: Word; Shift: TShiftState);
Var
   i: Integer;
Begin
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]).isVisual And (TD2VisualObject(FChildren[i]).Visible) And TD2VisualObject(FChildren[i]).Enabled Then
         Begin
            TD2VisualObject(FChildren[i]).DialogKey(Key, Shift);
            If Key = 0 Then
               Break;
         End;
End;

Procedure TD2VisualObject.Capture;
Begin
   If (FScene <> nil) Then
   Begin
      FScene.SetCaptured(Self);
   End;
End;

Procedure TD2VisualObject.ReleaseCapture;
Begin
   If (FScene <> nil) And (FScene.GetCaptured = Self) Then
   Begin
      FScene.SetCaptured(nil);
   End;
End;

Procedure TD2VisualObject.MouseEnter;
Begin
   FIsMouseOver := True;
   StartTriggerAnimation(Self, 'IsMouseOver');
   ApplyTriggerEffect(Self, 'IsMouseOver');
   If Assigned(FOnMouseEnter) Then
      FOnMouseEnter(Self);
End;

Procedure TD2VisualObject.MouseLeave;
Begin
   FIsMouseOver := False;
   StartTriggerAnimation(Self, 'IsMouseOver');
   ApplyTriggerEffect(Self, 'IsMouseOver');
   If Assigned(FOnMouseLeave) Then
      FOnMouseLeave(Self);
End;

Function TD2VisualObject.EnterFocusChildren(AObject: TD2VisualObject): Boolean;
Begin
   Result := False;
End;

Procedure TD2VisualObject.EnterFocus;
Var
   P: TD2Object;
Begin
   If Not CanFocused Then
      Exit;

   P := Parent;
   While P <> nil Do
   Begin
      If P.IsVisual Then
         If P.Visual.EnterFocusChildren(Self) Then
            Break;
      P := P.Parent;
   End;
   FIsFocused := True;
   FRecalcUpdateRect := True;
   Repaint;
   If Assigned(FOnEnterFocus) Then
      FOnEnterFocus(Self);
   If DisableFocusEffect Then
      Exit;
   If GvarD2GlobalDisableFocusEffect Then
      Exit;
   StartTriggerAnimation(Self, 'IsFocused');
   ApplyTriggerEffect(Self, 'IsFocused');
End;

Procedure TD2VisualObject.SetNewScene(AScene: Id2Scene);
Begin
   If (AScene = nil) And (FIsFocused) Then
      KillFocus;
   Inherited;
End;

Procedure TD2VisualObject.KillFocus;
Begin
   If Not CanFocused Then
      Exit;

   FRecalcUpdateRect := True;
   Repaint;
   FIsFocused := False;
   If Assigned(FOnKillFocus) Then
      FOnKillFocus(Self);
   If DisableFocusEffect Then
      Exit;
   If GvarD2GlobalDisableFocusEffect Then
      Exit;
   StartTriggerAnimation(Self, 'IsFocused');
   ApplyTriggerEffect(Self, 'IsFocused');
End;

Procedure TD2VisualObject.SetFocus;
Var
   C: Boolean;
Begin
   If Not CanFocused Then
      Exit;
   If Assigned(FOnCanFocused) Then
   Begin
      C := True;
      FOnCanFocused(Self, C);
      If Not C Then
         Exit;
   End;
   FScene.SetFocused(Self);
End;

Procedure TD2VisualObject.ContextMenu(Const ScreenPosition: TD2Point);
Begin
   If FPopupMenu <> nil Then
   Begin
      FPopupMenu.PopupComponent := Self;
      FPopupMenu.Popup(round(ScreenPosition.X), round(ScreenPosition.Y));
      Exit;
   End;
   If FPopup <> nil Then
   Begin
      FPopup.StaysOpen := False;
      FPopup.PopupModal;
   End;
End;

Procedure TD2VisualObject.Click;
Begin
   If Assigned(FOnClick) Then
      FOnClick(Self);
End;

Procedure TD2VisualObject.DblClick;
Begin
   If Assigned(FOnDblClick) Then
      FOnDblClick(Self);
End;

Function TD2VisualObject.MakeScreenshot: TD2Bitmap;
Begin
   Result := TD2Bitmap.Create(round(Width), round(Height));
   Result.Clear(0);

   Result.Canvas.BeginScene;
   PaintTo(Result.Canvas, d2Rect(0, 0, Result.Width, Result.Height));
   Result.Canvas.EndScene;
End;

Procedure TD2VisualObject.BeginAutoDrag;
Var
   B, S: TD2Bitmap;
   R: TD2Rect;
Begin
   S := MakeScreenshot;
   If (S.Width > 512) Or (S.Height > 512) Then
   Begin
      R := d2Rect(0, 0, S.Width, S.Height);
      d2FitRect(R, d2Rect(0, 0, 512, 512));
      B := TD2Bitmap.Create(round(d2RectWidth(R)), round(d2RectHeight(R)));
      B.Canvas.BeginScene;
      B.Canvas.DrawBitmap(S, d2Rect(0, 0, S.Width, S.Height), d2Rect(0, 0, B.Width, B.Height), 0.7, True);
      B.Canvas.EndScene;
   End Else Begin
      B := TD2Bitmap.Create(S.Width, S.Height);
      B.Canvas.BeginScene;
      B.Canvas.DrawBitmap(S, d2Rect(0, 0, B.Width, B.Height), d2Rect(0, 0, B.Width, B.Height), 0.7, True);
      B.Canvas.EndScene;
   End;
   FScene.BeginDragObj(Self, B);
   B.Free;
   S.Free;
End;

Procedure TD2VisualObject.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   VP: TD2Point;
Begin
   If (ssDouble In Shift) And (((FScene <> nil) And (FScene.GetDesignTime))) Then
      FScene.SetDesignRoot(Self);
   If (Assigned(FScene) And Not (FScene.GetDesignTime)) And CanFocused And Not FIsFocused And (FScene <> nil) And (FScene.GetFocused <> Self) Then
      SetFocus;
   If Assigned(FOnMouseDown) Then
      FOnMouseDown(Self, Button, Shift, X, Y);
   If (Button = mbRight) Then
   Begin
      VP := LocalToAbsolute(d2Point(X, Y));
      VP := Scene.LocalToScreen(VP);
      ContextMenu(VP);
      Exit;
   End;
   If FAutoCapture Then
      Capture;
   If (ssDouble In Shift) Then
   Begin
      DblClick;
      FDoubleClick := True;
   End Else
   If Button = mbLeft Then
   Begin
      FPressed := True;
   End;
End;

Procedure TD2VisualObject.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   If Assigned(FOnMouseMove) Then
      FOnMouseMove(Self, Shift, X, Y, Dx, Dy);
End;

Procedure TD2VisualObject.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   ReleaseCapture;
   If Assigned(FOnMouseUp) Then
      FOnMouseUp(Self, Button, Shift, X, Y);

   If FPressed And Not (FDoubleClick) And d2PtInRect(d2Point(X, Y), LocalRect) Then
   Begin
      FPressed := False;
      Click;
   End;
   FPressed := False;
   FDoubleClick := False;
End;

Procedure TD2VisualObject.MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean);
Begin
   If Assigned(FOnMouseWheel) Then
      If FScene <> nil Then
         FOnMouseWheel(Self, Shift, WheelDelta, FScene.GetMousePos, Handled)
      Else
         FOnMouseWheel(Self, Shift, WheelDelta, d2Point(0, 0), Handled);
End;

Procedure TD2VisualObject.DragEnter(Const aData: TD2DragObject; Const Point: TD2Point);
Begin
   FIsDragOver := True;
   Repaint;
   StartTriggerAnimation(Self, 'IsDragOver');
   ApplyTriggerEffect(Self, 'IsDragOver');
   If Assigned(OnDragEnter) Then
      OnDragEnter(Self, aData, Point);
End;

Procedure TD2VisualObject.DragLeave;
Begin
   FIsDragOver := False;
   Repaint;
   StartTriggerAnimation(Self, 'IsDragOver');
   ApplyTriggerEffect(Self, 'IsDragOver');
   If Assigned(OnDragLeave) Then
      OnDragLeave(Self);
End;

Procedure TD2VisualObject.DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean);
Begin
   If Assigned(OnDragOver) Then
      OnDragOver(Self, aData, Point, Accept);
End;

Procedure TD2VisualObject.DragDrop(Const aData: TD2DragObject; Const Point: TD2Point);
Begin
   FIsDragOver := False;
   Repaint;
   StartTriggerAnimation(Self, 'IsDragOver');
   ApplyTriggerEffect(Self, 'IsDragOver');
   If Assigned(OnDragDrop) Then
      OnDragDrop(Self, aData, Point);
End;

Procedure TD2VisualObject.DragEnd;
Begin
   If DragMode = d2DragAutomatic Then
      MouseUp(mbLeft, [ssLeft], $FFFF, $FFFF);
   If Assigned(OnDragEnd) Then
      OnDragEnd(Self);
End;

Procedure TD2VisualObject.SetEnabled(Const Value: Boolean);
Begin
   If FEnabled <> Value Then
   Begin
      FEnabled := Value;
      RecalcEnabled;
      RecalcOpacity;
      Repaint;
   End;
End;

Function TD2VisualObject.GetAbsoluteEnabled: Boolean;
Begin
   If FRecalcEnabled Then
   Begin
      If (FParent <> nil) And (FParent.IsVisual) And (Not FParent.Visual.AbsoluteEnabled) Then
         FAbsoluteEnabled := False
      Else
         FAbsoluteEnabled := FEnabled;

      Result := FAbsoluteEnabled;
      FRecalcEnabled := False;

      If Not Result And Assigned(FScene) And CanFocused And IsFocused Then
         FScene.SetFocused(nil);
   End Else Begin
      Result := FAbsoluteEnabled;
   End;
End;

Procedure TD2VisualObject.RecalcEnabled;
Var
   i: Integer;
Begin
   FRecalcEnabled := True;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]).IsVisual Then
            TD2VisualObject(FChildren[i]).RecalcEnabled;
End;

Procedure TD2VisualObject.SetTempCanvas(Const Value: TD2Canvas);
Var
   i: Integer;
Begin
   FTempCanvas := Value;
   If (FChildren <> nil) And (FChildren.Count > 0) Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]).IsVisual Then
            TD2VisualObject(FChildren[i]).TempCanvas := Value;
End;

Procedure TD2VisualObject.SetHitTest(Const Value: Boolean);
Begin
   FHitTest := Value;
End;

Procedure TD2VisualObject.SetClipChildren(Const Value: Boolean);
Begin
   If FClipChildren <> Value Then
   Begin
      FClipChildren := Value;
      Repaint;
   End;
End;

Procedure TD2VisualObject.SetAlign(Const Value: TD2Align);
Begin
   If FAlign <> Value Then
   Begin
      FAlign := Value;
      If (FParent <> nil) And (FParent.IsVisual) Then
      Begin
         FParent.Visual.FNeedAlign := True;
         If Not (csLoading In ComponentState) Then
            FParent.Visual.Realign;
      End;
   End;
End;

Procedure TD2VisualObject.SetVisible(Const Value: Boolean);
Begin
   If FVisible <> Value Then
   Begin
      If FVisible Then
      Begin
         If FScene <> nil Then
            FScene.AddUpdateRect(UpdateRect);
      End;
      FVisible := Value;
      If Not (csLoading In ComponentState) Then
      Begin
         If (Parent <> nil) And (Parent.IsVisual) And (Align <> vaNone) Then
            TD2VisualObject(Parent).Realign;
      End;
      If FVisible Then
      Begin
         RecalcUpdateRect;
         If FScene <> nil Then
            FScene.AddUpdateRect(UpdateRect);
         StartTriggerAnimation(Self, 'IsVisible');
      End Else
      If CanFocused And FIsFocused Then
         FScene.SetFocused(nil);
   End;
End;

Procedure TD2VisualObject.SetPosition(Const Value: TD2Position);
Begin
   FPosition.Assign(Value);
End;

Procedure TD2VisualObject.SetRotateAngle(Const Value: Single);
Begin
   If FRotateAngle <> Value Then
   Begin
      FRotateAngle := d2NormalizeAngle(Value);
      MatrixChanged(Self);
   End;
End;

Procedure TD2VisualObject.SetBounds(X, Y, AWidth, AHeight: Single);
Var
   SizeChanged: Boolean;
Begin
   Repaint;
   SizeChanged := False;
   If (FHeight <> AHeight) Then
   Begin
      FHeight := ABS(AHeight);
      If (FHeight < 0) And (((FScene <> nil) And (FScene.GetDesignTime))) Then
      Begin
         FHeight := Abs(FHeight);
         FScale.Y := -FScale.Y;
      End;
      SizeChanged := True;
   End;
   If (FWidth <> AWidth) Then
   Begin
      FWidth := ABS(AWidth);
      If (FWidth < 0) And (((FScene <> nil) And (FScene.GetDesignTime))) Then
      Begin
         FWidth := Abs(FWidth);
         FScale.X := -FScale.X;
      End;
      SizeChanged := True;
   End;

   If (X <> FPosition.X) Or (Y <> FPosition.Y) Then
   Begin
      FPosition.FX := X;
      FPosition.FY := Y;
      MatrixChanged(Self);
   End;

   If Not (csLoading In ComponentState) And (SizeChanged) Then
   Begin
      If (Parent <> nil) And (Parent.IsVisual) And (Align <> vaNone) Then
      Begin
         TD2VisualObject(Parent).Realign;
      End;
      If (FChildren <> nil) Then
         Realign;
   End;
   If Not (csLoading In ComponentState) Then
   Begin
      RecalcUpdateRect;
      Repaint;
   End;
End;

Procedure TD2VisualObject.SetSizeWithoutChange(AWidth, AHeight: Single);
Begin
   FWidth := ABS(AWidth);
   FLastWidth := ABS(AWidth);
   FHeight := ABS(AHeight);
   FLastHeight := ABS(AHeight);
End;

Procedure TD2VisualObject.SetHeight(Const Value: Single);
Begin
   If FHeight <> Value Then
   Begin
      Repaint;
      FHeight := ABS(Value);
      If (FHeight < 0) And (((FScene <> nil) And (FScene.GetDesignTime))) Then
      Begin
         FHeight := Abs(FHeight);
         FScale.Y := -FScale.Y;
      End;
      If Not (csLoading In ComponentState) And (FScene <> nil) Then
      Begin
         UpdateEffects;
         RecalcUpdateRect;
         If (Parent <> nil) And (Parent.IsVisual) And ((Align <> vaNone) Or (Parent Is TD2ScrollContent)) Then
         Begin
            TD2VisualObject(Parent).Realign;
         End;
         If (FChildren <> nil) Then
            Realign;
      End;
   End;
End;

Procedure TD2VisualObject.SetWidth(Const Value: Single);
Begin
   If FWidth <> Value Then
   Begin
      Repaint;
      FWidth := ABS(Value);
      If (FWidth < 0) And (((FScene <> nil) And (FScene.GetDesignTime))) Then
      Begin
         FWidth := Abs(FWidth);
         FScale.X := -FScale.X;
      End;
      If Not (csLoading In ComponentState) And (FScene <> nil) Then
      Begin
         UpdateEffects;
         RecalcUpdateRect;
         If (Parent <> nil) And (Parent.IsVisual) And ((Align <> vaNone) Or (Parent Is TD2ScrollContent)) Then
         Begin
            TD2VisualObject(Parent).Realign;
         End;
         If (FChildren <> nil) Then
            Realign;
      End;
   End;
End;

Function TD2VisualObject.isOpacityStored: Boolean;
Begin
   Result := FOpacity <> 1;
End;

Procedure TD2VisualObject.SetOpacity(Const Value: Single);
Begin
   If FOpacity <> Value Then
   Begin
      FOpacity := Value;
      If FOpacity < 0 Then
         FOpacity := 0;
      If FOpacity > 1 Then
         FOpacity := 1;
      RecalcOpacity;
      Repaint;
   End;
End;

Procedure TD2VisualObject.UpdateDesignHide(Const Value: Boolean);
Var
   i: Integer;
Begin
   FDesignHide := Value;
   For i := 0 To ChildrenCount - 1 Do
   Begin
      If Children[i].IsVisual Then
         Children[i].Visual.UpdateDesignHide(Value);
   End;
End;

Procedure TD2VisualObject.SetDesignHide(Const Value: Boolean);
Begin
   If FDesignHide <> Value Then
   Begin
      FDesignHide := Value;
      If (FScene <> nil) And FScene.GetDesignTime And (Parent <> nil) Then
      Begin
         If FDesignHide And (Scene.GetSelected = Self) And (GvarD2Designer <> nil) Then
            GvarD2Designer.SelectObject(Owner, Parent.Visual, []);
         Parent.Visual.Repaint;
      End;
   End;
End;

Procedure TD2VisualObject.SetCursor(Const Value: TCursor);
Begin
   If FCursor <> Value Then
   Begin
      FCursor := Value;
   End;
End;

Procedure TD2VisualObject.FixupTabList;
Var
   I, J: Integer;
   List: TList;
   Control: TD2VisualObject;
Begin
   If Not Assigned(Scene) Then
      Exit;
   If FTabList = nil Then
      Exit;
   List := TList.Create;
   Try
      List.Count := FTabList.Count;
      For i := 0 To FTabList.Count - 1 Do
      Begin
         Control := TD2VisualObject(FTabList[i]);
         j := Control.FTabOrder;
         If (j >= 0) And (j < FTabList.Count) Then
            List[j] := Control;
      End;
      For I := 0 To FTabList.Count - 1 Do
      Begin
         Control := TD2VisualObject(List[I]);
         If Control <> nil Then
            Control.UpdateTabOrder(I);
      End;
   Finally
      List.Free;
   End;
End;

Procedure TD2VisualObject.GetTabOrderList(List: TList; aChildren: Boolean);
Var
   i: Integer;
   Control: TD2VisualObject;
Begin
   If FTabList <> nil Then
      For I := 0 To FTabList.Count - 1 Do
      Begin
         Control := TD2VisualObject(FTabList[I]);
         List.Add(Control);
         If aChildren Then
            Control.GetTabOrderList(List, aChildren);
      End;
End;

Function TD2VisualObject.GetTabOrder: TTabOrder;
Begin
   If (FParent <> nil) And (FParent.IsVisual) And (FParent.Visual.FTabList <> nil) Then
      Result := TD2VisualObject(FParent).FTabList.IndexOf(Self)
   Else
      Result := -1;
End;

Procedure TD2VisualObject.UpdateTabOrder(Value: TTabOrder);
Var
   CurIndex, Count: Integer;
Begin
   CurIndex := GetTabOrder;
   If CurIndex >= 0 Then
   Begin
      Count := TD2VisualObject(FParent).FTabList.Count;
      If Value < 0 Then
         Value := 0;
      If Value >= Count Then
         Value := Count - 1;
      If Value <> CurIndex Then
      Begin
         TD2VisualObject(FParent).FTabList.Delete(CurIndex);
         TD2VisualObject(FParent).FTabList.Insert(Value, Self);
      End;
   End;
End;

Procedure TD2VisualObject.SetTabOrder(Const Value: TTabOrder);
Begin
   If csLoading In ComponentState Then
      FTabOrder := Value
   Else
      UpdateTabOrder(Value);
End;

Function TD2VisualObject.GetHint: String;  // 7777
Begin
   Result := UTF8Encode(FHint);
End;

Procedure TD2VisualObject.SetHint(Const Value: String);  // 7777
Var
   ss: WideString;
Begin
   ss := UTF8Decode(Value);
   If FHint <> ss Then
      FHint := ss;
End;

//=============== TD2Control ==========================================

Constructor TD2Control.Create(AOwner: TComponent);
Begin
   Inherited;
   FHelpType := htContext;
   FNeedResource := True;
End;

Destructor TD2Control.Destroy;
Var
   i: Integer;
Begin
   If FActionLink <> nil Then
   Begin
      FActionLink.Free;
      FActionLink := nil;
   End;

   If FBindingObjects <> nil Then
   Begin
      For i := FBindingObjects.Count - 1 Downto 0 Do
         RemoveBindingObject(TD2Control(FBindingObjects[i]));
      FreeAndNil(FBindingObjects);
   End;
   Inherited;
End;

Procedure TD2Control.Loaded;
Begin
   Inherited;
   If Assigned(FScene) And (FScene.GetActiveControl = Self) Then
      SetFocus;
End;

Procedure TD2Control.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (FBindingSource = AComponent) Then
      BindingSource := nil;
   If (Operation = opRemove) And (FBindingObjects <> nil) And (FBindingObjects.IndexOf(AComponent) >= 0) Then
      FBindingObjects.Remove(AComponent);
   If (Operation = opRemove) And (AComponent = Action) Then
      Action := nil;
End;

Procedure TD2Control.SetData(Const Value: Variant);
Begin
End;

Procedure TD2Control.AddBindingObject(AObject: TD2Control);
Begin
   If FBindingObjects = nil Then
      FBindingObjects := TList.Create;
   If FBindingObjects.IndexOf(AObject) < 0 Then
   Begin
      AObject.AddFreeNotify(Self);
      FBindingObjects.Add(AObject);

      ToBindingObjects;
   End;
End;

Procedure TD2Control.RemoveBindingObject(AObject: TD2Control);
Begin
   If FBindingObjects = nil Then
      Exit;
   If FBindingObjects.IndexOf(AObject) >= 0 Then
   Begin
      AObject.RemoveFreeNotify(Self);
      FBindingObjects.Remove(AObject);
   End;
End;

Procedure TD2Control.SetBindingSource(Const Value: TD2Control);
Begin
   If FBindingSource <> Value Then
   Begin
      If FBindingSource <> nil Then
         FBindingSource.RemoveBindingObject(Self);
      FBindingSource := Value;
      If FBindingSource <> nil Then
         FBindingSource.AddBindingObject(Self);
   End;
End;

Procedure TD2Control.ToBindingObjects;
Var
   i: Integer;
Begin
   If FBindingObjects = nil Then
      Exit;
   For i := 0 To FBindingObjects.Count - 1 Do
      TD2Object(FBindingObjects[i]).Data := Data;
End;

Function TD2Control.FindResource(Const AResource: String): TD2Object;
Begin
   Result := nil;
   If FResourceLink <> nil Then
      Result := FResourceLink.FindResource(AResource);
   If Result = nil Then
      Result := Inherited FindResource(AResource);
End;

Function TD2Control.GetResourceObject: TD2VisualObject;
Var
   Obj: TD2Object;
   ResourceObject: TD2VisualObject;
   S: TStream;
   StyleName: String;
Begin
   ResourceObject := nil;
   If (FResource <> '') And (FResource = FResourceName) Then
   Begin
      Result := ResourceObject;
      Exit;
   End;
   If (FResource <> '') Then
   Begin
      Obj := nil;
      If Assigned(FScene) And (FScene.GetStyle <> nil) And (FScene.GetStyle.FRoot <> nil) Then
         Obj := TD2VisualObject(FScene.GetStyle.FRoot.FindResource(FResource));
      If Obj = nil Then
         If GvarD2DefaultStyles <> nil Then
            Obj := TD2VisualObject(GvarD2DefaultStyles.FindResource(FResource));
      If Obj = nil Then
         Obj := orca_scene2d.FindResource(FResource);
      If (Obj <> nil) And (Obj.isVisual) Then
      Begin
         ResourceObject := TD2VisualObject(Obj.Clone(Self));
         ResourceObject.ResourceName := '';
      End;
   End;
   If ResourceObject = nil Then
   Begin
      If GvarD2DefaultStyles = nil Then
      Begin
         If System.FindResource(HInstance, PChar('DEFAULTORCASTYLE'), RT_RCDATA) <> 0 Then
         Begin
            S := TResourceStream.Create(HInstance, 'DEFAULTORCASTYLE', RT_RCDATA);
            GvarD2DefaultStyles := CreateD2ObjectFromBinStream(nil, S);
            S.Free;
         End;
      End;
      If Assigned(FScene) And (FScene.GetStyle <> nil) And (FScene.GetStyle.FRoot <> nil) Then
      Begin
         If FResource <> '' Then
         Begin
            StyleName := FResource;
            ResourceObject := TD2VisualObject(FScene.GetStyle.FRoot.FindResource(StyleName));
            If ResourceObject <> nil Then
               ResourceObject := TD2VisualObject(ResourceObject.Clone(Self));
         End;
         If ResourceObject = nil Then
         Begin
            StyleName := ClassName + 'style';
            Delete(StyleName, 1, 3);
            ResourceObject := TD2VisualObject(FScene.GetStyle.FRoot.FindResource(StyleName));
            If ResourceObject <> nil Then
               ResourceObject := TD2VisualObject(ResourceObject.Clone(Self));
         End;
      End;
      If (ResourceObject = nil) And (GvarD2DefaultStyles <> nil) Then
      Begin
         If FResource <> '' Then
         Begin
            StyleName := FResource;
            ResourceObject := TD2VisualObject(GvarD2DefaultStyles.FindResource(StyleName));
            If ResourceObject <> nil Then
               ResourceObject := TD2VisualObject(ResourceObject.Clone(Self));
         End;
         If ResourceObject = nil Then
         Begin
            StyleName := ClassName + 'style';
            Delete(StyleName, 1, 3);
            ResourceObject := TD2VisualObject(GvarD2DefaultStyles.FindResource(StyleName));
            If ResourceObject <> nil Then
               ResourceObject := TD2VisualObject(ResourceObject.Clone(Self))
            Else Begin
               StyleName := ClassParent.ClassName + 'style';
               Delete(StyleName, 1, 3);
               ResourceObject := TD2VisualObject(GvarD2DefaultStyles.FindResource(StyleName));
               If ResourceObject <> nil Then
                  ResourceObject := TD2VisualObject(ResourceObject.Clone(Self));
            End;
         End;
      End;
   End;
   Result := ResourceObject;
End;

Procedure TD2Control.ApplyResource;
Var
   ResourceObject: TD2VisualObject;
   SaveUpdate: Boolean;
Begin
   If FNeedResource Then
   Begin
      Inherited;
      FNeedResource := False;
      ResourceObject := GetResourceObject;
      If ResourceObject <> nil Then
      Begin
         If csLoading In ResourceObject.ComponentState Then
            CallLoaded(ResourceObject);
         If (FScene <> nil) And (csLoading In ComponentState) Then
         Begin
            SaveUpdate := FScene.GetDisableUpdate;
            FScene.SetDisableUpdate(True);
         End;
         If FResourceLink <> nil Then
         Begin
            FreeStyle;
            FResourceLink.Free;
            FResourceLink := nil;
         End;
         ResourceObject.FAlign := vaContents;
         ResourceObject.DesignHide := False;
         FResourceLink := ResourceObject;
         AddObject(ResourceObject);

         FChildren.Remove(ResourceObject);
         FChildren.Insert(0, ResourceObject);

         ResourceObject.Stored := False;
         ResourceObject.Lock;
         ApplyStyle;
         If (FScene <> nil) And (csLoading In ComponentState) Then
         Begin
            FScene.SetDisableUpdate(SaveUpdate);
         End;
         FUpdateEffects := True;
         If Assigned(FOnApplyResource) Then
            FOnApplyResource(Self);
      End;
   End;
End;

Procedure TD2Control.UpdateResource;
Begin
   If csLoading In ComponentState Then
      Exit;
   If csDestroying In ComponentState Then
      Exit;
   Inherited;
   FNeedResource := True;
   ApplyResource;
End;

Procedure TD2Control.ApplyStyle;
Var
   NewT: WideString;
Begin
   If FIsFocused And CanFocused And Not FDisableFocusEffect And Not GvarD2GlobalDisableFocusEffect Then
   Begin
      FRecalcUpdateRect := True;
      Repaint;
      StartTriggerAnimation(Self, 'IsFocused');
      ApplyTriggerEffect(Self, 'IsFocused');
   End;

   If FAutoTranslate And ShowHint And (Hint <> '') Then
   Begin
      NewT := Translate(Hint);
      If (FScene <> nil) And Not (FScene.GetDesignTime) Then
         Hint := NewT;
   End;
End;

Procedure TD2Control.FreeStyle;
Begin
End;

Procedure TD2Control.EnterFocus;
Begin
   ApplyResource;
   Inherited;
End;

Procedure TD2Control.BeforePaint;
Begin
   Inherited;
   ApplyResource;
End;

Procedure TD2Control.Paint;
Begin
   Inherited;
End;

Procedure TD2Control.SetResource(Const Value: String);
Begin
   FResource := Value;
   FNeedResource := True;
   If Not (csLoading In ComponentState) And (FUpdating = 0) Then
   Begin
      ApplyResource;
   End;
End;

Procedure TD2Control.InitiateAction;
Begin
   If FActionLink <> nil Then
      FActionLink.Update;
End;

Procedure TD2Control.ActionChange(Sender: TObject; CheckDefaults: Boolean);
Begin
   If Sender Is TCustomAction Then
      With TCustomAction(Sender) Do
      Begin
         If Self Is TD2TextControl Then
            If Not CheckDefaults Or (TD2TextControl(Self).TextW = '') Or (TD2TextControl(Self).TextW = Self.Name) Then
               TD2TextControl(Self).TextW := Caption;
         If Not CheckDefaults Or (Self.Enabled = True) Then
            Self.Enabled := Enabled;
         If Not CheckDefaults Or (Self.Hint = '') Then
            Self.Hint := Hint;
         If Not CheckDefaults Or (Self.Visible = True) Then
            Self.Visible := Visible;
         If Not CheckDefaults Or Not Assigned(Self.OnClick) Then
            Self.OnClick := OnExecute;
      End;
End;

Procedure TD2Control.DoActionChange(Sender: TObject);
Begin
   If Sender = Action Then
      ActionChange(Sender, False);
End;

Function TD2Control.GetAction: TBasicAction;
Begin
   If FActionLink <> nil Then
      Result := FActionLink.Action
   Else
      Result := nil;
End;

Procedure TD2Control.SetAction(Value: TBasicAction);
Begin
   If Value = nil Then
   Begin
      FActionLink.Free;
      FActionLink := nil;
   End Else Begin
      If FActionLink = nil Then
         FActionLink := TD2ControlActionLink.Create(Self);
      FActionLink.Action := Value;
      FActionLink.OnChange := DoActionChange;
      ActionChange(Value, csLoading In Value.ComponentState);
      Value.FreeNotification(Self);
   End;
End;

Function TD2Control.IsHelpContextStored: Boolean;
Begin
   Result := (FActionLink = nil) Or Not FActionLink.IsHelpContextLinked;
End;

Procedure TD2Control.SetHelpContext(Const Value: THelpContext);
Begin
   If Not (csLoading In ComponentState) Then
      FHelpType := htContext;
   FHelpContext := Value;
End;

Procedure TD2Control.SetHelpKeyword(Const Value: String);
Begin
   If Not (csLoading In ComponentState) Then
      FHelpType := htKeyword;
   FHelpKeyword := Value;
End;

//=============== TD2Background =========================================

Constructor TD2Background.Create(AOwner: TComponent);
Begin
   Inherited;
   FFill := TD2Brush.Create(d2BrushNone, $FFE0E0E0);
   FFill.OnChanged := FillChanged;
End;

Destructor TD2Background.Destroy;
Begin
   FFill.Free;
   Inherited;
End;

Procedure TD2Background.FillChanged(Sender: TObject);
Begin
   Repaint;
End;

Procedure TD2Background.BeforePaint;
Begin
   Inherited BeforePaint;
   If Assigned(FOnBeforePaint) Then
   Begin
      If Self.Scene.GetRoot = Self Then
         FOnBeforePaint(Self, Self.Canvas,
            d2Rect(0, 0, FWidth, FHeight));
   End;
End;

Procedure TD2Background.Paint;
Begin
   If FFill.Style <> d2BrushNone Then
   Begin
      Canvas.Fill.Assign(FFill);
      Canvas.FillRect(LocalRect, 0, 0, AllCorners, AbsoluteOpacity);
   End;
End;

Procedure TD2Background.PaintChildren;
Begin
   If FFill.Style <> d2BrushNone Then
   Begin
      If (FResourceLink <> nil) Then
         FResourceLink.Visual.FVisible := False;
      Inherited;
      If FResourceLink <> nil Then
         FResourceLink.Visual.FVisible := True;
   End Else
   If (Parent = nil) And (FScene <> nil) And (FScene.GetRoot = Self) And (FScene.GetTransparency) Then
   Begin
      If FResourceLink <> nil Then
         FResourceLink.Visual.FVisible := False;
      Inherited;
      If FResourceLink <> nil Then
         FResourceLink.Visual.FVisible := True;
   End Else
      Inherited;
End;

Procedure TD2Background.SetFill(Const Value: TD2Brush);
Begin
   FFill.Assign(Value);
End;

//=============== TD2Content =========================================

Constructor TD2Content.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2Content.Destroy;
Begin
   Inherited;
End;

Function TD2Content.GetParentComponent: TComponent;
Begin
   Result := Inherited GetParentComponent;
End;

Procedure TD2Content.Realign;
Begin
   If (Parent <> nil) And Not (csLoading In Parent.ComponentState) Then
      Inherited;
   If Not FParentAligning And Not (csLoading In ComponentState) Then
   Begin
      FParentAligning := True;
      If (Parent <> nil) And (Parent.IsVisual) And Not (csLoading In Parent.ComponentState) Then
         TD2VisualObject(Parent).Realign;
      FParentAligning := False;
   End;
End;

Procedure TD2Content.Paint;
Begin
   Inherited;
End;

//=============== TD2DropTarget ====================================

Constructor TD2DropTarget.Create(AOwner: TComponent);
Begin
   Inherited;
   DragDisableHighlight := True;
   Width := 120;
   Height := 120;
End;

Procedure TD2DropTarget.DragDrop(Const aData: TD2DragObject; Const Point: TD2Point);
Begin
   Inherited;
   If Assigned(FOnDrop) Then
      FOnDrop(Self, aData, Point);
End;

Procedure TD2DropTarget.DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean);
Begin
   Inherited;
   Accept := True;
End;

//=============== TD2SizeGrip ====================================

Constructor TD2SizeGrip.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2SizeGrip.Destroy;
Begin
   Inherited;
End;

//=============== TD2GroupBox =======================================

Constructor TD2GroupBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FautoTranslate := True;
   CanFocused := False;
   Width := 120;
   Height := 100;
End;

Destructor TD2GroupBox.Destroy;
Begin
   Inherited;
End;

Procedure TD2GroupBox.ApplyStyle;
Begin
   Inherited;
End;

//=============== TD2Splitter ===========================================

Constructor TD2Splitter.Create(AOwner: TComponent);
Begin
   Inherited;
   FMinSize := 20;
   FShowGrip := True;
   AutoCapture := True;
   Width := 5;
   Align := vaLeft;
   Cursor := crHSplit;
End;

Destructor TD2Splitter.Destroy;
Begin
   Inherited;
End;

Procedure TD2Splitter.ApplyStyle;
Var
   grip: TD2Object;
Begin
   Inherited;
   grip := FindResource('grip');
   If (grip <> nil) And (grip Is TD2VisualObject) Then
   Begin
      TD2VisualObject(grip).Visible := FShowGrip;
   End;
End;

Procedure TD2Splitter.Paint;
Var
   R: TD2Rect;
Begin
   Inherited;
   If Assigned(Scene) And Scene.GetDesignTime And Not Locked And Not FInPaintTo Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      Canvas.StrokeDash := d2DashSolid;
   End;
End;

Procedure TD2Splitter.SetAlign(Const Value: TD2Align);
Begin
   Inherited;
   If Align In [vaTop, vaBottom] Then
      Cursor := crVSplit
   Else
      Cursor := crHSplit;
End;

Function TD2Splitter.FindObject: TD2VisualObject;
Var
   P: TD2Point;
   I: Integer;
   R: TD2Rect;
Begin
   Result := nil;
   P := Position.Point;
   Case Align Of
      vaLeft, vaMostLeft: P.X := P.X - 1;
      vaRight, vaMostRight: P.X := P.X + Width + 1;
      vaTop: P.Y := P.Y - 1;
      vaBottom: P.Y := P.Y + Height + 1;
      Else Exit;
   End;
   If Parent <> nil Then
      For I := 0 To Parent.ChildrenCount - 1 Do
      Begin
         If Not Parent.Children[I].IsVisual Then
            Continue;
         If TD2VisualObject(Parent.Children[I]).Locked Then
            Continue;
         If (Align In [vaLeft, vaMostLeft, vaRight, vaMostRight]) And Not ((TD2VisualObject(Parent.Children[I]).Align In [vaLeft, vaMostLeft, vaRight, vaMostRight])) Then
            Continue;
         If (Align In [vaTop, vaBottom, vaMostTop, vaMostBottom]) And Not ((TD2VisualObject(Parent.Children[I]).Align In [vaTop, vaBottom, vaMostTop, vaMostBottom])) Then
            Continue;

         Result := Parent.Children[I].Visual;
         If Result.Visible Then
         Begin
            R := Result.LocalRect;
            d2OffsetRect(R, Result.Position.X, Result.Position.Y);
            If (R.Right - R.Left) = 0 Then
               If Align In [vaTop, vaLeft, vaMostLeft] Then
                  R.Left := R.Left - 1
               Else
                  R.Right := R.Right + 1;
            If (R.Bottom - R.Top) = 0 Then
               If Align In [vaTop, vaLeft, vaMostLeft] Then
                  R.Top := R.Top - 1
               Else
                  R.Bottom := R.Bottom + 1;
            If d2PtInRect(P, R) Then
               Exit;
         End;
      End;
   Result := nil;
End;

Procedure TD2Splitter.UpdateSize(X, Y: Single);
Begin
   CalcSplitSize(X, Y, FNewSize, FSplit);
End;

Procedure TD2Splitter.CalcSplitSize(X, Y: Single; Var NewSize, Split: Single);
Var
   S: Single;
Begin
   If Align In [vaLeft, vaRight, vaMostLeft, vaMostRight] Then
      Split := X - FDownPos.X
   Else
      Split := Y - FDownPos.Y;
   S := 0;
   Case Align Of
      vaLeft, vaMostLeft: S := FControl.Width + Split;
      vaRight, vaMostRight: S := FControl.Width - Split;
      vaTop: S := FControl.Height + Split;
      vaBottom: S := FControl.Height - Split;
   End;
   NewSize := S;
   If S < FMinSize Then
      NewSize := FMinSize
   Else
   If S > FMaxSize Then
      NewSize := FMaxSize;
   If S <> NewSize Then
   Begin
      If Align In [vaRight, vaMostRight, vaBottom] Then
         S := S - NewSize
      Else
         S := NewSize - S;
      Split := Split + S;
   End;
End;

Function TD2Splitter.DoCanResize(Var NewSize: Single): Boolean;
Begin
   Result := True;
   If (NewSize <= FMinSize) Then
      NewSize := FMinSize;
End;

Procedure TD2Splitter.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   i: Integer;
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      FPressed := True;
      FDownPos := d2Point(X, Y);
      FControl := FindObject;
      If Assigned(FControl) Then
      Begin
         If Align In [vaLeft, vaMostLeft, vaRight, vaMostRight] Then
         Begin
            FMaxSize := Parent.Visual.Width - FMinSize - Parent.Visual.Margins.left - Parent.Visual.Margins.right;
            For I := 0 To Parent.ChildrenCount - 1 Do
            Begin
               If Not Parent.Children[I].IsVisual Then
                  Continue;
               With Parent.Children[I].Visual Do
                  If (Align In [vaLeft, vaRight, vaMostLeft, vaMostRight]) Then
                     FMaxSize := FMaxSize - Width - Padding.Left - Padding.Right;
            End;
            FMaxSize := FMaxSize + FControl.Width;
         End Else Begin
            FMaxSize := Parent.Visual.Height - FMinSize - Parent.Visual.Margins.top - Parent.Visual.Margins.bottom;
            For I := 0 To Parent.ChildrenCount - 1 Do
            Begin
               If Not Parent.Children[I].IsVisual Then
                  Continue;
               With Parent.Children[I].Visual Do
                  If Align In [vaTop, vaBottom] Then
                     FMaxSize := FMaxSize - Height - Padding.top - Padding.bottom;
            End;
            FMaxSize := FMaxSize + FControl.Height;
         End;
         UpdateSize(X, Y);
      End;
   End;
End;

Procedure TD2Splitter.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   NewSize, Split: Single;
Begin
   Inherited;
   If FPressed And Assigned(FControl) Then
   Begin
      CalcSplitSize(X, Y, NewSize, Split);
      If DoCanResize(NewSize) Then
      Begin
         FNewSize := NewSize;
         FSplit := Split;
         UpdateControlSize;
      End;
   End;
End;

Procedure TD2Splitter.UpdateControlSize;
Begin
   If FNewSize <> FOldSize Then
   Begin
      Case Align Of
         vaLeft, vaMostLeft: FControl.Width := FNewSize;
         vaTop: FControl.Height := FNewSize;
         vaRight, vaMostRight:
         Begin
            FControl.Position.X := FControl.Position.X + (FControl.Width - FNewSize);
            FControl.Width := FNewSize;
         End;
         vaBottom:
         Begin
            FControl.Position.Y := FControl.Position.Y + (FControl.Height - FNewSize);
            FControl.Height := FNewSize;
         End;
      End;

      FOldSize := FNewSize;
   End;
End;

Procedure TD2Splitter.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   FPressed := False;
   FControl := nil;
End;

Procedure TD2Splitter.SetShowGrip(Const Value: Boolean);
Begin
   If FShowGrip <> Value Then
   Begin
      FShowGrip := Value;
      ApplyStyle;
   End;
End;

//=============== TD2Thumb ========================================

Constructor TD2Thumb.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
   AutoCapture := True;
End;

Destructor TD2Thumb.Destroy;
Begin
   Inherited;
End;

Procedure TD2Thumb.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If (Button = mbLeft) And Enabled Then
   Begin
      FPressed := True;
      FDownOffset := d2Point(X, Y);

      If FTrack <> nil Then
         FTrack.SetFocus;
   End;
End;

Procedure TD2Thumb.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   P: TD2Point;
Begin
   Inherited;
   If FPressed And (FTrack <> nil) And (Enabled) And (Parent <> nil) And (Parent.IsVisual) Then
   Begin
      If FTrack.Orientation = d2Horizontal Then
      Begin
         P := FTrack.AbsoluteToLocal(LocalToAbsolute(d2Point(X, 0)));
         If FTrack.ViewportSize = 0 Then
            FTrack.Value := FTrack.Min + (((P.X) / FTrack.Width) * (FTrack.FMax - FTrack.FMin))
         Else
            FTrack.Value := FTrack.Min + (((P.X - FDownOffset.X) / FTrack.Width) * (FTrack.FMax - FTrack.FMin));
      End Else Begin
         P := FTrack.AbsoluteToLocal(LocalToAbsolute(d2Point(0, Y)));
         If FTrack.ViewportSize = 0 Then
            FTrack.Value := FTrack.Min + (((P.Y) / FTrack.Height) * (FTrack.FMax - FTrack.FMin))
         Else
            FTrack.Value := FTrack.Min + (((P.Y - FDownOffset.Y) / FTrack.Height) * (FTrack.FMax - FTrack.FMin));
      End;
   End;
End;

Procedure TD2Thumb.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   V: Single;
Begin
   Inherited;
   If FPressed Then
   Begin
      If Not FTrack.Tracking And Assigned(FTrack.FOnChange) Then
      Begin
         FTrack.FTracking := True;
         V := FTrack.FValue;
         FTrack.FValue := $FFFF;
         FTrack.Value := V;
         FTrack.FTracking := False;
      End;
      FPressed := False;
   End;
End;


//=============== TD2Expander =======================================================

Constructor TD2Expander.Create(AOwner: TComponent);
Begin
   Inherited;
   Width := 130;
   Height := 130;

   FAutoTranslate := True;

   FIsExpanded := True;
   FIsChecked := True;

   FContent := TD2Content.Create(Self);
   FContent.Parent := Self;
   FContent.ClipChildren := False;
   FContent.HitTest := False;
   FContent.Locked := True;
   FContent.Stored := False;
   FContent.Padding.Top := 25;
   FContent.Width := Width;
   FContent.Height := Height;
End;

Procedure TD2Expander.AddObject(AObject: TD2Object);
Begin
   If (FContent <> nil) And (AObject <> FContent) And (AObject <> FResourceLink) Then
   Begin
      FContent.AddObject(AObject);
   End Else
      Inherited;
End;

Procedure TD2Expander.ApplyStyle;
Var
   O: TD2Object;
Begin
   Inherited;
   O := FindResource('checkbox');
   If (O <> nil) And (O Is TD2CheckBox) Then
   Begin
      FCheck := TD2CheckBox(O);
      FCheck.Visible := FShowCheck;
      FCheck.IsChecked := FIsChecked;
      FCheck.OnChange := DoCheckChange;
   End;
   O := FindResource('button');
   If (O <> nil) And (O Is TD2CustomButton) Then
   Begin
      FButton := TD2CustomButton(O);
      FButton.OnClick := DoButtonClick;
      FButton.ApplyResource;
      FButton.StartTriggerAnimation(Self, 'IsExpanded');
      FButton.CanFocused := False;
   End;
   StartTriggerAnimation(Self, 'IsExpanded');
End;

Procedure TD2Expander.FreeStyle;
Begin
   Inherited;
   FCheck := nil;
   FButton := nil;
End;

Destructor TD2Expander.Destroy;
Begin
   Inherited;
End;

Procedure TD2Expander.DoButtonClick(Sender: TObject);
Begin
   IsExpanded := Not FIsExpanded;
End;

Procedure TD2Expander.DefineProperties(Filer: TFiler);
Begin
   Inherited;
   Filer.DefineProperty('ContentSize', ReadContentSize, WriteContentSize, True);
End;

Procedure TD2Expander.ReadContentSize(Reader: TReader);
Begin
   If FContent <> nil Then
      FContent.Height := d2StrToFloat(Reader.ReadString);
End;

Procedure TD2Expander.WriteContentSize(Writer: TWriter);
Begin
   If FContent <> nil Then
      Writer.WriteString(d2FloatToStr(FContent.Height));
End;

Procedure TD2Expander.Realign;
Begin
   Inherited;
   If csLoading In ComponentState Then
      Exit;
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;

   If (FContent <> nil) And (IsExpanded) Then
   Begin
      FContent.Position.X := 0;
      FContent.Position.Y := FContent.Padding.Top;
      FContent.Width := Width;
      FContent.Height := Height - FContent.Padding.Top;
   End;
   FDisableAlign := False;
End;

Procedure TD2Expander.SetIsExpanded(Const Value: Boolean);
Begin
   If FIsExpanded <> Value Then
   Begin
      If FResourceLink = nil Then
         ApplyResource;
      FIsExpanded := Value;
      If FIsExpanded Then
      Begin
         FContent.Visible := FIsExpanded;
         If FButton <> nil Then
            Height := FButton.Height + FContent.Height;
         Repaint;
      End Else Begin
         Repaint;
         FContent.Visible := FIsExpanded;
         If FButton <> nil Then
            Height := FButton.Height;
      End;
      StartTriggerAnimation(Self, 'IsExpanded');
      If FButton <> nil Then
         FButton.StartTriggerAnimation(Self, 'IsExpanded');
   End;
End;

Procedure TD2Expander.DoCheckChange(Sender: TObject);
Begin
   If (FCheck <> nil) Then
   Begin
      FIsChecked := FCheck.IsChecked;
      FContent.Enabled := FIsChecked;
      If Assigned(OnCheckChange) Then
         OnCheckChange(Self);
   End;
End;

Procedure TD2Expander.SetIsChecked(Const Value: Boolean);
Begin
   If FIsChecked <> Value Then
   Begin
      FIsChecked := Value;
      FContent.Enabled := FIsChecked;
      If FCheck <> nil Then
         FCheck.IsChecked := FIsChecked;
   End;
End;

Procedure TD2Expander.SetShowCheck(Const Value: Boolean);
Begin
   If FShowCheck <> Value Then
   Begin
      FShowCheck := Value;
      If FCheck <> nil Then
         FCheck.Visible := FShowCheck;
   End;
End;

Procedure TD2Expander.DesignClick;
Begin
   Inherited;
   IsExpanded := Not IsExpanded;
End;


//============  TD2GradientEdit ===========================================

Constructor TD2GradientEdit.Create(AOwner: TComponent);
Begin
   Inherited;
   FGradient := TD2Gradient.Create;
   FGradient.OnChanged := DoChanged;
   Width := 200;
   Height := 20;
   AutoCapture := True;
End;

Destructor TD2GradientEdit.Destroy;
Begin
   If FBitmap <> nil Then
      FreeAndNil(FBitmap);
   FGradient.Free;
   Inherited;
End;

Function TD2GradientEdit.GetPointRect(Const Point: Integer): TD2Rect;
Begin
   If (Point >= 0) And (Point < FGradient.Points.Count) Then
      With FGradient Do
      Begin
         Result := d2Rect(0 + cnColorPickSize + (Points[Point].Offset * (Width - ((0 + cnColorPickSize) * 2))), Height - 0 - cnColorPickSize, 0 + cnColorPickSize + (Points[Point].Offset * (Width - ((0 + cnColorPickSize) * 2))), Height - 0);
         d2InflateRect(Result, cnColorPickSize / 2, 0);
      End Else
      Result := d2Rect(0, 0, 0, 0);
End;

Procedure TD2GradientEdit.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   NewOffset: Single;
   NewColor: TD2Color;
   i: Integer;
Begin
   Inherited;
   FMoving := False;
   If Button = mbLeft Then
   Begin
      For i := 0 To FGradient.Points.Count - 1 Do
         If d2PtInRect(d2Point(X, Y), GetPointRect(i)) Then
         Begin
            CurrentPoint := i;
            If Assigned(OnSelectPoint) Then
               OnSelectPoint(Self);
            FMoving := True;
            Repaint;
            Exit;
         End;

      If (Y > 0) And (Y < Height - 0 - cnColorPickSize) Then
      Begin
         NewOffset := ((X - 0 - cnColorPickSize) / (Width - ((0 + cnColorPickSize) * 2)));
         If NewOffset < 0 Then
            NewOffset := 0;
         If NewOffset > 1 Then
            NewOffset := 1;
         NewColor := FGradient.InterpolateColor(NewOffset);
         For i := 1 To FGradient.Points.Count - 1 Do
            If NewOffset < FGradient.Points[i].Offset Then
               With TD2GradientPoint(FGradient.Points.Add) Do
               Begin
                  Index := i;
                  CurrentPoint := Index;
                  IntColor := NewColor;
                  Offset := NewOffset;
                  Repaint;
                  DoChanged(Self);
                  Break;
               End;
      End;
   End;
End;

Procedure TD2GradientEdit.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If ssLeft In Shift Then
   Begin
      If FMoving Then
      Begin
         FCurrentPointInvisible := ((Y < -10) Or (Y > Height + 10)) And (FGradient.Points.Count > 1) And (CurrentPoint <> 0) And (CurrentPoint <> FGradient.Points.Count - 1);

         FGradient.Points[CurrentPoint].Offset := ((X - 0 - cnColorPickSize) / (Width - ((0 + cnColorPickSize) * 2)));
         If FGradient.Points[CurrentPoint].Offset < 0 Then
            FGradient.Points[CurrentPoint].Offset := 0;
         If FGradient.Points[CurrentPoint].Offset > 1 Then
            FGradient.Points[CurrentPoint].Offset := 1;

         If CurrentPoint < FGradient.Points.Count - 1 Then
            If FGradient.Points[CurrentPoint].Offset > FGradient.Points[CurrentPoint + 1].Offset Then
            Begin
               FGradient.Points[CurrentPoint].Index := FGradient.Points[CurrentPoint].Index + 1;
               CurrentPoint := CurrentPoint + 1;
            End;

         If CurrentPoint > 0 Then
            If FGradient.Points[CurrentPoint].Offset < FGradient.Points[CurrentPoint - 1].Offset Then
            Begin
               FGradient.Points[CurrentPoint].Index := FGradient.Points[CurrentPoint].Index - 1;
               CurrentPoint := CurrentPoint - 1;
            End;
         Repaint;
         DoChanged(Self);
      End;
   End;
End;

Procedure TD2GradientEdit.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   FCurrentPointInvisible := False;
   If FMoving Then
   Begin
      If (Y > Height + 10) And (FGradient.Points.Count > 2) Then
      Begin
         FGradient.Points.Delete(CurrentPoint);
         CurrentPoint := CurrentPoint - 1;
         If CurrentPoint < 0 Then
            CurrentPoint := 0;
         Repaint;
         DoChanged(Self);
         FMoving := False;
         Exit;
      End;
   End;
   FMoving := False;
End;

Procedure TD2GradientEdit.Paint;
Var
   i, j: Integer;
   R: TD2Rect;
   SaveIndex: Integer;
Begin
   If FBitmap = nil Then
   Begin
      FBitmap := TD2Bitmap.Create(Trunc(Width - (0 * 2)), Trunc(Height - (0 * 2) - cnColorPickSize));
   End;
   If FBitmap <> nil Then
   Begin
      For j := 0 To FBitmap.Height - 1 Do
      Begin
         For i := 0 To FBitmap.Width - 1 Do
         Begin
            FBitmap.Scanline[j][i] := d2CorrectColor(FGradient.InterpolateColor(i / FBitmap.Width));
            {$ifdef FPC_BIG_ENDIAN}
        ReverseBytes(@FBitmap.Scanline[j][i],4);
   {$endif}
         End;
      End;
   End;


   R := d2Rect(0 + cnColorPickSize, 0, Width - 0 - cnColorPickSize, Height - 0 - cnColorPickSize);
   SaveIndex := Canvas.SaveCanvas;
   Canvas.IntersectClipRect(R);
   Canvas.Stroke.Style := d2BrushNone;
   Canvas.Fill.Style := d2BrushSolid;
   Canvas.Fill.SolidColor := $FFFFFFFF;
   Canvas.FillRect(R, 0, 0, AllCorners, AbsoluteOpacity);
   Canvas.Fill.SolidColor := $FFD3D3D3;
   For i := 0 To Trunc(Width / 10) + 1 Do
      For j := 0 To Trunc(Height / 10) + 1 Do
      Begin
         If Odd(i + j) Then
         Begin
            Canvas.FillRect(d2Rect(i * 10, j * 10, (i + 1) * 10, (j + 1) * 10), 0, 0, AllCorners, AbsoluteOpacity);
         End;
      End;
   Canvas.RestoreCanvas(SaveIndex);

   Canvas.Stroke.Style := d2BrushSolid;
   Canvas.StrokeThickness := 0;
   If FBitmap <> nil Then
   Begin
      Canvas.DrawBitmap(FBitmap, d2Rect(0, 0, FBitmap.Width, FBitmap.Height),
         d2Rect(0 + cnColorPickSize, 0, Width - 0 - cnColorPickSize, Height - 0 - cnColorPickSize), AbsoluteOpacity);
   End;

   For i := 0 To FGradient.Points.Count - 1 Do
   Begin
      If FCurrentPointInvisible And (i = CurrentPoint) Then
         Continue;
      R := GetPointRect(i);
      d2InflateRect(R, -1, -1);
      Canvas.Stroke.SolidColor := $FF757575;
      Canvas.Fill.SolidColor := FGradient.Points[i].IntColor;
      Canvas.FillEllipse(R, AbsoluteOpacity);
      Canvas.DrawEllipse(R, AbsoluteOpacity);

      If CurrentPoint = i Then
      Begin
         d2InflateRect(R, 1, 1);
         Canvas.Stroke.SolidColor := $FF000000;
         Canvas.Stroke.SolidColor := $FFFFFFFF;
         Canvas.DrawEllipse(R, AbsoluteOpacity);
      End;
   End;
End;

Procedure TD2GradientEdit.SetGradient(Const Value: TD2Gradient);
Begin
   FGradient.Assign(Value);
End;

Procedure TD2GradientEdit.DoChanged(Sender: TObject);
Begin
   If Assigned(FOnChange) Then
      FOnChange(Self);
   UpdateGradient;
End;

Procedure TD2GradientEdit.SetCurrentPoint(Const Value: Integer);
Begin
   If FCurrentPoint <> Value Then
   Begin
      FCurrentPoint := Value;
      If Assigned(OnSelectPoint) Then
         OnSelectPoint(Self);
      If (FColorPicker <> nil) And (CurrentPoint >= 0) Then
         FColorPicker.Color := Gradient.Points[CurrentPoint].IntColor;
   End;
End;

Procedure TD2GradientEdit.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FColorPicker) Then
      ColorPicker := nil;
End;

Procedure TD2GradientEdit.SetColorPicker(Const Value: TD2ColorPicker);
Begin
   FColorPicker := Value;
   If (FColorPicker <> nil) And (CurrentPoint >= 0) Then
      FColorPicker.Color := Gradient.Points[CurrentPoint].IntColor;
End;

Procedure TD2GradientEdit.UpdateGradient;
Begin
   If (FColorPicker <> nil) And (CurrentPoint >= 0) Then
      FColorPicker.Color := Gradient.Points[CurrentPoint].IntColor;
End;


//================= TD2ColorQuad ==============================

Constructor TD2ColorQuad.Create(AOwner: TComponent);
Begin
   Inherited;
   FAlpha := 1;
   AutoCapture := True;
End;

Destructor TD2ColorQuad.Destroy;
Begin
   If (FColorBitmap <> nil) Then
      FColorBitmap.Free;
   Inherited;
End;

Function TD2ColorQuad.GetAbsoluteRect: TD2Rect;
Begin
   Result := Inherited GetAbsoluteRect;
   d2InflateRect(Result, cnColorPickSize + 1, cnColorPickSize + 1);
End;

Function TD2ColorQuad.pointInObject(X, Y: Single): Boolean;
Var
   P: TD2Point;
Begin
   Result := False;
   P := AbsoluteToLocal(d2Point(X, Y));
   If (P.X > -cnColorPickSize / 2) And (P.X < Width + cnColorPickSize / 2) And (P.Y > -cnColorPickSize / 2) And (P.Y < Height + cnColorPickSize / 2) Then
   Begin
      Result := True;
   End;
End;

Procedure TD2ColorQuad.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If FPressed Then
   Begin
      If Height <> 0 Then
         Lum := 1 - ((Y) / (Height));
      If Width <> 0 Then
         Sat := ((X) / (Width));
   End;
End;

Procedure TD2ColorQuad.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   If FPressed Then
      MouseMove([ssLeft], X, Y, 0, 0);
   Inherited;
End;

Procedure TD2ColorQuad.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FColorBox) Then
      ColorBox := nil;
End;

Procedure TD2ColorQuad.Paint;
Var
   i, j: Integer;
   R: TD2Rect;
Begin
   If FColorBitmap = nil Then
   Begin
      FColorBitmap := TD2Bitmap.Create(Trunc(Width), Trunc(Height));
      If FColorBitmap <> nil Then
      Begin
         For i := 0 To FColorBitmap.Width - 1 Do
         Begin
            For j := 0 To FColorBitmap.Height - 1 Do
            Begin
               FColorBitmap.Scanline[j][i] := d2CorrectColor(d2HSLtoRGB(FHue, i / FColorBitmap.Width, (1 - (j / FColorBitmap.Height))));
               {$ifdef FPC_BIG_ENDIAN}
          ReverseBytes(@FColorBitmap.Scanline[j][i],4);
   {$endif}
            End;
         End;
      End;
   End;
   If FColorBitmap <> nil Then
      Canvas.DrawBitmap(FColorBitmap, d2Rect(0, 0, FColorBitmap.Width, FColorBitmap.Height),
         d2Rect(0, 0, Width, Height), AbsoluteOpacity);

   R := d2Rect(FSat * (Width), (1 - FLum) * (Height), FSat * (Width), (1 - FLum) * (Height));
   d2InflateRect(R, cnColorPickSize / 2, cnColorPickSize / 2);
   Canvas.Stroke.Style := d2BrushSolid;
   Canvas.StrokeThickness := 1;
   Canvas.Stroke.SolidColor := $FF000000;
   Canvas.DrawEllipse(R, AbsoluteOpacity);
   d2InflateRect(R, -1, -1);
   Canvas.Stroke.SolidColor := $FFFFFFFF;
   Canvas.DrawEllipse(R, AbsoluteOpacity);
   d2InflateRect(R, -1, -1);
   Canvas.Fill.Style := d2BrushSolid;
   Canvas.Fill.SolidColor := d2HSLtoRGB(Hue, sat, Lum);
   Canvas.FillEllipse(R, AbsoluteOpacity);
End;

Procedure TD2ColorQuad.SetAlpha(Const Value: Single);
Begin
   If FAlpha <> Value Then
   Begin
      FAlpha := Value;
      If FAlpha < 0 Then
         FAlpha := 0;
      If FAlpha > 1 Then
         FAlpha := 1;
      If FColorBox <> nil Then
         FColorBox.Color := d2HSLtoRGB(Hue, Sat, Lum) And $FFFFFF Or (Round(Alpha * $FF) Shl 24);
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Procedure TD2ColorQuad.SetHue(Const Value: Single);
Begin
   If FHue <> Value Then
   Begin
      FHue := Value;
      If FHue < 0 Then
         FHue := 0;
      If FHue > 1 Then
         FHue := 1;
      If FColorBitmap <> nil Then
         FreeAndNil(FColorBitmap);
      If FColorBox <> nil Then
         FColorBox.Color := d2HSLtoRGB(Hue, Sat, Lum) And $FFFFFF Or (Round(Alpha * $FF) Shl 24);
      If Assigned(FOnChange) Then
         FOnChange(Self);
      Repaint;
   End;
End;

Procedure TD2ColorQuad.SetLum(Const Value: Single);
Begin
   If FLum <> Value Then
   Begin
      FLum := Value;
      If FLum < 0 Then
         FLum := 0;
      If FLum > 1 Then
         FLum := 1;
      If FColorBox <> nil Then
         FColorBox.Color := d2HSLtoRGB(Hue, Sat, Lum) And $FFFFFF Or (Round(Alpha * $FF) Shl 24);
      If Assigned(FOnChange) Then
         FOnChange(Self);
      Repaint;
   End;
End;

Procedure TD2ColorQuad.SetSat(Const Value: Single);
Begin
   If FSat <> Value Then
   Begin
      FSat := Value;
      If FSat < 0 Then
         FSat := 0;
      If FSat > 1 Then
         FSat := 1;
      If FColorBox <> nil Then
         FColorBox.Color := d2HSLtoRGB(Hue, Sat, Lum) And $FFFFFF Or (Round(Alpha * $FF) Shl 24);
      If Assigned(FOnChange) Then
         FOnChange(Self);
      Repaint;
   End;
End;

Procedure TD2ColorQuad.SetColorBox(Const Value: TD2ColorBox);
Begin
   If FColorBox <> Value Then
   Begin
      FColorBox := Value;
      If FColorBox <> nil Then
         FColorBox.Color := d2HSLtoRGB(Hue, Sat, Lum) And $FFFFFF Or (Round(Alpha * $FF) Shl 24);
   End;
End;

//============  TD2ColorPicker =================================================

Constructor TD2ColorPicker.Create(AOwner: TComponent);
Begin
   Inherited;
   AutoCapture := True;
End;

Destructor TD2ColorPicker.Destroy;
Begin
   If (FHueBitmap <> nil) Then
      FreeAndNil(FHueBitmap);
   Inherited;
End;

Procedure TD2ColorPicker.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FColorQuad) Then
      ColorQuad := nil;
End;

Function TD2ColorPicker.GetAbsoluteRect: TD2Rect;
Begin
   Result := Inherited GetAbsoluteRect;
   d2InflateRect(Result, 0, cnColorPickSize / 2);
End;

Function TD2ColorPicker.pointInObject(X, Y: Single): Boolean;
Var
   P: TD2Point;
Begin
   Result := False;
   P := AbsoluteToLocal(d2Point(X, Y));
   If (P.X > 0) And (P.X < Width) And (P.Y > -cnColorPickSize / 2) And (P.Y < Height + cnColorPickSize / 2) Then
   Begin
      Result := True;
   End;
End;

Procedure TD2ColorPicker.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If FPressed Then
   Begin
      If Height <> 0 Then
         Hue := ((Y) / (Height));
   End;
End;

Procedure TD2ColorPicker.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   If FPressed Then
      MouseMove([ssLeft], X, Y, 0, 0);
   Inherited;
End;

Procedure TD2ColorPicker.Paint;
Var
   i, j: Integer;
   R: TD2Rect;
Begin
   If FHueBitmap = nil Then
   Begin
      FHueBitmap := TD2Bitmap.Create(Trunc(Width), Trunc(Height));
      If FHueBitmap <> nil Then
      Begin
         For j := 0 To FHueBitmap.Height - 1 Do
         Begin
            For i := 0 To FHueBitmap.Width - 1 Do
            Begin
               FHueBitmap.Scanline[j][i] := d2CorrectColor(d2HSLtoRGB(j / FHueBitmap.Height, 0.9, 0.5));
               {$ifdef FPC_BIG_ENDIAN}
          ReverseBytes(@FHueBitmap.Scanline[j][i],4);
   {$endif}
            End;
         End;
      End;
   End;

   If FHueBitmap <> nil Then
      Canvas.DrawBitmap(FHueBitmap, d2Rect(0, 0, FHueBitmap.Width, FHueBitmap.Height),
         d2Rect(0, 0, Width, Height), AbsoluteOpacity);


   R := d2Rect(Width / 2, FHue * (Height), Width / 2, FHue * (Height));
   d2InflateRect(R, cnColorPickSize / 2, cnColorPickSize / 2);

   Canvas.Stroke.Style := d2BrushSolid;
   Canvas.StrokeThickness := 1;
   Canvas.Stroke.SolidColor := $FF000000;
   Canvas.DrawEllipse(R, AbsoluteOpacity);
   d2InflateRect(R, -1, -1);
   Canvas.Stroke.SolidColor := $FFFFFFFF;
   Canvas.DrawEllipse(R, AbsoluteOpacity);
   d2InflateRect(R, -1, -1);
   Canvas.Fill.Style := d2BrushSolid;
   Canvas.Fill.SolidColor := d2HSLtoRGB(Hue, 0.9, 0.5);
   Canvas.FillEllipse(R, AbsoluteOpacity);
End;

Function TD2ColorPicker.GetColor: TD2Color;
Begin
   Result := d2HSLToRGB(Hue, 1, 0.5);
End;

Procedure TD2ColorPicker.SetColor(Const Value: TD2Color);
Var
   H, S, L: Single;
Begin
   d2RGBtoHSL(Value, H, S, L);
   Hue := H;
   If FColorQuad <> nil Then
   Begin
      FColorQuad.Alpha := TD2ColorRec(Value).A / $FF;
      FColorQuad.Hue := H;
      FColorQuad.Sat := S;
      FColorQuad.Lum := L;
   End;
End;

Procedure TD2ColorPicker.SetHue(Const Value: Single);
Begin
   If FHue <> Value Then
   Begin
      FHue := Value;
      If FHue < 0 Then
         FHue := 0;
      If FHue > 1 Then
         FHue := 1;
      If FColorQuad <> nil Then
         FColorQuad.Hue := FHue;
      Repaint;
   End;
End;

//=============== TD2Resources ================================== ct2222

Const
   cnResFileId = 1000;
   cnResFileIdStr = 'ORCA 2D Style File';

Constructor TD2Resources.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := TStringList.Create;
   TStringList(FResource).OnChange := DoResourceChanged;
   FSceneList := TList.Create;
End;

Destructor TD2Resources.Destroy;
Begin
   If FRoot <> nil Then
      FreeAndNil(FRoot);
   If FSceneList <> nil Then
      FreeAndNil(FSceneList);
   If FResource <> nil Then
      FreeAndNil(FResource);
   Inherited;
End;

Procedure TD2Resources.Loaded;
Var
   ss: String;
Begin
   Inherited;
   If FFileName = '' Then
      Exit;

   ss := FFileName;

   If FileExists(ss) = False Then
   Begin
      ss := ExtractFilePath(ParamStr(0)) + FFileName;
      If FileExists(ss) = False Then
         exit;
   End;

   If LoadFromFile(ss) = False Then
      LoadFromTextFile(ss);
End;

Procedure TD2Resources.DefineProperties(Filer: TFiler);
Begin
   Inherited;
   Filer.DefineBinaryProperty('ResourcesBin', ReadResources, WriteResources, FRoot <> nil);
End;

Procedure TD2Resources.ReadResources(Stream: TStream);
Begin
   If (FRoot <> nil) Then
      FreeAndNil(FRoot);

   FRoot := CreateD2ObjectFromBinStream(nil, Stream);

   If FRoot = nil Then
      exit;

   If FRoot.IsVisual Then
      FRoot.Visual.UpdateDesignHide(False);
   UpdateScenes;
End;

Procedure TD2Resources.WriteResources(Stream: TStream);
Begin
   If FRoot = nil Then
      exit;
   FRoot.SaveToBinStream(Stream);
End;

Function TD2Resources.SaveToFile(Const aFilename: String): Boolean;
Var
   xM: TMemoryStream;
   xVer: Cardinal;
   xss: Array[0..50] Of Char;
Begin
   Result := False;
   If FRoot = nil Then
      exit;
   If aFilename = '' Then
      exit;

   xM := TMemoryStream.Create;
   Try
      xM.Position := 0;

      xss := cnResFileIdStr;
      xVer := cnResFileId;

      xM.Write(xss, Sizeof(xss));             // File ID Str
      xM.Write(xVer, Sizeof(Cardinal));        // File ID
      FRoot.SaveToBinStream(xM);
      xM.SaveToFile(aFilename);
      Result := True;
   Finally
      xM.Free;
   End;
End;

Function TD2Resources.LoadFromFile(Const aFilename: String): Boolean;
Var
   xM: TMemoryStream;
   xRoot: TD2Object;
   xss: Array[0..50] Of Char;
   xVer: Cardinal;
   xM2: TMemoryStream;
   xSaveChanged: TNotifyEvent;
Begin
   Result := False;

   If FileExists(aFilename) = False Then
      exit;

   xRoot := nil;
   xM2 := nil;

   xM := TMemoryStream.Create;
   Try
      xM.LoadFromFile(aFilename);
      xM.Position := 0;

      xM.Read(xss, Sizeof(xss));             // File ID Str
      xM.Read(xVer, Sizeof(Cardinal));        // File ID

      If xVer = cnResFileId Then
      Begin
         xRoot := CreateD2ObjectFromBinStream(nil, xM);

         If xRoot <> nil Then
         Begin
            xM2 := TMemoryStream.Create;
            xRoot.SaveToStream(xM2);
            xM2.Position := 0;
            xSaveChanged := TStringList(FResource).OnChange;
            TStringList(FResource).OnChange := nil;
            TStringList(FResource).LoadFromStream(xM2);
            TStringList(FResource).OnChange := xSaveChanged;

            DoResourceChanged(nil);
            Result := True;
         End;
      End;

   Finally
      xM.Free;
      If xM2 <> nil Then
         xM2.Free;
      If xRoot <> nil Then
         xRoot.Free;
   End;
End;


Function TD2Resources.SaveToTextFile(Const aFilename: String): Boolean;
Begin
   Result := False;
   If aFilename = '' Then
      exit;
   If FileExists(aFilename) = False Then
      exit;

   FResource.SaveToFile(aFilename);
   Result := True;
End;

Function TD2Resources.LoadFromTextFile(Const aFilename: String): Boolean;
Begin
   Result := False;
   If aFilename = '' Then
      exit;
   If FileExists(aFilename) = False Then
      exit;

   FResource.LoadFromFile(aFilename);
   Result := True;
End;

Procedure TD2Resources.FillStrings;
Var
   xM: TMemoryStream;
   xSaveChanged: TNotifyEvent;
Begin
   If FRoot = nil Then
      exit;

   xM := TMemoryStream.Create;
   Try

      FRoot.SaveToStream(xM);
      xM.Position := 0;
      xSaveChanged := TStringList(FResource).OnChange;
      TStringList(FResource).OnChange := nil;
      TStringList(FResource).LoadFromStream(xM);
      TStringList(FResource).OnChange := xSaveChanged;

   Finally
      xM.Free;
   End;
End;

Procedure TD2Resources.UpdateScenes;
Var
   i: Integer;
Begin
   For i := 0 To FSceneList.Count - 1 Do
      Id2Scene(FSceneList[i]).UpdateResource;
End;

Procedure TD2Resources.DoResourceChanged(Sender: TObject);
Var
   S: TStream;
Begin
   If (FRoot <> nil) Then
   Begin
      FreeAndNil(FRoot);
   End;

   S := TMemoryStream.Create;
   Try
      TStringList(FResource).SaveToStream(S);
      If S.Position > 0 Then
      Begin
         S.Position := 0;
         FRoot := CreateD2ObjectFromStream(nil, S);
         If FRoot.IsVisual Then
            FRoot.Visual.UpdateDesignHide(False);
      End;
   Finally
      S.Free;
   End;
   UpdateScenes;
End;

Procedure TD2Resources.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
End;

Procedure TD2Resources.SetFileName(Const Value: String);
Begin
   If FFileName <> Value Then
   Begin
      FFileName := Value;
      If Not (csLoading In ComponentState) Then
      Begin
         If LoadFromFile(FFileName) = False Then
            LoadFromTextFile(FFileName);
      End;
   End;
End;

Procedure TD2Resources.SetResource(Const Value: TStrings);
Begin
   FResource.Assign(Value);
End;

Procedure TD2Resources.AddSceneUpdater(Const Scene: Id2Scene);
Begin
   FSceneList.Add(Pointer(Scene));
End;

Procedure TD2Resources.RemoveSceneUpdater(Const Scene: Id2Scene);
Begin
   If FSceneList <> nil Then
      FSceneList.Remove(Pointer(Scene));
End;

//=============== TD2Lang ========================================

Constructor TD2Lang.Create(AOwner: TComponent);
Begin
   Inherited;
   FOriginal := TD2WideStringList.Create;
   FResources := TD2WideStringList.Create;
   FAutoSelect := True;
   FStoreInForm := True;
End;

Destructor TD2Lang.Destroy;
Var
   i: Integer;
Begin
   For i := 0 To FResources.Count - 1 Do
      TD2WideStrings(FResources.Objects[i]).Free;
   FResources.Free;
   FOriginal.Free;
   Inherited;
End;

Procedure TD2Lang.Loaded;
Var
   L: String;
Begin
   Inherited;
   If (FFileName <> '') And (FileExists(ExtractFileName(ParamStr(0)) + FFileName)) Then
      LoadFromFile(ExtractFileName(ParamStr(0)) + FFileName);
   If FAutoSelect Then
      GetLanguageIDs(L, FLang);
   If FLang <> '' Then
      LoadLangFromStrings(LangStr[FLang]);
End;

Procedure TD2Lang.DefineProperties(Filer: TFiler);
Begin
   Inherited;
   Filer.DefineBinaryProperty('ResourcesBin', ReadResources, WriteResources, StoreInForm And (FResources.Count > 0));
End;

Procedure TD2Lang.ReadResources(Stream: TStream);
Var
   Len: Cardinal;
   i: Integer;
   N: WideString;
   Str: TD2WideStrings;
Begin
   FOriginal.Text := ReadString(Stream);
   Stream.Read(Len, 4);
   For i := 0 To Len - 1 Do
   Begin
      N := ReadString(Stream);
      Str := TD2WideStringList.Create;
      Str.Sorted := True;
      TD2WideStringList(Str).CaseSensitive := True;
      Str.Text := ReadString(Stream);
      FResources.AddObject(N, Str);
   End;
End;

Procedure TD2Lang.WriteResources(Stream: TStream);
Var
   Len: Cardinal;
   i: Integer;
Begin
   WriteString(Stream, FOriginal.Text);
   Len := FResources.Count;
   Stream.Write(Len, 4);
   For i := 0 To Len - 1 Do
   Begin
      WriteString(Stream, FResources[i]);
      WriteString(Stream, TD2WideStrings(FResources.Objects[i]).Text);
   End;
End;

Procedure TD2Lang.LoadFromFile(AFileName: String);
Var
   S: TFileStream;
Begin
   If FileExists(AFileName) Then
   Begin
      S := TFileStream.Create(AFileName, fmOpenRead);
      ReadResources(S);
      S.Free;
   End;
End;

Procedure TD2Lang.SaveToFile(AFileName: String);
Var
   S: TFileStream;
Begin
   S := TFileStream.Create(AFileName, fmCreate);
   WriteResources(S);
   S.Free;
End;

Procedure TD2Lang.AddLang(AName: WideString);
Var
   Idx: Integer;
   Str: TD2WideStrings;
Begin
   Idx := FResources.IndexOf(AName);
   If Idx < 0 Then
   Begin
      Str := TD2WideStringList.Create;
      Str.Sorted := True;
      TD2WideStringList(Str).CaseSensitive := True;
      FResources.AddObject(AName, Str);
   End;
End;

Function TD2Lang.GetLangStr(Index: WideString): TD2WideStrings;
Var
   Idx: Integer;
Begin
   Idx := FResources.IndexOf(Index);
   If Idx >= 0 Then
      Result := TD2WideStrings(FResources.Objects[Idx])
   Else
      Result := nil;
End;

Procedure TD2Lang.SetLang(Const Value: String);
Begin
   FLang := Value;
   If Not (csLoading In ComponentState) Then
   Begin
      If FLang = 'en' Then
         ResetLang
      Else
         LoadLangFromStrings(LangStr[FLang]);
   End;
End;

//=============== TD2ControlActionLink =================================================

Procedure TD2ControlActionLink.AssignClient(AClient: TObject);
Begin
   FClient := AClient As TD2Control;
End;

Function TD2ControlActionLink.DoShowHint(Var HintStr: String): Boolean;
Begin
   Result := True;
   If Action Is TCustomAction Then
   Begin
      If TCustomAction(Action).DoHint(HintStr) And Application.HintShortCuts And (TCustomAction(Action).ShortCut <> scNone) Then
      Begin
{      if HintStr <> '' then
        HintStr:=Format('%s (%s)',[HintStr,ShortCutToText(TCustomAction(Action).ShortCut)]);}
      End;
   End;
End;

Function TD2ControlActionLink.IsCaptionLinked: Boolean;
Begin
   Result := Inherited IsCaptionLinked And (FClient Is TD2TextControl) And (TD2TextControl(FClient).TextW = (Action As TCustomAction).Caption);
End;

Function TD2ControlActionLink.IsEnabledLinked: Boolean;
Begin
   Result := Inherited IsEnabledLinked And (FClient.Enabled = (Action As TCustomAction).Enabled);
End;

Function TD2ControlActionLink.IsHintLinked: Boolean;
Begin
   Result := Inherited IsHintLinked And (FClient.Hint = (Action As TCustomAction).Hint);
End;

Function TD2ControlActionLink.IsVisibleLinked: Boolean;
Begin
   Result := Inherited IsVisibleLinked And (FClient.Visible = (Action As TCustomAction).Visible);
End;

Function TD2ControlActionLink.IsOnExecuteLinked: Boolean;
Begin
   Result := Inherited IsOnExecuteLinked And (@FClient.OnClick = @Action.OnExecute);
End;

Function TD2ControlActionLink.IsHelpLinked: Boolean;
Begin
   Result := Inherited IsHelpLinked And (FClient.HelpContext = TCustomAction(Action).HelpContext) And (FClient.HelpKeyword = TCustomAction(Action).HelpKeyword) And (FClient.HelpType = TCustomAction(Action).HelpType);
End;

Procedure TD2ControlActionLink.SetHelpKeyword(Const Value: String);
Begin
   If IsHelpLinked Then
      FClient.HelpKeyword := Value;
End;

Procedure TD2ControlActionLink.SetHelpContext(Value: THelpContext);
Begin
   If IsHelpLinked Then
      FClient.HelpContext := Value;
End;

Procedure TD2ControlActionLink.SetHelpType(Value: THelpType);
Begin
   If IsHelpLinked Then
      FClient.HelpType := Value;
End;

Procedure TD2ControlActionLink.SetCaption(Const Value: String);
Begin
   If IsCaptionLinked Then
      TD2TextControl(FClient).TextW := Value;
End;

Procedure TD2ControlActionLink.SetEnabled(Value: Boolean);
Begin
   If IsEnabledLinked Then
      FClient.Enabled := Value;
End;

Procedure TD2ControlActionLink.SetHint(Const Value: String);
Begin
   If IsHintLinked Then
      FClient.Hint := Value;
End;

Procedure TD2ControlActionLink.SetVisible(Value: Boolean);
Begin
   If IsVisibleLinked Then
      FClient.Visible := Value;
End;

Procedure TD2ControlActionLink.SetOnExecute(Value: TNotifyEvent);
Begin
   If IsOnExecuteLinked Then
      FClient.OnClick := Value;
End;


//============== TEdtActionStack ===============================

Constructor TEdtActionStack.Create(AOwner: TD2Memo);
Begin
   Inherited Create;
   FOwner := AOwner;
End;

Destructor TEdtActionStack.Destroy;
Var
   TmpItem: PEdtAction;
Begin
   While AtLeast(1) Do
   Begin
      TmpItem := Pop;
      Finalize(TmpItem^);
      FreeMem(TmpItem);
   End;
   Inherited;
End;

Procedure TEdtActionStack.FragmentDeleted(StartPos: Integer; Fragment: WideString);
Var
   TmpItem: PEdtAction;
Begin
   If Fragment = '' Then
      Exit;

   If (Not AtLeast(1)) Or Not ((PEdtAction(Peek)^.ActionType = atDelete) And (PEdtAction(Peek)^.StartPosition - StartPos - Length(Fragment) <= 1) And (PEdtAction(Peek)^.StartPosition - StartPos >= 0)) Then
   Begin
      New(TmpItem);
      Initialize(TmpItem^);
      Push(TmpItem);

      With TmpItem^ Do
      Begin
         ActionType := atDelete;
         StartPosition := StartPos;
         DeletedFragment := Fragment;
         PairedWithPriv := False;
      End;
   End Else
      Case PEdtAction(Peek)^.ActionType Of
         atDelete:
         Begin
            If StartPos > 0 Then
            Begin
               If StartPos < PEdtAction(Peek)^.StartPosition Then
                  PEdtAction(Peek)^.DeletedFragment := Fragment + PEdtAction(Peek)^.DeletedFragment
               Else
                  PEdtAction(Peek)^.DeletedFragment := PEdtAction(Peek)^.DeletedFragment + Fragment;
               PEdtAction(Peek)^.StartPosition := StartPos;
            End;
         End;
      End;
End;

Procedure TEdtActionStack.FragmentInserted(StartPos, FragmentLength: Integer; IsPairedWithPriv: Boolean);
Var
   TmpItem: PEdtAction;
Begin
   If FragmentLength = 0 Then
      Exit;

   If (Not AtLeast(1)) Or Not ((PEdtAction(Peek)^.ActionType = atInsert) And (PEdtAction(Peek)^.StartPosition + PEdtAction(Peek)^.Length = StartPos)) Then
   Begin
      New(TmpItem);
      Initialize(TmpItem^);
      Push(TmpItem);
      With TmpItem^ Do
      Begin
         ActionType := atInsert;
         StartPosition := StartPos;
         Length := FragmentLength;
         PairedWithPriv := IsPairedWithPriv;
      End;
   End Else
      Case PEdtAction(Peek)^.ActionType Of
         atInsert: PEdtAction(Peek)^.Length := PEdtAction(Peek)^.Length + FragmentLength;
      End;
End;

Procedure TEdtActionStack.CaretMovedBy(Shift: Integer);
Begin
End;

Function TEdtActionStack.RollBackAction: Boolean;
Var
   TmpItem: PEdtAction;
   WasPaired: Boolean;
   LTmpOptions: TInsertOptions;
Begin
   Result := AtLeast(1);
   If Not (Result And Assigned(FOwner)) Then
      Exit;

   Repeat
      TmpItem := Pop;

      With TmpItem^, FOwner Do
      Begin
         If DeletedFragment <> #13 + #10 Then
            LTmpOptions := [ioSelected]
         Else
            LTmpOptions := [];

         Case ActionType Of
            atDelete: InsertAfter(TextPosToPos(StartPosition - 1), DeletedFragment, LTmpOptions + [ioMoveCaret]);
            atInsert: DeleteFrom(TextPosToPos(StartPosition), Length, [doMoveCaret]);
         End;
      End;

      WasPaired := TmpItem^.PairedWithPriv;
      Finalize(TmpItem^);
      Dispose(TmpItem);
   Until (Not AtLeast(1)) Or (Not WasPaired);
End;

//=============== TD2CustomScene ================================================

Constructor TD2CustomScene.Create(AOwner: TComponent);
Begin
   Inherited;
   AddScene(Self);
   ShowHint := True;
   FAnimatedCaret := True;
   FDesignTime := csDesigning In ComponentState;
   ControlStyle := ControlStyle - [csAcceptsControls] + [csActionClient];
   OnDragOver := DoDragOver;
   OnDragDrop := DoDragDrop;
   ControlStyle := ControlStyle + [csCaptureMouse, csOpaque, csDoubleClicks];
   FSnapToLines := True;
   FAlignRoot := True;
   FCanvas := GvarD2DefaultCanvasClass.Create(Width, Height);
   FCanvas.Scene := Self;
   FFill := TD2Brush.Create(d2BrushNone, $FF000000);
   FFill.OnChanged := FillChanged;
   FDesignPopupEnabled := True;
   FSnapGridSize := 1;

   Width := 200;
   Height := 200;

   If GvarD2Designer <> nil Then
      GvarD2Designer.AddScene(Self);

   GvarD2SceneCount := GvarD2SceneCount + 1;
End;

Destructor TD2CustomScene.Destroy;
Begin
   If FHovered <> nil Then
   Begin
      TD2VisualObject(FHovered).RemoveFreeNotify(Self);
      FHovered := nil;
   End;

   If FFocused <> nil Then
   Begin
      TD2VisualObject(FFocused).RemoveFreeNotify(Self);
      FFocused := nil;
   End;

   //if FStyle <> nil then FStyle.RemoveSceneUpdater(Self);

   If GvarD2SceneCount = 0 Then
   Begin
      If GvarD2aniThread <> nil Then
      Begin
         GvarD2aniThread.Free;
      End;
      GvarD2aniThread := nil;
   End;
   If FDesignPopup <> nil Then
      FreeAndNil(FDesignPopup);
   If GvarD2Designer <> nil Then
      GvarD2Designer.RemoveScene(Self);
   GvarD2SceneCount := GvarD2SceneCount - 1;
   If FOpenInFrame <> nil Then
      FOpenInFrame.SceneObject := nil;
   DeleteChildren;
   If FChildren <> nil Then
      FreeAndNil(FChildren);
   FreeAndNil(FFill);
   FreeAndNil(FCanvas);
   RemoveScene(Self);
   Inherited;
End;

Procedure TD2CustomScene.CreateHandle;
{$IFDEF ORCA2D_USE_GTK2}
var
  VarScreen:PGdkScreen;
  VarColorMap:PGdkColormap;
{$ENDIF}
{$IFDEF ORCA2D_USE_GTK3}
var
  VarScreen:PGdkScreen;
  VarColorMap:PGdkVisual;//PGdkColormap;
{$ENDIF}
{$IFDEF ORCA2D_USE_GTK4}
var
  VarScreen:PGdkScreen;
  VarColorMap:PGdkVisual;//PGdkColormap;
{$ENDIF}
{$IFDEF ORCA2D_USE_QT4}
{$ENDIF}
{$IFDEF ORCA2D_USE_QT5}
{$ENDIF}
{$IFDEF ORCA2D_USE_QT6}
{$ENDIF}
Begin
   Inherited;
   {$IFDEF ORCA2D_OS_WINDOWS}
  FPrevWndProc:=Windows.WNDPROC(SetWindowLongPtrW(Self.Handle,GWL_WNDPROC,PtrInt(@WndCallback)));
   {$ENDIF}

   {$IFNDEF UCL}
   {$IFDEF ORCA2D_OS_WINDOWS}
    SetWindowLongW(Handle,GWL_WNDPROC,GetWindowLong(Handle,GWL_WNDPROC));
    Canvas.Parent:=Handle;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK2}
    Canvas.Parent:=PtrInt(GetFixedWidget(pgtkwidget(Handle)));
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK3}
    Canvas.Parent:=PtrInt(PGdkVisual(Pointer(Handle)));
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK4}
    Canvas.Parent:=PtrInt(PGdkVisual(Pointer(Handle)));
   {$ENDIF}

   {$IFDEF ORCA2D_USE_QT4}
   {$ENDIF}
   {$IFDEF ORCA2D_USE_QT5}
   {$ENDIF}
   {$IFDEF ORCA2D_USE_QT6}
   {$ENDIF}
   {$IFDEF ORCA2D_USE_CARBON}
   {$ENDIF}
   {$IFDEF ORCA2D_USE_COCOA}
   {$ENDIF}

   {$ENDIF}
   Canvas.ResizeBuffer(Width, Height);
   RealignRoot;
   AddUpdateRect(d2Rect(0, 0, Width, Height));

   {$IFNDEF UCL}

   If FTransparency And Not (GetDesignTime) Then
   Begin
      If Parent Is TCustomForm Then
      Begin
         {$IFDEF ORCA2D_USE_CARBON}
         WndEventHandlerUPP:=NewEventHandlerUPP(EventHandlerProcPtr(Pointer(@WndEventHandler)));
         EventKinds[0].eventClass:=kEventClassControl;
         EventKinds[0].eventKind:=kEventControlDraw;
         InstallEventHandler(GetControlEventTarget(TCarbonWindow(TCustomForm(Parent).Handle).Widget),WndEventHandlerUPP,1,@EventKinds[0],Self,nil);
         EventKinds[0].eventClass:=kEventClassWindow;
         EventKinds[0].eventKind:=kEventWindowGetRegion;
         InstallEventHandler(GetWindowEventTarget(TCarbonWindow(TCustomForm(Parent).Handle).Window),WndEventHandlerUPP,1,@EventKinds[0],Self,nil);
         SetWindowAlpha(TCarbonWindow(TCustomForm(Parent).Handle).Window,0.999);
         ReshapeCustomWindow(TCarbonWindow(TCustomForm(Parent).Handle).Window);
         {$ENDIF}

         {$IFDEF ORCA2D_USE_COCOA}
         {$ENDIF}

         {$IFDEF ORCA2D_USE_GTK2}
         VarScreen:=gtk_widget_get_screen(pgtkwidget(Parent.Handle));
         VarColorMap:=gdk_screen_get_rgba_colormap(VarScreen);

         if VarColorMap <> nil then
          gtk_widget_set_colormap(pgtkwidget(Parent.Handle),VarColorMap);

         gtk_widget_set_app_paintable(pgtkwidget(Parent.Handle),true);
         {$ENDIF}

         {$IFDEF ORCA2D_USE_GTK3}
         {$ENDIF}
         {$IFDEF ORCA2D_USE_GTK4}
         {$ENDIF}
         {$IFDEF ORCA2D_USE_QT4}
         {$ENDIF}
         {$IFDEF ORCA2D_USE_QT5}
         {$ENDIF}
         {$IFDEF ORCA2D_USE_QT6}
         {$ENDIF}

         {$IFDEF ORCA2D_OS_WINDOWS}
      if FTransparency and not (GetDesignTime) and (Parent is TCustomForm) then
        SetWindowLong(Parent.Handle,GWL_EXSTYLE,GetWindowLong(Parent.Handle,GWL_EXSTYLE) or WS_EX_LAYERED);
      UpdateLayer;
      AddUpdateRect(d2Rect(0,0,$FFFF,$FFFF));
         {$ENDIF}
      End;
   End;

   {$IFDEF ORCA2D_USE_CARBON}
  SetControlDragTrackingEnabled(TCarbonWidget(Handle).Widget,true);
  if Parent is TCustomForm then
    SetAutomaticControlDragTrackingEnabledForWindow(TCarbonWindow(TCustomForm(Parent).Handle).Window,true);
  WndEventHandlerUPP:=NewEventHandlerUPP(EventHandlerProcPtr(Pointer(@CtrlEventHandler)));
  EventKinds[0].eventClass:=kEventClassControl;
  EventKinds[0].eventKind:=kEventControlDraw;
  EventKinds[1].eventClass:=kEventClassControl;
  EventKinds[1].eventKind:=kEventControlDragEnter;
  EventKinds[2].eventClass:=kEventClassControl;
  EventKinds[2].eventKind:=kEventControlDragReceive;
  EventKinds[3].eventClass:=kEventClassControl;
  EventKinds[3].eventKind:=kEventControlDragWithin;
  EventKinds[4].eventClass:=kEventClassControl;
  EventKinds[4].eventKind:=kEventControlDragLeave;
  InstallEventHandler(GetControlEventTarget(TCarbonWidget(Handle).Widget),WndEventHandlerUPP,5,@EventKinds[0],Self,nil);
   {$ENDIF}
   {$ENDIF}
End;


Procedure TD2CustomScene.CreateWnd;
Begin
   Inherited;
End;

Procedure TD2CustomScene.DestroyWnd;
Begin
   {$ifdef ORCA2D_OS_WINDOWS}
  if Canvas <> nil then
    Canvas.Handle:=0;
   {$endif}
   {$IFNDEF UCL}
   {$IFdef ORCA2D_OS_WINDOWS}
  if HandleAllocated then
    RevokeDragDrop(Handle);
  if PtrInt(SetWindowLongPtrW(Self.Handle,GWL_WNDPROC,PtrInt(@WndCallback)))=PtrInt(@WndCallback) then
    SetWindowLongPtrW(Self.Handle,GWL_WNDPROC,PtrInt(@FPrevWndProc));
   {$ENDIF}
{$ENDIF}
   Inherited;
End;

Procedure TD2CustomScene.Loaded;
Begin
   Inherited;
   FLoadCursor := Cursor;
   If FSnapToLines Then
      FSnapToGrid := False;
   FShowTimer := TD2Timer.Create(Self);
   FShowTimer.Interval := 1;
   FShowTimer.OnTimer := DoShowTimer;
End;

Procedure TD2CustomScene.InitiateAction;

   Procedure TraverseClients(Container: TD2VisualObject);
   Var
      I: Integer;
      Control: TD2VisualObject;
   Begin
      If Container.CheckParentVisible Then
         For I := 0 To Container.ChildrenCount - 1 Do
         Begin
            If Not Container.Children[I].IsVisual Then
               Continue;
            Control := Container.Children[I].Visual;
            If (Control Is TD2Control) And Control.Visible And (TD2Control(Control).Action <> nil) Then
               TD2Control(Control).InitiateAction;
            TraverseClients(Control);
         End;
   End;

Begin
   Inherited;
   If Root <> nil Then
      TraverseClients(TD2VisualObject(Root));
End;

Procedure TD2CustomScene.CreateEmbedded(Const AWidth, AHeight: Integer; AOnFlush: TNotifyEvent);
Begin
   FOnFlush := AOnFlush;
   Width := AWidth;
   Height := AHeight;
   Canvas.ResizeBuffer(AWidth, AHeight);
   RealignRoot;
   AddUpdateRect(d2Rect(0, 0, AWidth, AHeight));
End;

Procedure TD2CustomScene.EmbeddedMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Begin
   MouseDown(Button, Shift, X, Y);
End;

Procedure TD2CustomScene.EmbeddedMouseMove(Shift: TShiftState; X, Y: Integer);
Begin
   MouseMove(Shift, X, Y);
End;

Procedure TD2CustomScene.EmbeddedMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Begin
   MouseUp(Button, Shift, X, Y);
End;

Function TD2CustomScene.EmbeddedMouseWheel(Shift: TShiftState; WheelDelta: Integer): Boolean;
Var
   MousePos: TPoint;
Begin
   Result := DoMouseWheel(Shift, WheelDelta, MousePos);
End;

Procedure TD2CustomScene.EmbeddedKeyUp(Var Key: Word; Var Char: System.Widechar; Shift: TShiftState);
Begin
   UnicodeKeyUp(Key, Char, Shift);
End;

Procedure TD2CustomScene.EmbeddedKeyDown(Var Key: Word; Var Char: System.Widechar; Shift: TShiftState);
Begin
   UnicodeKeyDown(Key, Char, Shift);
End;

Procedure TD2CustomScene.DoShowTimer(Sender: TObject);
Begin
   FShowTimer.Enabled := False;
   {$IFDEF ORCA2D_USE_CARBON}
  HIViewSetNeedsDisplay(HiViewRef(TCarbonWidget(Handle).Widget),true);
{$ENDIF}
   {$IFdef ORCA2D_OS_WINDOWS}
  AddUpdateRect(d2Rect(0,0,$FFFF,$FFFF));
{$ENDIF}
   FShowTimer.Free;
End;

Procedure TD2CustomScene.UpdateBuffer;
Begin
   {$IFDEF ORCA2D_USE_CARBON}
  HIViewSetNeedsDisplay(HiViewRef(TCarbonWidget(Handle).Widget),true);
{$ENDIF}
   {$IFdef ORCA2D_OS_WINDOWS}
  SetLength(FUpdateRects,1);
  FUpdateRects[0]:=d2Rect(0,0,Width,Height);
  Draw;
{$ENDIF}
End;

Procedure TD2CustomScene.UpdateLayer;
{$IFNDEF UCL}
{$IFDEF ORCA2D_OS_WINDOWS}
var
  Blend:TBLENDFUNCTION;
  Origin,Size,BitmapOrigin:Windows.TPoint;
  i,j:integer;
  SaveBits:PD2ColorRecArray;
{$ENDIF}
{$ENDIF}
Begin
   {$IFNDEF UCL}
   {$IFDEF ORCA2D_OS_WINDOWS}
  if (GetDesignTime) then Exit;
  if not (Owner is TWinControl) then Exit;
  if Parent.Handle=0 then Exit;

  Origin:=Point(Parent.Left + Left,Parent.Top + Top);
  Size:=Point(Width,Height);

  with Blend do
  begin
    BlendOp:=AC_SRC_OVER;
    AlphaFormat:=$01;
    BlendFlags:=0;
    SourceConstantAlpha:=$FF;
  end;
  BitmapOrigin:=Point(0,0);


  if ControlCount > 0 then
  begin

    GetMem(SaveBits,Width * Height * 4);
    d2MoveLongword(Canvas.FBufferBits,SaveBits,Width * Height);

    PaintControls(Canvas.Handle,nil);

    for j:=0 to Height - 1 do
      for i:=0 to Width - 1 do
        PD2ColorRecArray(Canvas.FBufferBits)[i + (j * Width)].A:=SaveBits[i + (j * Width)].A;
    FreeMem(SaveBits,Width * Height * 4);
  end;

  VarUpdateLayeredWindow(Parent.Handle,0,@Origin,@Size,Canvas.Handle,@BitmapOrigin,$00000000,@Blend,ULW_ALPHA);
   {$ENDIF}
   {$ENDIF}
End;

Procedure TD2CustomScene.GetChildren(Proc: TGetChildProc; Root: TComponent);
Var
   i: Integer;
Begin
   Inherited;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]).Stored Then
            Proc(FChildren[i]);
End;

Procedure TD2CustomScene.AddUpdateRect(R: TD2Rect);
{$IFdef ORCA2D_OS_WINDOWS}
var
  WR:TRect;
{$ENDIF}
{$IFDEF ORCA2D_USE_GTK2}
var
  GR:TGdkRectangle;
{$ENDIF}
{$IFDEF ORCA2D_USE_GTK3}
var
  GR:TGdkRectangle;
{$ENDIF}
Begin
   If FDisableUpdate Then
      Exit;
   If csLoading In ComponentState Then
      Exit;
   If Not Assigned(FOnFlush) And Not HandleAllocated Then
      Exit;
   If csDestroying In ComponentState Then
      Exit;
   If (Canvas.FBuffered) And (Canvas.FBufferBits = nil) Then
      Exit;
   If (d2RectWidth(R) = 0) Or (d2RectHeight(R) = 0) Then
      Exit;

   R := d2Rect(Trunc(R.Left), Trunc(R.Top), Trunc(R.Right) + 1, Trunc(R.Bottom) + 1);
   If Not d2IntersectRect(R, d2Rect(0, 0, Width, Height)) Then
      Exit;

   If Assigned(FOnFlush) Then
   Begin
      {$IFdef ORCA2D_OS_WINDOWS}
    SetLength(FUpdateRects,Length(FUpdateRects) + 1);
    FUpdateRects[High(FUpdateRects)]:=R;
    PostMessage(Handle,WM_ADDUPDATERECT,0,0);
      {$ENDIF}
      Exit;
   End;

   {$IFDEF ORCA2D_USE_CARBON}
    HIViewSetNeedsDisplayInRect(HiViewRef(TCarbonWidget(Handle).Widget),GetMacRectFromRect(R),true);
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK2}
    GR.x:=Trunc(R.Left);
    GR.y:=Trunc(R.Top);
    GR.width:=Trunc(d2RectWidth(R));
    GR.height:=Trunc(d2RectHeight(R));
    SetLength(FUpdateRects,Length(FUpdateRects) + 1);
    FUpdateRects[High(FUpdateRects)]:=R;
    if HandleAllocated and GDK_IS_WINDOW(pgtkwidget(Handle)^.window) then
     gdk_window_invalidate_rect(pgtkwidget(Handle)^.window,@GR,true);
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK3}
    GR.x:=Trunc(R.Left);
    GR.y:=Trunc(R.Top);
    GR.width:=Trunc(d2RectWidth(R));
    GR.height:=Trunc(d2RectHeight(R));
    SetLength(FUpdateRects,Length(FUpdateRects) + 1);
    FUpdateRects[High(FUpdateRects)]:=R;
    if HandleAllocated and (PGdkWindow(Handle)<>Nil) then
     gdk_window_invalidate_rect(PGdkWindow(Handle),@GR,true);
   {$ENDIF}


   {$IFDEF ORCA2D_OS_WINDOWS}
   if Transparency and (Parent is TCustomForm) and not FDEsigntime then
   begin
     SetLength(FUpdateRects,Length(FUpdateRects) + 1);
     FUpdateRects[High(FUpdateRects)]:=R;
     PostMessage(Handle,WM_ADDUPDATERECT,0,0);
   end
   else
   begin
     WR:=Rect(Trunc(R.Left),Trunc(R.Top),Trunc(R.Right),Trunc(R.Bottom));
     Windows.InvalidateRect(Handle,@WR,false);
   end;
   {$ENDIF}
End;

//============================================================= 8888
{$IFDEF ORCA2D_OS_WINDOWS}
type
  TParentControl=class(TWinControl);

procedure GetControls(X,Y,W,H:integer;
                      Control:TCustomControl; Dest:TCanvas);
var
  I,Count,SaveIndex:integer;
  DC:HDC;
  R,SelfR,CtlR:TRect;
  Ctrl:TControl;
begin
  Count:=Control.ControlCount;
  DC:=Dest.Handle;
  SelfR:=Bounds(0,0,W,H);

  for I:=0 to Count - 1 do
  begin
    Ctrl:=Control.Controls[I];
    if (Ctrl <> nil) and (Ctrl is TCustomControl)
    then
      begin
        with Ctrl do
        begin
          CtlR:=Bounds(X + Left,Y + Top,Width,Height);
          if Bool(IntersectRect(R,SelfR,CtlR)) and Visible then
          begin
            SaveIndex:=SaveDC(DC);
            SetViewportOrgEx(DC,Left + X,Top + Y,nil);
            IntersectClipRect(DC,0,0,Width,Height);
            Perform(WM_PAINT,DC,0);
            RestoreDC(DC,SaveIndex);
            if TCustomControl(Ctrl).ControlCount <> 0
            then
              GetControls(Left + X,Top + Y,W,H,
              TCustomControl(Ctrl),Dest);
          end;
       end;
    end;
  end;
end;

procedure DrawParentImage(Control:TControl; DestDC:HDC);
var
  I,Count,X,Y,SaveIndex:integer;
  Dest:TCanvas;
  R,SelfR,CtlR:TRect;
  Ctrl:TControl;
begin
  if Control.Parent=nil then Exit;
  Count:=Control.Parent.ControlCount;
  Dest:=TCanvas.Create;
  try
    Dest.Handle:=DestDC;
    SelfR:=Bounds(Control.Left,Control.Top,Control.Width,Control.Height);
    X:=-Control.Left; Y:=-Control.Top;

    if Control.Parent is TForm then
    begin
        SaveIndex:=SaveDC(DestDC);
        SetViewportOrgEx(DestDC,X,Y,nil);
        IntersectClipRect(DestDC,0,0,Control.Parent.ClientWidth,
           Control.Parent.ClientHeight);
        if (Control.Parent is TForm) and
            (TForm(Control.Parent).FormStyle=fsMDIForm)
         then
           begin
             SendMessage(TForm(Control.Parent).ClientHandle,WM_ERASEBKGND,DestDC,0);
           end
        else
          SendMessage(Control.Parent.Handle,WM_ERASEBKGND,DestDC,0);
        RestoreDC(DestDC,SaveIndex);
      end
    else
      begin
        SaveIndex:=SaveDC(DestDC);
        SetViewportOrgEx(DestDC,X,Y,nil);
        IntersectClipRect(DestDC,0,0,Control.Parent.ClientWidth,
           Control.Parent.ClientHeight);
        TParentControl(Control.Parent).Perform(WM_ERASEBKGND,DestDC,0);
        TParentControl(Control.Parent).Perform(WM_PAINT,DestDC,0);
        RestoreDC(DestDC,SaveIndex);
      end;


    for I:=0 to Count - 1 do
    begin
      Ctrl:=Control.Parent.Controls[I];
      if Ctrl=Control then Break;
      if (Ctrl <> nil) and
         ((Ctrl is TGraphicControl) or (Ctrl is TCustomControl))
      then
        with Ctrl do
        begin
          CtlR:=Bounds(Left,Top,Width,Height);
          if Bool(IntersectRect(R,SelfR,CtlR)) and Visible then
          begin
            SaveIndex:=SaveDC(DestDC);
            SetViewportOrgEx(DestDC,Left + X,Top + Y,nil);
            IntersectClipRect(DestDC,0,0,Width,Height);
            Perform(WM_PAINT,DestDC,0);
            RestoreDC(DestDC,SaveIndex);
            if Ctrl is TCustomControl then
              GetControls(Left + X,Top + Y,
                Control.Width,Control.Height,
                TCustomControl(Ctrl),Dest);
          end;
       end;
    end;
  finally
    Dest.Handle:=0;
    Dest.Free;
  end;
end;
{$ENDIF}

Procedure TD2CustomScene.Draw;
Var
   i, j: Integer;
   R: TD2Rect;
   ScaleMatrix: TD2Matrix;
   {$IFdef ORCA2D_OS_WINDOWS}
  Rgn,NRgn:HRGN;
   {$ENDIF}
Begin
   If Not (FDesignTime) And (FOpenInFrame <> nil) Then
      Exit;
   If FDrawing Then
      Exit;
   If Length(FUpdateRects) > 0 Then
   Begin
      FDrawing := True;
      Try

         If (Length(FUpdateRects) > 20) Then
         Begin
            For i := 1 To High(FUpdateRects) Do
               FUpdateRects[0] := d2UnionRect(FUpdateRects[0], FUpdateRects[i]);
            SetLength(FUpdateRects, 1);
         End;

         If Canvas.BeginScene Then
         Begin
            Canvas.ResetClipRect;
            ScaleMatrix := IdentityMatrix;
            Canvas.SetMatrix(ScaleMatrix);
            Canvas.SetClipRects(FUpdateRects);

            If FTransparency And Not (Parent Is TCustomForm) Then
            Begin
               If FFill.Style = d2BrushNone Then
               Begin
                  {$IFdef ORCA2D_OS_WINDOWS}
            j:=SaveDC(Canvas.Handle);
            with FUpdateRects[0] do
              Rgn:=CreateRectRgn(trunc(left),trunc(top),trunc(right),trunc(bottom));
            for i:=1 to High(FUpdateRects) do
            begin
              with FUpdateRects[i] do
                NRgn:=CreateRectRgn(trunc(left),trunc(top),trunc(right),trunc(bottom));
              CombineRgn(Rgn,Rgn,NRgn,RGN_OR);
              DeleteObject(NRgn);
            end;
            SelectClipRgn(Canvas.Handle,Rgn);
            DrawParentImage(Self,Canvas.Handle);
            RestoreDC(Canvas.Handle,j);
                  {$ENDIF}
               End Else Begin
                  Canvas.Fill.Assign(FFill);
                  Canvas.FillRect(d2Rect(-1, -1, Width + 1, Height + 1), 0, 0, AllCorners, 1);
               End;
            End Else Begin
               If (FFill.Style = d2BrushNone) Or ((FFill.SolidColor And $FF000000 = 0) And (FFill.Style = d2BrushSolid)) Then
               Begin
                  For i := 0 To High(FUpdateRects) Do
                  Begin
                     If FTransparency Then
                        Canvas.ClearRect(FUpdateRects[i], 0)
                     Else
                        Canvas.ClearRect(FUpdateRects[i], FFill.SolidColor And $FFFFFF);
                  End;
               End Else Begin
                  Canvas.Fill.Assign(FFill);
                  Canvas.FillRect(d2Rect(-1, -1, Width + 1, Height + 1), 0, 0, AllCorners, 1);
               End;
            End;

            Canvas.StrokeThickness := 1;
            Canvas.StrokeCap := d2CapFlat;
            Canvas.StrokeJoin := d2JoinMiter;
            Canvas.StrokeDash := d2DashSolid;
            Canvas.Stroke.Style := d2BrushSolid;
            Canvas.Fill.Style := d2BrushSolid;
            If FChildren <> nil Then
               For i := 0 To FChildren.Count - 1 Do
               Begin
                  If Not (TD2Object(FChildren[i]).IsVisual) Then
                     Continue;
                  If Not TD2VisualObject(FChildren[i]).Visible Then
                     Continue;

                  ScaleMatrix := IdentityMatrix;
                  For j := 0 To High(FUpdateRects) Do
                     If d2IntersectRect(FUpdateRects[j], TD2VisualObject(FChildren[i]).UpdateRect) Then
                     Begin
                        Canvas.SetMatrix(d2MatrixMultiply(ScaleMatrix, TD2VisualObject(FChildren[i]).AbsoluteMatrix));
                        TD2VisualObject(FChildren[i]).BeforePaint;
                        TD2VisualObject(FChildren[i]).Paint;
                        TD2VisualObject(FChildren[i]).AfterPaint;
                        TD2VisualObject(FChildren[i]).PaintChildren;
                        If Assigned(TD2VisualObject(FChildren[i]).OnPaint) Then
                        Begin
                           Canvas.SetMatrix(d2MatrixMultiply(ScaleMatrix, TD2VisualObject(FChildren[i]).AbsoluteMatrix));
                           TD2VisualObject(FChildren[i]).OnPaint(TD2VisualObject(FChildren[i]), Canvas, TD2VisualObject(FChildren[i]).LocalRect);
                        End;
                        Break;
                     End;
               End;


            If FSnapGridShow And (FSnapGridSize <> 0) Then
            Begin
               ScaleMatrix := IdentityMatrix;
               Canvas.SetMatrix(ScaleMatrix);
               Canvas.Stroke.Style := d2BrushSolid;
               Canvas.StrokeThickness := 1;
            End;
            If (FSelected <> nil) And Not FSelected.DisableDesignResize Then
            Begin
               Canvas.Fill.Style := d2BrushSolid;
               Canvas.Fill.SolidColor := $FFFFFFFF;
               Canvas.StrokeThickness := 1;
               Canvas.Stroke.Style := d2BrushSolid;
               Canvas.Stroke.SolidColor := $FF1072C5;
               ScaleMatrix := orca_scene2d.IdentityMatrix;
               Canvas.SetMatrix(d2MatrixMultiply(ScaleMatrix, FSelected.AbsoluteMatrix));
               R := FSelected.BoundsRect;
               d2InflateRect(R, -0.5, -0.5);
               Canvas.StrokeDash := d2DashDash;
               Canvas.DrawRect(R, 0, 0, AllCorners, 1);
               Canvas.StrokeDash := d2DashSolid;
               Begin
                  If FRotateHot Then
                     Canvas.Fill.SolidColor := $FFFF0000
                  Else
                     Canvas.Fill.SolidColor := $FFFFFFFF;
                  R := FSelected.BoundsRect;
                  d2InflateRect(R, -0.5, -0.5);
                  Canvas.DrawLine(d2Point((R.Left + R.Right) / 2, R.Top), d2Point((R.Left + R.Right) / 2, R.Top - cnRotSize), 1);
                  Canvas.Fillellipse(d2Rect((R.Left + R.Right) / 2 - (cnGripSize), R.Top - cnRotSize - (cnGripSize), (R.Left + R.Right) / 2 + (cnGripSize), R.Top - cnRotSize + (cnGripSize)), Opaque);
                  Canvas.DrawEllipse(d2Rect((R.Left + R.Right) / 2 - (cnGripSize), R.Top - cnRotSize - (cnGripSize), (R.Left + R.Right) / 2 + (cnGripSize), R.Top - cnRotSize + (cnGripSize)), Opaque);

                  If FLeftTopHot Then
                     Canvas.Fill.SolidColor := $FFFF0000
                  Else
                     Canvas.Fill.SolidColor := $FFFFFFFF;
                  R := FSelected.BoundsRect;
                  d2InflateRect(R, -0.5, -0.5);
                  Canvas.Fillellipse(d2Rect(R.Left - (cnGripSize), R.Top - (cnGripSize), R.Left + (cnGripSize), R.Top + (cnGripSize)), Opaque);
                  Canvas.DrawEllipse(d2Rect(R.Left - (cnGripSize), R.Top - (cnGripSize), R.Left + (cnGripSize), R.Top + (cnGripSize)), Opaque);

                  If FRightTopHot Then
                     Canvas.Fill.SolidColor := $FFFF0000
                  Else
                     Canvas.Fill.SolidColor := $FFFFFFFF;
                  R := FSelected.BoundsRect;
                  d2InflateRect(R, -0.5, -0.5);
                  Canvas.Fillellipse(d2Rect(R.Right - (cnGripSize), R.Top - (cnGripSize), R.Right + (cnGripSize), R.Top + (cnGripSize)), Opaque);
                  Canvas.DrawEllipse(d2Rect(R.Right - (cnGripSize), R.Top - (cnGripSize), R.Right + (cnGripSize), R.Top + (cnGripSize)), Opaque);

                  If FLeftBottomHot Then
                     Canvas.Fill.SolidColor := $FFFF0000
                  Else
                     Canvas.Fill.SolidColor := $FFFFFFFF;
                  R := FSelected.BoundsRect;
                  d2InflateRect(R, -0.5, -0.5);
                  Canvas.Fillellipse(d2Rect(R.Left - (cnGripSize), R.Bottom - (cnGripSize), R.Left + (cnGripSize), R.Bottom + (cnGripSize)), Opaque);
                  Canvas.DrawEllipse(d2Rect(R.Left - (cnGripSize), R.Bottom - (cnGripSize), R.Left + (cnGripSize), R.Bottom + (cnGripSize)), Opaque);

                  If FRightBottomHot Then
                     Canvas.Fill.SolidColor := $FFFF0000
                  Else
                     Canvas.Fill.SolidColor := $FFFFFFFF;
                  R := FSelected.BoundsRect;
                  d2InflateRect(R, -0.5, -0.5);
                  Canvas.FillEllipse(d2Rect(R.Right - (cnGripSize), R.Bottom - (cnGripSize), R.Right + (cnGripSize), R.Bottom + (cnGripSize)), Opaque);
                  Canvas.DrawEllipse(d2Rect(R.Right - (cnGripSize), R.Bottom - (cnGripSize), R.Right + (cnGripSize), R.Bottom + (cnGripSize)), Opaque);

                  If FSelected.Width > cnGripSize * 4 Then
                  Begin
                     If FTopHot Then
                        Canvas.Fill.SolidColor := $FFFF0000
                     Else
                        Canvas.Fill.SolidColor := $FFFFFFFF;
                     R := FSelected.BoundsRect;
                     d2InflateRect(R, -0.5, -0.5);
                     Canvas.FillRect(d2Rect(R.Left + d2RectWidth(R) / 2 - (cnGripSize), R.Top - (cnGripSize), R.Left + d2RectWidth(R) / 2 + (cnGripSize), R.Top + (cnGripSize)), 0, 0, [], Opaque);
                     Canvas.DrawRect(d2Rect(R.Left + d2RectWidth(R) / 2 - (cnGripSize), R.Top - (cnGripSize), R.Left + d2RectWidth(R) / 2 + (cnGripSize), R.Top + (cnGripSize)), 0, 0, [], Opaque);
                     If FBottomHot Then
                        Canvas.Fill.SolidColor := $FFFF0000
                     Else
                        Canvas.Fill.SolidColor := $FFFFFFFF;
                     R := FSelected.BoundsRect;
                     d2InflateRect(R, -0.5, -0.5);
                     Canvas.FillRect(d2Rect(R.Left + d2RectWidth(R) / 2 - (cnGripSize), R.Bottom - (cnGripSize), R.Left + d2RectWidth(R) / 2 + (cnGripSize), R.Bottom + (cnGripSize)), 0, 0, [], Opaque);
                     Canvas.DrawRect(d2Rect(R.Left + d2RectWidth(R) / 2 - (cnGripSize), R.Bottom - (cnGripSize), R.Left + d2RectWidth(R) / 2 + (cnGripSize), R.Bottom + (cnGripSize)), 0, 0, [], Opaque);
                  End;
                  If FSelected.Height > cnGripSize * 4 Then
                  Begin
                     If FLeftHot Then
                        Canvas.Fill.SolidColor := $FFFF0000
                     Else
                        Canvas.Fill.SolidColor := $FFFFFFFF;
                     R := FSelected.BoundsRect;
                     d2InflateRect(R, -0.5, -0.5);
                     Canvas.FillRect(d2Rect(R.Left - (cnGripSize), R.Top + d2RectHeight(R) / 2 - (cnGripSize), R.Left + (cnGripSize), R.Top + d2RectHeight(R) / 2 + (cnGripSize)), 0, 0, [], Opaque);
                     Canvas.DrawRect(d2Rect(R.Left - (cnGripSize), R.Top + d2RectHeight(R) / 2 - (cnGripSize), R.Left + (cnGripSize), R.Top + d2RectHeight(R) / 2 + (cnGripSize)), 0, 0, [], Opaque);
                     If FRightHot Then
                        Canvas.Fill.SolidColor := $FFFF0000
                     Else
                        Canvas.Fill.SolidColor := $FFFFFFFF;
                     R := FSelected.BoundsRect;
                     d2InflateRect(R, -0.5, -0.5);
                     Canvas.FillRect(d2Rect(R.Right - (cnGripSize), R.Top + d2RectHeight(R) / 2 - (cnGripSize), R.Right + (cnGripSize), R.Top + d2RectHeight(R) / 2 + (cnGripSize)), 0, 0, [], Opaque);
                     Canvas.DrawRect(d2Rect(R.Right - (cnGripSize), R.Top + d2RectHeight(R) / 2 - (cnGripSize), R.Right + (cnGripSize), R.Top + d2RectHeight(R) / 2 + (cnGripSize)), 0, 0, [], Opaque);
                  End;

                  If (FMoving Or FLeftTop Or FRightTop Or FLeftBottom Or FRightBottom Or FTop Or FBottom Or FLeft Or FRight) And (Length(FDesignGridLines) > 0) And (FSelected.Parent <> nil) And (FSelected.Parent.IsVisual) Then
                  Begin
                     ScaleMatrix := orca_scene2d.IdentityMatrix;
                     Canvas.SetMatrix(d2MatrixMultiply(ScaleMatrix, TD2VisualObject(FSelected.Parent).AbsoluteMatrix));
                     Canvas.StrokeDash := d2DashDash;
                     For i := 0 To High(FDesignGridLines) Do
                     Begin
                        If (FDesignGridLines[i].Position.Y + round(FDesignGridLines[i].Height / 2)) = (FSelected.Position.Y + round(FSelected.Height / 2)) Then
                        Begin
                           Canvas.DrawLine(d2Point(FSelected.Position.X + 0.5, Trunc(FSelected.Position.Y + (FSelected.Height / 2)) + 0.5),
                              d2Point(FDesignGridLines[i].Position.X + 0.5, Trunc(FSelected.Position.Y + (FSelected.Height / 2)) + 0.5), 1);
                        End;
                        If (FDesignGridLines[i].Position.X + round(FDesignGridLines[i].Width / 2)) = (FSelected.Position.X + round(FSelected.Width / 2)) Then
                        Begin
                           Canvas.DrawLine(d2Point(Trunc(FSelected.Position.X + (FSelected.Width / 2)) + 0.5, FSelected.Position.Y + 0.5),
                              d2Point(Trunc(FDesignGridLines[i].Position.X + (FDesignGridLines[i].Width / 2)) + 0.5, FDesignGridLines[i].Position.Y + 0.5), 1);
                        End;
                        If (FSelected.Position.X = FDesignGridLines[i].Position.X) Or (FSelected.Position.Y = FDesignGridLines[i].Position.Y) Then
                        Begin
                           Canvas.DrawLine(d2Point(FSelected.Position.X + 0.5, FSelected.Position.Y + 0.5), d2Point(FDesignGridLines[i].Position.X + 0.5, FDesignGridLines[i].Position.Y + 0.5), 1);
                        End;
                        If (FSelected.Position.X + FSelected.Width = FDesignGridLines[i].Position.X) Then
                        Begin
                           Canvas.DrawLine(d2Point(FDesignGridLines[i].Position.X + 0.5, FSelected.Position.Y + 0.5), d2Point(FDesignGridLines[i].Position.X + 0.5, FDesignGridLines[i].Position.Y + 0.5), 1);
                        End;
                        If (FSelected.Position.Y + FSelected.Height = FDesignGridLines[i].Position.Y) Then
                        Begin
                           Canvas.DrawLine(d2Point(FSelected.Position.X + 0.5, FDesignGridLines[i].Position.Y + 0.5), d2Point(FDesignGridLines[i].Position.X + 0.5, FDesignGridLines[i].Position.Y + 0.5), 1);
                        End;
                        If (FSelected.Position.X = FDesignGridLines[i].Position.X + FDesignGridLines[i].Width) Then
                        Begin
                           Canvas.DrawLine(d2Point(FDesignGridLines[i].Position.X + FDesignGridLines[i].Width + 0.5, FSelected.Position.Y + 0.5),
                              d2Point(FDesignGridLines[i].Position.X + FDesignGridLines[i].Width + 0.5, FDesignGridLines[i].Position.Y + 0.5), 1);
                        End;
                        If (FSelected.Position.Y = FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height) Then
                        Begin
                           Canvas.DrawLine(d2Point(FSelected.Position.X + 0.5, FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height + 0.5),
                              d2Point(FDesignGridLines[i].Position.X + 0.5, FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height + 0.5), 1);
                        End;
                        If (FSelected.Position.X + FSelected.Width = FDesignGridLines[i].Position.X + FDesignGridLines[i].Width) Then
                        Begin
                           Canvas.DrawLine(d2Point(FDesignGridLines[i].Position.X + FDesignGridLines[i].Width + 0.5, FSelected.Position.Y + 0.5),
                              d2Point(FDesignGridLines[i].Position.X + FDesignGridLines[i].Width + 0.5, FDesignGridLines[i].Position.Y + 0.5), 1);
                        End;
                        If (FSelected.Position.Y + FSelected.Height = FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height) Then
                        Begin
                           Canvas.DrawLine(d2Point(FSelected.Position.X + 0.5, FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height + 0.5),
                              d2Point(FDesignGridLines[i].Position.X + 0.5, FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height + 0.5), 1);
                        End;
                     End;
                     Canvas.StrokeDash := d2DashSolid;
                  End;
               End;

               If FDesignPlaceObject <> nil Then
               Begin
                  ScaleMatrix := orca_scene2d.IdentityMatrix;
                  Canvas.SetMatrix(ScaleMatrix);

                  R := FDesignPlaceObject.AbsoluteRect;
                  Canvas.Stroke.SolidColor := $FF5B91DE;
                  Canvas.DrawRect(R, 0, 0, AllCorners, 1);
                  Canvas.Font.Family := 'Tahoma';
                  Canvas.Font.Style := d2FontRegular;
                  Canvas.Font.Size := 9;
                  R.Bottom := R.Top;
                  R.Top := R.Bottom - 19;
                  R.Right := R.Left + 160;
                  Canvas.Fill.SolidColor := $FF5B91DE;
                  Canvas.FillRect(R, 0, 0, AllCorners, 1);
                  Canvas.Fill.SolidColor := $FFFFFFFF;
                  d2InflateRect(R, -2, -2);
                  If FDesignPlaceObject.Name <> '' Then
                     Canvas.FillText(R, R, 'CTRL-drag to place into [' + FDesignPlaceObject.Name + ']', False, 1, d2TextAlignNear, d2TextAlignCenter)
                  Else
                     Canvas.FillText(R, R, 'CTRL-drag to place into [' + FDesignPlaceObject.ClassName + ']', False, 1, d2TextAlignNear, d2TextAlignCenter);
               End;
            End;

            If FDesignTime Then
            Begin
               ScaleMatrix := IdentityMatrix;
               Canvas.SetMatrix(ScaleMatrix);

               Canvas.Stroke.SolidColor := $FF5B91DE;
               Canvas.Font.Family := 'Tahoma';
               Canvas.Font.Style := d2FontRegular;
               Canvas.Font.Size := 9;
               R := d2Rect(0, 0, 200, 17);
            End;

            If ShowUpdateRects Then
               With Canvas Do
               Begin
                  ResetClipRect;
                  ScaleMatrix := IdentityMatrix;
                  Canvas.SetMatrix(ScaleMatrix);
                  Stroke.Style := d2BrushSolid;
                  Stroke.Color := '#9000FF00';
                  StrokeThickness := 1;
                  Fill.Style := d2BrushNone;
                  For i := 0 To High(FUpdateRects) Do
                  Begin
                     R := FUpdateRects[i];
                     DrawRect(FUpdateRects[i], 0, 0, AllCorners, 0.5);
                  End;
               End;
            {$IFDEF UPDATERECT}
        if not ShowUpdateRects then
        with Canvas do
        begin
          ResetClipRect;
          ScaleMatrix:=IdentityMatrix;
          Canvas.SetMatrix(ScaleMatrix);
          Stroke.Style:=d2BrushSolid;
          Stroke.Color:='#A000FF00';
          StrokeThickness:=1;
          Fill.Style:=d2BrushNone;
          for i:=0 to High(FUpdateRects) do
          begin
            R:=FUpdateRects[i];
            DrawRect(FUpdateRects[i],0,0,AllCorners,0.5);
          end;
        end;
            {$ENDIF}
            Canvas.EndScene;
         End;

         If Assigned(OnFlush) Then
            OnFlush(Self)
         Else Begin
            {$IFDEF ORCA2D_OS_WINDOWS}
        if (not FTransparency) or (FTransparency and not (Parent is TCustomForm)) or (FDesignTime) then
            {$ENDIF}
            Begin
               For i := 0 To High(FUpdateRects) Do
               Begin
                  R := FUpdateRects[i];
                  {$IFDEF ORCA2D_OS_WINDOWS}
            Canvas.FlushBufferRect(0,0,FDC,FUpdateRects[i]);
                  {$ELSE}
                  Canvas.FlushBufferRect(0, 0, 0, R);
                  {$ENDIF}
               End;
            End;

            {$IFDEF ORCA2D_OS_WINDOWS}
        if FTransparency and (Parent is TCustomForm) then
          UpdateLayer;
            {$ENDIF}
         End;
      Finally
         setLength(FUpdateRects, 0);
         FDrawing := False;
      End;
   End;
End;


Type
   THackControl = Class(TCustomControl);

Procedure TD2CustomScene.BeginDragObj(Source: TObject; ABitmap: TD2Bitmap);
Begin
   FDragSourceObj := TCustomControl.Create(Self);
   FDragSourceObj.Parent := Self;
   FDragSourceObj.Width := 0;
   FDragSourceObj.Height := 0;
   FDragSourceObj.Tag := PtrInt(Source);
   FDragSourceObj.BeginDrag(True, -1);
   THackControl(FDragSourceObj).OnEndDrag := EndDragEvent;
End;

Procedure TD2CustomScene.EndDragEvent(Sender, Target: TObject; X, Y: Integer);
Begin
   If TObject(Pointer(FDragSourceObj.Tag)) Is TD2VisualObject Then
      TD2VisualObject(Pointer(FDragSourceObj.Tag)).DragEnd;
   FDragSourceObj.Tag := 0;
End;

Procedure TD2CustomScene.DoDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; Var Accept: Boolean);
Var
   P: TD2Point;
   NewTarget: TD2VisualObject;
   Data: TD2DragObject;
Begin
   Accept := False;
   If Root = nil Then
      Exit;

   FillChar(Data, SizeOf(Data), 0);
   If (FDragSourceObj <> nil) And (FDragSourceObj.Tag <> 0) Then
      Data.Source := TD2Object(FDragSourceObj.Tag)
   Else
      Data.Source := Source;

   P := d2Point(X, Y);
   NewTarget := Root.Visual.FindTarget(P, Data);

   If (FDragSourceObj <> nil) And (NewTarget = TD2Object(FDragSourceObj.Tag)) Then
   Begin
      If varD2FTarget <> nil Then
         TD2VisualObject(varD2FTarget).DragLeave;
      varD2FTarget := nil;
      Accept := False;
      Exit;
   End;

   If varD2FTarget <> nil Then
   Begin
      TD2VisualObject(varD2FTarget).DragOver(Data, P, Accept);
   End;
   If (NewTarget <> varD2FTarget) Then
   Begin
      If varD2FTarget <> nil Then
         TD2VisualObject(varD2FTarget).DragLeave;
      varD2FTarget := NewTarget;
      If varD2FTarget <> nil Then
      Begin
         TD2VisualObject(varD2FTarget).DragEnter(Data, P);
      End;
   End;
   If varD2FTarget = nil Then
      Accept := False;
End;

Procedure TD2CustomScene.DoDragDrop(Sender, Source: TObject; X, Y: Integer);
Var
   Data: TD2DragObject;
Begin
   If varD2FTarget = nil Then
      Exit;

   FillChar(Data, SizeOf(Data), 0);

   If (FDragSourceObj <> nil) And (FDragSourceObj.Tag <> 0) Then
      Data.Source := TD2Object(FDragSourceObj.Tag)
   Else
      Data.Source := Source;

   TD2VisualObject(varD2FTarget).DragDrop(Data, d2Point(X, Y));

   varD2FTarget := nil;
End;

Procedure TD2CustomScene.Paint;
Begin
   {$IFDEF ORCA2D_USE_GTK2}
  Canvas.Handle:=PtrInt(gdk_cairo_create(TGtkDeviceContext(inherited Canvas.Handle).Drawable));

  if FTransparency then
  begin
    cairo_set_operator(Pcairo_t(Canvas.Handle),CAIRO_OPERATOR_SOURCE);
    cairo_set_source_rgba(Pcairo_t(Canvas.Handle),0,0,0,0);
    cairo_paint(Pcairo_t(Canvas.Handle));
    cairo_set_operator(Pcairo_t(Canvas.Handle),CAIRO_OPERATOR_OVER);
  end;

  SetLength(FUpdateRects,1);
  FUpdateRects[0]:=d2Rect(0,0,Width,Height);

  Draw;
  cairo_destroy(Pcairo_t(Canvas.Handle));
  Canvas.Handle:=0;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK3}
  Canvas.Handle:=PtrInt(gdk_cairo_create(TGtk3DeviceContext(inherited Canvas.Handle).Window));

  if FTransparency then
  begin
    cairo_set_operator(Pcairo_t(Canvas.Handle),CAIRO_OPERATOR_SOURCE);
    cairo_set_source_rgba(Pcairo_t(Canvas.Handle),0,0,0,0);
    cairo_paint(Pcairo_t(Canvas.Handle));
    cairo_set_operator(Pcairo_t(Canvas.Handle),CAIRO_OPERATOR_OVER);
  end;

  SetLength(FUpdateRects,1);
  FUpdateRects[0]:=d2Rect(0,0,Width,Height);

  Draw;
  cairo_destroy(Pcairo_t(Canvas.Handle));
  Canvas.Handle:=0;
   {$ENDIF}
End;

Function TD2CustomScene.ObjectByPoint(X, Y: Single): TD2VisualObject;
Var
   i: Integer;
   Obj, NewObj: TD2Object;
Begin
   Result := nil;
   For i := Count - 1 Downto 0 Do
   Begin
      Obj := Children[i];
      If Not (Obj.IsVisual) Then
         Exit;
      If Not TD2VisualObject(Obj).Visible And Not (FDesignTime) Then
         Continue;

      NewObj := TD2VisualObject(Obj).ObjectByPoint(X, Y);
      If NewObj <> nil Then
      Begin
         Result := TD2VisualObject(NewObj);
         Exit;
      End;
   End;
End;

Procedure TD2CustomScene.CMShowingChanged(Var Message: TLMessage);
Begin
   Inherited;
   AddUpdateRect(d2Rect(0, 0, Width, Height));
End;

Procedure TD2CustomScene.CMDesignHitTest(Var Msg: TLMMouse);
Var
   Obj: TD2VisualObject;
   P: TD2Point;
Begin
   Inherited;

   If (FChildren = nil) Or (FChildren.Count = 0) Then
   Begin
      Msg.Result := 1;
      Exit;
   End;

   P := d2Point(Msg.XPos, Msg.YPos);
   If (FMoving Or FLeftTop Or FRightTop Or FLeftBottom Or FRightBottom) Then
   Begin
      Msg.Result := 1;
      Exit;
   End;
   Obj := ObjectByPoint(P.X, P.Y);
   If Obj = nil Then
   Begin
      If (FSelected <> nil) And (d2PtInRect(d2Point(P.X, P.Y), FSelected.AbsoluteRect)) Then
         Msg.Result := 1
      Else
         Msg.Result := 0;
   End Else Begin
      Msg.Result := 1;
   End;
End;

Procedure TD2CustomScene.CMHintShow(Var Message: TLMessage);
Var
   Obj: TD2VisualObject;
Begin
   Inherited;
   With TCMHintShow(Message).HintInfo^ Do
   Begin
      Obj := ObjectByPoint(CursorPos.X, CursorPos.Y);
      If (Obj <> nil) And (Obj.ShowHint) Then
      Begin
         HintStr := Obj.Hint;
         With Obj.AbsoluteRect Do
            CursorRect := Rect(Trunc(Left), Trunc(Top), Trunc(Right), Trunc(Bottom));
      End Else Begin
         HintStr := '';
      End;
   End;
End;


Procedure TD2CustomScene.UnicodeKeyDown(Var Key: Word; Var Char: System.Widechar; Shift: TShiftState);
Var
   List: TList;
   i, CurIdx: Integer;
   Found: Boolean;
   O: TComponent;
Begin
   If (Key = VK_ESCAPE) Or (Key = VK_RETURN) Or (Key = VK_LEFT) Or (Key = VK_RIGHT) Or (Key = VK_UP) Or (Key = VK_DOWN) Or (Key = VK_HOME) Or (Key = VK_END) Or (Key = VK_ADD) Or (Key = VK_SUBTRACT) Then
   Begin
      TD2VisualObject(Root).DialogKey(Key, Shift);
      If Key = 0 Then
         Exit;
   End;

   If (Key = VK_ESCAPE) Then
   Begin
      O := Owner;
      While O <> nil Do
      Begin
         If (O Is TCustomForm) And (fsModal In TCustomForm(O).FormState) Then
         Begin
            TCustomForm(O).ModalResult := mrCancel;
            Key := 0;
            Break;
         End;
         O := O.Owner;
      End;
      If Key = 0 Then
         Exit;
   End;

   If (Key = VK_TAB) And (Root <> nil) Then
   Begin
      Key := 0;
      List := TList.Create;
      TD2VisualObject(Root).GetTabOrderList(List, True);

      Found := False;
      If ssShift In Shift Then
      Begin
         If FFocused <> nil Then
            CurIdx := List.IndexOf(FFocused) - 1
         Else
            CurIdx := List.Count - 1;
         If CurIdx > 0 Then
            For i := CurIdx - 1 Downto 0 Do
               If TD2Object(List[i]).isVisual And (TD2VisualObject(List[i]).CheckParentVisible) And (TD2VisualObject(List[i]).CanFocused) And (TD2VisualObject(List[i]).AbsoluteEnabled) Then
               Begin
                  TD2VisualObject(List[i]).SetFocus;
                  Found := True;
                  Break;
               End;

         If Not Found Then
            If (List.Count > 2) And (CurIdx < List.Count) Then
               For i := List.Count - 1 Downto CurIdx Do
                  If TD2Object(List[i]).isVisual And (TD2VisualObject(List[i]).CheckParentVisible) And (TD2VisualObject(List[i]).CanFocused) And (TD2VisualObject(List[i]).AbsoluteEnabled) Then
                  Begin
                     TD2VisualObject(List[i]).SetFocus;
                     Break;
                  End;
      End Else Begin
         If FFocused <> nil Then
            CurIdx := List.IndexOf(FFocused) + 1
         Else
            CurIdx := 0;

         If (List.Count > 2) And (CurIdx < List.Count) Then
            For i := CurIdx To List.Count - 1 Do
               If TD2Object(List[i]).isVisual And (TD2VisualObject(List[i]).CheckParentVisible) And (TD2VisualObject(List[i]).CanFocused) And (TD2VisualObject(List[i]).AbsoluteEnabled) Then
               Begin
                  TD2VisualObject(List[i]).SetFocus;
                  Found := True;
                  Break;
               End;

         If Not Found Then
            If CurIdx > 0 Then
               For i := 0 To CurIdx - 1 Do
                  If TD2Object(List[i]).isVisual And (TD2VisualObject(List[i]).CheckParentVisible) And (TD2VisualObject(List[i]).CanFocused) And (TD2VisualObject(List[i]).AbsoluteEnabled) Then
                  Begin
                     TD2VisualObject(List[i]).SetFocus;
                     Break;
                  End;
      End;
      List.Free;
      Exit;
   End;

   If FFocused <> nil Then
   Begin
      TD2VisualObject(FFocused).KeyDown(Key, Char, Shift);
   End;
End;

Procedure TD2CustomScene.UnicodeKeyUp(Var Key: Word; Var Char: System.Widechar; Shift: TShiftState);
Begin
   If FDesignTime Or (FDesignTime) Then
   Begin
      If (Key = VK_DELETE) And (FSelected <> nil) Then
      Begin
         FSelected.Free;
         FSelected := nil;
      End;
   End;

   If FFocused <> nil Then
   Begin
      TD2VisualObject(FFocused).KeyUp(Key, Char, Shift);
   End;
End;

Procedure TD2CustomScene.DeleteChildren;
Var
   Child: TD2Object;
Begin
   If Assigned(FChildren) Then
   Begin
      While FChildren.Count > 0 Do
      Begin
         Child := TD2Object(FChildren[0]);
         FChildren.Delete(0);
         TD2VisualObject(Child).FParent := nil;
         TD2VisualObject(Child).FScene := nil;
         Child.Free;
      End;
      FreeAndNil(FChildren);
   End;
End;

Procedure TD2CustomScene.AddObject(AObject: TD2Object);
Begin
   If AObject = nil Then
      Exit;
   If AObject.Parent <> nil Then
      AObject.Parent := nil;
   If FChildren = nil Then
      FChildren := TList.Create;
   FChildren.Add(AObject);
   TD2VisualObject(AObject).SetNewScene(Self);
   If AObject.IsVisual And Not (csDestroying In ComponentState) Then
   Begin
      TD2VisualObject(AObject).RecalcUpdateRect;
      TD2VisualObject(AObject).RecalcAbsolute;
      TD2VisualObject(AObject).Repaint;
      RealignRoot;
   End;
End;

Procedure TD2CustomScene.RemoveObject(AObject: TD2Object);
Begin
   If FChildren <> nil Then
   Begin
      Invalidate;
      TD2VisualObject(AObject).SetNewScene(nil);
      Notification(AObject, opRemove);
      FChildren.Remove(AObject);
   End;
End;

Function TD2CustomScene.GetCount: Integer;
Begin
   If FChildren <> nil Then
      Result := FChildren.Count
   Else
      Result := 0;
End;

Function TD2CustomScene.GetChildrenObject(Index: Integer): TD2Object;
Begin
   If FChildren <> nil Then
      Result := TD2Object(FChildren[Index])
   Else
      Result := nil;
End;

Procedure TD2CustomScene.SetChildren(Index: Integer; Const Value: TD2Object);
Begin
End;

Procedure TD2CustomScene.BeginDrag;
Var
   P: TPoint;
Begin
   If (Parent <> nil) And (Parent.Tag = $FFFF) Then
      Exit;

   FDragging := True;

   GetCursorPos(P);
   P := TWinControl(Owner).ScreenToClient(P);
   FDownPos := d2Point(P.X, P.Y);
   MouseCapture;
End;

Procedure TD2CustomScene.BeginResize;
Var
   P: TPoint;
Begin
   FResizing := True;
   GetCursorPos(P);
   P := TWinControl(Owner).ScreenToClient(P);
   FDownPos := d2Point(P.X, P.Y);
   FResizeSize := Point(TWinControl(Owner).Width, TWinControl(Owner).Height);
   MouseCapture;
End;

Function TD2CustomScene.SnapToGridValue(Value: Single): Single;
Begin
   If (DesignSnapToGrid) And (DesignSnapGridSize <> 0) Then
      Result := Trunc(Value / DesignSnapGridSize) * DesignSnapGridSize
   Else
      Result := Value;
End;

Procedure TD2CustomScene.AddUpdateRectsFromGridLines;

   Procedure IntAddUpdateRect(Const R: TD2Rect);
   Var
      i: Integer;
   Begin
      For i := 0 To High(FUpdateRects) Do
         With FUpdateRects[i] Do
            If (R.Left = Left) And (R.Top = Top) And (R.Right = Right) And (R.Bottom = Bottom) Then
            Begin
               Exit;
            End;
      AddUpdateRect(R);
   End;

Var
   i: Integer;
Begin
   If FDesignTime And (FSelected <> nil) And Not FSelected.DisableDesignResize And (FMoving Or FLeftTop Or FRightTop Or FLeftBottom Or FRightBottom Or FTop Or FBottom Or FLeft Or FRight) And
      (Length(FDesignGridLines) > 0) And (FSelected.Parent <> nil) And (FSelected.Parent.IsVisual) Then
   Begin
      For i := 0 To High(FDesignGridLines) Do
      Begin
         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FSelected.Position.X - 1, FSelected.Position.Y + (FSelected.Height / 2) - 1, FDesignGridLines[i].Position.X + 1, FDesignGridLines[i].Position.Y + (FDesignGridLines[i].Height / 2) + 1)));
         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FSelected.Position.X + (FSelected.Width / 2) - 1, FSelected.Position.Y - 1, FDesignGridLines[i].Position.X + (FDesignGridLines[i].Width / 2) + 1, FDesignGridLines[i].Position.Y + 1)));

         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FSelected.Position.X - 1, FSelected.Position.Y - 1, FDesignGridLines[i].Position.X + 1, FDesignGridLines[i].Position.Y + 1)));
         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FDesignGridLines[i].Position.X - 1, FSelected.Position.Y - 1, FDesignGridLines[i].Position.X + 1, FDesignGridLines[i].Position.Y + 1)));
         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FSelected.Position.X - 1, FDesignGridLines[i].Position.Y - 1, FDesignGridLines[i].Position.X + 1, FDesignGridLines[i].Position.Y + 1)));
         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FDesignGridLines[i].Position.X + FDesignGridLines[i].Width - 1, FSelected.Position.Y - 1, FDesignGridLines[i].Position.X + FDesignGridLines[i].Width + 1, FDesignGridLines[i].Position.Y + 1)));
         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FSelected.Position.X - 1, FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height - 1, FDesignGridLines[i].Position.X + 1, FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height + 1)));
         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FDesignGridLines[i].Position.X + FDesignGridLines[i].Width - 1, FSelected.Position.Y - 1, FDesignGridLines[i].Position.X + FDesignGridLines[i].Width + 1, FDesignGridLines[i].Position.Y + 1)));
         IntAddUpdateRect(d2NormalizeRect2(d2Rect(FSelected.Position.X - 1, FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height - 1, FDesignGridLines[i].Position.X + 1, FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height + 1)));
      End;
   End;
End;

Procedure TD2CustomScene.SnapToGridLines(AllowChangePosition: Boolean);

   Procedure AddGridLine(Const Obj: TD2VisualObject);
   Var
      i: Integer;
   Begin
      For i := 0 To High(FDesignGridLines) Do
         If FDesignGridLines[i] = Obj Then
            Exit;
      SetLength(FDesignGridLines, Length(FDesignGridLines) + 1);
      FDesignGridLines[High(FDesignGridLines)] := Obj;
   End;

Const
   SnapLineSize = 2;
Var
   i: Integer;
Begin
   If (DesignSnapToLines) And (FSelected.Parent <> nil) Then
      For i := 0 To FSelected.Parent.ChildrenCount - 1 Do
      Begin
         If TD2Object(FSelected.Parent.Children[i]) = FSelected Then
            Continue;
         If Not TD2Object(FSelected.Parent.Children[i]).isVisual Then
            Continue;
         With TD2VisualObject(FSelected.Parent.Children[i]) Do
         Begin
            If (Abs((Position.Y + round(Height / 2)) - (FSelected.Position.Y + round(FSelected.Height / 2))) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.Y := FMousePos.Y + ((Position.Y + round(Height / 2)) - (FSelected.Position.Y + round(FSelected.Height / 2)));
                  FSelected.Position.Y := (Position.Y + round(Height / 2) - round(FSelected.Height / 2));
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs((Position.X + round(Width / 2)) - (FSelected.Position.X + round(FSelected.Width / 2))) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.X := FMousePos.X + ((Position.X + round(Width / 2)) - (FSelected.Position.X + round(FSelected.Width / 2)));
                  FSelected.Position.X := (Position.X + round(Width / 2) - round(FSelected.Width / 2));
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs(Position.X - FSelected.Position.X) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.X := FMousePos.X + (Position.X - FSelected.Position.X);
                  FSelected.Position.X := Position.X;
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs(Position.Y - FSelected.Position.Y) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.Y := FMousePos.Y + (Position.Y - FSelected.Position.Y);
                  FSelected.Position.Y := Position.Y;
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs(Position.X - (FSelected.Position.X + FSelected.Width)) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.X := FMousePos.X + (Position.X - (FSelected.Position.X + FSelected.Width));
                  FSelected.Position.X := Position.X - FSelected.Width;
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs(Position.Y - (FSelected.Position.Y + FSelected.Height)) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.Y := FMousePos.Y + (Position.Y - (FSelected.Position.Y + FSelected.Height));
                  FSelected.Position.Y := Position.Y - FSelected.Height;
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs((Position.X + Width) - FSelected.Position.X) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.X := FMousePos.X + ((Position.X + Width) - FSelected.Position.X);
                  FSelected.Position.X := Position.X + Width;
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs((Position.Y + Height) - FSelected.Position.Y) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.Y := FMousePos.Y + ((Position.Y + Height) - FSelected.Position.Y);
                  FSelected.Position.Y := Position.Y + Height;
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs((Position.X + Width) - (FSelected.Position.X + FSelected.Width)) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.X := FMousePos.X + ((Position.X + Width) - (FSelected.Position.X + FSelected.Width));
                  FSelected.Position.X := Position.X + Width - FSelected.Width;
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
            If (Abs((Position.Y + Height) - (FSelected.Position.Y + FSelected.Height)) < SnapLineSize) Then
            Begin
               If AllowChangePosition Then
               Begin
                  FMousePos.Y := FMousePos.Y + ((Position.Y + Height) - (FSelected.Position.Y + FSelected.Height));
                  FSelected.Position.Y := Position.Y + Height - FSelected.Height;
               End;
               AddGridLine(TD2VisualObject(FSelected.Parent.Children[i]));
               Continue;
            End;
         End;
      End;
   AddUpdateRectsFromGridLines;
End;

Function TD2CustomScene.SnapPointToGridLines(Const APoint: TD2Point): TD2Point;
Var
   i: Integer;
Begin
   Result := APoint;
   If Not DesignSnapToLines Then
      Exit;
   If FSelected = nil Then
      Exit;
   If FSelected.Parent = nil Then
      Exit;
   SnapToGridLines(False);
   If Length(FDesignGridLines) > 0 Then
   Begin
      Result := FSelected.LocalToAbsolute(APoint);
      Result := TD2VisualObject(FSelected.Parent).AbsoluteToLocal(Result);
      For i := 0 To High(FDesignGridLines) Do
      Begin
         If Abs(Result.X - FDesignGridLines[i].Position.X) < (4) Then
            Result.X := FDesignGridLines[i].Position.X;
         If Abs(Result.Y - FDesignGridLines[i].Position.Y) < (4) Then
            Result.Y := FDesignGridLines[i].Position.Y;
         If Abs(Result.X - (FDesignGridLines[i].Position.X + FDesignGridLines[i].Width)) < (4) Then
            Result.X := FDesignGridLines[i].Position.X + FDesignGridLines[i].Width;
         If Abs(Result.Y - (FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height)) < (4) Then
            Result.Y := FDesignGridLines[i].Position.Y + FDesignGridLines[i].Height;
      End;
      Result := TD2VisualObject(FSelected.Parent).LocalToAbsolute(Result);
      Result := FSelected.AbsolutetoLocal(Result);
   End;
End;

Procedure TD2CustomScene.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Var
   P: TD2Point;
   R: TD2Rect;
   Obj: TD2VisualObject;
   SG: Id2SizeGrip;
   i: Integer;
Begin
   Inherited;

   //------------------------------- 7777
   If FDesignPopup <> nil Then
   Begin
      FDesignPopup.Close;
      FDesignPopup.Free;
      FDesignPopup := nil;
      Button := mbLeft;
   End;
   //-------------------------------------

   If Not (FDesignTime) And Not Assigned(FOnFlush) And Not (csDesigning In ComponentState) Then
      SetFocus;

   FUnsnapMousePos := d2Point(x, y);
   FMousePos := d2Point(SnapToGridValue(x), SnapToGridValue(y));
   FDownPos := FMousePos;
   SetLength(FDesignGridLines, 0);

   If FDesignTime Then
   Begin
      // Check and Add Root if Not exists
      If ((FChildren = nil) Or (FChildren.Count = 0)) Then
      Begin
         Obj := TD2Background.Create(Owner);
         AddObject(Obj);
         If GvarD2Designer <> nil Then
         Begin
            Obj.Name := GvarD2Designer.UniqueName(self.Owner, 'Root');
            GvarD2Designer.Add2DObject(Obj);
            GvarD2Designer.SelectObject(Owner, Obj, []);
         End;
         RealignRoot;
      End;

      If Button = mbRight Then
         OpenDesignPopup;

      If (FSelected <> nil) And (Not FSelected.DisableDesignResize) Then
      Begin
         P := FSelected.AbsoluteToLocal(d2Point(FUnsnapMousePos.X, FUnsnapMousePos.Y));
         R := d2Rect(FSelected.Width / 2 - (cnGripSize), -cnRotSize - (cnGripSize), (FSelected.Width) / 2 + (cnGripSize), -cnRotSize + (cnGripSize));
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FRotate := True;
            FMoving := False;
            Exit;
         End;
         P := FSelected.AbsoluteToLocal(d2Point(FUnsnapMousePos.X, FUnsnapMousePos.Y));
         R := d2Rect(-cnGripSize, -cnGripSize, cnGripSize, cnGripSize);
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FLeftTop := True;
            FMoving := False;
            Exit;
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(R.Right - cnGripSize, -cnGripSize, R.Right + cnGripSize, cnGripSize);
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FRightTop := True;
            FMoving := False;
            Exit;
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(-cnGripSize, R.Bottom - cnGripSize, cnGripSize, R.Bottom + cnGripSize);
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FLeftBottom := True;
            FMoving := False;
            Exit;
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(R.Right - cnGripSize, R.Bottom - cnGripSize, R.Right + cnGripSize, R.Bottom + cnGripSize);
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FRightBottom := True;
            FMoving := False;
            Exit;
         End;

         R := FSelected.BoundsRect;
         R := d2Rect(d2RectWidth(R) / 2 - cnGripSize, -cnGripSize, d2RectWidth(R) / 2 + cnGripSize, cnGripSize);
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FTop := True;
            FMoving := False;
            Exit;
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(d2RectWidth(R) / 2 - cnGripSize, R.Bottom - cnGripSize, d2RectWidth(R) / 2 + cnGripSize, R.Bottom + cnGripSize);
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FBottom := True;
            FMoving := False;
            Exit;
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(-cnGripSize, d2RectHeight(R) / 2 - cnGripSize, cnGripSize, d2RectHeight(R) / 2 + cnGripSize);
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FLeft := True;
            FMoving := False;
            Exit;
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(R.Right - cnGripSize, d2RectHeight(R) / 2 - cnGripSize, R.Right + cnGripSize, d2RectHeight(R) / 2 + cnGripSize);
         If d2PtInRect(P, R) Then
         Begin
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, FSelected, []);
            If Assigned(FDesignChangeSelection) Then
               FDesignChangeSelection(Self);
            FRight := True;
            FMoving := False;
            Exit;
         End;
      End;

      Obj := ObjectByPoint(FUnsnapMousePos.X, FUnsnapMousePos.Y);
      If (Obj <> nil) And (Obj = FSelected) And (ssDouble In Shift) Then
      Begin
         TD2VisualObject(Obj).DesignClick;
      End;
      If (Obj <> nil) Then
      Begin
         If (ssCtrl In Shift) And (Obj <> Selected) Then
         Begin
            For i := 0 To High(FSelection) Do
               If FSelection[i] = Obj Then
               Begin
                  FSelection[i] := Selected;
                  Obj := nil;
               End;
            If Obj <> nil Then
            Begin
               SetLength(FSelection, Length(FSelection) + 1);
               FSelection[High(FSelection)] := Obj;
            End;
            If GvarD2Designer <> nil Then
               GvarD2Designer.SelectObject(Owner, Selected, FSelection);
            Exit;
         End;
         SetLength(FSelection, 0);
         If FSelected <> nil Then
         Begin
            TD2VisualObject(FSelected).RecalcUpdateRect;
            FSelected.Repaint;
         End;
         FSelected := Obj;
         TD2VisualObject(FSelected).DesignSelect;

         If GvarD2Designer <> nil Then
            GvarD2Designer.SelectObject(Owner, Obj, []);
         If Assigned(FDesignChangeSelection) Then
            FDesignChangeSelection(Self);

         TD2VisualObject(FSelected).RecalcUpdateRect;
         FSelected.Repaint;

         If (Obj = FSelected) Then
            FMoving := True;
      End;
      Exit;
   End;

   If Not FDesignTime Then
   Begin
      Obj := TD2VisualObject(ObjectByPoint(FMousePos.X, FMousePos.Y));
      If (Obj <> nil) Then
      Begin
         If (TD2VisualObject(Obj).QueryInterface(Id2SizeGrip, SG) = 0) And (Assigned(SG)) Then
         Begin
            BeginResize;
         End Else Begin
            P := Obj.AbsoluteToLocal(d2Point(FMousePos.X, FMousePos.Y));
            TD2VisualObject(Obj).MouseDown(Button, Shift, ABS(P.X), ABS(P.Y));
            If (TD2VisualObject(Obj).FDragMode = d2DragAutomatic) Then
            Begin
               TD2VisualObject(Obj).BeginAutoDrag;
            End;
         End;
      End Else Begin
         BeginDrag;
      End;
   End;

   //............................
   If GvarD2Designer <> nil Then
      If FSelected <> nil Then
         GvarD2Designer.SelectObject(self.Owner, FSelected, []);
End;

Procedure TD2CustomScene.MouseMove(Shift: TShiftState; X, Y: Integer);
Var
   R: TD2Rect;
   P, P1: TD2Point;
   Obj: TD2VisualObject;
   SG: Id2SizeGrip;
   NewCursor: TCursor;
Begin
   Inherited;
   NewCursor := FLoadCursor;

   If FDragging Then
   Begin
      If Owner = nil Then
         Exit;

      With TWinControl(Owner) Do     //=== 4443 ==================================
      {$IF DEFINED(ORCA2D_USE_GTK2)}
         If FFixMouseMove Then
            SetBounds(Round(Left + (X - FDownPos.X)), Round(Top + (Y - FDownPos.Y)), Width, -Height)
         Else
            SetBounds(Round(Left + (X - FDownPos.X)), Round(Top + (Y - FDownPos.Y)), Width, Height);
      {$ELSE}
      SetBounds(Round(Left + (X - FDownPos.X)),Round(Top + (Y - FDownPos.Y)),Width, Height);
      {$ENDIF}

      Exit;
   End;

   If FResizing Then
   Begin
      If Owner = nil Then
         Exit;

      FResizeSize.X := Round(FResizeSize.X + (X - FUnsnapMousePos.X));
      FResizeSize.Y := Round(FResizeSize.Y + (Y - FUnsnapMousePos.Y));
      With TWinControl(Owner) Do
         SetBounds(Left, Top, FResizeSize.X, FResizeSize.Y);
      Cursor := crSizeNWSE;
      FUnsnapMousePos := d2Point(x, y);
      Exit;
   End;

   FMousePos := d2Point(SnapToGridValue(x), SnapToGridValue(y));
   FUnsnapMousePos := d2Point(x, y);

   If FDesignTime Then
   Begin
      If (FSelected <> nil) Then
      Begin
         P := FSelected.AbsoluteToLocal(d2Point(FUnsnapMousePos.X, FUnsnapMousePos.Y));
         R := d2Rect(FSelected.Width / 2 - (cnGripSize), -cnRotSize - (cnGripSize), (FSelected.Width) / 2 + (cnGripSize), -cnRotSize + (cnGripSize));
         If FRotateHot <> d2PtInRect(P, R) Then
         Begin
            FRotateHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;
         P := FSelected.AbsoluteToLocal(d2Point(FUnsnapMousePos.X, FUnsnapMousePos.Y));
         R := d2Rect(-cnGripSize, -cnGripSize, cnGripSize, cnGripSize);
         If FLeftTopHot <> d2PtInRect(P, R) Then
         Begin
            FLeftTopHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(R.Right - cnGripSize, -cnGripSize, R.Right + cnGripSize, cnGripSize);
         If FRightTopHot <> d2PtInRect(P, R) Then
         Begin
            FRightTopHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(-cnGripSize, R.Bottom - cnGripSize, cnGripSize, R.Bottom + cnGripSize);
         If FLeftBottomHot <> d2PtInRect(P, R) Then
         Begin
            FLeftBottomHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(R.Right - cnGripSize, R.Bottom - cnGripSize, R.Right + cnGripSize, R.Bottom + cnGripSize);
         If FRightBottomHot <> d2PtInRect(P, R) Then
         Begin
            FRightBottomHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;

         R := FSelected.BoundsRect;
         R := d2Rect(d2RectWidth(R) / 2 - cnGripSize, -cnGripSize, d2RectWidth(R) / 2 + cnGripSize, cnGripSize);
         If FTopHot <> d2PtInRect(P, R) Then
         Begin
            FTopHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(d2RectWidth(R) / 2 - cnGripSize, R.Bottom - cnGripSize, d2RectWidth(R) / 2 + cnGripSize, R.Bottom + cnGripSize);
         If FBottomHot <> d2PtInRect(P, R) Then
         Begin
            FBottomHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(-cnGripSize, d2RectHeight(R) / 2 - cnGripSize, cnGripSize, d2RectHeight(R) / 2 + cnGripSize);
         If FLeftHot <> d2PtInRect(P, R) Then
         Begin
            FLeftHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;
         R := FSelected.BoundsRect;
         R := d2Rect(R.Right - cnGripSize, d2RectHeight(R) / 2 - cnGripSize, R.Right + cnGripSize, d2RectHeight(R) / 2 + cnGripSize);
         If FRightHot <> d2PtInRect(P, R) Then
         Begin
            FRightHot := d2PtInRect(P, R);
            R.TopLeft := FSelected.LocaltoAbsolute(R.TopLeft);
            R.BottomRight := FSelected.LocaltoAbsolute(R.BottomRight);
            d2InflateRect(R, 2, 2);
            AddUpdateRect(R);
         End;
      End;

      If (ssLeft In Shift) And (FSelected <> nil) And (FMoving) Then
      Begin
         P := d2Point(FUnsnapMousePos.X - FDownPos.X, FUnsnapMousePos.Y - FDownPos.Y);
         If (FSelected.Parent <> nil) And (FSelected.Parent.IsVisual) Then
         Begin
            With TD2VisualObject(FSelected.Parent).AbsoluteToLocalVector(d2Vector(P.X, P.Y)) Do
               P := d2Point(X, Y);
         End Else Begin
            With FSelected.AbsoluteToLocalVector(d2Vector(P.X, P.Y)) Do
               P := d2Point(X, Y);
         End;
         FSelected.Position.X := SnapToGridValue(FSelected.Position.X + P.X);
         FSelected.Position.Y := SnapToGridValue(FSelected.Position.Y + P.Y);

         SnapToGridLines(True);

         TD2VisualObject(FSelected).FLocked := True;
         Obj := TD2VisualObject(ObjectByPoint(FUnsnapMousePos.X, FUnsnapMousePos.Y));
         TD2VisualObject(FSelected).FLocked := False;

         If (Obj <> nil) And (Obj <> FSelected.Parent) And (Obj <> FSelected) Then
         Begin
            If FDesignPlaceObject <> nil Then
               FDesignPlaceObject.Repaint;
            FDesignPlaceObject := Obj;
            If FDesignPlaceObject <> nil Then
            Begin
               TD2VisualObject(FDesignPlaceObject).RecalcUpdateRect;
               FDesignPlaceObject.Repaint;
            End;
            If (ssCtrl In Shift) Then
            Begin
               // EndDrag Procedure 4444

               P := FSelected.LocalToAbsolute(d2Point(0, 0));
               FSelected.Parent := FDesignPlaceObject;
               P := FDesignPlaceObject.AbsoluteToLocal(P);
               FSelected.Position.X := P.X;
               FSelected.Position.Y := P.Y;

               If GvarD2Designer = nil Then
               Begin
                  // Update Typhon IDE
                  GvarD2Designer.Modified(Owner);
                  GvarD2Designer.SelectObject(Owner, FSelected, []);
               End;
            End;
         End Else Begin
            If FDesignPlaceObject <> nil Then
               FDesignPlaceObject.Repaint;
            FDesignPlaceObject := nil;
         End;
      End;
      If (ssLeft In Shift) And (FSelected <> nil) Then
      Begin
         If (ssLeft In Shift) And (FSelected <> nil) And (FRotate) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P1 := FSelected.AbsoluteToLocal(FDownPos);
            If d2VectorCrossProductZ(d2Vector(P.X - (FSelected.Width / 2), P.Y - (FSelected.Height / 2)), d2Vector(P1.X - (FSelected.Width / 2), P1.Y - (FSelected.Height / 2))) < 0 Then
               FSelected.RotateAngle := FSelected.RotateAngle + d2RadToDeg(ArcCos(d2VectorAngleCosine(d2Vector(P.X - (FSelected.Width / 2), P.Y - (FSelected.Height / 2)), d2Vector(P1.X - (FSelected.Width / 2), P1.Y - (FSelected.Height / 2)))))
            Else
               FSelected.RotateAngle := FSelected.RotateAngle - d2RadToDeg(ArcCos(d2VectorAngleCosine(d2Vector(P.X - (FSelected.Width / 2), P.Y - (FSelected.Height / 2)), d2Vector(P1.X - (FSelected.Width / 2), P1.Y - (FSelected.Height / 2)))));
         End;
         If (ssLeft In Shift) And (FSelected <> nil) And (FLeftTop) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P := SnapPointToGridLines(P);
            FSelected.BoundsRect := d2Rect(P.X, P.Y, R.Right, R.Bottom);
         End;
         If (ssLeft In Shift) And (FSelected <> nil) And (FRightTop) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P := SnapPointToGridLines(P);
            FSelected.BoundsRect := d2Rect(R.Left, P.Y, P.X, R.Bottom);
         End;
         If (ssLeft In Shift) And (FSelected <> nil) And (FLeftBottom) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P := SnapPointToGridLines(P);
            FSelected.BoundsRect := d2Rect(P.X, R.Top, R.Right, P.Y);
         End;
         If (ssLeft In Shift) And (FSelected <> nil) And (FRightBottom) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P := SnapPointToGridLines(P);
            FSelected.BoundsRect := d2Rect(R.Left, R.Top, P.X, P.Y);
         End;

         If (ssLeft In Shift) And (FSelected <> nil) And (FTop) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P := SnapPointToGridLines(P);
            FSelected.BoundsRect := d2Rect(R.Left, P.Y, R.Right, R.Bottom);
         End;
         If (ssLeft In Shift) And (FSelected <> nil) And (FBottom) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P := SnapPointToGridLines(P);
            FSelected.BoundsRect := d2Rect(R.Left, R.Top, R.Right, P.Y);
         End;
         If (ssLeft In Shift) And (FSelected <> nil) And (FLeft) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P := SnapPointToGridLines(P);
            FSelected.BoundsRect := d2Rect(P.X, R.Top, R.Right, R.Bottom);
         End;
         If (ssLeft In Shift) And (FSelected <> nil) And (FRight) Then
         Begin
            R := FSelected.BoundsRect;
            P := FSelected.AbsoluteToLocal(FMousePos);
            P := SnapPointToGridLines(P);
            FSelected.BoundsRect := d2Rect(R.Left, R.Top, P.X, R.Bottom);
         End;
         FDownPos := FMousePos;
      End;
      Exit;
   End;

   If Not FDesignTime Then
   Begin
      If (FCaptured <> nil) Then
      Begin
         P := FCaptured.AbsoluteToLocal(d2Point(FMousePos.X, FMousePos.Y));
         TD2VisualObject(FCaptured).MouseMove(Shift, P.X, P.Y, 0, 0);
         Exit;
      End;
      Obj := TD2VisualObject(ObjectByPoint(FMousePos.X, FMousePos.Y));
      If (Obj <> nil) Then
      Begin
         If (Obj.ShowHint) Then
            Hint := Obj.Hint
         Else
            Hint := '';

         If ((TD2VisualObject(Obj).QueryInterface(Id2SizeGrip, SG) = 0) And Assigned(SG)) Then
            NewCursor := crSizeNWSE
         Else
            NewCursor := Obj.Cursor;

         If (Obj <> FHovered) Then
         Begin
            If FHovered <> nil Then
            Begin
               TD2VisualObject(FHovered).MouseInObject := False;
               TD2VisualObject(FHovered).MouseLeave;
               TD2VisualObject(FHovered).RemoveFreeNotify(Self);
            End;
            FHovered := Obj;
            TD2VisualObject(FHovered).MouseInObject := True;
            TD2VisualObject(FHovered).MouseEnter;
            TD2VisualObject(FHovered).AddFreeNotify(Self);
         End;

         P := Obj.AbsoluteToLocal(d2Point(FMousePos.X, FMousePos.Y));
         TD2VisualObject(Obj).MouseMove(Shift, P.X, P.Y, 0, 0);
      End Else Begin
         If FHovered <> nil Then
         Begin
            TD2VisualObject(FHovered).MouseInObject := False;
            TD2VisualObject(FHovered).MouseLeave;
            TD2VisualObject(FHovered).RemoveFreeNotify(Self);
            FHovered := nil;
         End;
      End;
   End;
   Cursor := NewCursor;
   FDownPos := FMousePos;
End;

Procedure TD2CustomScene.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Var
   P: TD2Point;
   Obj: TD2VisualObject;
Begin
   Inherited;

   If FDesignPlaceObject <> nil Then
      FDesignPlaceObject.Repaint;
   FDesignPlaceObject := nil;
   AddUpdateRectsFromGridLines;
   SetLength(FDesignGridLines, 0);

   If (FSelected <> nil) And FMoving Then
   Begin
      If (FSelected.Parent <> nil) And (TD2Object(FSelected.Parent).IsVisual) Then
         TD2VisualObject(FSelected.Parent).Realign;

      If (GvarD2Designer <> nil) Then
         GvarD2Designer.Modified(Owner);
      If FSelected.Parent = nil Then
         RealignRoot;
   End;

   If (FSelected <> nil) And (FLeftTop Or FLeftBottom Or FLeftBottom Or FRightBottom) Then
   Begin
      If (FSelected.Parent <> nil) And (TD2Object(FSelected.Parent).IsVisual) Then
         TD2VisualObject(FSelected.Parent).Realign;

      If (GvarD2Designer <> nil) Then
         GvarD2Designer.Modified(Owner);
      If FSelected.Parent = nil Then
         RealignRoot;
   End;

   FMoving := False;
   FLeftTop := False;
   FLeftBottom := False;
   FRightTop := False;
   FRightBottom := False;
   FTop := False;
   FBottom := False;
   FLeft := False;
   FRight := False;
   FRotate := False;

   If FDragging Then
   Begin
      FDragging := False;
      ReleaseCapture;     //=== ct7777 ===========
   End;

   If FResizing Then
   Begin
      FResizing := False;
      ReleaseCapture;   //=== ct7777 ===========
   End;

   If Not FDesignTime Then
   Begin
      If (FCaptured <> nil) Then
      Begin
         P := FCaptured.AbsoluteToLocal(d2Point(FMousePos.X, FMousePos.Y));
         TD2VisualObject(FCaptured).MouseUp(Button, Shift, P.X, P.Y);
         Exit;
      End;
      Obj := TD2VisualObject(ObjectByPoint(FMousePos.X, FMousePos.Y));
      If (Obj <> nil) Then
      Begin
         P := Obj.AbsoluteToLocal(d2Point(FMousePos.X, FMousePos.Y));
         TD2VisualObject(Obj).MouseUp(Button, Shift, P.X, P.Y);
      End;
   End;
End;

Function TD2CustomScene.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean;
Var
   Obj: TD2VisualObject;
Begin
   Result := Inherited DoMouseWheel(Shift, WheelDelta, MousePos);

   If Not FDesignTime Then
   Begin
      {$IFDEF ORCA2D_USE_CARBON}
//    WheelDelta:=WheelDelta * 40;
      {$ENDIF}
      If (FCaptured <> nil) Then
      Begin
         TD2VisualObject(FCaptured).MouseWheel(Shift, WheelDelta, Result);
         Exit;
      End;
      Obj := TD2VisualObject(ObjectByPoint(FMousePos.X, FMousePos.Y));
      While (Obj <> nil) Do
      Begin
         TD2VisualObject(Obj).MouseWheel(Shift, WheelDelta, Result);
         If Result Then
            Break;
         If (Obj.Parent <> nil) And (Obj.Parent.IsVisual) Then
            Obj := TD2VisualObject(Obj.Parent)
         Else
            Obj := nil;
      End;
   End;
End;

Procedure TD2CustomScene.KeyDown(Var Key: Word; Shift: TShiftState);
Var
   List: TList;
   i, CurIdx: Integer;
   Found: Boolean;
   K: Word;
   O: TComponent;
   Ch, Char: System.Widechar;
Begin
   Inherited;
   FShift := Shift;
   If ssMeta In Shift Then
   Begin
      K := 0;
      Case Key Of
         67:
         Begin
            Ch := 'c';
            UnicodeKeyDown(K, Ch, [ssCtrl]);
            UnicodeKeyUp(K, Ch, [ssCtrl]);
            Exit;
         End;
         86:
         Begin
            Ch := 'v';
            UnicodeKeyDown(K, Ch, [ssCtrl]);
            UnicodeKeyUp(K, Ch, [ssCtrl]);
            Exit;
         End;
         88:
         Begin
            Ch := 'x';
            UnicodeKeyDown(K, Ch, [ssCtrl]);
            UnicodeKeyUp(K, Ch, [ssCtrl]);
            Exit;
         End;
         99:
         Begin
            Ch := 'z';
            UnicodeKeyDown(K, Ch, [ssCtrl]);
            UnicodeKeyUp(K, Ch, [ssCtrl]);
            Exit;
         End;
      End;
   End;

   If (Key = VK_ESCAPE) Or (Key = VK_RETURN) Or (Key = VK_LEFT) Or (Key = VK_RIGHT) Or (Key = VK_UP) Or (Key = VK_DOWN) Or (Key = VK_HOME) Or (Key = VK_END) Or (Key = VK_ADD) Or (Key = VK_SUBTRACT) Then
   Begin
      TD2VisualObject(Root).DialogKey(Key, Shift);
      If Key = 0 Then
         Exit;
   End;

   If (Key = VK_ESCAPE) Then
   Begin
      O := Owner;
      While O <> nil Do
      Begin
         If (O Is TCustomForm) And (fsModal In TCustomForm(O).FormState) Then
         Begin
            TCustomForm(O).ModalResult := mrCancel;
            Key := 0;
            Break;
         End;
         O := O.Owner;
      End;
      If Key = 0 Then
         Exit;
   End;

   If (Key = VK_TAB) And (Root <> nil) Then
   Begin
      Key := 0;
      List := TList.Create;
      TD2VisualObject(Root).GetTabOrderList(List, True);

      Found := False;
      If ssShift In Shift Then
      Begin
         If FFocused <> nil Then
            CurIdx := List.IndexOf(FFocused) - 1
         Else
            CurIdx := List.Count - 1;
         If CurIdx > 0 Then
            For i := CurIdx - 1 Downto 0 Do
               If TD2Object(List[i]).isVisual And (TD2VisualObject(List[i]).CheckParentVisible) And (TD2VisualObject(List[i]).CanFocused) And (TD2VisualObject(List[i]).AbsoluteEnabled) Then
               Begin
                  TD2VisualObject(List[i]).SetFocus;
                  Found := True;
                  Break;
               End;

         If Not Found Then
            If (List.Count > 2) And (CurIdx < List.Count) Then
               For i := List.Count - 1 Downto CurIdx Do
                  If TD2Object(List[i]).isVisual And (TD2VisualObject(List[i]).CheckParentVisible) And (TD2VisualObject(List[i]).CanFocused) And (TD2VisualObject(List[i]).AbsoluteEnabled) Then
                  Begin
                     TD2VisualObject(List[i]).SetFocus;
                     Break;
                  End;
      End Else Begin
         If FFocused <> nil Then
            CurIdx := List.IndexOf(FFocused) + 1
         Else
            CurIdx := 0;

         If (List.Count > 2) And (CurIdx < List.Count) Then
            For i := CurIdx To List.Count - 1 Do
               If TD2Object(List[i]).isVisual And (TD2VisualObject(List[i]).CheckParentVisible) And (TD2VisualObject(List[i]).CanFocused) And (TD2VisualObject(List[i]).AbsoluteEnabled) Then
               Begin
                  TD2VisualObject(List[i]).SetFocus;
                  Found := True;
                  Break;
               End;

         If Not Found Then
            If CurIdx > 0 Then
               For i := 0 To CurIdx - 1 Do
                  If TD2Object(List[i]).isVisual And (TD2VisualObject(List[i]).CheckParentVisible) And (TD2VisualObject(List[i]).CanFocused) And (TD2VisualObject(List[i]).AbsoluteEnabled) Then
                  Begin
                     TD2VisualObject(List[i]).SetFocus;
                     Break;
                  End;
      End;
      List.Free;
      Exit;
   End;

   If FFocused <> nil Then
   Begin
      Char := #0;
      FFocused.KeyDown(Key, Char, Shift);
   End;
End;

Procedure TD2CustomScene.UTF8KeyPress(Var UTF8Key: TUTF8Char);
Var
   K: Word;
   C: System.Widechar;
Begin
   K := 0;
   C := UTF8ToUTF16(UTF8Key)[1];
   Case C Of
      #3:
      Begin
         C := 'c';
         UnicodeKeyDown(K, C, FShift);
         UnicodeKeyUp(K, C, FShift);
      End;
      #$16:
      Begin
         C := 'v';
         UnicodeKeyDown(K, C, FShift);
         UnicodeKeyUp(K, C, FShift);
      End;
      #$18:
      Begin
         C := 'x';
         UnicodeKeyDown(K, C, FShift);
         UnicodeKeyUp(K, C, FShift);
      End;
      #$1A:
      Begin
         C := 'z';
         UnicodeKeyDown(K, C, FShift);
         UnicodeKeyUp(K, C, FShift);
      End;
      Else UnicodeKeyDown(K, C, FShift);
         UnicodeKeyUp(K, C, FShift);
   End;
   UTF8Key := '';
End;

Procedure TD2CustomScene.KeyPress(Var Key: Char);
Begin
   Inherited KeyPress(Key);
End;

Procedure TD2CustomScene.KeyUp(Var Key: Word; Shift: TShiftState);
Var
   Char: system.Widechar;
Begin
   Inherited;
   FShift := [];
   If FDesignTime Then
   Begin
      If (Key = VK_DELETE) And (FSelected <> nil) Then
      Begin
         FSelected.Free;
         FSelected := nil;
      End;
   End;

   If FFocused <> nil Then
   Begin
      Char := #0;
      FFocused.KeyUp(Key, Char, Shift);
   End;
End;


Procedure TD2CustomScene.RealignRoot;
Begin
   If (FChildren <> nil) And (FChildren.Count > 0) And (TD2Object(FChildren[0]).isVisual) Then
      With TD2VisualObject(FChildren[0]) Do
      Begin
         If (RotateAngle = 90) Or (RotateAngle = -90) Or (RotateAngle = -270) Or (RotateAngle = 270) Then
            SetBounds((Self.Width - Self.Height) / 2, -(Self.Width - Self.Height) / 2, Self.Height / Scale.Y, Self.Width / Scale.X)
         Else
            SetBounds(0, 0, Self.Width / Scale.X, Self.Height / Scale.Y);
      End;
End;

Procedure TD2CustomScene.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
Begin
   Inherited;
   If (Canvas <> nil) And (HandleAllocated) Then
   Begin
      Canvas.ResizeBuffer(AWidth, AHeight);
      RealignRoot;
   End;
End;

Procedure TD2CustomScene.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FActiveControl) Then
      FActiveControl := nil;
   If (Operation = opRemove) And (AComponent = FCaptured) Then
      FCaptured := nil;
   If (Operation = opRemove) And (AComponent = FSelected) Then
      FSelected := nil;
   If (Operation = opRemove) And (AComponent = FHovered) Then
      FHovered := nil;
   If (Operation = opRemove) And (AComponent = FFocused) Then
      FFocused := nil;
   If (Operation = opRemove) And (AComponent = FDesignPlaceObject) Then
      FDesignPlaceObject := nil;
   If (Operation = opRemove) And (AComponent = FStyle) Then
      Style := nil;
   If (Operation = opRemove) And (AComponent = FOpenInFrame) Then
      FOpenInFrame := nil;
End;

Procedure TD2CustomScene.SetFill(Const Value: TD2Brush);
Begin
   FFill.Assign(Value);
End;

Procedure TD2CustomScene.FillChanged(Sender: TObject);
Begin
   SetLength(FUpdateRects, 0);
   AddUpdateRect(d2Rect(0, 0, Width, Height));
End;

Procedure TD2CustomScene.SetSnapGridShow(Const Value: Boolean);
Begin
   If FSnapGridShow <> Value Then
   Begin
      FSnapGridShow := Value;
      SetLength(FUpdateRects, 0);
      AddUpdateRect(d2Rect(0, 0, Width, Height));
   End;
End;

Procedure TD2CustomScene.SetSnapGridSize(Const Value: Single);
Begin
   If FSnapGridSize <> Value Then
   Begin
      FSnapGridSize := Value;
      If FSnapGridSize < 0.01 Then
         FSnapGridSize := 0.01;
      If FsnapGridShow Then
         Repaint;
   End;
End;

Procedure TD2CustomScene.InsertObject(Const aClassName: String);
Var
   P: TPoint;
   Obj: TD2Object;
   OldSel: TD2VisualObject;
   InsertPos: TD2Point;
Begin
   If GetClass(aClassName) <> nil Then
   Begin
      If GetClass(aClassName).InheritsFrom(TD2Object) Then
      Begin
         If (FSelected = nil) And (Root <> nil) And (Root.IsVisual) Then
            FSelected := Root.Visual;
         If FSelected <> nil Then
         Begin
            Try
               Obj := TD2ObjectClass(GetClass(aClassName)).Create(Owner);
               OldSel := FSelected;

               If GvarD2Designer <> nil Then
                  Obj.Name := GvarD2Designer.UniqueName(Self.Owner, Obj.ClassName);

               FSelected.AddObject(Obj);

               If GvarD2Designer <> nil Then
               Begin
                  GvarD2Designer.Add2DObject(Obj);
                  GvarD2Designer.SelectObject(Owner, Obj, []);
                  If Assigned(FDesignChangeSelection) Then
                     FDesignChangeSelection(Self);
                  GvarD2Designer.Modified(Owner);
               End;

               //.....

               If Obj.IsVisual Then
               Begin
                  If GetPropInfo(Obj.ClassInfo, 'Text', [tkString, tkLString, tkWString]) <> nil Then
                     SetStrProp(Obj, 'Text', Copy(Obj.ClassName, 4, Length(Obj.ClassName)));

                  If (Owner Is TWinControl) And (FPopupPos.X > 0) Then
                  Begin
                     If Pos('Item', Obj.ClassName) = 0 Then
                     Begin
                        P := ScreenToClient(FPopupPos);
                        InsertPos := OldSel.AbsoluteToLocal(d2Point(P.X, P.Y));

                        TD2VisualObject(Obj).Position.X := InsertPos.X;
                        TD2VisualObject(Obj).Position.Y := InsertPos.Y;
                     End;
                  End;
                  FPopupPos := Point(-1, -1);
                  FSelected := TD2VisualObject(Obj);
                  TD2VisualObject(FSelected).DesignInsert;
                  TD2VisualObject(FSelected).DesignSelect;
               End;

               If OldSel.IsVisual Then
                  OldSel.Visual.Realign;

            Finally
            End;
         End Else
         If (FChildren = nil) Or (FChildren.Count = 0) Then
         Begin
            // add root object
            Try
               Obj := TD2ObjectClass(GetClass(aClassName)).Create(Owner);

               If GvarD2Designer <> nil Then
                  Obj.Name := GvarD2Designer.UniqueName(Self.Owner, Obj.ClassName);

               AddObject(Obj);

               If GvarD2Designer <> nil Then
               Begin
                  GvarD2Designer.Add2DObject(Obj);
                  GvarD2Designer.SelectObject(Owner, Obj, []);
                  If Assigned(FDesignChangeSelection) Then
                     FDesignChangeSelection(Self);
                  GvarD2Designer.Modified(Owner);
               End;

               If Obj.IsVisual Then
               Begin
                  If (Owner Is TWinControl) And (FPopupPos.X > 0) Then
                  Begin
                     P := TWinControl(Owner).ScreenToClient(FPopupPos);
                     InsertPos := d2Point(P.X, P.Y);
                     TD2VisualObject(Obj).Position.X := P.X;
                     TD2VisualObject(Obj).Position.Y := P.Y;
                  End;
                  FPopupPos := Point(-1, -1);
                  FSelected := TD2VisualObject(Obj);
                  TD2VisualObject(FSelected).DesignSelect;
               End;
            Finally
            End;
         End;
      End Else Begin
         FInsertObject := aClassName;
      End;
   End Else
      FInsertObject := '';
End;

Procedure TD2CustomScene.Resize;
Begin
   Inherited;
End;

Procedure TD2CustomScene.doDesignPopupDesignHide(Sender: TObject);
Begin
   If (FSelected <> nil) And (FSelected <> Root) Then
   Begin
      FSelected.DesignHide := Not FSelected.DesignHide;
      If FSelected.DesignHide And (FSelected.Parent.IsVisual) Then
      Begin
         FSelected := TD2VisualObject(FSelected.Parent);
      End;
      AddUpdateRect(d2Rect(0, 0, Width, Height));
   End;
End;

Procedure TD2CustomScene.doDesignPopupDelete(Sender: TObject);
Var
   Obj: TD2VisualObject;
Begin
   If (FSelected <> nil) And (FSelected <> Root) Then
   Begin
      Obj := FSelected;
      If (Obj.Parent <> nil) And (Obj.Parent.IsVisual) Then
      Begin
         FSelected := TD2VisualObject(Obj.Parent);
         TD2VisualObject(FSelected).DesignSelect;
      End Else Begin
         FSelected := TD2VisualObject(Root);
         If FSelected <> nil Then
            TD2VisualObject(FSelected).DesignSelect;
      End;
      Obj.Free;
   End;
End;

Procedure TD2CustomScene.doDesignPopupAddItem(Sender: TObject);
Begin
   If FSelected <> nil Then
   Begin
      InsertObject(TMenuItem(Sender).Hint);
   End;
End;

Function StripHotkey(S: String): String;
Begin
   Result := S;
End;

Procedure TD2CustomScene.doDesignPopupAdd(Sender: TObject);
Var
   S: String;
Begin
   S := StripHotkey(TMenuItem(Sender).Caption);
   If (S <> '') Then
      S := 'TD2' + S;
   InsertObject(S);
End;

Procedure TD2CustomScene.doDesignPopupReorder(Sender: TObject);
Begin
   If FSelected = nil Then
      Exit;
   If Not (Sender Is TMenuItem) Then
      Exit;

   If TMenuItem(Sender).Tag = 1010 Then
      FSelected.BringToFront;

   If TMenuItem(Sender).Tag = 1020 Then
      FSelected.SendToBack;

   If GvarD2Designer <> nil Then
   Begin
      GvarD2Designer.Modified(Owner);
      GvarD2Designer.SelectObject(Owner, FSelected, []);
   End;
End;

Procedure TD2CustomScene.doDesignPopupGrid(Sender: TObject);
Begin
   Case TMenuItem(Sender).Tag Of
      1: FSnapGridShow := TMenuItem(Sender).Checked;
      2:
      Begin
         FSnapToGrid := TMenuItem(Sender).Checked;
         FSnapToLines := Not FSnapToGrid;
      End;
      3:
      Begin
         FSnapToLines := TMenuItem(Sender).Checked;
         FSnapToGrid := Not FSnapToLines;
      End;
      4:
      Begin
         FSnapToLines := False;
         FSnapToGrid := False;
      End;
   End;
   SetLength(FUpdateRects, 0);
   AddUpdateRect(d2Rect(0, 0, Width, Height));
End;

//..........................................................
Var
   ClipboardText: Ansistring;

Procedure TD2CustomScene.DesignReCreateSubObjects(aObj, aParent: TD2Object);
Var
   j: Integer;
   jObj: TD2Object;
   xOldO: TComponent;
Begin
   If GvarD2Designer = nil Then
      Exit;
   If aObj = nil Then
      Exit;

   xOldO := aObj.Owner;

   //........................................
   jObj := aObj.Clone(Self.Owner);
   If aObj = nil Then
      Exit;

   //........................................
   aObj.Stored := False;
   If xOldO <> nil Then
      xOldO.RemoveComponent(aObj);

   //........................................
   If aParent <> nil Then
      aParent.AddObject(jObj)
   Else
      Root.AddObject(jObj);

   jObj.Name := GvarD2Designer.UniqueName(Self.Owner, jObj.ClassName);
   GvarD2Designer.Add2DObject(jObj);

   If jObj.ChildrenCount > 0 Then
      For j := 0 To jObj.ChildrenCount - 1 Do
         DesignReCreateSubObjects(jObj.Children[j], jObj);
End;

Procedure TD2CustomScene.doDesignPopupClone(Sender: TObject);
Var
   xObj: TD2Object;
   i: Integer;
Begin
   If FSelected = nil Then
      Exit;

   Try
      xObj := FSelected.Clone(Self.Owner);
      If xObj = nil Then
         Exit;

      If FSelected.Parent <> nil Then
         FSelected.Parent.AddObject(xObj)
      Else
         Root.AddObject(xObj);

      If GvarD2Designer <> nil Then
      Begin
         xObj.Name := GvarD2Designer.UniqueName(Self.Owner, xObj.ClassName);
         GvarD2Designer.Add2DObject(xObj);

         If xObj.ChildrenCount > 0 Then
            For i := 0 To xObj.ChildrenCount - 1 Do
               DesignReCreateSubObjects(xObj.Children[i], xObj);

         GvarD2Designer.Modified(Owner);
         GvarD2Designer.SelectObject(Owner, xObj, []);
      End;

   Finally
   End;
End;

Procedure TD2CustomScene.doDesignPopupPaste(Sender: TObject);
Var
   S: TStringStream;
   xObj: TD2Object;
   i: Integer;
Begin
   If ClipboardText = '' Then
      Exit;

   Try
      S := TStringStream.Create(ClipboardText);
      xObj := CreateD2ObjectFromStream(Owner, S);

      If FSelected <> nil Then
         xObj.Parent := FSelected
      Else
         xObj.Parent := Root;

      If GvarD2Designer <> nil Then
      Begin
         xObj.Name := GvarD2Designer.UniqueName(Self.Owner, xObj.ClassName);
         GvarD2Designer.Add2DObject(xObj);

         If xObj.ChildrenCount > 0 Then
            For i := 0 To xObj.ChildrenCount - 1 Do
               DesignReCreateSubObjects(xObj.Children[i], xObj);

         GvarD2Designer.Modified(Owner);
         GvarD2Designer.SelectObject(Owner, xObj, []);
      End;

   Finally
      S.Free;
   End;
End;

Procedure TD2CustomScene.doDesignPopupLoadFromFile(Sender: TObject);
Var
   S: TMemoryStream;
   xObj: TD2Object;
   i: Integer;
   xOpenDlg: TOpenDialog;
Begin
   Try
      xOpenDlg := TOpenDialog.Create(nil);
      xOpenDlg.Filter := 'd2Scene Files|*.d2scene;*.d2';

      If xOpenDlg.Execute Then
      Begin
         S := TMemoryStream.Create;
         S.LoadFromFile(xOpenDlg.FileName);
         xObj := CreateD2ObjectFromStream(Owner, S);

         If FSelected <> nil Then
            xObj.Parent := FSelected
         Else
            xObj.Parent := Root;

         If GvarD2Designer <> nil Then
         Begin
            xObj.Name := GvarD2Designer.UniqueName(Self.Owner, xObj.ClassName);
            GvarD2Designer.Add2DObject(xObj);

            If xObj.ChildrenCount > 0 Then
               For i := 0 To xObj.ChildrenCount - 1 Do
                  DesignReCreateSubObjects(xObj.Children[i], xObj);

            GvarD2Designer.Modified(Owner);
            GvarD2Designer.SelectObject(Owner, xObj, []);
         End;

         S.Free;
      End;

   Finally
      xOpenDlg.Free;
   End;
End;


Procedure TD2CustomScene.doDesignPopupFixObj(Sender: TObject);
Begin
   If FSelected = nil Then
      Exit;

   If GvarD2Designer <> nil Then
   Begin
      If FSelected.Name = '' Then
         FSelected.Name := GvarD2Designer.UniqueName(Self.Owner, FSelected.ClassName);

      GvarD2Designer.Add2DObject(FSelected);
      GvarD2Designer.Modified(Owner);
      GvarD2Designer.SelectObject(Owner, FSelected, []);
   End;
End;

Procedure TD2CustomScene.doDesignPopupCopy(Sender: TObject);
Var
   S: TStringStream;
Begin
   If FSelected = nil Then
      Exit;

   Try
      S := TStringStream.Create('');
      FSelected.SaveToStream(S);
      ClipboardText := S.DataString;
   Finally
      S.Free;
   End;
End;

Procedure TD2CustomScene.doDesignPopupCut(Sender: TObject);
Begin
   If FSelected = nil Then
      Exit;

   doDesignPopupCopy(self);
   doDesignPopupDelete(self);
End;

Procedure TD2CustomScene.doDesignPopupSaveToFile(Sender: TObject);
Var
   S: TMemoryStream;
   xSaveDlg: TSaveDialog;
Begin
   If self.Selected = nil Then
      Exit;

   Try
      xSaveDlg := TSaveDialog.Create(nil);
      xSaveDlg.Filter := 'd2Scene Files|*.d2scene;*.d2';

      If xSaveDlg.Execute Then
      Begin
         S := TMemoryStream.Create;
         S.Position := 0;
         self.Selected.SaveToStream(S);
         s.SaveToFile(xSaveDlg.FileName);
      End;

   Finally
      xSaveDlg.Free;
   End;
End;

Procedure TD2CustomScene.doDesignPopupDeleteAnimations(Sender: TObject);
Var
   i: Integer;
Begin
   If FSelected = nil Then
      Exit;
   If FSelected.ChildrenCount = 0 Then
      Exit;

   For i := FSelected.ChildrenCount - 1 Downto 0 Do
   Begin
      If (FSelected.Children[i] Is TD2Animation) Then
      Begin
         If GvarD2Designer <> nil Then
            GvarD2Designer.Delete2DObject(FSelected.Children[i]);
         TD2Animation(FSelected.Children[i]).Free;
      End;
   End;

   If GvarD2Designer <> nil Then
      GvarD2Designer.Modified(Owner);
End;

Procedure TD2CustomScene.doDesignPopupDeleteEffects(Sender: TObject);
Var
   i: Integer;
Begin
   If FSelected = nil Then
      Exit;
   If FSelected.ChildrenCount = 0 Then
      Exit;

   For i := FSelected.ChildrenCount - 1 Downto 0 Do
   Begin
      If (FSelected.Children[i] Is TD2Effect) Then
      Begin
         If GvarD2Designer <> nil Then
            GvarD2Designer.Delete2DObject(FSelected.Children[i]);
         TD2Effect(FSelected.Children[i]).Free;
      End;
   End;

   If GvarD2Designer <> nil Then
      GvarD2Designer.Modified(Owner);
End;

Procedure TD2CustomScene.doDesignPopupCreateStyle(Sender: TObject);
Var
   R: TD2Resources;
   CurrentRes, Link: TD2VisualObject;
   LinkedList: TList;
   i: Integer;
Begin
   If FSelected = nil Then
      Exit;
   If Not Assigned(FSelected.Scene) Then
      Exit;

   If (FSelected.Scene.GetStyle = nil) Then
   Begin
      R := TD2Resources.Create(FSelected.Scene.GetOwner);
      R.Root := TD2Background.Create(nil);

      If GvarD2Designer <> nil Then
      Begin
         R.Name := GvarD2Designer.UniqueName(Self.Owner, R.ClassName);
         GvarD2Designer.AddObject(R);
      End;

      FSelected.Scene.SetStyle(R);
   End Else Begin
      R := FSelected.Scene.GetStyle;
   End;

   If (TD2Control(FSelected).Resource = '') And (GvarD2Designer <> nil) Then
   Begin
      TD2Control(FSelected).Resource := GvarD2Designer.UniqueName(Self.Owner, FSelected.Name + 'Style');
   End;

   If (R.Root = nil) Then
   Begin
      R.Root := TD2Background.Create(nil);

      If GvarD2Designer <> nil Then
      Begin
         R.Name := GvarD2Designer.UniqueName(Self.Owner, R.ClassName);
         GvarD2Designer.AddObject(R);
      End;
   End;

   If (R.Root.FindResource(TD2Control(FSelected).Resource) = nil) Then
   Begin
      CurrentRes := TD2Control(FSelected).GetResourceObject;
      If CurrentRes = nil Then
         Exit;
      CurrentRes.Align := vaNone;
      CurrentRes.Parent := R.Root;
      If GvarD2Designer <> nil Then
      Begin
         CurrentRes.ResourceName := GvarD2Designer.UniqueName(Self.Owner, FSelected.Name + 'Style');
         TD2Control(FSelected).Resource := CurrentRes.ResourceName;
      End Else Begin
         CurrentRes.ResourceName := FSelected.Name;
         TD2Control(FSelected).Resource := CurrentRes.ResourceName;
      End;


      LinkedList := TList.Create;
      TD2Control(CurrentRes).AddControlsToList(LinkedList);
      For i := 0 To LinkedList.Count - 1 Do
      Begin
         If R.Root.FindResource(TD2Control(LinkedList[i]).Resource) = nil Then
         Begin
            Link := TD2Control(LinkedList[i]).GetResourceObject;
            Link.Align := vaNone;
            Link.Parent := R.Root;
            If GvarD2Designer <> nil Then
            Begin
               Link.ResourceName := GvarD2Designer.UniqueName(Self.Owner, FSelected.Name + TD2Control(LinkedList[i]).ResourceName);
               TD2Control(LinkedList[i]).Resource := Link.ResourceName;
            End;
         End;
      End;
      LinkedList.Free;
   End;

   If GvarD2Designer <> nil Then
   Begin
      GvarD2Designer.EditStyle(R, TD2Control(FSelected).Resource);
   End;
End;

Procedure TD2CustomScene.doDesignPopupEditStyle(Sender: TObject);
Var
   R: TD2Resources;
   CurrentRes, Link: TD2VisualObject;
   LinkedList: TList;
   i: Integer;
   StyleName: String;
Begin
   If FSelected = nil Then
      Exit;
   If Not Assigned(FSelected.Scene) Then
      Exit;

   If (FSelected.Scene.GetStyle = nil) Then
   Begin
      R := TD2Resources.Create(FSelected.Scene.GetOwner);
      R.Root := TD2Background.Create(nil);
      If GvarD2Designer <> nil Then
      Begin
         R.Name := GvarD2Designer.UniqueName(Self.Owner, R.ClassName);
         GvarD2Designer.AddObject(R);
      End;
      FSelected.Scene.SetStyle(R);
   End Else Begin
      R := FSelected.Scene.GetStyle;
   End;


   StyleName := TD2Control(FSelected).Resource;
   If StyleName = '' Then
   Begin
      StyleName := FSelected.ClassName + 'Style';
      Delete(StyleName, 1, 3);
   End;
   If (R.Root = nil) Then
   Begin
      R.Root := TD2Background.Create(nil);

      If GvarD2Designer <> nil Then
      Begin
         R.Name := GvarD2Designer.UniqueName(Self.Owner, R.ClassName);
         GvarD2Designer.AddObject(R);
      End;
   End;

   If (R.Root.FindResource(StyleName) = nil) Then
   Begin
      CurrentRes := TD2Control(FSelected).GetResourceObject;
      If CurrentRes = nil Then
         Exit;
      CurrentRes.Align := vaNone;
      CurrentRes.Parent := R.Root;


      LinkedList := TList.Create;
      TD2Control(CurrentRes).AddControlsToList(LinkedList);
      For i := 0 To LinkedList.Count - 1 Do
      Begin
         StyleName := TD2Control(LinkedList[i]).Resource;
         If StyleName = '' Then
         Begin
            StyleName := TD2Control(LinkedList[i]).ClassName + 'Style';
            Delete(StyleName, 1, 3);
         End;
         If R.Root.FindResource(StyleName) = nil Then
         Begin
            Link := TD2Control(LinkedList[i]).GetResourceObject;
            Link.Align := vaNone;
            Link.ResourceName := StyleName;
            Link.Parent := R.Root;
         End;
      End;
      LinkedList.Free;
   End;

   If GvarD2Designer <> nil Then
   Begin
      StyleName := TD2Control(FSelected).Resource;
      If StyleName = '' Then
      Begin
         StyleName := FSelected.ClassName + 'Style';
         Delete(StyleName, 1, 3);
      End;
      GvarD2Designer.EditStyle(R, StyleName);
   End;
End;

Procedure TD2CustomScene.doDesignTabOrderBtnClick(Sender: TObject);
Var
   Obj: TD2VisualObject;
Begin
   If TD2Button(Sender).TextW = 'Up' Then
   Begin
      Obj := TD2VisualObject(TD2Listbox(TD2Button(Sender).TagObject).Selected.TagObject);
      Obj.TabOrder := Obj.TabOrder - 1;
   End;
   If TD2Button(Sender).TextW = 'Down' Then
   Begin
      Obj := TD2VisualObject(TD2Listbox(TD2Button(Sender).TagObject).Selected.TagObject);
      Obj.TabOrder := Obj.TabOrder + 1;
   End;
   doDesignTabOrderRebuildList(TD2Listbox(TD2Button(Sender).TagObject));
End;

Procedure TD2CustomScene.doDesignTabOrderRebuildList(ListBox: TD2VisualObject);
Var
   Item: TD2ListboxItem;
   List: TD2ListBox;
   Idx, i: Integer;
   L: TList;
Begin
   If FSelected = nil Then
      Exit;
   If FSelected.Parent = nil Then
      Exit;
   If Not FSelected.Parent.IsVisual Then
      Exit;
   List := TD2ListBox(Listbox);
   If List.Selected <> nil Then
      Idx := TD2VisualObject(List.Selected.TagObject).TabOrder
   Else
      Idx := 0;

   List.BeginUpdate;
   List.Clear;
   L := TList.Create;
   TD2VisualObject(FSelected.Parent).GetTabOrderList(L, False);
   For i := 0 To L.Count - 1 Do
   Begin
      Item := TD2ListboxItem.Create(List.Owner);
      Item.Parent := List;
      Item.TextW := TD2VisualObject(L[i]).Name;
      If Item.TextW = '' Then
         Item.TextW := TD2VisualObject(L[i]).ClassName;
      Item.TagObject := TD2VisualObject(L[i]);
   End;
   L.Free;
   List.EndUpdate;
   List.ItemIndex := Idx;
End;

Procedure TD2CustomScene.doDesignPopupTabOrder(Sender: TObject);
Var
   Form: TForm;
   Scene: TD2Scene;
   Wnd: TD2HudWindow;
   List: TD2Listbox;
   BtnUp, BtnDown: TD2Button;
Begin
   If FSelected = nil Then
      Exit;
   If FSelected.Parent = nil Then
      Exit;
   If Not FSelected.Parent.IsVisual Then
      Exit;

   Form := TForm.CreateNew(Application, 0);
   Form.BorderStyle := bsNone;
   Form.Position := poScreenCenter;
   Form.SetBounds(0, 0, 300, 400);
   Scene := TD2Scene.Create(Form);
   Scene.Parent := Form;
   Scene.Align := alClient;
   Scene.Transparency := True;
   Wnd := TD2HudWindow.Create(Form);
   Wnd.ShowSizeGrip := False;
   Wnd.TextW := 'TabOrder Editor';
   Scene.AddObject(Wnd);

   BtnUp := TD2HudButton.Create(Form);
   BtnUp.Parent := Wnd;
   BtnUp.SetBounds(Form.Width - 70, 50, 50, 26);
   BtnUp.TextW := 'Up';
   BtnUp.OnClick := doDesignTabOrderBtnClick;
   BtnDown := TD2HudButton.Create(Form);
   BtnDown.Parent := Wnd;
   BtnDown.SetBounds(Form.Width - 70, 90, 50, 26);
   BtnDown.TextW := 'Down';
   BtnDown.OnClick := doDesignTabOrderBtnClick;

   List := TD2HudListbox.Create(Form);
   List.Parent := Wnd;
   List.Align := vaClient;
   List.Padding.Rect := d2Rect(26, 45, 86, 33);
   List.HideSelectionUnfocused := False;
   List.ItemHeight := 20;

   Form.SetBounds(0, 0, 300, 400);
   BtnUp.TagObject := List;
   BtnDown.TagObject := List;
   doDesignTabOrderRebuildList(List);
   List.ItemIndex := FSelected.TabOrder;
   Form.ShowModal;
   Form.Free;
End;

Function TD2CustomScene.HasEffects(Value: TD2VisualObject): Boolean;
Var
   i: Integer;
Begin
   Result := False;
   If Value = nil Then
      Exit;
   If Value.ChildrenCount = 0 Then
      Exit;

   For i := 0 To Value.ChildrenCount - 1 Do
   Begin
      Result := (Value.Children[i] Is TD2Effect);
      If Result Then
         Exit;
   End;
End;

Function TD2CustomScene.HasAnimations(Value: TD2VisualObject): Boolean;
Var
   i: Integer;
Begin
   Result := False;
   If Value = nil Then
      Exit;
   If Value.ChildrenCount = 0 Then
      Exit;

   For i := 0 To Value.ChildrenCount - 1 Do
   Begin
      Result := (Value.Children[i] Is TD2Animation);
      If Result Then
         Exit;
   End;
End;

Procedure TD2CustomScene.OpenDesignPopup;
Var
   i, j: Integer;
   S, T: Ansistring;
   OItem, SItem, Item: TMenuItem;
   CatList: TStringList;
   xSelNotNil: Boolean;
   xSelNotNilNotRoot: Boolean;
   xPic: TPicture;
Begin
   If Not DesignPopupEnabled Then
      Exit;

   // Free OLD FDesignPopup
   If FDesignPopup <> nil Then
   Begin
      FDesignPopup.Free;
      FDesignPopup := nil;
   End;

   If FDesignPopup = nil Then
   Begin
      FDesignPopup := TPopupMenu.Create(Self);

      If GvarD2ObjectList <> nil Then
      Begin
         xPic := TPicture.Create;
         //..............................................
         // Show FSelected Info to PopupMenu
         If FSelected <> nil Then
            s := FSelected.Name
         Else
            s := 'None';
         If (FSelected <> nil) And (FSelected.ClassName <> '') Then
            s := s + ' :' + FSelected.ClassName;

         Item := NewItem('Sel Object: ' + s, 0, False, True, doDesignPopupFixObj, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_info');
         Item.Bitmap := xPic.Bitmap;
         FDesignPopup.Items.Add(Item);

         Item := NewItem('-', 0, False, True, nil, 0, ''); //---- Separator
         FDesignPopup.Items.Add(Item);

         //..............................................
         If (FSelected <> nil) And (FSelected.ItemClass <> '') Then
         Begin
            S := FSelected.ItemClass;
            T := d2GetToken(S, ';');
            While T <> '' Do
            Begin
               If Pos('TD2', T) = 1 Then
                  Delete(T, 1, 3);
               Item := NewItem('Add ' + T, 0, False, True, doDesignPopupAddItem, 0, '');
               Item.Hint := 'TD2' + T;
               FDesignPopup.Items.Add(Item);
               T := d2GetToken(S, ';');
            End;
         End;

         Item := NewItem('Add 2D object', 0, False, True, nil, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_add');
         Item.Bitmap := xPic.Bitmap;

         For i := 0 To GvarD2ObjectList.Count - 1 Do
         Begin
            If Item.Find(GvarD2ObjectList[i]) = nil Then
            Begin
               SItem := NewItem(GvarD2ObjectList[i], 0, False, True, nil, 0, '');
               Item.Add(SItem);
            End;
         End;

         CatList := TStringList.Create;
         CatList.Sorted := True;
         For i := 0 To GvarD2ObjectList.Count - 1 Do
         Begin
            S := TD2ObjectClass(GvarD2ObjectList.Objects[i]).ClassName;
            If Pos('TD2', S) = 1 Then
               Delete(S, 1, 3);
            CatList.Add(S);
         End;

         For i := 0 To CatList.Count - 1 Do
         Begin
            OItem := NewItem(CatList[i], 0, False, True, doDesignPopupAdd, 0, '');
            OItem.Tag := $FF;
            For j := 0 To GvarD2ObjectList.Count - 1 Do
            Begin
               S := TD2ObjectClass(GvarD2ObjectList.Objects[j]).ClassName;
               If Pos('TD2', S) = 1 Then
                  Delete(S, 1, 3);
               If S = CatList[i] Then
               Begin
                  SItem := Item.Find(GvarD2ObjectList[j]);
                  Break;
               End;
            End;
            If SItem <> nil Then
               SItem.Add(OItem);
         End;
         CatList.Free;
         FDesignPopup.Items.Add(Item);
         //................................................

         xSelNotNil := Not (Selected = nil);
         xSelNotNilNotRoot := (xSelNotNil And (Selected <> Root));

         Item := NewItem('Delete object', 0, False, True, doDesignPopupDelete, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_delete');
         Item.Bitmap := xPic.Bitmap;
         Item.Enabled := xSelNotNilNotRoot;
         FDesignPopup.Items.Add(Item);

         //.... Edit ..................
         Item := NewItem('-', 0, False, True, nil, 0, ''); //---- Separator
         FDesignPopup.Items.Add(Item);

         Item := NewItem('Clone', 0, False, True, doDesignPopupClone, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_clone');
         Item.Bitmap := xPic.Bitmap;
         FDesignPopup.Items.Add(Item);
         Item := NewItem('Cut', 0, False, True, doDesignPopupCut, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_cut');
         Item.Bitmap := xPic.Bitmap;
         Item.Enabled := xSelNotNilNotRoot;
         FDesignPopup.Items.Add(Item);
         Item := NewItem('Copy to clipboard', 0, False, True, doDesignPopupCopy, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_copy');
         Item.Bitmap := xPic.Bitmap;
         FDesignPopup.Items.Add(Item);
         Item := NewItem('Paste from clipboard', 0, False, True, doDesignPopupPaste, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_paste');
         Item.Bitmap := xPic.Bitmap;
         FDesignPopup.Items.Add(Item);

         //.... Order ..................
         Item := NewItem('-', 0, False, True, nil, 0, ''); //---- Separator
         FDesignPopup.Items.Add(Item);

         Item := NewItem('Bring to front', 0, False, True, doDesignPopupReorder, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_front');
         Item.Bitmap := xPic.Bitmap;
         Item.Enabled := xSelNotNilNotRoot;
         Item.Tag := 1010;  // Used by doDesignPopupReorder
         FDesignPopup.Items.Add(Item);
         Item := NewItem('Send to back', 0, False, True, doDesignPopupReorder, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_back');
         Item.Bitmap := xPic.Bitmap;
         Item.Enabled := xSelNotNilNotRoot;
         Item.Tag := 1020;  // Used by doDesignPopupReorder
         FDesignPopup.Items.Add(Item);

         //.... Load/Save Obj ..................
         Item := NewItem('-', 0, False, True, nil, 0, ''); //---- Separator
         FDesignPopup.Items.Add(Item);

         Item := NewItem('Load From File...', 0, False, True, doDesignPopupLoadFromFile, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_load');
         Item.Bitmap := xPic.Bitmap;
         Item.Enabled := xSelNotNil;
         FDesignPopup.Items.Add(Item);
         Item := NewItem('Save To File...', 0, False, True, doDesignPopupSaveToFile, 0, '');
         xPic.LoadFromResourceName(HInstance, 'orca2d_popup_save');
         Item.Bitmap := xPic.Bitmap;
         Item.Enabled := xSelNotNil;
         FDesignPopup.Items.Add(Item);

         //.... Animations/Effects Actions ..................
         Item := NewItem('-', 0, False, True, nil, 0, ''); //---- Separator
         FDesignPopup.Items.Add(Item);

         Item := NewItem('Delete ALL Animations', 0, False, True, doDesignPopupDeleteAnimations, 0, '');
         Item.Enabled := HasAnimations(Selected);
         FDesignPopup.Items.Add(Item);

         Item := NewItem('Delete ALL Effects', 0, False, True, doDesignPopupDeleteEffects, 0, '');
         Item.Enabled := HasEffects(Selected);
         FDesignPopup.Items.Add(Item);

         //.... Properties of Obj ..................
         Item := NewItem('-', 0, False, True, nil, 0, ''); //---- Separator
         FDesignPopup.Items.Add(Item);

         If FSelected <> nil Then
         Begin
            Item := NewItem('Hide in Design-time', 0, FSelected.DesignHide, True, doDesignPopupDesignHide, 0, '');
            Item.AutoCheck := True;
            Item.Enabled := xSelNotNil;
            FDesignPopup.Items.Add(Item);

            If (FSelected Is TD2Control) And (TD2Control(FSelected).GetResourceObject <> nil) Then
            Begin
               Item := NewItem('Edit Default Style...', 0, False, True, doDesignPopupEditStyle, 0, '');
               Item.Enabled := xSelNotNil;
               FDesignPopup.Items.Add(Item);
               Item := NewItem('Edit Custom Style...', 0, False, True, doDesignPopupCreateStyle, 0, '');
               Item.Enabled := xSelNotNil;
               FDesignPopup.Items.Add(Item);
            End;

            If (FSelected.IsVisual) And (FSelected.CanFocused) Then
            Begin
               Item := NewItem('Tab Order...', 0, False, True, doDesignPopupTabOrder, 0, '');
               Item.Enabled := xSelNotNil;
               FDesignPopup.Items.Add(Item);
            End;
         End;

         //.... General ..................
         Item := NewItem('Grid', 0, False, True, nil, 0, '');
         SItem := NewItem('Show snap grid', 0, FSnapGridShow, True, doDesignPopupGrid, 0, '');
         SItem.Tag := 1;
         SItem.AutoCheck := True;

         Item.Add(SItem);
         SItem := NewItem('Snap to grid', 0, FSnapToGrid, True, doDesignPopupGrid, 0, '');
         SItem.Tag := 2;
         SItem.RadioItem := True;
         SItem.AutoCheck := True;

         Item.Add(SItem);
         SItem := NewItem('Snap to lines', 0, FSnapToLines, True, doDesignPopupGrid, 0, '');
         SItem.Tag := 3;
         SItem.RadioItem := True;
         SItem.AutoCheck := True;

         Item.Add(SItem);
         SItem := NewItem('Snap disabled', 0, Not FSnapToLines Or Not FSnapToLines, True, doDesignPopupGrid, 0, '');
         SItem.Tag := 4;
         SItem.RadioItem := True;
         SItem.AutoCheck := True;

         Item.Add(SItem);
         FDesignPopup.Items.Add(Item);

         If xPic <> nil Then
            xPic.Free;
      End;
   End;
   GetCursorPos(FPopupPos);
   FDesignPopup.Popup(FPopupPos.X, FPopupPos.Y);
End;

Procedure TD2CustomScene.DefineProperties(Filer: TFiler);
Begin
   Inherited DefineProperties(Filer);
   Filer.DefineProperty('DesignSnapGridShow', ReadDesignSnapGridShow, WriteDesignSnapGridShow, True);
   Filer.DefineProperty('DesignSnapToGrid', ReadDesignSnapToGrid, WriteDesignSnapToGrid, True);
   Filer.DefineProperty('DesignSnapToLines', ReadDesignSnapToLines, WriteDesignSnapToLines, True);
End;

Procedure TD2CustomScene.ReadDesignSnapGridShow(Reader: TReader);
Begin
   FSnapGridShow := Reader.ReadBoolean;
   If Not (FDesignTime) Then
      FSnapGridShow := False;
End;

Procedure TD2CustomScene.ReadDesignSnapToGrid(Reader: TReader);
Begin
   FSnapToGrid := Reader.ReadBoolean;
End;

Procedure TD2CustomScene.ReadDesignSnapToLines(Reader: TReader);
Begin
   FSnapToLines := Reader.ReadBoolean;
End;

Procedure TD2CustomScene.WriteDesignSnapGridShow(Writer: TWriter);
Begin
   Writer.WriteBoolean(FSnapGridShow);
End;

Procedure TD2CustomScene.WriteDesignSnapToGrid(Writer: TWriter);
Begin
   Writer.WriteBoolean(FSnapToGrid);
End;

Procedure TD2CustomScene.WriteDesignSnapToLines(Writer: TWriter);
Begin
   Writer.WriteBoolean(FSnapToLines);
End;

Function TD2CustomScene.GetStyle: TD2Resources;
Begin
   Result := FStyle;
End;

Function TD2CustomScene.GetRoot: TD2Object;
Begin
   If (FChildren <> nil) And (FChildren.Count > 0) Then
      Result := TD2Object(FChildren[0])
   Else
      Result := nil;
End;

Procedure TD2CustomScene.SetFocused(Const Value: TD2VisualObject);
Begin
   If FFocused <> Value Then
   Begin
      If FFocused <> nil Then
      Begin
         TD2VisualObject(FFocused).KillFocus;
         TD2VisualObject(FFocused).RemoveFreeNotify(Self);
      End;
      FFocused := Value;
      If FFocused <> nil Then
      Begin
         TD2VisualObject(FFocused).EnterFocus;
         TD2VisualObject(FFocused).AddFreeNotify(Self);
      End;
   End;
End;

Procedure TD2CustomScene.DoDesignSelect(AObject: TObject);
Begin
   If (AObject <> nil) And (AObject Is TD2VisualObject) And (TD2VisualObject(AObject).FScene.GetComponent = Self) Then
   Begin
      FSelected := TD2VisualObject(AObject);
      TD2VisualObject(FSelected).DesignSelect;
      AddUpdateRect(d2Rect(0, 0, 10000, 10000));
      Draw;
   End;
End;

Procedure TD2CustomScene.SetActiveControl(AControl: TD2Control);
Begin
   If AControl <> FActiveControl Then
   Begin
      FActiveControl := AControl;
      If (FActiveControl <> nil) And Not (csLoading In ComponentState) Then
         FActiveControl.SetFocus;
   End;
End;

Procedure TD2CustomScene.SetTransparency(Const Value: Boolean);
Begin
   If FTransparency <> Value Then
   Begin
      FTransparency := Value;
      AddUpdateRect(d2Rect(0, 0, 10000, 10000));
   End;
End;

Procedure TD2CustomScene.SetSelected(Const Value: TD2VisualObject);
Begin
   If FSelected <> Value Then
   Begin
      If FSelected <> nil Then
         FSelected.Repaint;
      FSelected := Value;
      If FSelected <> nil Then
         FSelected.Repaint;
      If Assigned(FDesignChangeSelection) Then
         FDesignChangeSelection(Self);
      AddUpdateRect(d2Rect(0, 0, Width, Height));

      // Update Typhon IDE
      If GvarD2Designer <> nil Then
         GvarD2Designer.SelectObject(Owner, FSelected, []);
   End;
End;

Function TD2CustomScene.GetActiveControl: TD2Control;
Begin
   Result := FActiveControl;
End;

Function TD2CustomScene.GetDisableUpdate: Boolean;
Begin
   Result := FDisableUpdate;
End;

Function TD2CustomScene.GetDesignTime: Boolean;
Begin
   Result := FDesignTime;
End;

Function TD2CustomScene.GetCanvas: TD2Canvas;
Begin
   Result := FCanvas;
End;

Function TD2CustomScene.GetOwner: TComponent;
Begin
   Result := Owner;
End;

Function TD2CustomScene.GetComponent: TComponent;
Begin
   Result := Self;
End;

Function TD2CustomScene.GetSelected: TD2VisualObject;
Begin
   Result := FSelected;
End;

Function TD2CustomScene.GetDesignPlaceObject: TD2VisualObject;
Begin
   Result := FDesignPlaceObject;
End;

Procedure TD2CustomScene.SetDisableUpdate(Value: Boolean);
Begin
   FDisableUpdate := Value;
End;

Function TD2CustomScene.GetUpdateRectsCount: Integer;
Begin
   Result := Length(FUpdateRects);
End;

Function TD2CustomScene.GetUpdateRect(Const Index: Integer): TD2Rect;
Begin
   Result := FUpdateRects[Index];
End;

Function TD2CustomScene.GetAnimatedCaret: Boolean;
Begin
   Result := FAnimatedCaret;
End;

Function TD2CustomScene.LocalToScreen(Const Point: TD2Point): TD2Point;
Begin
   With ClientToScreen(Classes.Point(trunc(Point.x), trunc(Point.y))) Do
      Result := d2Point(x, y);
End;

Function TD2CustomScene.GetCaptured: TD2VisualObject;
Begin
   Result := FCaptured;
End;

Procedure TD2CustomScene.SetCaptured(Const Value: TD2VisualObject);
Begin
   FCaptured := Value;

   If Assigned(FCaptured) Then
      If Not Assigned(FOnFlush) Then
         MouseCapture := True;

   If Not Assigned(FCaptured) Then
      If Not Assigned(FOnFlush) Then
         MouseCapture := False;
End;

Function TD2CustomScene.GetTransparency: Boolean;
Begin
   Result := FTransparency;
End;

Function TD2CustomScene.GetFocused: TD2VisualObject;
Begin
   Result := FFocused;
End;

Procedure TD2CustomScene.SetDesignRoot(Const Value: TD2VisualObject);
Begin
   FDesignRoot := Value;
End;

Function TD2CustomScene.GetMousePos: TD2Point;
Begin
   Result := FMousePos;
End;

Procedure TD2CustomScene.UpdateResource;
Begin
   If Root <> nil Then
      Root.UpdateResource;
End;

Procedure TD2CustomScene.SetStyle(Const Value: TD2Resources);
Begin
   If FStyle <> Value Then
   Begin
      If FStyle <> nil Then
         FStyle.RemoveSceneUpdater(Self);
      FStyle := Value;
      If FStyle <> nil Then
         FStyle.AddSceneUpdater(Self);
      UpdateResource;
   End;
End;

Function TD2CustomScene.ShowKeyboardForControl(AObject: TD2Object): Boolean;
Begin
   Result := False;
End;

Function TD2CustomScene.HideKeyboardForControl(AObject: TD2Object): Boolean;
Begin
   Result := False;
End;


Procedure TD2CustomScene.WMEraseBkgnd(Var Msg: TLMEraseBkgnd);
Var
   rgnStatus: Integer;
   rgn: HRgn;
Begin
   If (Msg.DC <> 0) And (Canvas <> nil) Then
   Begin
      rgn := CreateRectRgn(0, 0, 1, 1);
      {$IFDEF ORCA2D_OS_WINDOWS}
    rgnStatus:=GetUpdateRgn(Handle,rgn,false);
      {$ENDIF}
      rgnStatus := 1;
      If (rgnStatus = 1) Then
      Begin
         Canvas.FlushBuffer(0, 0, Msg.DC);
      End;
      DeleteObject(rgn);
   End;
   Msg.Result := 1;
End;

Procedure TD2CustomScene.CMMouseLeave(Var Message: TLMessage);
Begin
   Inherited;
   If FHovered <> nil Then
   Begin
      TD2VisualObject(FHovered).MouseInObject := False;
      TD2VisualObject(FHovered).MouseLeave;
      TD2VisualObject(FHovered).RemoveFreeNotify(Self);
      FHovered := nil;
   End;
End;


//========================================================================= 8888
//=========================================================================
//=========================================================================


{$IFNDEF ORCA2D_OS_WINDOWS}
Procedure TD2CustomScene.WMPaint(Var Msg: TLMPaint);
Begin
   If FDrawing Then
      Exit;

   Inherited;
   Draw;
   FDrawing := False;
End;

{$ELSE}

type
  PRgnRects=^TRgnRects;
  TRgnRects=array [0..0] of TRect;

procedure TD2CustomScene.WMPaint(var Msg:TLMPaint);
var
  i,rgnStatus:integer;
  rgn:HRgn;
  rgnSize:integer;
  rgnData:PRgnData;
  R:windows.TRect;
begin
  if FDrawing then Exit;
  rgn:=CreateRectRgn(0,0,1,1);
  rgnStatus:=GetUpdateRgn(Handle,rgn,false);
  if (rgnStatus=2) or (rgnStatus=3) then
  begin
    rgnSize:=GetRegionData(rgn,$FFFF,nil);
    if rgnSize > 0 then
    begin
      GetMem(rgnData,rgnSize);
      rgnSize:=GetRegionData(rgn,rgnSize,rgnData);
      if rgnSize=rgnSize then
      begin
        SetLength(FUpdateRects,rgnData.rdh.nCount);

        for i:=0 to rgnData.rdh.nCount - 1 do
        begin
          R:=PRgnRects(@rgnData.buffer[0])[i];
          with R do
            FUpdateRects[i]:=d2Rect(left,top,right,bottom);
        end;

      end;
      FreeMem(rgnData,rgnSize);

      FDC:=GetDC(Handle);
      Draw;
      ReleaseDC(Handle,FDC);
      FDC:=0;

      FDrawing:=true;
      Msg.result:=CallWindowProcW(@FPrevWndProc,Handle,Msg.Msg,TMessage(Msg).WParam,TMessage(Msg).LParam);
      FDrawing:=false;

    end else
    begin
      FDrawing:=true;
      Msg.result:=CallWindowProcW(@FPrevWndProc,Handle,Msg.Msg,TMessage(Msg).WParam,TMessage(Msg).LParam);
      FDrawing:=false;

    end;

  end else
  begin
    FDrawing:=true;
    Msg.result:=CallWindowProcW(@FPrevWndProc,Handle,Msg.Msg,TMessage(Msg).WParam,TMessage(Msg).LParam);
    FDrawing:=false;
  end;

  DeleteObject(rgn);
end;

const
  IID_IDropTargetHelper:TGUID=(
    D1:$4657278b; D2:$411b; D3:$11d2; D4:($83,$9a,$00,$c0,$4f,$d9,$18,$d0));
  SID_IDropTargetHelper='{4657278B-411B-11d2-839A-00C04FD918D0}';
  CLSID_DragDropHelper:TGUID=(
    D1:$4657278a; D2:$411b; D3:$11d2; D4:($83,$9a,$00,$c0,$4f,$d9,$18,$d0));

type

  IDropTargetHelper=interface(IUnknown)
    [SID_IDropTargetHelper]
    function DragEnter(hwndTarget:HWND; const DataObj:IDataObject;
      var pt:TPoint; dwEffect:Longint):HResult; stdcall;
    function DragLeave:HResult; stdcall;
    function DragOver(var pt:TPoint; dwEffect:longInt):HResult; stdcall;
    function Drop(const DataObj:IDataObject; var pt:TPoint;
      dwEffect:longInt):HResult; stdcall;
    function Show(Show:BOOL):HResult; stdcall;
  end;

var
  FDropTargetHelper:IDropTargetHelper;
  FDataObj:IDataObject;

function TD2CustomScene.GetDataObject:TD2DragObject;
var
  formatEtc:TFORMATETC;
  stgMedium:TSTGMEDIUM;
  str:wideString;
  drop:HDrop;
  i,numFiles:integer;
  buffer :array[0..MAX_PATH] of widechar;
begin
  FillChar(Result,SizeOf(Result),0);
  if not Assigned(FDataObj) then Exit;

  with formatEtc do
  begin
    cfFormat:=CF_HDROP;
    ptd:=nil;
    dwAspect:=DVASPECT_CONTENT;
    lindex:=-1;
    tymed:=TYMED_HGLOBAL;
  end;

  str:='';
  if FDataObj.GetData(formatEtc,stgMedium)=S_OK then
  begin
    try
      drop:=HDrop(GlobalLock(stgMedium.hGlobal));

      numFiles:=DragQueryFile(drop,$FFFFFFFF,nil,0);
      SetLength(Result.Files,numFiles);
      for i:=0 to numFiles - 1 do
      begin
        DragQueryFileW(drop,i,@buffer,sizeof(buffer));
        Result.Files[i]:=buffer;
        if i=0 then
          Result.Data:=Result.Files[0];
      end;
    finally
      GlobalUnlock(stgMedium.hGlobal);
      ReleaseStgMedium(@stgMedium);
    end;
  end else
  begin

    formatEtc.cfFormat:=CF_UNICODETEXT;
    if FDataObj.GetData(formatEtc,stgMedium)=S_OK then
    begin
      try
        str:=PWideChar(GlobalLock(stgMedium.hGlobal));
        Result.Data:=str;
      finally
        GlobalUnlock(stgMedium.hGlobal);
        ReleaseStgMedium(@stgMedium);
      end;
    end
  end;
end;

function TD2CustomScene.DragEnter(const dataObj:IDataObject; grfKeyState:DWORD;
      pt:TPoint; var dwEffect:DWORD):HResult;
begin
  try
    if (Root=nil) and not (Root.IsVisual) then
    begin
      dwEffect:=DROPEFFECT_NONE;
      Result:=E_UNEXPECTED;
      Exit;
    end;
    FDataObj:=dataObj;
    Result:=S_OK;
    dwEffect:=DROPEFFECT_NONE;
    if (Succeeded(CoCreateInstance(CLSID_DragDropHelper,nil,CLSCTX_INPROC_SERVER,
      IDropTargetHelper,FDropTargetHelper))) and
      (FDropTargetHelper <> nil) then
    begin
      if (Failed(FDropTargetHelper.DragEnter(Handle,DataObj,pt,dwEffect))) then
        FDropTargetHelper:=nil;
    end;
  except
    dwEffect:=DROPEFFECT_NONE;
    Result:=E_UNEXPECTED;
  end;
end;

function TD2CustomScene.DragOver(grfKeyState:DWORD; pt:TPoint; var dwEffect:DWORD):HResult;
var
  P:TD2Point;
  NewTarget:TD2VisualObject;
begin
  try
    dwEffect:=DROPEFFECT_NONE;
    Result:=S_OK;
    with ScreenToClient(pt) do
      P:=d2Point(X,Y);
    NewTarget:=Root.Visual.FindTarget(P,GetDataObject);
    if (NewTarget <> varD2FTarget) then
    begin
      if varD2FTarget <> nil then  TD2VisualObject(varD2FTarget).DragLeave;

      varD2FTarget:=NewTarget;
      if varD2FTarget <> nil then
      begin
        TD2VisualObject(varD2FTarget).DragEnter(GetDataObject,P);
      end;
    end;
    if NewTarget <> nil then
      dwEffect:=DROPEFFECT_LINK;
    if FDropTargetHelper <> nil then
      FDropTargetHelper.DragOver(pt,dwEffect);
  except
    dwEffect:=DROPEFFECT_NONE;
    Result:=E_UNEXPECTED;
  end;
end;

function TD2CustomScene.DragLeave:HResult;
begin
  if varD2FTarget <> nil then TD2VisualObject(varD2FTarget).DragLeave;

  if (FDropTargetHelper <> nil) then  FDropTargetHelper.DragLeave;
  varD2FTarget:=nil;
  FDropTargetHelper:=nil;
  FDataObj:=nil;
  Result:=S_OK;
end;

function TD2CustomScene.Drop(const dataObj:IDataObject; grfKeyState:DWORD; pt:TPoint; var dwEffect:DWORD):HResult;
var
  P:TD2Point;
begin
  if (dataObj=nil) then Exit;

  try
    if varD2FTarget <> nil then
    begin
      with ScreenToClient(pt) do
        P:=d2Point(X,Y);
      TD2VisualObject(varD2FTarget).DragDrop(GetDataObject,P);
    end;

    if (FDropTargetHelper <> nil) then
      FDropTargetHelper.Drop(DataObj,pt,dwEffect);

  finally
    FDataObj:=nil;
    FDropTargetHelper:=nil;
  end;
end;

procedure TD2CustomScene.WMAddUpdateRect(var Msg:TMessage);
 procedure ProcessUpdateMessages;
 var
   Msg:TMsg;
 begin
   while PeekMessage(Msg,Handle,WM_ADDUPDATERECT,WM_ADDUPDATERECT,PM_REMOVE) do
   begin
     if Msg.message=WM_QUIT then
     begin
       PostQuitMessage(Msg.WParam);
       Break;
     end;
   end;
 end;
begin
  ProcessUpdateMessages;
  Draw;
end;
{$ENDIF}

//=============== TD2PopupForm ===========================================

Constructor TD2PopupForm.Create(AOwner: TComponent);
Begin
   Inherited CreateNew(AOwner, 0);
   Tag := $FFFF;
   BorderStyle := bsNone;
   FormStyle := fsStayOnTop;
   Position := poDesigned;
End;

Destructor TD2PopupForm.Destroy;
Begin
   If FPopup <> nil Then
      FPopup.ClosePopup;
   Inherited;
End;

Procedure TD2PopupForm.CreateParams(Var Params: TCreateParams);
Begin
   Inherited;
   With Params Do
   Begin
   End;
End;

Procedure TD2PopupForm.WMDeactivate(Var Msg: TLMActivate);
Var
   C: TWinControl;
Begin
   Inherited;
   If (Not Msg.Active = 0) And (FPopup <> nil) And (Not FPopup.StaysOpen) Then
   Begin
      C := FindControl(Msg.ActiveWindow);
      If (C <> nil) And (C Is TD2PopupForm) And (Not TD2PopupForm(C).FPopup.StaysOpen) Then
      Begin
         If (varD2PopupList.IndexOf(TD2PopupForm(C).FPopup) >= 0) And (varD2PopupList.IndexOf(FPopup) > varD2PopupList.IndexOf(TD2PopupForm(C).FPopup)) Then
            FPopup.ClosePopup;
         Exit;
      End;
      CloseAllPopups;
   End;
End;

Procedure TD2PopupForm.DoClose(Var anAction: TCloseAction);
Begin
   Inherited;
   anAction := caFree;
   If (FPopup <> nil) Then
   Begin
      FNoFree := True;
      FPopup.ClosePopup;
   End;
End;

//=============== TD2Popup ==================================

Constructor TD2Popup.Create(AOwner: TComponent);
Begin
   Inherited;
   FPlacementRectangle := TD2Bounds.Create(d2Rect(0, 0, 0, 0));
   Visible := False;
   CanFocused := True;
End;

Destructor TD2Popup.Destroy;
Begin
   ClosePopup;
   FPlacementRectangle.Free;
   Inherited;
End;

Procedure TD2Popup.Paint;
Var
   R: TD2Rect;
Begin
   Inherited;
   If Assigned(Scene) And Scene.GetDesignTime Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
   End;
End;

Procedure TD2Popup.ApplyPlacement;
Var
   Target: TD2VisualObject;
   AbsolutePos, LocalPos: TD2Point;
   Pos: TPoint;
Begin
   Target := PlacementTarget;

   If (Target = nil) And (Parent <> nil) Then
      Target := Parent.Visual;
   If Target = nil Then
   Begin
      Case Placement Of
         d2PlacementBottom:
         Begin
            LocalPos := d2Point(FPlacementRectangle.left + HorizontalOffset, FPlacementRectangle.bottom + VerticalOffset);
         End;
         d2PlacementTop:
         Begin
            LocalPos := d2Point(FPlacementRectangle.left + HorizontalOffset, FPlacementRectangle.top - Height - VerticalOffset);
         End;
         d2PlacementLeft:
         Begin
            LocalPos := d2Point(FPlacementRectangle.left - Width - HorizontalOffset, FPlacementRectangle.top + VerticalOffset);
         End;
         d2PlacementRight:
         Begin
            LocalPos := d2Point(FPlacementRectangle.right + HorizontalOffset, FPlacementRectangle.top + VerticalOffset);
         End;
         d2PlacementBottomCenter:
         Begin
            LocalPos := d2Point(FPlacementRectangle.left + HorizontalOffset, FPlacementRectangle.bottom + VerticalOffset);
         End;
         d2PlacementTopCenter:
         Begin
            LocalPos := d2Point(FPlacementRectangle.left + HorizontalOffset, FPlacementRectangle.top - Height - VerticalOffset);
         End;
         d2PlacementLeftCenter:
         Begin
            LocalPos := d2Point(FPlacementRectangle.left - Width - HorizontalOffset, FPlacementRectangle.top + VerticalOffset);
         End;
         d2PlacementRightCenter:
         Begin
            LocalPos := d2Point(FPlacementRectangle.right + HorizontalOffset, FPlacementRectangle.top + VerticalOffset);
         End;
         d2PlacementCenter:
         Begin
            LocalPos := d2Point(FPlacementRectangle.left + (FPlacementRectangle.right - FPlacementRectangle.left - Width) / 2, FPlacementRectangle.top + (FPlacementRectangle.bottom - FPlacementRectangle.top - Height) / 2);
            LocalPos.X := LocalPos.X + HorizontalOffset;
            LocalPos.Y := LocalPos.Y + VerticalOffset;
         End;
         d2PlacementAbsolute:
         Begin
            If FPlacementScene = d2PlacementSceneNew Then
            Begin
               FPopupForm.Left := Round(FPlacementRectangle.Left);
               FPopupForm.Top := Round(FPlacementRectangle.Top);
            End;
            If FPlacementScene = d2PlacementSceneTarget Then
            Begin
{            FPopupForm.Left:=Round(FPlacementRectangle.Left);
            FPopupForm.Top:=Round(FPlacementRectangle.Top);}
            End;

            Exit;
         End;
         d2PlacementMouse:
         Begin
            If IsOpen Then
               Exit;

            GetCursorPos(Pos);
            If FPlacementScene = d2PlacementSceneNew Then
            Begin
               FPopupForm.Left := Pos.X;
               FPopupForm.Top := Pos.Y;
            End;
            If FPlacementScene = d2PlacementSceneTarget Then
            Begin
            End;

            Exit;
         End;
         d2PlacementMouseCenter:
         Begin
            If IsOpen Then
               Exit;

            GetCursorPos(Pos);
            If FPlacementScene = d2PlacementSceneNew Then
            Begin
               FPopupForm.Left := Pos.X - round(Width / 2);
               FPopupForm.Top := Pos.Y;
            End;
            If FPlacementScene = d2PlacementSceneTarget Then
            Begin
            End;

            Exit;
         End;
      End;
      Pos := Point(Trunc(LocalPos.X), Trunc(LocalPos.Y));
   End Else Begin
      Case Placement Of
         d2PlacementBottom:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point(HorizontalOffset, Target.Height + VerticalOffset)
            Else
               LocalPos := d2Point(FPlacementRectangle.left + HorizontalOffset, FPlacementRectangle.bottom + VerticalOffset);
         End;
         d2PlacementTop:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point(HorizontalOffset, -Height - HorizontalOffset)
            Else
               LocalPos := d2Point(FPlacementRectangle.left + HorizontalOffset, FPlacementRectangle.top - Height - VerticalOffset);
         End;
         d2PlacementLeft:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point(-Width - HorizontalOffset, VerticalOffset)
            Else
               LocalPos := d2Point(FPlacementRectangle.left - Width - HorizontalOffset, FPlacementRectangle.top + VerticalOffset);
         End;
         d2PlacementRight:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point(Target.Width + HorizontalOffset, VerticalOffset)
            Else
               LocalPos := d2Point(FPlacementRectangle.right + HorizontalOffset, FPlacementRectangle.top + VerticalOffset);
         End;
         d2PlacementBottomCenter:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point(HorizontalOffset - (Width / 2) + (Target.Width / 2), Target.Height + VerticalOffset)
            Else
               LocalPos := d2Point((FPlacementRectangle.left + FPlacementRectangle.right) / 2 - (Width / 2) + HorizontalOffset, FPlacementRectangle.bottom + VerticalOffset);
         End;
         d2PlacementTopCenter:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point(HorizontalOffset - (Width / 2) + (Target.Width / 2), -Height - HorizontalOffset)
            Else
               LocalPos := d2Point((FPlacementRectangle.left + FPlacementRectangle.right) / 2 - (Width / 2) + HorizontalOffset, FPlacementRectangle.top - Height - VerticalOffset);
         End;
         d2PlacementLeftCenter:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point(-Width - HorizontalOffset, VerticalOffset - (Height / 2) + (Target.Height / 2))
            Else
               LocalPos := d2Point(FPlacementRectangle.left - Width - HorizontalOffset, FPlacementRectangle.top + VerticalOffset);
         End;
         d2PlacementRightCenter:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point(Target.Width + HorizontalOffset, VerticalOffset - (Height / 2) + (Target.Height / 2))
            Else
               LocalPos := d2Point(FPlacementRectangle.right + HorizontalOffset, FPlacementRectangle.top + VerticalOffset);
         End;
         d2PlacementCenter:
         Begin
            If FPlacementRectangle.empty Then
               LocalPos := d2Point((Target.Width - Width) / 2, (Target.Height - Height) / 2)
            Else
               LocalPos := d2Point(FPlacementRectangle.left + (FPlacementRectangle.right - FPlacementRectangle.left - Width) / 2, FPlacementRectangle.top + (FPlacementRectangle.bottom - FPlacementRectangle.top - Height) / 2);
            LocalPos.X := LocalPos.X + HorizontalOffset;
            LocalPos.Y := LocalPos.Y + VerticalOffset;
         End;
         d2PlacementAbsolute:
         Begin
            If FPlacementScene = d2PlacementSceneNew Then
            Begin
               FPopupForm.Left := Round(FPlacementRectangle.Left);
               FPopupForm.Top := Round(FPlacementRectangle.Top);
            End;
            If FPlacementScene = d2PlacementSceneTarget Then
            Begin
            End;

            Exit;
         End;
         d2PlacementMouse:
         Begin
            If IsOpen Then
               Exit;

            GetCursorPos(Pos);
            If FPlacementScene = d2PlacementSceneNew Then
            Begin
               FPopupForm.Left := Pos.X;
               FPopupForm.Top := Pos.Y;
               If FPopupForm.Monitor <> nil Then
               Begin
                  If FPopupForm.Left + FPopupForm.Width > FPopupForm.Monitor.Left + FPopupForm.Monitor.Width Then
                     FPopupForm.Left := FPopupForm.Monitor.Left + FPopupForm.Monitor.Width - FPopupForm.Width;
                  If FPopupForm.Top + FPopupForm.Height > FPopupForm.Monitor.Top + FPopupForm.Monitor.Height Then
                     FPopupForm.Top := FPopupForm.Monitor.Top + FPopupForm.Monitor.Height - FPopupForm.Height;
               End;
            End;
            If FPlacementScene = d2PlacementSceneTarget Then
            Begin
            End;

            Exit;
         End;
         d2PlacementMouseCenter:
         Begin
            If IsOpen Then
               Exit;

            GetCursorPos(Pos);
            If FPlacementScene = d2PlacementSceneNew Then
            Begin
               FPopupForm.Left := Pos.X - round(Width / 2);
               FPopupForm.Top := Pos.Y;
               If FPopupForm.Monitor <> nil Then
               Begin
                  If FPopupForm.Left + FPopupForm.Width > FPopupForm.Monitor.Left + FPopupForm.Monitor.Width Then
                     FPopupForm.Left := FPopupForm.Monitor.Left + FPopupForm.Monitor.Width - FPopupForm.Width;
                  If FPopupForm.Top + FPopupForm.Height > FPopupForm.Monitor.Top + FPopupForm.Monitor.Height Then
                     FPopupForm.Top := FPopupForm.Monitor.Top + FPopupForm.Monitor.Height - FPopupForm.Height;
               End;
            End;
            If FPlacementScene = d2PlacementSceneTarget Then
            Begin
            End;

            Exit;
         End;
      End;
      AbsolutePos := Target.LocalToAbsolute(LocalPos);
      Pos := Point(Trunc(AbsolutePos.X), Trunc(AbsolutePos.Y));
      If FPlacementScene = d2PlacementSceneNew Then
      Begin
         If (Target.Scene <> nil) Then
            Pos := Point(round(Target.Scene.LocalToScreen(d2Point(Pos.X, Pos.Y)).x), round(Target.Scene.LocalToScreen(d2Point(Pos.X, Pos.Y)).y))
         Else
         If (Scene <> nil) Then
            Pos := Point(round(Scene.LocalToScreen(d2Point(Pos.X, Pos.Y)).x), round(Scene.LocalToScreen(d2Point(Pos.X, Pos.Y)).y));
      End;
   End;

   If FPlacementScene = d2PlacementSceneNew Then
   Begin
      FPopupForm.Left := Pos.X;
      FPopupForm.Top := Pos.Y;
      If FPopupForm.Monitor <> nil Then
      Begin
         If FPopupForm.Left + FPopupForm.Width > FPopupForm.Monitor.Left + FPopupForm.Monitor.Width Then
            FPopupForm.Left := FPopupForm.Monitor.Left + FPopupForm.Monitor.Width - FPopupForm.Width;
         If FPopupForm.Top + FPopupForm.Height > FPopupForm.Monitor.Top + FPopupForm.Monitor.Height Then
            FPopupForm.Top := FPopupForm.Monitor.Top + FPopupForm.Monitor.Height - FPopupForm.Height;
      End;
   End;
   If FPlacementScene = d2PlacementSceneTarget Then
   Begin
      If (Parent <> nil) And (Parent.IsVisual) Then
         AbsolutePos := Parent.Visual.AbsoluteToLocal(AbsolutePos);
      Position.Point := AbsolutePos;
   End;
End;

Procedure TD2Popup.Popup;
Begin
   If FAnimating Then
   Begin
      FIsOpen := False;
      Exit;
   End;
   If FPopupForm <> nil Then
   Begin
      ClosePopup;
      Exit;
   End;

   FSaveScale := Scale.Point;
   FSaveParent := Parent;
   Case PlacementScene Of
      d2PlacementSceneNew:
      Begin
         FPopupForm := TD2PopupForm.Create(Application);
         If Owner Is TCustomForm Then
            TD2PopupForm(FPopupForm).FOwnerForm := TCustomForm(Owner);
         If (Scene <> nil) And (Scene.GetComponent <> nil) And (Scene.GetComponent.Owner Is TCustomForm) Then
            TD2PopupForm(FPopupForm).FOwnerForm := TCustomForm(Scene.GetComponent.Owner);
         FPopupForm.Width := Round(Width * AbsoluteMatrix.m11);
         FPopupForm.Height := Round(Height * AbsoluteMatrix.m22);
         Scale.X := AbsoluteMatrix.m11;
         Scale.Y := AbsoluteMatrix.m22;
         ApplyPlacement;
         TD2PopupForm(FPopupForm).FPopup := Self;
         TD2PopupForm(FPopupForm).FNoFree := False;
         FPopupScene := TD2Scene.Create(FPopupForm);
         With FPopupScene Do
         Begin
            Transparency := True;
            Parent := FPopupForm;
            Align := alClient;
            Style := Self.FStyle;
            If (FStyle = nil) And Assigned(FScene) Then
               Style := FScene.GetStyle;
         End;
         FPopupScene.Width := Round(Width);
         FPopupScene.Height := Round(Height);

         Visible := True;

         FPopupScene.AddObject(Self);
         SetNewScene(FPopupScene);
         FPopupScene.ActiveControl := Self;

         FNeedResource := True;
         ApplyResource;

         FPopupForm.Show;
         FPopupScene.SetFocus;
         SetFocus;
      End;
      d2PlacementSceneTarget:
      Begin
         FPopupLayout := TD2VisualObject.Create(Self);
         If (FPlacementTarget <> nil) And (FPlacementTarget.Scene <> nil) Then
            FPopupLayout.Parent := TD2VisualObject(FPlacementTarget.Scene.GetRoot)
         Else
            FPopupLayout.Parent := Scene.GetRoot;
         FPopupLayout.Align := vaContents;
         FPopupLayout.OnClick := DoPopupLayoutClick;
         FPopupLayout.HitTest := Not FStaysOpen;

         If FScene <> nil Then
            FSaveFocused := FScene.GetFocused;
         FSaveParent := Parent;
         Parent := FPopupLayout;
         SetNewScene(FPopupLayout.Scene);
         SetFocus;
         ApplyPlacement;

         Visible := True;

         FNeedResource := True;
         ApplyResource;

         BringToFront;
         CanFocused := True;
         SetFocus;
      End;
   End;
   If varD2PopupList = nil Then
      varD2PopupList := TList.Create;
   If Not StaysOpen Then
      varD2PopupList.Add(Self);

   FIsOpen := True;
   ApplyTriggerEffect(Self, 'IsOpen');
   StartTriggerAnimation(Self, 'IsOpen');


   If (FPlacementScene = d2PlacementSceneNew) And FDragWithParent And FStaysOpen Then
   Begin
      FDragTimer := TD2Timer.Create(Self);
      FDragTimer.Interval := 10;
      FDragTimer.OnTimer := DoTimer;
      FDragTimer.Enabled := True;
   End;
End;

Procedure TD2Popup.SetModalResult(Const Value: TModalResult);
Begin
   FModalResult := Value;

   If FPopupForm <> nil Then
      FPopupForm.ModalResult := Value;
End;

Function TD2Popup.PopupModal: TModalResult;
Begin
   If FAnimating Then
   Begin
      FIsOpen := False;
      Exit;
   End;
   If FPopupForm <> nil Then
   Begin
      If FModal Then
      Begin
         FModalResult := mrCancel;
         FModal := False;
         Exit;
      End;
      ClosePopup;
      Exit;
   End;
   Popup;
   FModalResult := 0;
   FModal := True;
   Repeat
      Application.HandleMessage;
      If Application.Terminated Then
      Begin
         FModalResult := mrCancel;
         If FPopupForm <> nil Then
            FPopupForm.ModalResult := mrCancel;
      End;
      If FPopupForm <> nil Then
      Begin
         ApplyPlacement;
         FModalResult := FPopupForm.ModalResult;
      End;
   Until Not ((FModalResult = 0) And FModal);
   FModal := True;
   Result := FModalResult;
   ClosePopup;
End;

Procedure TD2Popup.DialogKey(Var Key: Word; Shift: TShiftState);
Begin
   Inherited DialogKey(Key, Shift);
   If (Key = VK_ESCAPE) And (IsOpen) And Not (StaysOpen) Then
   Begin
      IsOpen := False;
      Key := 0;
   End;
End;

Procedure TD2Popup.ClosePopup;
Begin
   If FAnimating Then
      Exit;
   If (PlacementScene = d2PlacementSceneNew) And (FPopupScene = nil) Then
      Exit;
   If FModal And (FModalResult = 0) Then
   Begin
      ModalResult := mrCancel;
      Exit;
   End;

   If (FDragTimer <> nil) Then
      FreeAndNil(FDragTimer);
   FAnimating := True;
   FIsOpen := False;
   If Not (csDestroying In ComponentState) Then
   Begin
      ApplyTriggerEffect(Self, 'IsOpen');
      StartTriggerAnimationWait(Self, 'IsOpen');
   End;
   Visible := False;
   If FPlacementScene = d2PlacementSceneNew Then
   Begin
      FPopupScene.RemoveObject(Self);
      SetNewScene(nil);

      If Not TD2PopupForm(FPopupForm).FNoFree Then
      Begin
         FPopupScene := nil; // Free called by PopupForm.Release
         TD2PopupForm(FPopupForm).FPopup := nil;
         FPopupForm.Release;
         FPopupForm := nil;
      End Else
         FPopupForm := nil;

      FPopupScene := nil;
      Scale.Point := FSaveScale;
      Parent := FSaveParent;
   End;
   If FPlacementScene = d2PlacementSceneTarget Then
   Begin
      SetNewScene(nil);
      Parent := FSaveParent;
      If FSaveFocused <> nil Then
         FSaveFocused.SetFocus;
      FSaveFocused := nil;
      FPopupLayout.Free;
      FPopupLayout := nil;
   End;
   FAnimating := False;
   FSaveParent := nil;

   If Assigned(FOnClosePopup) Then
      FOnClosePopup(Self);
   If (varD2PopupList <> nil) Then
      varD2PopupList.Remove(Self);
End;

Procedure TD2Popup.KillFocus;
Begin
   Inherited;
End;

Procedure TD2Popup.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (AComponent = FPlacementTarget) And (Operation = opRemove) Then
      FPlacementTarget := nil;
   If (AComponent = FSaveParent) And (Operation = opRemove) Then
      FSaveParent := nil;
End;

Procedure TD2Popup.SetIsOpen(Const Value: Boolean);
Begin
   If FIsOpen <> Value Then
   Begin
      If Assigned(Scene) And Scene.GetDesignTime Then
      Begin
         FIsOpen := False;
         Exit;
      End;
      FIsOpen := Value;
      If FIsOpen Then
         Popup
      Else
         ClosePopup;
   End;
End;

Procedure TD2Popup.DoFormClose(Sender: TObject; Var anAction: TCloseAction);
Begin
   //..nothing...
End;

Procedure TD2Popup.DoPopupLayoutClick(Sender: TObject);
Begin
   If (FIsOpen) And (Not StaysOpen) Then
      ClosePopup;
End;

Procedure TD2Popup.Dotimer(Sender: TObject);
Begin
   ApplyPlacement;
End;

Procedure TD2Popup.SetPlacementRectangle(Const Value: TD2Bounds);
Begin
End;

//=============== TD2PopupItem =======================================

Constructor TD2PopupItem.Create(AOwner: TComponent);
Begin
   Inherited;
   FPopup := TD2Popup.Create(Self);
   FPopup.PlacementTarget := Self;
   //  FPopup.PlacementScene:=d2PlacementSceneTarget;
   FPopup.StaysOpen := False;
   FPopup.Stored := False;
   FPopup.Parent := Self;
   FPopup.Locked := True;
   FPopup.DesignHide := True;
   FPopup.OnClosePopup := DoClosePopup;
End;

Destructor TD2PopupItem.Destroy;
Begin
   Inherited;
End;

Procedure TD2PopupItem.AddObject(AObject: TD2Object);
Begin
   If (FPopup <> nil) And (AObject <> FPopup) And (AObject <> FResourceLink) And Not (AObject Is TD2Effect) And Not (AObject Is TD2Animation) Then
   Begin
      FPopup.AddObject(AObject);
   End Else
      Inherited;
End;

Procedure TD2PopupItem.GetChildren(Proc: TGetChildProc; Root: TComponent);
Var
   j: Integer;
Begin
   Inherited;
   If (FPopup <> nil) Then
      If (FPopup.ChildrenCount > 0) Then
      Begin
         For j := 0 To FPopup.ChildrenCount - 1 Do
            If FPopup.Children[j].Stored Then
               Proc(TComponent(FPopup.Children[j]));
      End;
End;

Procedure TD2PopupItem.Realign;
Begin
   Inherited;
   ApplyPlacement;
End;

Procedure TD2PopupItem.ApplyPlacement;
Var
   SavePlacement: TD2PlacementScene;
Begin
   FPopup.Placement := Placement;
   FPopup.VerticalOffset := VerticalOffset;
   FPopup.HorizontalOffset := HorizontalOffset;
   If (FScene <> nil) And (FScene.GetDesignTime) And Not (FPopup.DesignHide) Then
   Begin
      SavePlacement := FPopup.PlacementScene;
      FPopup.PlacementScene := d2PlacementSceneTarget;
      FPopup.ApplyPlacement;
      FPopup.PlacementScene := SavePlacement;
   End;
End;

Procedure TD2PopupItem.DesignClick;
Begin
   Inherited;
   FPopup.DesignHide := Not FPopup.DesignHide;
   ApplyPlacement;
   FPopup.Locked := FPopup.DesignHide;
End;

Procedure TD2PopupItem.DefineProperties(Filer: TFiler);
Begin
   Inherited;
   Filer.DefineProperty('PopupSize', ReadRect, WriteRect, True);
End;

Procedure TD2PopupItem.ReadRect(Reader: TReader);
Var
   R: TD2Rect;
Begin
   R := d2StringToRect(Reader.ReadString);
   FPopup.SetBounds(FPopup.Position.X, FPopup.Position.Y, d2RectWidth(R), d2RectHeight(R));
End;

Procedure TD2PopupItem.WriteRect(Writer: TWriter);
Begin
   Writer.WriteString(d2RectToString(FPopup.LocalRect));
End;

Procedure TD2PopupItem.DoClosePopup(Sender: TObject);
Var
   List: TList;
   i: Integer;
Begin
   // close all children
   List := TList.Create;
   AddControlsToList(List);
   For i := 0 To List.Count - 1 Do
      If (TObject(List[i]) Is TD2PopupItem) Then
         TD2PopupItem(List[i]).MouseLeave;
   List.Free;
End;

Procedure TD2PopupItem.MouseEnter;
Begin
   Inherited;
   If (FPopup <> nil) And Not (FPopup.IsOpen) Then
   Begin
      ApplyPlacement;
      FPopup.IsOpen := True;
   End;
End;

Procedure TD2PopupItem.MouseLeave;
Var
   P: TPoint;
Begin
   Inherited;
   If (FPopup <> nil) And (FPopup.IsOpen) Then
   Begin
      If FPopup.FPopupScene <> nil Then
      Begin
         GetCursorPos(P);
         With FPopup.FPopupScene.ScreenToClient(P) Do
         Begin
            If FPopup.PointInObject(x, y) Then
               Exit;
         End;
      End Else Begin
         GetCursorPos(P);
         With TD2Scene(FScene.GetComponent).ScreenToClient(P) Do
         Begin
            If FPopup.PointInObject(x, y) Then
               Exit;
            If PointInObject(x, y) Then
               Exit;
         End;
      End;
      FPopup.IsOpen := False;
   End;
End;

Procedure TD2PopupItem.SetPlacement(Value: TD2Placement);
Begin
   If FPlacement <> Value Then
   Begin
      FPlacement := Value;
      ApplyPlacement;
   End;
End;

Procedure TD2PopupItem.SetHorizontalOffset(Value: Single);
Begin
   If FHorizontalOffset <> Value Then
   Begin
      FHorizontalOffset := Value;
      ApplyPlacement;
   End;
End;

Procedure TD2PopupItem.SetVerticalOffset(Value: Single);
Begin
   If FVerticalOffset <> Value Then
   Begin
      FVerticalOffset := Value;
      ApplyPlacement;
   End;
End;

//=============== TD2MessagePopup =================================

Constructor TD2MessagePopup.Create(AOwner: TComponent);
Begin
   Inherited;
   FDragWithParent := True;
   FStaysOpen := True;
   FDisableScene := True;
End;

Destructor TD2MessagePopup.Destroy;
Begin
   Inherited;
End;

Function TD2MessagePopup.PopupModal: TModalResult;
Var
   SaveEnabled: Boolean;
Begin
   If (Scene <> nil) And (Scene.GetRoot <> nil) And FDisableScene Then
   Begin
      SaveEnabled := Scene.GetRoot.Visual.Enabled;
      Scene.GetRoot.Visual.Enabled := False;
   End;
   Result := Inherited PopupModal;
   If (Scene <> nil) And (Scene.GetRoot <> nil) And FDisableScene Then
   Begin
      Scene.GetRoot.Visual.Enabled := SaveEnabled;
   End;
End;

Procedure TD2MessagePopup.ApplyPlacement;
Begin
   If PlacementTarget = nil Then
   Begin
      If (Owner <> nil) And (Owner Is TCustomForm) Then
      Begin
         With TCustomForm(Owner).ClientToScreen(Point(0, 0)) Do
            PlacementRectangle.Rect := d2Rect(X, Y, X + Width, Y);
         HorizontalOffset := (TCustomForm(Owner).Width - Width) / 2;
      End;
   End Else Begin
      HorizontalOffset := (PlacementTarget.Width - Width) / 2;
   End;
   Inherited;
End;

Function MessagePopup(Const ACaption, AMessage: WideString; AType: TD2MessageType; Buttons: TD2MessageButtons; Const AOwner: Id2Scene; Const Target: TD2VisualObject = nil; Const ADisableScene: Boolean = True;
   Const ABitmap: TD2Bitmap = nil; Const AStyle: TD2Resources = nil): Integer;
Var
   B, DefaultButton, CancelButton: TD2MessageButton;
   PopupCtrl: TD2MessagePopup;
   C, M: TD2Label;
   L: TD2Layout;
   I: TD2VisualObject;
   Btn: TD2Button;
   SaveEnabled: Boolean;
Begin
   If (AOwner <> nil) And (AOwner.GetRoot <> nil) And ADisableScene Then
   Begin
      SaveEnabled := AOwner.GetRoot.Visual.Enabled;
      AOwner.GetRoot.Visual.Enabled := False;
   End;
   Try
      If (AOwner <> nil) And (AOwner.GetComponent <> nil) Then
         PopupCtrl := TD2MessagePopup.Create(AOwner.GetComponent.Owner)
      Else
         PopupCtrl := TD2MessagePopup.Create(AOwner.GetComponent);
      PopupCtrl.StaysOpen := True;
      PopupCtrl.DragWithParent := True;
      PopupCtrl.PlacementScene := d2PlacementSceneNew;
      PopupCtrl.Width := 500;
      PopupCtrl.Height := 150;
      PopupCtrl.Style := AStyle;
      PopupCtrl.PlacementTarget := Target;
      If Target <> nil Then
      Begin
         PopupCtrl.Scale.Point := d2Point(Target.AbsoluteMatrix.m11, Target.AbsoluteMatrix.m22);
      End;
      If (AStyle = nil) And (AOwner <> nil) And (AOwner.GetStyle <> nil) Then
         PopupCtrl.Style := AOwner.GetStyle;

      PopupCtrl.ApplyResource;

      If ABitmap <> nil Then
      Begin
         I := TD2Image.Create(PopupCtrl);
         With TD2Image(I) Do
         Begin
            Parent := PopupCtrl;
            SetBounds(30, 10, 64, 64);
            Bitmap.Assign(ABitmap);
         End;
      End Else Begin
         I := TD2Control.Create(PopupCtrl);
         With I Do
         Begin
            Parent := PopupCtrl;
            SetBounds(30, 10, 64, 64);
         End;
         Case AType Of
            d2MessageWarning: TD2Control(I).Resource := 'iconwarning';
            d2MessageError: TD2Control(I).Resource := 'iconerror';
            d2MessageInformation: TD2Control(I).Resource := 'iconinformation';
            d2MessageConfirmation: TD2Control(I).Resource := 'iconconfirmation';
         End;
      End;

      If d2ButtonOk In Buttons Then
         DefaultButton := d2ButtonOk
      Else
      If d2ButtonYes In Buttons Then
         DefaultButton := d2ButtonYes
      Else
         DefaultButton := d2ButtonRetry;
      If d2ButtonCancel In Buttons Then
         CancelButton := d2ButtonCancel
      Else
      If d2ButtonNo In Buttons Then
         CancelButton := d2ButtonNo
      Else
         CancelButton := d2ButtonOk;

      C := TD2Label.Create(PopupCtrl);
      With C Do
      Begin
         Parent := PopupCtrl;
         Align := vaTop;
         Height := 30;
         Padding.Rect := d2Rect(30, 10, 30, 0);
         If I <> nil Then
            Padding.Left := Padding.Rect.Left + I.Width + 10;
         TextW := ACaption;
         TextAlign := d2TextAlignNear;
         Font.Size := Font.Size * 1.4;
      End;
      M := TD2Label.Create(PopupCtrl);
      With M Do
      Begin
         Parent := PopupCtrl;
         Align := vaClient;
         Height := 50;
         Padding.Rect := d2Rect(30, 0, 30, 0);
         If I <> nil Then
            Padding.Left := Padding.Rect.Left + I.Width + 10;
         TextW := AMessage;
         TextAlign := d2TextAlignNear;
      End;
      L := TD2Layout.Create(PopupCtrl);
      With L Do
      Begin
         Parent := PopupCtrl;
         Align := vaBottom;
         Height := 20;
         Padding.Rect := d2Rect(30, 0, 30, 30);
      End;
      For B := Low(TD2MessageButton) To High(TD2MessageButton) Do
      Begin
         If Not (B In Buttons) Then
            Continue;
         Btn := TD2Button.Create(PopupCtrl);
         With Btn Do
         Begin
            Parent := L;
            Align := vaRight;
            Position.X := 1000;
            Padding.Rect := d2Rect(7, 0, 0, 0);
            TextW := MessageButtonNames[B];
            ModalResult := ModalResults[B];
            If B = DefaultButton Then
               Default := True;
            If B = CancelButton Then
               Cancel := True;
         End;
      End;

      Result := PopupCtrl.PopupModal;
      PopupCtrl.Free;
   Finally
      If (AOwner <> nil) And (AOwner.GetRoot <> nil) And ADisableScene Then
         AOwner.GetRoot.Visual.Enabled := SaveEnabled;
   End;
End;

//=============== TD2PopupBox ==============================================

Constructor TD2PopupBox.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := True;
   Height := 21;
   FItems := TD2WideStringList.Create;
   ;
   TD2WideStringList(FItems).OnChange := DoItemsChanged;

   If FPopupMenu = nil Then
      FPopupMenu := TPopupMenu.Create(nil);

   FItemIndex := -1;
   FText := '';
End;

Destructor TD2PopupBox.Destroy;
Begin
   If FPopupMenu <> nil Then
      FreeAndNil(FPopupMenu);
   FreeAndNil(FItems);
   Inherited;
End;

Function TD2PopupBox.GetData: Variant;
Begin
   Result := TextW;
End;

Procedure TD2PopupBox.SetData(Const Value: Variant);
Var
   S: WideString;
Begin
   If VarIsNull(Value) Then
      ItemIndex := -1
   Else
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
   If VarIsNumeric(Value) Then
      ItemIndex := Value
   Else
   If VarIsStr(Value) Then
   Begin
      S := VarToWideStr(Value);
      If FItems.IndexOf(S) < 0 Then
         TextW := S
      Else
         ItemIndex := FItems.IndexOf(S);
   End;
End;

Procedure TD2PopupBox.ApplyStyle;
Begin
   Inherited;
End;

Procedure TD2PopupBox.Click;
Begin
   Inherited;
   DoPopup;
End;

Procedure TD2PopupBox.DoPopup;
Var
   Item: TMenuItem;
   VP: TD2Point;
   i: Integer;
Begin
   FPopupMenu.Items.Clear;
   For i := 0 To FItems.Count - 1 Do
   Begin
      Item := NewItem(UTF8Encode(FItems[i]), 0, i = FItemIndex, True, DoItemClick, 0, '');
      FPopupMenu.Items.Add(Item);
   End;
   If Scene <> nil Then
   Begin
      VP := LocalToAbsolute(d2Point(0, Trunc((Height / 2) - ((FItems.Count * 20) Div 2))));
      VP := Scene.LocalToScreen(VP);
      FPopupMenu.Popup(round(VP.X), round(VP.Y));
   End;
End;

Procedure TD2PopupBox.DoItemClick(Sender: TObject);
Begin
   ItemIndex := TMenuItem(Sender).MenuIndex;
End;

Procedure TD2PopupBox.DoItemsChanged(Sender: TObject);
Begin
   Repaint;
End;

Procedure TD2PopupBox.SetItemIndex(Const Value: Integer);
Begin
   If FItemIndex <> Value Then
   Begin
      FItemIndex := Value;
      If (FItemIndex >= 0) And (Items.Count > 0) Then
         TextW := Items[FItemIndex]
      Else Begin
         TextW := '';
         FItemIndex := -1;
      End;
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Procedure TD2PopupBox.SetText(Const Value: String);
Var
   ss: WideString;
Begin
   ss := UTF8Decode(Value);

   If FItems.Count > 0 Then
   Begin
      FItemIndex := Items.IndexOf(ss);
      If FItemIndex >= 0 Then
         Inherited SetTextW(ss)
      Else
         Inherited SetTextW('');
   End Else Begin
      FItemIndex := -1;
      Inherited SetTextW('');
   End;
End;

Procedure TD2PopupBox.SetItems(Const Value: TD2WideStrings);
Begin
   FItems.Assign(Value);
End;


//==================== TD2CustomTextBox ================================================

Constructor TD2CustomTextBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FShowCaret := True;
   FFont := TD2Font.Create;
   FFont.OnChanged := FontChanged;
   FFontFill := TD2Brush.Create(d2BrushSolid, $FF000000);
   FSelectionFill := TD2Brush.Create(d2BrushSolid, $802A8ADF);
   CanFocused := True;
   Cursor := crIBeam;
   TextAlign := d2TextAlignNear;
   AutoCapture := True;
   Width := 100;
   Height := 22;
   FCaretPosition := 0;
   FSelStart := 0;
   FSelLength := 0;
   FFirstVisibleChar := 1;
   CreatePopupMenu;
End;

Destructor TD2CustomTextBox.Destroy;
Begin
   FSelectionFill.Free;
   FFontFill.Free;
   FFont.Free;
   FPopupMenu.Free;
   Inherited;
End;

Procedure TD2CustomTextBox.CreatePopupMenu;
Var
   TmpItem: TMenuItem;
Begin
   FPopupMenu := TPopupMenu.Create(Self);

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Cut';
      OnClick := DoCut;
   End;
   FPopupMenu.Items.Add(TmpItem);

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Copy';
      OnClick := DoCopy;
   End;
   FPopupMenu.Items.Add(TmpItem);

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Paste';
      OnClick := DoPaste;
   End;
   FPopupMenu.Items.Add(TmpItem);

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Delete';
      OnClick := DoDelete;
   End;
   FPopupMenu.Items.Add(TmpItem);

   //  FPopupMenu.Items.NewBottomLine;

   TmpItem := TMenuItem.Create(FPopupMenu);
   With TmpItem Do
   Begin
      Caption := 'Select All';
      OnClick := DoSelectAll;
   End;
   FPopupMenu.Items.Add(TmpItem);
End;

Procedure TD2CustomTextBox.DoSelectAll(Sender: TObject);
Begin
   SelectAll;
End;

Procedure TD2CustomTextBox.DoCut(Sender: TObject);
Begin
   CutToClipboard;
End;

Procedure TD2CustomTextBox.DoCopy(Sender: TObject);
Begin
   CopyToClipboard;
End;

Procedure TD2CustomTextBox.DoDelete(Sender: TObject);
Begin
   ClearSelection;
End;

Procedure TD2CustomTextBox.DoPaste(Sender: TObject);
Begin
   PasteFromClipboard;
End;

Procedure TD2CustomTextBox.UpdatePopupMenuItems;
Var
   SelTextEmpty: Boolean;
Begin
   SelTextEmpty := SelText <> '';
   FPopupMenu.Items.Find('Cut').Enabled := SelTextEmpty And Not ReadOnly;
   FPopupMenu.Items.Find('Copy').Enabled := SelTextEmpty;
   FPopupMenu.Items.Find('Paste').Enabled := (Clipboard.AsText <> '') And Not ReadOnly;
   FPopupMenu.Items.Find('Delete').Enabled := SelTextEmpty And Not ReadOnly;
   FPopupMenu.Items.Find('Select All').Enabled := SelText <> TextW;
End;

Function TD2CustomTextBox.GetData: Variant;
Begin
   Result := FText;
End;

Procedure TD2CustomTextBox.SetData(Const Value: Variant);
Begin
   If VarIsNull(Value) Then
      TextW := ''
   Else
   If VarIsType(Value, varDate) Then
      TextW := DateTimeToStr(VarToDateTime(Value))
   Else
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
      TextW := VarToWideStr(Value);
End;

Procedure TD2CustomTextBox.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('content');
   If (T <> nil) And (T Is TD2Content) Then
   Begin
      TD2Content(T).OnPaint := DoContentPaint;
   End;
   T := FindResource('selection');
   If (T <> nil) And (T Is TD2BrushObject) Then
   Begin
      FSelectionFill.Assign(TD2BrushObject(T).Brush);
   End;
   { from style }
   T := FindResource('foreground');
   If (T <> nil) And (T Is TD2BrushObject) Then
      FFontFill.Assign(TD2BrushObject(T).Brush);
End;

Function TD2CustomTextBox.ContentRect: TD2Rect;
Var
   T: TD2Object;
Begin
   T := FindResource('content');
   If (T <> nil) And (T.IsVisual) Then
   Begin
      Result := TD2VisualObject(T).ParentedRect;
   End Else Begin
      Result := LocalRect;
   End;
End;

Procedure TD2CustomTextBox.Paint;
Begin
   Inherited;
End;

Procedure TD2CustomTextBox.DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
Var
   i: Integer;
   R: TD2Rect;
   SaveIdx: Cardinal;
Begin
   { draw TextW }
   If FText = '' Then
      Exit;

   SaveIdx := aCanvas.SaveCanvas;
   aCanvas.IntersectClipRect(ARect);

   aCanvas.Font.Assign(Font);
   aCanvas.Fill.Assign(FFontFill);
   If FPassword Then
   Begin
      R := ARect;
      R.Right := R.Left + GetPasswordCharWidth - 1;
      R.Top := (d2RectHeight(ARect) - d2RectWidth(R)) / 2;
      R.Bottom := R.Top + d2RectWidth(R);
      For i := FFirstVisibleChar To Length(TextW) Do
      Begin
         aCanvas.FillEllipse(R, AbsoluteOpacity);
         d2OffsetRect(R, d2RectWidth(R) + 1, 0);
      End;
   End Else Begin
      R := ARect;
      If Textalign = d2TextAlignNear Then
         R.Right := R.Right + (Font.Size / 2);

      aCanvas.FillText(R, R, Copy(TextW, FFirstVisibleChar, Length(TextW) - FFirstVisibleChar + 1),
         False, AbsoluteOpacity, TextAlign, d2TextAlignCenter);
   End;

   If IsFocused Then
   Begin
      If SelLength > 0 Then
      Begin
         aCanvas.Fill.Assign(FSelectionFill);
         R := GetSelRect;
         With ContentRect Do
            d2OffsetRect(R, -Left, -Top);
         aCanvas.FillRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      End;
   End;

   aCanvas.RestoreCanvas(SaveIdx);
End;

Procedure TD2CustomTextBox.InsertText(Const AText: WideString);
Var
   TmpS: WideString;
Begin
   If ReadOnly Then
      Exit;

   TmpS := TextW;
   Delete(TmpS, SelStart + 1, SelLength);

   Insert(AText, TmpS, SelStart + 1);
   If (MaxLength <= 0) Or (Length(TmpS) <= MaxLength) Then
   Begin
      TextW := TmpS;
      CaretPosition := SelStart + Length(AText);
   End;
   SelLength := 0;
End;

Procedure TD2CustomTextBox.UpdateFirstVisibleChar;
Var
   LEditRect: TD2Rect;
Begin
   If FFirstVisibleChar >= (FCaretPosition + 1) Then
   Begin
      FFirstVisibleChar := FCaretPosition;
      If FFirstVisibleChar < 1 Then
         FFirstVisibleChar := 1;
   End Else Begin
      LEditRect := ContentRect;

      If Password Then
         While ((FCaretPosition - FFirstVisibleChar + 1) * GetPasswordCharWidth > LEditRect.Right - LEditRect.Left) And (FFirstVisibleChar < Length(TextW)) Do
            Inc(FFirstVisibleChar)
      Else Begin
         While (TextWidth(Copy(TextW, FFirstVisibleChar, FCaretPosition - FFirstVisibleChar + 1)) > LEditRect.Right - LEditRect.Left) And (FFirstVisibleChar < Length(TextW)) Do
         Begin
            If TextWidth(Copy(TextW, FFirstVisibleChar + 500, (FCaretPosition - FFirstVisibleChar + 500) + 1)) > LEditRect.Right - LEditRect.Left Then
               Inc(FFirstVisibleChar, 500)
            Else
            If TextWidth(Copy(TextW, FFirstVisibleChar + 100, (FCaretPosition - FFirstVisibleChar + 100) + 1)) > LEditRect.Right - LEditRect.Left Then
               Inc(FFirstVisibleChar, 100)
            Else
            If TextWidth(Copy(TextW, FFirstVisibleChar + 50, (FCaretPosition - FFirstVisibleChar + 100) + 1)) > LEditRect.Right - LEditRect.Left Then
               Inc(FFirstVisibleChar, 50)
            Else
            If TextWidth(Copy(TextW, FFirstVisibleChar + 10, (FCaretPosition - FFirstVisibleChar + 10) + 1)) > LEditRect.Right - LEditRect.Left Then
               Inc(FFirstVisibleChar, 10)
            Else
               Inc(FFirstVisibleChar);
         End;
      End;
   End;
   Repaint;
End;

Procedure TD2CustomTextBox.UpdateCaretePosition;
Begin
   SetCaretPosition(CaretPosition);
End;

Function TD2CustomTextBox.GetPasswordCharWidth: Single;
Begin
   Result := Font.Size / 2;
End;

Function TD2CustomTextBox.TextWidth(Const Str: WideString): Single;
Var
   R: TD2Rect;
Begin
   R := ContentRect;
   R.Right := 10000;
   Canvas.Font.Assign(Font);
   If FPassword Then
   Begin
      R.Right := R.Left + GetPasswordCharWidth * Length(Str);
   End Else
      Canvas.MeasureText(R, R, Str, False, d2TextAlignNear, d2TextAlignCenter);
   Result := d2RectWidth(R);
End;

Function TD2CustomTextBox.GetCoordinatePosition(x: Single): Integer;
Var
   TmpX, WholeTextWidth, EditRectWidth: Single;
   Str, StrA: WideString;
Begin
   Result := FFirstVisibleChar - 1;
   If Length(TextW) = 0 Then
      Exit;

   If FPassword Then
      WholeTextWidth := Length(TextW) * GetPasswordCharWidth
   Else
      WholeTextWidth := TextWidth(Copy(TextW, 1, Length(TextW)));

   EditRectWidth := ContentRect.Right - ContentRect.Left;
   TmpX := x;
   If WholeTextWidth < EditRectWidth Then
      Case TextAlign Of
         d2TextAlignFar: TmpX := x - (EditRectWidth - WholeTextWidth);
         d2TextAlignCenter: TmpX := x - ((EditRectWidth - WholeTextWidth) / 2);
      End;

   If FPassword Then
   Begin
      Result := Result + Trunc((TmpX - ContentRect.Left) / GetPasswordCharWidth);
      If Result < 0 Then
         Result := 0
      Else
      If Result > Length(TextW) Then
         Result := Length(TextW);
   End Else Begin
      TmpX := TmpX - ContentRect.Left;
      StrA := System.Copy(TextW, FFirstVisibleChar, Result - FFirstVisibleChar + 1);
      Str := System.Copy(TextW, FFirstVisibleChar, Result - FFirstVisibleChar + 2);
      While (TextWidth(StrA) < TmpX) And (Result < Length(TextW)) Do
      Begin
         If (TmpX > TextWidth(StrA) + ((TextWidth(Str) - TextWidth(StrA)) / 2)) And (TmpX < TextWidth(Str)) Then
         Begin
            Result := Result + 1;
            Break;
         End;
         If TmpX < TextWidth(Str) Then
            Break;
         Result := Result + 1;
         StrA := Str;
         Str := Copy(TextW, FFirstVisibleChar, Result - FFirstVisibleChar + 2);
      End;
   End;
End;

Function TD2CustomTextBox.GetCharX(a: Integer): Single;
Var
   WholeTextWidth: Single;
   EditRectWidth: Single;
   R: TD2Rect;
   T: WideString;
Begin
   If FPassword Then
   Begin
      WholeTextWidth := Length(TextW) * GetPasswordCharWidth;
      Result := ContentRect.Left;
      If a > 0 Then
      Begin
         If FPassword Then
         Begin
            If a <= Length(TextW) Then
               Result := Result + (a - FFirstVisibleChar + 1) * GetPasswordCharWidth
            Else
               Result := Result + (Length(TextW) - FFirstVisibleChar + 1) * GetPasswordCharWidth;
         End;
      End;
      EditRectWidth := ContentRect.Right - ContentRect.Left;
      If WholeTextWidth < EditRectWidth Then
         Case TextAlign Of
            d2TextAlignFar: Result := Result + (EditRectWidth - WholeTextWidth);
            d2TextAlignCenter: Result := Result + ((EditRectWidth - WholeTextWidth) / 2);
         End;
      Exit;
   End;

   R := ContentRect;
   Canvas.Font.Assign(Font);
   T := TextW;
   If TextW = '' Then
      T := 'a';
   Canvas.MeasureText(R, R, T, False, d2TextAlignNear, d2TextAlignCenter);
   WholeTextWidth := R.Right - ContentRect.Left;
   Result := ContentRect.Left;

   If a > 0 Then
   Begin
      If a <= Length(TextW) Then
      Begin
         R := ContentRect;
         Canvas.MeasureText(R, R, Copy(TextW, FFirstVisibleChar, a - FFirstVisibleChar + 1), False, d2TextAlignNear, d2TextAlignCenter);
         Result := R.Right;
      End Else Begin
         R := ContentRect;
      End;
   End;

   EditRectWidth := ContentRect.Right - ContentRect.Left;
   If WholeTextWidth < EditRectWidth Then
      Case TextAlign Of
         d2TextAlignFar: Result := Result + (EditRectWidth - WholeTextWidth);
         d2TextAlignCenter: Result := Result + ((EditRectWidth - WholeTextWidth) / 2);
      End;
End;

Function TD2CustomTextBox.GetNextWordBeging(StartPosition: Integer): Integer;
Var
   SpaceFound, WordFound: Boolean;
Begin
   Result := StartPosition;
   SpaceFound := False;
   WordFound := False;
   While (Result + 2 <= Length(TextW)) And ((Not ((TextW[Result + 1] <> d2WideSpace) And SpaceFound)) Or Not WordFound) Do
   Begin
      If TextW[Result + 1] = d2WideSpace Then
         SpaceFound := True;
      If TextW[Result + 1] <> d2WideSpace Then
      Begin
         WordFound := True;
         SpaceFound := False;
      End;

      Result := Result + 1;
   End;
   If Not SpaceFound Then
      Result := Result + 1;
End;

Function TD2CustomTextBox.GetPrivWordBeging(StartPosition: Integer): Integer;
Var
   WordFound: Boolean;
Begin
   Result := StartPosition;
   WordFound := False;
   While (Result > 0) And ((TextW[Result] <> d2WideSpace) Or Not WordFound) Do
   Begin
      If TextW[Result] <> d2WideSpace Then
         WordFound := True;
      Result := Result - 1;
   End;
End;

Function TD2CustomTextBox.GetSelStart: Integer;
Begin
   If FSelLength > 0 Then
      Result := FSelStart
   Else
   If FSelLength < 0 Then
      Result := FSelStart + FSelLength
   Else
      Result := CaretPosition;
End;

Function TD2CustomTextBox.GetSelRect: TD2Rect;
Begin
   Result := ContentRect;
   Result.Left := GetCharX(SelStart);
   Result.Right := GetCharX(SelStart + SelLength) + 1;
End;

Function TD2CustomTextBox.GetSelLength: Integer;
Begin
   Result := Abs(FSelLength);
End;

Function TD2CustomTextBox.GetSelText: WideString;
Begin
   Result := Copy(TextW, SelStart + 1, SelLength);
End;

Procedure TD2CustomTextBox.SetSelLength(Const Value: Integer);
Begin
   If FSelLength <> Value Then
   Begin
      FSelLength := Value;
      Repaint;
   End;
End;

Procedure TD2CustomTextBox.SetSelStart(Const Value: Integer);
Begin
   If FSelStart <> Value Then
   Begin
      SelLength := 0;
      FSelStart := Value;
      CaretPosition := FSelStart;
      Repaint;
   End;
End;

Procedure TD2CustomTextBox.SetCaretPosition(Const Value: Integer);
Begin
   If Value < 0 Then
      FCaretPosition := 0
   Else
   If Value > Length(TextW) Then
      FCaretPosition := Length(TextW)
   Else
      FCaretPosition := Value;

   UpdateFirstVisibleChar;

   If SelLength <= 0 Then
      FSelStart := Value;

   Repaint;
   If IsFocused And FShowCaret Then
   Begin
      SetCaretSize(d2Point(2, (Font.Size * 1.25)));
      SetCaretPos(d2Point(GetCharX(FCaretPosition) - 1, (ContentRect.Top + ContentRect.Bottom - (Font.Size * 1.25)) / 2));
      SetCaretColor(FFontFill.SolidColor);
   End;
End;

Procedure TD2CustomTextBox.SetMaxLength(Const Value: Integer);
Begin
   If FMaxLength <> Value Then
   Begin
      FMaxLength := Value;
   End;
End;

Procedure TD2CustomTextBox.CopyToClipboard;
Begin
   If SelText <> '' Then
      Clipbrd.Clipboard.AsText := UTF8Encode(SelText);
End;

Procedure TD2CustomTextBox.PasteFromClipboard;
Begin
   If ReadOnly Then
      Exit;

   InsertText(UTF8Decode(Clipbrd.Clipboard.AsText));
End;

Procedure TD2CustomTextBox.ClearSelection;
Var
   TmpS: WideString;
Begin
   If ReadOnly Then
      Exit;

   TmpS := TextW;
   Delete(TmpS, SelStart + 1, SelLength);
   CaretPosition := SelStart;
   TextW := TmpS;
   SelLength := 0;
End;

Procedure TD2CustomTextBox.CutToClipboard;
Begin
   CopyToClipboard;
   ClearSelection;
End;

Procedure TD2CustomTextBox.SelectAll;
Begin
   SelStart := 0;
   SelLength := Length(TextW);
   SetCaretPosition(Length(TextW));
   Repaint;
End;

Procedure TD2CustomTextBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Var
   S: WideString;
   TmpS: WideString;
   OldCaretPosition: Integer;
Begin
   Inherited;
   OldCaretPosition := CaretPosition;
   FLastChar := KeyChar;
   FLastKey := Key;
   Case Key Of
      VK_RETURN: Change;
      VK_END: CaretPosition := Length(TextW);
      VK_HOME: CaretPosition := 0;
      VK_LEFT:
         If ssCtrl In Shift Then
            CaretPosition := GetPrivWordBeging(CaretPosition)
         Else
            CaretPosition := CaretPosition - 1;
      VK_RIGHT:
         If ssCtrl In Shift Then
            CaretPosition := GetNextWordBeging(CaretPosition)
         Else
            CaretPosition := CaretPosition + 1;
      VK_DELETE, 8:
         If Not ReadOnly Then
         Begin
            If SelLength <> 0 Then
            Begin
               If Shift = [ssShift] Then
                  CutToClipboard
               Else
                  ClearSelection;
            End Else Begin
               TmpS := TextW;
               If TmpS <> '' Then
                  If Key = VK_DELETE Then
                  Begin
                     Delete(TmpS, CaretPosition + 1, 1);
                  End Else Begin
                     Delete(TmpS, CaretPosition, 1);
                     CaretPosition := CaretPosition - 1;
                  End;
               TextW := TmpS;
               If Assigned(FOnTyping) Then
                  FOnTyping(Self);
            End;
         End;
      VK_INSERT:
         If Shift = [ssCtrl] Then
         Begin
            CopyToClipboard;
         End Else
         If Shift = [ssShift] Then
         Begin
            PasteFromClipboard;
            If Assigned(FOnTyping) Then
               FOnTyping(Self);
         End;
   End;

   If (KeyChar <> #0) And (FFilterChar <> '') And (Pos(KeyChar, FFilterChar) = 0) Then
   Begin
      KeyChar := #0;
   End;
   Case KeyChar Of
      'a', 'A':
         If Shift = [ssCtrl] Then
         Begin
            SelectAll;
            KeyChar := #0;
         End;
      'c', 'C':
         If Shift = [ssCtrl] Then
         Begin
            CopyToClipboard;
            KeyChar := #0;
         End;
      'v', 'V':
         If Shift = [ssCtrl] Then
         Begin
            PasteFromClipboard;
            If Assigned(FOnTyping) Then
               FOnTyping(Self);
            KeyChar := #0;
         End;
      'x', 'X':
         If Shift = [ssCtrl] Then
         Begin
            CutToClipboard;
            KeyChar := #0;
         End;
      'z', 'Z':
         If Shift = [ssCtrl] Then
         Begin
            KeyChar := #0;
         End;
   End;

   If Key In [VK_END, VK_HOME, VK_LEFT, VK_RIGHT] Then
   Begin
      If ssShift In Shift Then
      Begin
         If SelLength = 0 Then
            FSelStart := OldCaretPosition;
         FSelStart := CaretPosition;
         FSelLength := FSelLength - (CaretPosition - OldCaretPosition);
      End Else
         FSelLength := 0;
      Repaint;
   End;

   If (Ord(KeyChar) >= 32) And Not ReadOnly Then
   Begin
      S := KeyChar;
      InsertText(S);
      If Assigned(FOnTyping) Then
         FOnTyping(Self);
   End;
   If FResourceLink <> nil Then
      FResourceLink.Visual.UpdateEffects;
   UpdateCaretePosition;
End;

Procedure TD2CustomTextBox.KeyUp(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
End;

Procedure TD2CustomTextBox.DblClick;
Begin
   Inherited;
   SelectAll;
End;

Procedure TD2CustomTextBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   OldFocused: Boolean;
Begin
   OldFocused := IsFocused;
   Inherited;
   If Button = mbLeft Then
      FLMouseSelecting := True;

   If (Button = mbLeft) And Not (ssDouble In Shift) Then
   Begin
      If OldFocused Then
      Begin
         CaretPosition := GetCoordinatePosition(x);
         SelLength := 0;
      End;
   End;
End;

Procedure TD2CustomTextBox.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Var
   OldCaretPosition: Integer;
   TmpNewPosition: Integer;
Begin
   Inherited;
   If FLMouseSelecting Then
   Begin
      TmpNewPosition := GetCoordinatePosition(x);
      OldCaretPosition := CaretPosition;
      If (x > ContentRect.Right) Then
         CaretPosition := TmpNewPosition + 1
      Else
         CaretPosition := TmpNewPosition;
      If SelLength = 0 Then
         FSelStart := OldCaretPosition;
      FSelStart := CaretPosition;
      FSelLength := FSelLength - (CaretPosition - OldCaretPosition);
   End;
End;

Procedure TD2CustomTextBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   FLMouseSelecting := False;
End;

Procedure TD2CustomTextBox.Change;
Begin
   If FNeedChange Then
   Begin
      If Assigned(FBindingObjects) Then
         ToBindingObjects;
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Procedure TD2CustomTextBox.ContextMenu(Const ScreenPosition: TD2Point);
Begin
   Inherited;
   If csDesigning In ComponentState Then
      Exit;
   If Popup <> nil Then
      Exit;

   UpdatePopupMenuItems;
   FPopupMenu.PopupComponent := Self;
   FPopupMenu.Popup(round(ScreenPosition.X), round(ScreenPosition.Y));
End;

Procedure TD2CustomTextBox.EnterFocus;
Begin
   Inherited;
   FNeedChange := False;
   If FShowCaret Then
      ShowCaretProc;
   If FScene.ShowKeyboardForControl(Self) Then
      CaretPosition := Length(TextW)
   Else
      SelectAll;
End;

Procedure TD2CustomTextBox.KillFocus;
Begin
   If Not Assigned(FScene) Then
   Begin
      Inherited;
      Exit;
   End;
   FScene.HideKeyboardForControl(Self);
   If FShowCaret Then
      HideCaret;
   Inherited;
   Change;
End;

Procedure TD2CustomTextBox.SetTextW(Const Value: WideString);
Begin
   If FText <> Value Then
   Begin
      FText := Value;
      If FCaretPosition > Length(TextW) Then
         SetCaretPosition(Length(TextW));
      If Not (csLoading In ComponentState) And Assigned(OnChangeTracking) Then
         OnChangeTracking(Self);
      FNeedChange := True;
      Repaint;
   End;
End;

Function TD2CustomTextBox.GetTextW: WideString;
Begin
   Result := ftext;
End;

Procedure TD2CustomTextBox.SetText(Const Value: String);
Var
   ss: WideString;
Begin
   ss := UTF8Decode(Value);
   SetTextW(ss);
End;

Function TD2CustomTextBox.GetText: String;
Begin
   Result := UTF8Encode(ftext);
End;

Procedure TD2CustomTextBox.SetPassword(Const Value: Boolean);
Begin
   If FPassword <> Value Then
   Begin
      FPassword := Value;
      Repaint;
   End;
End;

Procedure TD2CustomTextBox.SetFont(Const Value: TD2Font);
Begin
   FFont.Assign(Value);
End;

Procedure TD2CustomTextBox.SetTextAlign(Const Value: TD2TextAlign);
Begin
   If FTextAlign <> Value Then
   Begin
      FTextAlign := Value;
      Repaint;
   End;
End;

Procedure TD2CustomTextBox.FontChanged(Sender: TObject);
Begin
   Repaint;
End;

//====== TD2NumberBox =========================

Constructor TD2NumberBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FDecimalDigits := 2;
   FFilterChar := '0123456789.,-+';
   Max := 10;
   VertIncrement := 5;
   HorzIncrement := 1;
   TextW := '0';
   Value := 0;
   AutoCapture := True;
End;

Destructor TD2NumberBox.Destroy;
Begin
   Inherited;
End;

Function TD2NumberBox.GetData: Variant;
Begin
   Result := Value;
End;

Procedure TD2NumberBox.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
      Self.Value := Value;
End;

Procedure TD2NumberBox.Change;
Begin
   Try
      FValue := StrToFloat(TextW);
      If FValue > FMax Then
         FValue := FMax;
      If FValue < FMin Then
         FValue := FMin;
      If (frac(FValue) = 0) Or (FValueType = d2ValueInteger) Then
         FText := IntToStr(Trunc(FValue))
      Else
         FText := FloattoStr(FValue);
   Except
      If (frac(FValue) = 0) Or (FValueType = d2ValueInteger) Then
         FText := IntToStr(Trunc(FValue))
      Else
         FText := FloattoStr(FValue);
   End;
   Repaint;
   Inherited;
End;

Procedure TD2NumberBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Case Key Of
      VK_UP: Value := Value + HorzIncrement;
      VK_DOWN: Value := Value - HorzIncrement;
      Else Inherited;
         Exit;
   End;
   Key := 0;
End;

Procedure TD2NumberBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      FPressed := True;
      FPressedPos := d2Point(X, Y);
      FPressedVert := False;
      FPressedInc := 0;
   End;
End;

Procedure TD2NumberBox.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
   If FPressed Then
   Begin
      If Abs(X - FPressedPos.X) >= Abs(Y - FPressedPos.Y) Then
      Begin
         If X > FPressedPos.X Then
            Value := Value + HorzIncrement
         Else
            Value := Value - HorzIncrement;
         FPressedInc := X - FPressedPos.X;
         FPressedVert := False;
      End Else Begin
         If Y < FPressedPos.Y Then
            Value := Value + VertIncrement
         Else
            Value := Value - VertIncrement;
         FPressedInc := X - FPressedPos.X;
         FPressedVert := True;
      End;
      FNeedChange := True;
      FPressedPos := d2Point(X, Y);
   End;
End;

Procedure TD2NumberBox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If FPressed Then
   Begin
      FPressed := False;
      Change;
      Repaint;
   End;
End;

Procedure TD2NumberBox.Paint;
Begin
   Inherited;
End;

Procedure TD2NumberBox.PaintChildren;
Var
   R: TD2Rect;
Begin
   If FPressed Then
      FDisableCaret := True;
   Inherited;
   If FPressed Then
   Begin
      Canvas.SetMatrix(AbsoluteMatrix);
      Canvas.Fill.Style := d2BrushSolid;
      Canvas.Fill.SolidColor := $AA505050;
      R := LocalRect;
      If FPressedVert Then
      Begin
         d2InflateRect(R, -1, -1);
         R.Left := R.Right - 5;
         Canvas.FillRect(R, 1, 1, AllCorners, AbsoluteOpacity);
         d2InflateRect(R, -1, -1);
      End Else Begin
         d2InflateRect(R, -1, -1);
         R.Top := R.Bottom - 5;
         Canvas.FillRect(R, 1, 1, AllCorners, AbsoluteOpacity);
         d2InflateRect(R, -1, -1);
      End;
   End;
   If FPressed Then
      FDisableCaret := False;
End;

Procedure TD2NumberBox.SetMax(Const Value: Single);
Begin
   FMax := Value;
End;

Procedure TD2NumberBox.SetMin(Const Value: Single);
Begin
   FMin := Value;
End;

Procedure TD2NumberBox.SetDecimalDigits(Const Value: Integer);
Begin
   FDecimalDigits := Value;
End;

Procedure TD2NumberBox.SetValue(Const AValue: Single);
Begin
   If FValue <> AValue Then
   Begin
      FValue := AValue;
      If FValue > FMax Then
         FValue := FMax;
      If FValue < FMin Then
         FValue := FMin;
      If (frac(FValue) = 0) Or (FValueType = d2ValueInteger) Then
         FText := IntToStr(Trunc(FValue))
      Else
         FText := Format('%.' + IntToStr(FDecimalDigits) + 'f', [FValue]);
      SelLength := 0;
      Repaint;
   End;
End;

Procedure TD2NumberBox.SetValueType(Const Value: TD2ValueType);
Begin
   FValueType := Value;
End;

Procedure TD2NumberBox.SetText(Const Value: String); // 7777
Begin
   Inherited;
End;

//============================= TD2SpinBox ===========================================

Constructor TD2SpinBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FDecimalDigits := 2;
   FFilterChar := '0123456789.,-+';
   TextAlign := d2TextAlignCenter;
   Increment := 1;
   Max := 10;
   fText := '0';
   Value := 0;
End;

Destructor TD2SpinBox.Destroy;
Begin
   Inherited;
End;

Procedure TD2SpinBox.ApplyStyle;
Var
   B: TD2Object;
Begin
   Inherited;
   B := FindResource('minusbutton');
   If (B <> nil) And (B Is TD2CustomButton) Then
   Begin
      FMinus := TD2CustomButton(B);
      FMinus.OnClick := DoMinusClick;
   End;
   B := FindResource('plusbutton');
   If (B <> nil) And (B Is TD2CustomButton) Then
   Begin
      FPlus := TD2CustomButton(B);
      FPlus.OnClick := DoPlusClick;
   End;
End;

Procedure TD2SpinBox.FreeStyle;
Begin
   Inherited;
   FMinus := nil;
   FPlus := nil;
End;

Procedure TD2SpinBox.DoMinusClick(Sender: TObject);
Begin
   SetFocus;
   Value := Value - Increment;
   FNeedChange := True;
   Change;
End;

Procedure TD2SpinBox.DoPlusClick(Sender: TObject);
Begin
   SetFocus;
   Value := Value + Increment;
   FNeedChange := True;
   Change;
End;

Function TD2SpinBox.GetData: Variant;
Begin
   Result := Value;
End;

Procedure TD2SpinBox.SetData(Const Value: Variant);
Begin
   If VarIsEvent(Value) Then
      OnChange := VariantToEvent(Value)
   Else
      Self.Value := Value;
End;

Procedure TD2SpinBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Case Key Of
      VK_UP: Value := Value + Increment;
      VK_DOWN: Value := Value - Increment;
      Else Inherited;
         Exit;
   End;
   Key := 0;
End;

Procedure TD2SpinBox.Change;
Begin
   Try
      FValue := StrToFloat(TextW);
      If FValue > FMax Then
         FValue := FMax;
      If FValue < FMin Then
         FValue := FMin;
      If (frac(FValue) = 0) Or (FValueType = d2ValueInteger) Then
         FText := IntToStr(Trunc(FValue))
      Else
         FText := FloattoStr(FValue);
   Except
      If (frac(FValue) = 0) Or (FValueType = d2ValueInteger) Then
         FText := IntToStr(Trunc(FValue))
      Else
         FText := FloattoStr(FValue);
   End;
   Repaint;
   Inherited;
End;

Procedure TD2SpinBox.SetMax(Const Value: Single);
Begin
   FMax := Value;
End;

Procedure TD2SpinBox.SetMin(Const Value: Single);
Begin
   FMin := Value;
End;

Procedure TD2SpinBox.SetValue(Const AValue: Single);
Var
   xDFS: TFormatSettings;
Begin
   If FValue <> AValue Then
   Begin
      FValue := AValue;
      If FValue > FMax Then
         FValue := FMax;
      If FValue < FMin Then
         FValue := FMin;

      If (frac(FValue) = 0) Or (FValueType = d2ValueInteger) Then
      Begin
         FText := IntToStr(Trunc(FValue));
      End Else Begin
         xDFS := DefaultFormatSettings;
         xDFS.CurrencyDecimals := self.DecimalDigits;
         FText := FloatToStr(FValue, xDFS);
      End;

      SelLength := 0;
      Repaint;
   End;
End;

Procedure TD2SpinBox.SetValueType(Const Value: TD2ValueType);
Begin
   If FValueType <> Value Then
   Begin
      FValueType := Value;
      If (FValueType = d2ValueInteger) And (Frac(Increment) <> 0) Then
         Increment := 1;
   End;
End;

Procedure TD2SpinBox.SetDecimalDigits(Const Value: Integer);
Begin
   FDecimalDigits := Value;
End;

Procedure TD2SpinBox.SetText(Const Value: String); // 7777
Begin
   Inherited;
End;

//============  TD2ColorBox ==========================

Constructor TD2ColorBox.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Procedure TD2ColorBox.Paint;
Var
   i, j: Integer;
   SaveIndex: Integer;
Begin
   SaveIndex := Canvas.SaveCanvas;
   Canvas.IntersectClipRect(LocalRect);
   Canvas.Stroke.Style := d2BrushNone;
   Canvas.Fill.Style := d2BrushSolid;
   Canvas.Fill.SolidColor := $FFFFFFFF;
   Canvas.FillRect(LocalRect, 0, 0, AllCorners, AbsoluteOpacity);
   Canvas.Fill.SolidColor := $FFD3D3D3;
   For i := 0 To Trunc(Width / 5) + 1 Do
      For j := 0 To Trunc(Height / 5) + 1 Do
      Begin
         If Odd(i + j) Then
         Begin
            Canvas.FillRect(d2Rect(i * 5, j * 5, (i + 1) * 5, (j + 1) * 5), 0, 0, AllCorners, AbsoluteOpacity);
         End;
      End;
   Canvas.RestoreCanvas(SaveIndex);

   Canvas.Fill.Style := d2BrushSolid;
   Canvas.Fill.SolidColor := FColor;
   Canvas.FillRect(LocalRect, 0, 0, AllCorners, AbsoluteOpacity);
End;

Procedure TD2ColorBox.SetColor(Const Value: TD2Color);
Begin
   If FColor <> Value Then
   Begin
      FColor := Value;
      Repaint;
   End;
End;


//=============== TD2CalendarBox =================================================

Constructor TD2CalendarBox.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := True;
   Cursor := crDefault;
   TextAlign := d2TextAlignNear;
   FResource := 'combotextboxstyle';
   FPopup := TD2Popup.Create(Self);
   FPopup.FResource := 'combopopupstyle';
   FPopup.PlacementTarget := Self;
   FPopup.StaysOpen := False;
   FPopup.Stored := False;
   FPopup.Parent := Self;
   FPopup.Locked := True;
   FPopup.DesignHide := True;
   FPopup.OnClosePopup := DoClosePopup;
   FPopup.Width := 186;
   FPopup.Height := 166;
   FCalendar := TD2Calendar.Create(Self);
   FCalendar.Parent := FPopup;
   FCalendar.Stored := False;
   FCalendar.Padding.Rect := d2Rect(3, 3, 3, 3);
   FCalendar.Align := vaClient;
   FCalendar.OnChange := DoCalendarChanged;
   FCalendar.OnDayChange := DoDayChanged;
   TextW := DateTimeToStr(FCalendar.DateTime);
   Width := 100;
   Height := 22;
End;

Destructor TD2CalendarBox.Destroy;
Begin
   Inherited;
End;

Procedure TD2CalendarBox.DoCalendarChanged(Sender: TObject);
Begin
   TextW := DateTimeToStr(FCalendar.DateTime);
End;

Procedure TD2CalendarBox.DoDayChanged(Sender: TObject);
Begin
   If FPopup.IsOpen Then
      DropDown;
End;

Procedure TD2CalendarBox.DoClosePopup(Sender: TObject);
Begin
End;

Procedure TD2CalendarBox.DropDown;
Begin
   If Not FPopup.IsOpen Then
   Begin
      FPopup.Placement := FPlacement;
      FPopup.IsOpen := True;
   End Else Begin
      FPopup.IsOpen := False;
   End;
End;

Function TD2CalendarBox.GetDate: TDate;
Begin
   Result := FCalendar.Date;
End;

Procedure TD2CalendarBox.SetDate(Const Value: TDate);
Begin
   FCalendar.Date := Value;
   TextW := DateTimeToStr(FCalendar.DateTime);
End;

Procedure TD2CalendarBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If (Button = mbLeft) Then
   Begin
      DropDown;
   End;
End;

Procedure TD2CalendarBox.DoContentPaint(Sender: TObject; aCanvas: TD2Canvas; Const ARect: TD2Rect);
Begin
   aCanvas.Font.Assign(Font);
   aCanvas.Fill.Assign(FontFill);
   aCanvas.FillText(ARect, ARect, TextW, False, AbsoluteOpacity, TextAlign, d2TextAlignCenter);
End;

Procedure TD2CalendarBox.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('Content');
   If (T <> nil) And (T Is TD2Content) Then
   Begin
      TD2Content(T).OnPaint := DoContentPaint;
   End;
End;

//=============== TD2CalendarTextBox =====================================================

Constructor TD2CalendarTextBox.Create(AOwner: TComponent);
Begin
   Inherited;
   Cursor := crDefault;
   FFilterChar := '0123456789./';
   FResource := 'combotextboxstyle';
   FPopup := TD2Popup.Create(Self);
   FPopup.FResource := 'combopopupstyle';
   FPopup.PlacementTarget := Self;
   FPopup.StaysOpen := False;
   FPopup.Stored := False;
   FPopup.Parent := Self;
   FPopup.Locked := True;
   FPopup.DesignHide := True;
   FPopup.OnClosePopup := DoClosePopup;
   FPopup.Width := 186;
   FPopup.Height := 166;
   FCalendar := TD2Calendar.Create(Self);
   FCalendar.Parent := FPopup;
   FCalendar.Stored := False;
   FCalendar.Padding.Rect := d2Rect(3, 3, 3, 3);
   FCalendar.Align := vaClient;
   FCalendar.OnChange := DoCalendarChanged;
   FCalendar.OnDayChange := DoDayChanged;
   TextW := DateTimeToStr(FCalendar.DateTime);
End;

Destructor TD2CalendarTextBox.Destroy;
Begin
   Inherited;
End;

Procedure TD2CalendarTextBox.DoCalendarChanged(Sender: TObject);
Begin
   If FPopup.IsOpen Then
   Begin
      TextW := DateTimeToStr(FCalendar.DateTime);
      CaretPosition := Length(TextW);
      Change;
      FNeedChange := False;
      DropDown;
   End;
End;

Procedure TD2CalendarTextBox.DoDayChanged(Sender: TObject);
Begin
End;

Procedure TD2CalendarTextBox.DoClosePopup(Sender: TObject);
Begin
   If ShowCaret And IsFocused Then
      ShowCaretProc;
End;

Procedure TD2CalendarTextBox.DropDown;
Begin
   If Not FPopup.IsOpen Then
   Begin
      If ShowCaret Then
         HideCaret;
      FPopup.Placement := FPlacement;
      FPopup.IsOpen := True;
   End Else Begin
      FPopup.IsOpen := False;
   End;
End;

Function TD2CalendarTextBox.GetDate: TDate;
Begin
   Result := FCalendar.Date;
End;

Procedure TD2CalendarTextBox.SetDate(Const Value: TDate);
Begin
   FCalendar.Date := Value;
   TextW := DateTimeToStr(FCalendar.DateTime);
End;

Procedure TD2CalendarTextBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If (Button = mbLeft) And Not d2PtInRect(d2Point(X, Y), ContentRect) Then
   Begin
      DropDown;
   End;
End;

//=============== TD2PaintBox ===========================================

Constructor TD2PaintBox.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2PaintBox.Destroy;
Begin
   Inherited;
End;

Procedure TD2PaintBox.Paint;
Begin
   Inherited;
   If Assigned(FOnPaint) Then
      FOnPaint(Self, Canvas, LocalRect);
End;


//================= TD2TabItem =========================================

Constructor TD2TabItem.Create(AOwner: TComponent);
Begin
   Inherited;
   FTabIndex := -1;
   FAutoTranslate := True;
   Height := 20;
   Width := 80;
   HitTest := True;
End;

Procedure TD2TabItem.ApplyStyle;
Begin
   Inherited;
   If (Parent <> nil) And (Parent Is TD2TabControl) And (TD2TabControl(Parent).ItemIndex = Index) Then
      Select(True)
   Else
      Select(False);
End;

Destructor TD2TabItem.Destroy;
Begin
   Inherited;
End;

Procedure TD2TabItem.DesignSelect;
Begin
   Inherited;
   If (Parent <> nil) And (Parent Is TD2TabControl) Then
      TD2TabControl(Parent).ItemIndex := Index;
End;

Procedure TD2TabItem.Realign;
Begin
   Inherited;
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;
   If (FLayout <> nil) And (Parent <> nil) And (Parent Is TD2TabControl) Then
   Begin
      FLayout.Position.X := TD2TabControl(Parent).Margins.left + FLayout.Padding.left;
      FLayout.Position.Y := Self.Height + TD2TabControl(Parent).Margins.top + FLayout.Padding.bottom;
      FLayout.Width := TD2TabControl(Parent).Width - TD2TabControl(Parent).Margins.left - TD2TabControl(Parent).Margins.right - FLayout.Padding.left - FLayout.Padding.Right;
      FLayout.Height := TD2TabControl(Parent).Height - Self.Height - TD2TabControl(Parent).Margins.top - TD2TabControl(Parent).Margins.bottom - FLayout.Padding.top - FLayout.Padding.bottom;
   End;
   FDisableAlign := False;
End;

Procedure TD2TabItem.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      If (Parent <> nil) And (Parent Is TD2TabControl) Then
      Begin
         TD2TabControl(Parent).ItemIndex := Index;
      End;
   End;
End;

Procedure TD2TabItem.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FLayout) Then
      FLayout := nil;
End;

Procedure TD2TabItem.Select(ASelected: Boolean);
Begin
   FIsSelected := ASelected;
   StartTriggerAnimation(Self, 'IsSelected');
   ApplyTriggerEffect(Self, 'IsSelected');
End;

Procedure TD2TabItem.SetTabIndex(Const Value: Integer);
Begin
   If FTabIndex <> Value Then
   Begin
      Inherited Index := Value;
      Realign;
   End;
End;

Procedure TD2TabItem.SetVisible(Const Value: Boolean);
Var
   S: TD2TabItem;
Begin
   If Value <> Visible Then
   Begin
      S := TD2TabControl(Parent).TabItem(TD2TabControl(Parent).ItemIndex);
      Inherited;
      If Not Visible And IsSelected Then
      Begin
         Select(False);
         TD2TabControl(Parent).FItemIndex := -1;
         If Index >= TD2TabControl(Parent).TabCount Then
            TD2TabControl(Parent).ItemIndex := TD2TabControl(Parent).TabCount - 1
         Else
            TD2TabControl(Parent).ItemIndex := Index;
      End Else Begin
         TD2TabControl(Parent).Realign;
         If S <> nil Then
         Begin
            TD2TabControl(Parent).FItemIndex := -1;
            TD2TabControl(Parent).ItemIndex := S.Index;
         End;
      End;
   End;
End;

Procedure TD2TabItem.DesignInsert;
Var
   L: TD2Layout;
Begin
   Inherited;
   If FLayout = nil Then
   Begin
      L := TD2Layout.Create(Owner);
      L.Parent := Parent;
      If GvarD2Designer <> nil Then
         L.Name := GvarD2Designer.UniqueName(Self.Owner, L.ClassName);
      Layout := L;
   End;
End;

//============ TD2TabControl ================================================

Constructor TD2TabControl.Create(AOwner: TComponent);
Begin
   Inherited;
   FItemIndex := -1;
   AutoCapture := True;
   Width := 200;
   Height := 200;
End;

Destructor TD2TabControl.Destroy;
Begin
   Inherited;
End;

Function TD2TabControl.ItemClass: String;
Begin
   Result := 'TD2TabItem';
End;

Procedure TD2TabControl.AddObject(AObject: TD2Object);
Begin
   Inherited;
   If AObject Is TD2TabItem Then
      Realign;
End;

Procedure TD2TabControl.FreeStyle;
Begin
   Inherited;
   FBackground := nil;
End;

Procedure TD2TabControl.ApplyStyle;
Var
   B: TD2Object;
Begin
   Inherited;
   B := FindResource('background');
   If (B <> nil) And (B.IsVisual) Then
   Begin
      FBackground := B.Visual;
   End;
   Realign;
End;

Procedure TD2TabControl.PaintChildren;
Var
   Sel: TD2TabItem;
   SaveOp: Single;
Begin
   Sel := TabItem(ItemIndex);
   If (Sel <> nil) And (Sel.Visible) Then
   Begin
      SaveOp := Sel.Opacity;
      Sel.Opacity := 0;
      Inherited;
      Sel.Opacity := SaveOp;

      Canvas.SetMatrix(Sel.AbsoluteMatrix);
      Sel.BeforePaint;
      Sel.Paint;
      Sel.PaintChildren;
   End Else
      Inherited;
End;

Procedure TD2TabControl.Realign;
Var
   Idx, i: Integer;
   CurX, CurY: Single;
   AutoWidth, MaxHeight: Single;
   B: TD2Object;
Begin
   Inherited;
   If FDisableAlign Then
      Exit;
   FDisableAlign := True;

   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If Not (TD2Object(FChildren[i]) Is TD2TabItem) Then
            TD2Object(FChildren[i]).Index := FChildren.Count - 1;

   MaxHeight := 0;
   Idx := 0;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]) Is TD2TabItem Then
            With TD2TabItem(FChildren[i]) Do
            Begin
               If Not Visible Then
                  Continue;
               FIndex := Idx;
               If Height + Padding.Top + Padding.Bottom > MaxHeight Then
                  MaxHeight := Height + Padding.Top + Padding.Bottom;
               Idx := Idx + 1;
            End;
   If Idx = 0 Then
      MaxHeight := 0
   Else
   If FItemHeight > 0 Then
      MaxHeight := FItemHeight;

   If FResourceLink <> nil Then
   Begin
      B := FResourceLink;
      If (B <> nil) And (B.IsVisual) Then
         With TD2VisualObject(B) Do
         Begin
            Align := vaNone;
            Position.X := Padding.Left;
            Position.Y := MaxHeight + Padding.Top;
            Width := Self.Width - Padding.Left - Padding.Top;
            Height := Self.Height - MaxHeight - Padding.Top - Padding.Bottom;
            BringToFront;
         End;
   End;

   CurX := 0;
   CurY := 0;

   AutoWidth := Width;
   If FBackground <> nil Then
      AutoWidth := Width - FBackground.Margins.Left - FBackground.Margins.Right;
   If FFullSize And (Idx > 0) Then
      AutoWidth := AutoWidth / Idx
   Else
      AutoWidth := AutoWidth;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]) Is TD2TabItem Then
            With TD2TabItem(FChildren[i]) Do
            Begin
               If Not Visible Then
                  Continue;
               Align := vaNone;
               If Layout <> nil Then
               Begin
                  Layout.Align := vaNone;
                  Layout.Visible := Index = ItemIndex;
                  Layout.DesignHide := Not (Index = ItemIndex);
                  Layout.ClipChildren := True;
                  If Layout.Visible Then
                     Layout.BringToFront;
               End;
               If FFullSize Then
                  SetBounds(CurX + Padding.Left, CurY + Padding.Top, AutoWidth, MaxHeight - Padding.Top - Padding.Bottom)
               Else
                  SetBounds(CurX + Padding.Left, CurY + Padding.Top, Width, MaxHeight - Padding.Top - Padding.Bottom);
               CurX := CurX + Padding.Left + Width + Padding.Right;
               Realign;
            End;
   FDisableAlign := False;
End;

Function TD2TabControl.TabCount: Integer;
Var
   i: Integer;
Begin
   Result := 0;
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]) Is TD2TabItem Then
         Begin
            If TD2TabItem(FChildren[i]).Visible Then
            Begin
               Inc(Result);
            End;
         End;
End;

Function TD2TabControl.TabItem(AIndex: Integer): TD2TabItem;
Var
   i: Integer;
Begin
   If FChildren <> nil Then
      For i := 0 To FChildren.Count - 1 Do
         If TD2Object(FChildren[i]) Is TD2TabItem Then
         Begin
            If TD2TabItem(FChildren[i]).Visible And (TD2TabItem(FChildren[i]).Index = AIndex) Then
            Begin
               Result := TD2TabItem(FChildren[i]);
               Exit;
            End;
         End;
   Result := nil;
End;

Procedure TD2TabControl.KeyDown(Var Key: Word; Var KeyChar: Widechar; Shift: TShiftState);
Begin
   //  inherited ;
End;

Procedure TD2TabControl.KeyUp(Var Key: Word; Var KeyChar: Widechar; Shift: TShiftState);
Begin
   //  inherited ;
End;

Procedure TD2TabControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
//var
// Item:TD2TabItem;
Begin
   Inherited;
   Realign;
{  if Button=mbLeft then
  begin
    Item:=ItemByPoint(X,Y);
    if Item <> nil then
      ItemIndex:=Item.Index;
    FMouseSelecting:=true;
  end;}
End;

Procedure TD2TabControl.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
//var
//  Item:TD2TabItem;
Begin
   Inherited;
{  if (ssLeft in Shift) and FMouseSelecting then
  begin
    Item:=ItemByPoint(X,Y);
    if Item <> nil then
      ItemIndex:=Item.Index;
  end;}
End;

Procedure TD2TabControl.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   //  FMouseSelecting:=false;
End;

Procedure TD2TabControl.MouseWheel(Shift: TShiftState; WheelDelta: Integer; Var Handled: Boolean);
Begin
   Inherited;
End;

Procedure TD2TabControl.SetItemIndex(Const Value: Integer);
Begin
   If FItemIndex <> Value Then
   Begin
      If TabItem(FItemIndex) <> nil Then
         TabItem(FItemIndex).Select(False);

      FItemIndex := Value;
      Realign;

      If TabItem(FItemIndex) <> nil Then
         TabItem(FItemIndex).Select(True);

      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Procedure TD2TabControl.SetItem(AItem: TD2TabItem);
Begin
   ItemIndex := AItem.Index;
End;

Procedure TD2TabControl.SetItemHeight(Const Value: Single);
Begin
   If FItemHeight <> Value Then
   Begin
      FItemHeight := Value;
      Realign;
   End;
End;

Procedure TD2TabControl.SetFullSize(Const Value: Boolean);
Begin
   If FFullSize <> Value Then
   Begin
      FFullSize := Value;
      Realign;
   End;
End;

//============ TD2HudTabControl ====================

Function TD2HudTabControl.ItemClass: String;
Begin
   Result := 'TD2HudTabItem';
End;


//============== TD2TreeViewItem =============================

Constructor TD2TreeViewItem.Create(AOwner: TComponent);
Begin
   Inherited;
   Position.Point := d2Point(5000, 5000);
   FAutoTranslate := True;
   TextAlign := d2TextAlignNear;
   Height := 19;
   HitTest := False;
   CanFocused := False;
End;

Procedure TD2TreeViewItem.Realign;
Begin
   If (TreeView <> nil) And (TreeView.FUpdating > 0) Then
      Exit;
   Inherited;
End;

Procedure TD2TreeViewItem.DragEnd;
Begin
   Inherited;
   DragLeave;
   If (TreeView <> nil) And (TreeView.FDragItem <> nil) Then
   Begin
      TreeView.FDragItem.RemoveFreeNotify(TreeView);
      TreeView.FDragItem := nil;
   End;
End;

Function TD2TreeViewItem.GetCount: Integer;
Var
   i: Integer;
Begin
   Result := 0;
   If ChildrenCount > 0 Then
      For i := 0 To ChildrenCount - 1 Do
         If Children[i] Is TD2TreeViewItem Then
         Begin
            Inc(Result);
         End;
End;

Function TD2TreeViewItem.ItemByPoint(Const X, Y: Single): TD2TreeViewItem;
Var
   i: Integer;
   P, P1: TD2Point;
Begin
   P := LocaltoAbsolute(d2Point(X, Y));
   For i := 0 To Count - 1 Do
      With ItemByIndex(i) Do
      Begin
         If Not Visible Then
            Continue;
         If pointInObject(P.X, P.Y) Then
         Begin
            Result := Self.ItemByIndex(i);
            Exit;
         End Else
         If (Count > 0) And (IsExpanded) Then
         Begin
            P1 := AbsoluteToLocal(P);
            Result := ItemByPoint(P1.X, P1.Y);
            If Result <> nil Then
               Exit;
         End;
      End;
   Result := nil;
End;

Function TD2TreeViewItem.ItemByIndex(Const Idx: Integer): TD2TreeViewItem;
Var
   c, i: Integer;
Begin
   c := 0;
   If ChildrenCount > 0 Then
      For i := 0 To ChildrenCount - 1 Do
         If Children[i] Is TD2TreeViewItem Then
         Begin
            If c = Idx Then
            Begin
               Result := TD2TreeViewItem(Children[i]);
               Exit;
            End;
            Inc(c);
         End;
   Result := nil;
End;

Procedure TD2TreeViewItem.Paint;
Var
   R: TD2Rect;
Begin
   Inherited Paint;
   If Assigned(Scene) And Scene.GetDesignTime And Not Locked And Not FInPaintTo Then
   Begin
      R := LocalRect;
      d2InflateRect(R, -0.5, -0.5);
      Canvas.StrokeThickness := 1;
      Canvas.StrokeDash := d2DashDash;
      Canvas.Stroke.Style := d2BrushSolid;
      Canvas.Stroke.SolidColor := $A0909090;
      Canvas.DrawRect(R, 0, 0, AllCorners, AbsoluteOpacity);
      Canvas.StrokeDash := d2DashSolid;
   End;
End;

Function TD2TreeViewItem.ItemClass: String;
Begin
   Result := ClassName;
End;

Procedure TD2TreeViewItem.DesignClick;
Begin
   Inherited;
   IsExpanded := Not IsExpanded;
End;

Function TD2TreeViewItem.EnterFocusChildren(AObject: TD2VisualObject): Boolean;
Begin
   Result := Inherited EnterFocusChildren(AObject);
   If (TreeView <> nil) Then
   Begin
      TreeView.Selected := Self;
      Result := True;
   End;
End;

Function TD2TreeViewItem.Level: Integer;
Var
   P: TD2Object;
Begin
   Result := 0;
   P := Parent;
   While (P <> nil) And Not (P Is TD2TreeView) And Not (P Is TD2Content) Do
   Begin
      Result := Result + 1;
      P := P.Parent;
   End;
End;

Function TD2TreeViewItem.TreeView: TD2TreeView;
Var
   P: TD2Object;
Begin
   P := Parent;
   While (P <> nil) Do
   Begin
      If P Is TD2TreeView Then
      Begin
         Result := TD2TreeView(P);
         Exit;
      End;
      P := P.Parent;
   End;
   Result := nil;
End;

Procedure TD2TreeViewItem.FreeStyle;
Begin
   Inherited;
   FButton := nil;
   FCheck := nil;
End;

Procedure TD2TreeViewItem.ApplyStyle;
Var
   B: TD2Object;
Begin
   Inherited;
   B := FindResource('button');
   If (B <> nil) And (B Is TD2CustomButton) Then
   Begin
      FButton := TD2CustomButton(B);
      FButton.OnClick := DoButtonClick;
      FButton.Visible := Count > 0;
      If FButton.Visible Then
      Begin
         FButton.ApplyResource;
         FButton.StartTriggerAnimation(Self, 'IsExpanded');
      End;
   End;
   B := FindResource('check');
   If (B <> nil) And (B Is TD2CheckBox) Then
   Begin
      FCheck := TD2CheckBox(B);
      FCheck.IsChecked := IsChecked;
      FCheck.OnChange := DoCheckClick;
      If TreeView <> nil Then
         FCheck.Visible := TreeView.ShowCheckboxes;
   End;
   StartTriggerAnimation(Self, 'IsSelected');
End;

Procedure TD2TreeViewItem.DoCheckClick(Sender: TObject);
Begin
   If FCheck <> nil Then
      FIsChecked := FCheck.IsChecked;
   If TreeView <> nil Then
   Begin
      TreeView.SetFocus;
      TreeView.Selected := Self;
      If Assigned(TreeView.OnChangeCheck) Then
         TreeView.OnChangeCheck(Self);
   End;
End;

Procedure TD2TreeViewItem.UpdateCheck;
Var
   i: Integer;
Begin
   If (TreeView <> nil) And (FCheck <> nil) Then
      FCheck.Visible := TreeView.ShowCheckboxes;
   If ChildrenCount > 0 Then
      For i := 0 To ChildrenCount - 1 Do
         If Children[i] Is TD2TreeViewItem Then
            TD2TreeViewItem(Children[i]).UpdateCheck;
End;

Procedure TD2TreeViewItem.SetIsChecked(Const Value: Boolean);
Begin
   If FIsChecked <> Value Then
   Begin
      FIsChecked := Value;
      If FCheck <> nil Then
         FCheck.IsChecked := FIsChecked;
   End;
End;

Procedure TD2TreeViewItem.SetIsSelected(Const Value: Boolean);
Begin
   If FIsSelected <> Value Then
   Begin
      FIsSelected := Value;
      StartTriggerAnimation(Self, 'IsSelected');
      If Treeview <> nil Then
         Treeview.UpdateSelection;
   End;
End;

Procedure TD2TreeViewItem.DoButtonClick(Sender: TObject);
Begin
   IsExpanded := Not IsExpanded;
End;

Procedure TD2TreeViewItem.SetIsExpanded(Const Value: Boolean);
Begin
   If FIsExpanded <> Value Then
   Begin
      FIsExpanded := Value;
      If (FButton <> nil) And Not (csLoading In ComponentState) Then
      Begin
         FButton.Visible := Count > 0;
         If FButton.Visible Then
            FButton.StartTriggerAnimation(Self, 'IsExpanded');
      End;
      If TreeView <> nil Then
         TreeView.Realign;
   End;
End;

Function TD2TreeViewItem.GetItem(Const aIndex: Integer): TD2TreeViewItem;
Begin
   Result := ItemByIndex(aIndex);
End;

//============== TD2TreeView ==================================================

Constructor TD2TreeView.Create(AOwner: TComponent);
Begin
   Inherited;
   FGlobalList := TList.Create;
   FGlobalList.Capacity := 100;
   CanFocused := True;
   AutoCapture := True;
   HideSelectionUnfocused := True;
   Width := 100;
   Height := 100;
   FItemHeight := 0;
End;

Destructor TD2TreeView.Destroy;
Begin
   If FSelections <> nil Then
      FreeAndNil(FSelections);
   FreeAndNil(FGlobalList);
   Inherited;
End;

Function TD2TreeView.ItemClass: String;
Begin
   Result := 'TD2TreeViewItem';
End;

Procedure TD2TreeView.ApplyStyle;
Var
   T: TD2Object;
Begin
   Inherited;
   T := FindResource('selection');
   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      FSelection := TD2VisualObject(T);
      FSelection.Visible := False;
   End;

   If (T <> nil) And (T Is TD2VisualObject) Then
   Begin
      TD2VisualObject(T).Visible := False;
   End;

   UpdateSelection;
End;

Procedure TD2TreeView.FreeStyle;
Begin
   Inherited;
   FSelection := nil;
   If FSelections <> nil Then
      FSelections.Clear;
End;

Procedure TD2TreeView.UpdateGlobalIndexes;
Var
   GlobalIdx: Integer;

   Procedure AlignItem(AItem: TD2TreeViewItem);
   Var
      i: Integer;
   Begin
      AItem.GlobalIndex := GlobalIdx;
      GlobalIdx := GlobalIdx + 1;
      FGlobalList.Add(AItem);
      If AItem.Count > 0 Then
      Begin
         If AItem.IsExpanded Then
            For i := 0 To AItem.Count - 1 Do
               AlignItem(AItem.ItemByIndex(i));
      End;
   End;

Var
   i: Integer;
Begin
   FGlobalList.Clear;
   GlobalIdx := 0;
   For i := 0 To Count - 1 Do
      AlignItem(ItemByIndex(i));
   FGlobalCount := GlobalIdx;
End;

Function CompareTreeItem(item1, item2: TD2Object): Integer;
Begin
   If (item1 Is TD2TreeViewItem) And (item2 Is TD2TreeViewItem) Then
   Begin
      If (TD2TreeViewItem(item1).TreeView <> nil) And Assigned(TD2TreeViewItem(item1).TreeView.OnCompare) Then
         Result := TD2TreeViewItem(item1).TreeView.OnCompare(TD2TreeViewItem(item1), TD2TreeViewItem(item2))
      Else
         Result := WideCompareText(TD2TreeViewItem(item1).TextW, TD2TreeViewItem(item2).TextW);
   End Else Begin
      Result := 0;
   End;
End;

Procedure TD2TreeView.SortItems;
Begin
   If Not FSorted Then
      Exit;
   FContent.Sort(CompareTreeItem);
End;

Function TD2TreeView.GetItemRect(Item: TD2TreeViewItem): TD2Rect;
Var
   P: TD2Point;
Begin
   If Item <> nil Then
   Begin
      P := Item.LocalToAbsolute(d2Point(0, 0));
      P := FContent.AbsoluteToLocal(P);
      Result := d2Rect(0, 0, Item.Width, Item.Height);
      d2OffsetRect(Result, P.X, P.Y);
   End Else Begin
      Result := d2Rect(0, 0, 0, 0);
   End;
End;

Procedure TD2TreeView.UpdateSelection;
Var
   i: Integer;
   P: TD2Point;
   R: TD2Rect;
   SelRects: Array Of TD2Rect;
   Clone: TD2VisualObject;
   Vis: Boolean;
Begin
   If FSelection = nil Then
      Exit;

   Vis := True;
   SetLength(SelRects, 0);
   For i := 0 To GlobalCount - 1 Do
   Begin
      If (ItemByGlobalIndex(i).IsSelected) Then
      Begin
         P := ItemByGlobalIndex(i).LocalToAbsolute(d2Point(0, 0));
         If (FSelection.Parent <> nil) And (FSelection.Parent.IsVisual) Then
            P := FSelection.Parent.Visual.AbsoluteToLocal(P);
         R := d2Rect(P.X, P.Y, P.X + ItemByGlobalIndex(i).Width, P.Y + ItemByGlobalIndex(i).Height);
         If (Length(SelRects) > 0) And (i > 0) And (ItemByGlobalIndex(i - 1).IsSelected) Then
            SelRects[High(SelRects)] := d2UnionRect(R, SelRects[High(SelRects)])
         Else Begin
            SetLength(SelRects, Length(SelRects) + 1);
            SelRects[High(SelRects)] := R;
         End;
      End;
   End;

   If FSelections = nil Then
      FSelections := TList.Create;

   If FSelections.Count < Length(SelRects) Then
      For i := FSelections.Count To Length(SelRects) - 1 Do
      Begin
         Clone := TD2VisualObject(FSelection.Clone(Self));
         FSelections.Add(Clone);
         Clone.Parent := FSelection.Parent;
      End;

   If Length(SelRects) < FSelections.Count Then
      For i := Length(SelRects) To FSelections.Count - 1 Do
      Begin
         TD2VisualObject(FSelections[i]).Visible := False;
      End;

   If HideSelectionUnfocused And Not IsFocused Then
      Vis := False;

   For i := 0 To High(SelRects) Do
   Begin
      TD2VisualObject(FSelections[i]).Visible := Vis;
      If Vis Then
      Begin
         With SelRects[i] Do
            TD2VisualObject(FSelections[i]).SetBounds(Left, Top, Right - Left, Bottom - Top);
      End;
   End;
End;

Function TD2TreeView.GetContentBounds: TD2Rect;
Const
   StepX = 19;
Var
   CurY, CurX: Single;
   R: TD2Rect;

//......................
   Procedure _HideItem(AItem: TD2TreeViewItem);
   Var
      i: Integer;
   Begin
      AItem.Visible := False;
      AItem.Opacity := 0;
      If AItem.Count > 0 Then
         For i := 0 To AItem.Count - 1 Do
            _HideItem(AItem.ItemByIndex(i));
   End;

   //......................
   Procedure _AlignItem(AItem: TD2TreeViewItem);
   Var
      i: Integer;
      P: TD2Point;
   Begin
      P := d2Point(CurX, CurY);
      P := FContent.LocalToAbsolute(P);
      P := AItem.Parent.Visual.AbsoluteToLocal(P);
      If FItemHeight <> 0 Then
         AItem.SetBounds(P.X + AItem.Padding.Left, P.Y + AItem.Padding.Top,
            R.Right - R.Left - AItem.Padding.Left - AItem.Padding.Right - (AItem.Level * StepX), FItemHeight)
      Else
         AItem.SetBounds(P.X + AItem.Padding.Left, P.Y + AItem.Padding.Top,
            R.Right - R.Left - AItem.Padding.Left - AItem.Padding.Right - (AItem.Level * StepX), AItem.Height);

      If AItem.FButton <> nil Then
         AItem.FButton.Visible := AItem.Count > 0;

      CurY := CurY + AItem.Height + AItem.Padding.Top + AItem.Padding.Bottom;

      If AItem.Count > 0 Then
      Begin
         If AItem.IsExpanded Then
         Begin
            CurX := CurX + StepX;

            For i := 0 To AItem.Count - 1 Do
            Begin
               With AItem.ItemByIndex(i) Do
               Begin
                  Visible := True;
                  Opacity := 1;
               End;
               _AlignItem(AItem.ItemByIndex(i));
            End;

            CurX := CurX - StepX;
         End Else Begin
            For i := 0 To AItem.Count - 1 Do
               _HideItem(AItem.ItemByIndex(i));
         End;
      End;
   End;

   //......................
Var
   i: Integer;
Begin
   Result := LocalRect;
   UpdateGlobalIndexes;
   If FUpdating > 0 Then
      Exit;
   If ContentLayout = nil Then
      Exit;
   R := ContentLayout.LocalRect;

   FCountExpanded := 0;
   If FContent <> nil Then
   Begin
      SortItems;
      CurY := 0;
      CurX := 0;

      For i := 0 To Count - 1 Do
         _AlignItem(ItemByIndex(i));

      R.Bottom := R.Top + CurY;
   End;
   If R.Bottom = R.Top Then
      R.Bottom := R.Top + 1;
   Result := R;
   UpdateSelection;
End;

Procedure TD2TreeView.HScrollChange(Sender: TObject);
Begin
   Inherited;
   UpdateSelection;
End;

Procedure TD2TreeView.VScrollChange(Sender: TObject);
Begin
   Inherited;
   UpdateSelection;
End;

Function TD2TreeView.ItemByIndex(Const Idx: Integer): TD2TreeViewItem;
Begin
   If (FContent <> nil) And (FContent.ChildrenCount > 0) And (Idx >= 0) And (Idx < FContent.ChildrenCount) Then
      Result := TD2TreeViewItem(FContent.Children[Idx])
   Else
      Result := nil;
End;

Function TD2TreeView.ItemByGlobalIndex(Const Idx: Integer): TD2TreeViewItem;
Begin
   Result := TD2TreeViewItem(FGlobalList[Idx]);
End;

Function TD2TreeView.ItemByPoint(Const X, Y: Single): TD2TreeViewItem;
Var
   i: Integer;
   P, P1: TD2Point;
Begin
   P := LocalToAbsolute(d2Point(X, Y));
   If (FContent <> nil) And (FContent.ChildrenCount > 0) Then
      For i := 0 To FContent.ChildrenCount - 1 Do
         If FContent.Children[i] Is TD2TreeViewItem Then
         Begin
            If Not TD2TreeViewItem(FContent.Children[i]).Visible Then
               Continue;
            If Not d2IntersectRect(TD2TreeViewItem(FContent.Children[i]).UpdateRect, UpdateRect) Then
               Continue;
            If TD2TreeViewItem(FContent.Children[i]).pointInObject(P.X, P.Y) Then
            Begin
               Result := TD2TreeViewItem(FContent.Children[i]);
               Exit;
            End Else
            If (TD2TreeViewItem(FContent.Children[i]).IsExpanded) And (TD2TreeViewItem(FContent.Children[i]).Count > 0) Then
            Begin
               P1 := TD2TreeViewItem(FContent.Children[i]).AbsoluteToLocal(P);
               Result := TD2TreeViewItem(FContent.Children[i]).ItemByPoint(P1.X, P1.Y);
               If Result <> nil Then
                  Exit;
            End;
         End;
   Result := nil;
End;

Procedure TD2TreeView.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
   If (Count > 0) And (Selected <> nil) Then
   Begin
      Case Key Of
         VK_DOWN:
            If Selected.GlobalIndex < GlobalCount - 1 Then
               Selected := ItemByGlobalIndex(Selected.GlobalIndex + 1);
         VK_UP:
            If Selected.GlobalIndex > 0 Then
               Selected := ItemByGlobalIndex(Selected.GlobalIndex - 1);
         VK_ADD: Selected.IsExpanded := True;
         VK_HOME: Selected := ItemByGlobalIndex(0);
         VK_END: Selected := ItemByGlobalIndex(GlobalCount - 1);
         VK_SUBTRACT: Selected.IsExpanded := False;
         Else Exit;
      End;
      Key := 0;
   End;
End;

Procedure TD2TreeView.KeyUp(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   Inherited;
End;

Procedure TD2TreeView.DragOver(Const aData: TD2DragObject; Const Point: TD2Point; Var Accept: Boolean);
Var
   Obj: TD2TreeViewItem;
Begin
   Inherited;
   With AbsoluteToLocal(Point) Do
      Obj := ItemByPoint(X, Y);
   If (Obj <> FDragItem) Then
   Begin
      If FDragItem <> nil Then
      Begin
         FDragItem.DragLeave;
         FDragItem.RemoveFreeNotify(Self);
      End;
      FDragItem := Obj;
      If FDragItem <> nil Then
      Begin
         FDragItem.AddFreeNotify(Self);
         FDragItem.DragEnter(aData, Point);
         Accept := True;
      End Else
         Accept := False;
   End Else
      Accept := True;

   If FDragItem = Selected Then
      Accept := False;
End;

Procedure TD2TreeView.DragDrop(Const aData: TD2DragObject; Const Point: TD2Point);
Var
   Obj: TD2TreeViewItem;
   Allow: Boolean;
Begin
   Inherited;
   If FDragItem <> nil Then
   Begin
      FDragItem.DragLeave;
      FDragItem.RemoveFreeNotify(Self);
      FDragItem := nil;
   End;
   With AbsoluteToLocal(Point) Do
      Obj := ItemByPoint(X, Y);

   If Obj = nil Then
   Begin
      Allow := True;
      If Assigned(OnDragChange) Then
         OnDragChange(TD2TreeViewItem(aData.Source), nil, Allow);
      If Allow Then
      Begin
         TD2TreeViewItem(aData.Source).Parent := Self;
         Realign;
      End;
   End Else Begin
      Allow := True;
      If Assigned(OnDragChange) Then
         OnDragChange(TD2TreeViewItem(aData.Source), Obj, Allow);
      If Allow Then
      Begin
         If Not Obj.IsExpanded Then
            Obj.IsExpanded := True;
         TD2TreeViewItem(aData.Source).Parent := Obj;
         Realign;
      End;
   End;
End;

Procedure TD2TreeView.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   Item: TD2TreeViewItem;
Begin
   Inherited;
   If Button = mbLeft Then
   Begin
      Item := ItemByPoint(X, Y);
      If Item <> nil Then
      Begin
         If Multiselect Then
         Begin
            If ssCtrl In Shift Then
               Item.IsSelected := Not Item.IsSelected
            Else
            If ssShift In Shift Then
            Begin
               SelectRange(Selected, Item);
               Selected := Item;
            End Else Begin
               SelectRange(Item, Item);
               Selected := Item;
            End;
            FFirstSelect := Item;
         End Else Begin
            If Selected <> Item Then
               Selected := Item
            Else
            If AllowDrag Then
               Scene.BeginDragObj(Selected, MakeScreenshot);
         End;
      End;
      FMouseSelecting := True;
   End;
End;

Procedure TD2TreeView.MouseMove(Shift: TShiftState; X, Y, Dx, Dy: Single);
Begin
   Inherited;
End;

Procedure TD2TreeView.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited;
   FFirstSelect := nil;
   FMouseSelecting := False;
End;

Procedure TD2TreeView.Clear;
Var
   i: Integer;
Begin
   BeginUpdate;
   If FContent <> nil Then
      If FContent.ChildrenCount > 0 Then
         For i := FContent.ChildrenCount - 1 Downto 0 Do
            If FContent.Children[i] Is TD2TreeViewItem Then
               TD2Object(FContent.Children[i]).Free;
   FScrollDesign.Y := 0;
   FScrollDesign.X := 0;
   FSelected := nil;
   UpdateGlobalIndexes;
   UpdateSelection;
   EndUpdate;
End;

Procedure TD2TreeView.SelectRange(Item1, Item2: TD2TreeViewItem);
Var
   i: Integer;
Begin
   If Item1 = nil Then
      Exit;
   If Item2 = nil Then
      Exit;
   For i := 0 To Min(Item1.GlobalIndex, Item2.GlobalIndex) - 1 Do
      ItemByGlobalIndex(i).IsSelected := False;
   For i := Max(Item1.GlobalIndex, Item2.GlobalIndex) + 1 To GlobalCount - 1 Do
      ItemByGlobalIndex(i).IsSelected := False;
   For i := Min(Item1.GlobalIndex, Item2.GlobalIndex) To Max(Item1.GlobalIndex, Item2.GlobalIndex) Do
      ItemByGlobalIndex(i).IsSelected := True;
End;

Procedure TD2TreeView.ClearSelection;
Var
   i: Integer;
Begin
   For i := 0 To GlobalCount - 1 Do
      ItemByGlobalIndex(i).IsSelected := False;
End;

Procedure TD2TreeView.SelectAll;
Var
   i: Integer;
Begin
   For i := 0 To GlobalCount - 1 Do
      ItemByGlobalIndex(i).IsSelected := True;
End;

Procedure TD2TreeView.EnterFocus;
Begin
   Inherited;
   If HideSelectionUnfocused And (Selected <> nil) Then
      UpdateSelection;
End;

Procedure TD2TreeView.KillFocus;
Begin
   Inherited;
   If HideSelectionUnfocused And (Selected <> nil) Then
      UpdateSelection;
End;

Procedure TD2TreeView.AddObject(AObject: TD2Object);
Begin
   If (FContent <> nil) And ((AObject Is TD2TreeViewItem) Or (AObject Is TD2SelectionItem)) Then
   Begin
      FContent.AddObject(AObject);
   End Else
      Inherited;
End;

Procedure TD2TreeViewItem.AddObject(AObject: TD2Object);
Begin
   Inherited;
   If AObject Is TD2TreeViewItem Then
      If FUpdating = 0 Then
      Begin
         TreeView.UpdateGlobalIndexes;
         TreeView.Realign;
      End;
End;

Procedure TD2TreeViewItem.RemoveObject(AObject: TD2Object);
Begin
   Inherited;
   If AObject Is TD2TreeViewItem Then
   Begin
      TD2TreeViewItem(AObject).IsSelected := False;
      If FUpdating = 0 Then
      Begin
         TreeView.UpdateGlobalIndexes;
         TreeView.Realign;
      End;
   End;
End;

Procedure TD2TreeView.ContentAddObject(AObject: TD2Object);
Begin
   Inherited;
   If AObject Is TD2TreeViewItem Then
      If FUpdating = 0 Then
      Begin
         UpdateGlobalIndexes;
         Realign;
      End;
End;

Procedure TD2TreeView.ContentRemoveObject(AObject: TD2Object);
Begin
   Inherited;
   If AObject Is TD2TreeViewItem Then
   Begin
      TD2TreeViewItem(AObject).IsSelected := False;
      If FUpdating = 0 Then
      Begin
         UpdateGlobalIndexes;
         Realign;
      End;
   End;
End;

Procedure TD2TreeView.SetSelected(Const Value: TD2TreeViewItem);
Var
   i: TD2Object;
   P: TD2Point;
Begin
   If FSelected <> Value Then
   Begin
      If (FSelected <> nil) And Not MultiSelect Then
         FSelected.IsSelected := False;

      FSelected := Value;
      If (FSelected <> nil) And (FContent <> nil) Then
      Begin
         i := FSelected.Parent;
         While ((i <> nil) And Not (i Is TD2TreeView)) Do
         Begin
            If (i Is TD2TreeViewItem) Then
               TD2TreeViewItem(i).IsExpanded := True;
            i := i.Parent;
         End;
         If (FContent <> nil) And (ContentLayout <> nil) And (VScrollBar <> nil) Then
         Begin
            P := ContentLayout.AbsoluteToLocal(FSelected.LocalToAbsolute(d2Point(0, 0)));
            If P.Y < 0 Then
               VScrollBar.Value := VScrollBar.Value + P.Y;
            If P.Y + FSelected.Padding.Top + FSelected.Padding.Bottom + FSelected.Height > ContentLayout.Height Then
               VScrollBar.Value := VScrollBar.Value + (P.Y + FSelected.Padding.Top + FSelected.Padding.Bottom + FSelected.Height - ContentLayout.Height);
         End;
         FSelected.IsSelected := True;
      End;
      If Assigned(FOnChange) Then
         FOnChange(Self);
   End;
End;

Procedure TD2TreeView.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (AComponent = FSelected) Then
      FSelected := nil;
   If (Operation = opRemove) And (AComponent = FDragItem) Then
      FDragItem := nil;
End;

Procedure TD2TreeView.SetItemHeight(Const Value: Single);
Begin
   If FItemHeight <> Value Then
   Begin
      FItemHeight := Value;
      Realign;
   End;
End;

Procedure TD2TreeView.CollapseAll;
Var
   i: Integer;
   item: TD2TreeViewItem;
Begin
   BeginUpdate;
   For i := 0 To Count - 1 Do
   Begin
      item := ItemByIndex(i);
      If item <> nil Then
         item.IsExpanded := False;
   End;
   EndUpdate;
End;

Procedure TD2TreeView.ExpandAll;
Var
   i: Integer;
   item: TD2TreeViewItem;
Begin
   BeginUpdate;
   For i := 0 To Count - 1 Do
   Begin
      item := ItemByIndex(i);
      If item <> nil Then
         item.IsExpanded := True;
   End;
   EndUpdate;
End;

Function TD2TreeView.GetSelection: TD2SelectionItem;
Var
   B: TD2Object;
Begin
   B := FindResource('selection');
   If (B <> nil) And (B Is TD2SelectionItem) Then
      Result := TD2SelectionItem(B)
   Else
      Result := nil;
End;

Procedure TD2TreeView.SetShowCheckboxes(Const Value: Boolean);
Var
   i: Integer;
Begin
   If FShowCheckboxes <> Value Then
   Begin
      FShowCheckboxes := Value;
      For i := 0 To Count - 1 Do
         If ItemByIndex(i) <> nil Then
            ItemByIndex(i).UpdateCheck;
   End;
End;

Function TD2TreeView.GetItem(Const aIndex: Integer): TD2TreeViewItem;
Begin
   Result := ItemByIndex(aIndex);
End;

Procedure TD2TreeView.SetSorted(Const Value: Boolean);
Begin
   If FSorted <> Value Then
   Begin
      FSorted := Value;
      Realign;
   End;
End;

Procedure TD2TreeView.SetAllowDrag(Const Value: Boolean);
Begin
   If FAllowDrag <> Value Then
   Begin
      FAllowDrag := Value;
      If FAllowDrag Then
         DragDisableHighlight := True;
   End;
End;

Procedure TD2TreeView.EndUpdate;
Begin
   Inherited;
End;

Function TD2TreeView.GetCount: Integer;
Begin
   Result := 0;
   If (FContent <> nil) Then
      Result := FContent.ChildrenCount;
End;


Const
   MaxMapSize = (MaxInt Div 2) Div SizeOf(Integer);  // 250 million

   //=========== TD2DBNavigator ==============================================

Var
   BtnTypeName: Array[TD2NavigateBtn] Of PChar = ('FIRST', 'PRIOR', 'NEXT', 'LAST', 'INSERT', 'DELETE', 'EDIT', 'POST', 'CANCEL', 'REFRESH');
   BtnTypePath: Array[TD2NavigateBtn] Of String = ('M 361.374,349.551 L 325.968,315.176 L 361.374,280.801 Z M 323.202,349.551 L 287.797,315.176 L 323.202,280.801 Z M 286.357,349.551 L 279.277,349.551 L 279.277,280.801 L 286.357,280.801 Z',
      'M 327.076,346.113 L 291.667,311.738 L 327.076,277.363 Z ', 'M 341.236,311.738 L 305.830,346.113 L 305.830,277.363 Z ',
      'M 361.374,349.551 L 354.294,349.551 L 354.294,280.801 L 361.374,280.801 Z M 352.854,315.176 L 317.448,349.551 L 317.448,280.801 Z M 314.682,315.176 L 279.277,349.551 L 279.277,280.801 Z',
      // plus
      'M 315.303,336.714 L 315.303,315.122 L 293.228,315.122 L 293.228,306.099 L 315.303,306.099 L 315.303,284.668 L 324.706,284.668 L 324.706,306.099 L 346.781,306.099 L 346.781,315.122 L ' + '324.706,315.122 L 324.706,336.714 Z ',
      // minus
      'M 286.766,375.304 L 286.766,364.321 L 352.763,364.321 L 352.763,375.304 Z ',
      // edit
      'M 350.074,271.455 L 350.074,350.947 L 289.995,350.947 L 289.995,271.455 Z M 347.362,274.087 L 292.704,274.087 L 292.704,348.315 L 347.362,348.315 Z M 300.892,337.681 L 300.892,335.049' +
         ' L 339.121,335.049 L 339.121,337.681 Z M 300.892,327.100 L 300.892,324.468 L 339.121,324.468 L 339.121,327.100 Z M 300.892,316.519 L 300.892,313.887 L 339.121,313.887 L 339.121,316.519 ' +
         'Z M 300.892,305.884 L 300.892,303.252 L 339.121,303.252 L 339.121,305.884 Z M 300.892,295.249 L 300.892,292.617 L 339.121,292.617 L 339.121,295.249 Z M 300.892,284.668 L 300.892,282.036 L' + ' 339.121,282.036 L 339.121,284.668 Z ',
      // post
      'M 358.467,266.729 L ' + '360.400,269.414 C 352.512,275.181 ' + '343.733,284.064 334.069,296.058 L ' + '334.069,296.058 C 324.407,308.056 ' + '317.029,319.261 311.940,329.678 L ' +
         '311.940,329.678 L 307.844,332.363 ' + 'C 304.454,334.659 302.148,336.358 ' + '300.929,337.466 L 300.929,337.466 ' + 'C 300.452,335.787 299.402,333.028 ' + '297.777,329.194 L 297.777,329.194 ' +
         'L 296.229,325.703 C ' + '294.017,320.695 291.959,316.989 ' + '290.059,314.588 L 290.059,314.588 ' + 'C 288.159,312.191 286.031,310.597 ' + '283.671,309.805 L 283.671,309.805 ' +
         'C 287.656,305.726 291.308,303.685 ' + '294.625,303.682 L 294.625,303.682 ' + 'C 297.465,303.685 300.620,307.428 ' + '304.085,314.907 L 304.085,314.907 ' + 'L 305.800,318.667 C ' +
         '312.034,308.465 320.037,298.549 ' + '329.809,288.915 L 329.809,288.915 ' + 'C 339.584,279.283 349.135,271.888 ' + '358.467,266.729 L 358.467,266.729 ' + 'Z ',
      // cancel
      'M 319.704,321.353 L 318.875,322.480 C 313.121,330.933 308.402,335.160 304.712,335.156 L 304.712,335.156 C 300.472,335.160 296.306,331.813 292.211,325.112 L 292.211,325.112 C 292.765,325.153 293.171,325.169 293.426,325.166 L 293.426,325.166 '
         +
         'C 298.260,325.169 ' + '303.645,321.588 309.580,314.424 L 309.580,314.424 L 311.074,312.598 L 309.140,310.557 C 303.719,304.974 301.006,300.231 301.006,296.323 L 301.006,296.323 C 301.006,293.141 303.977,289.381 309.912,285.044 L 309.912,285.044 C 310.761,290.596 ' + '313.289,296.004 ' + '317.492,301.265 L 317.492,301.265 L 319.150,303.306 L 320.480,301.641 C 326.640,294.017 332.226,290.204 337.241,290.200 L 337.241,290.200 C 341.152,290.204 344.123,293.087 346.150,298.848 L 346.150,298.848 C 345.559,298.781 345.136,298.744 ' + '344.878,298.740 ' + 'L 344.878,298.740 C 343.109,298.744 340.618,299.898 337.409,302.208 L 337.409,302.208 C 334.200,304.518 331.490,307.123 329.275,310.020 L 329.275,310.020 L 327.617,312.222 L 329.221,313.726 C 335.160,319.315 341.357,322.108 347.809,322.104 ' + 'L 347.809,322.104 ' + 'C 344.344,328.912 340.729,332.313 336.966,332.310 L 336.966,332.310 C 333.575,332.313 328.667,329.413 322.249,323.608 L 322.249,323.608 Z ',
      // refresh
      'M 354.848,307.012 C 354.848,312.779 353.633,318.224 351.196,323.340 L 351.196,323.340 C ' + '348.614,328.677 344.999,332.994 340.353,336.284 L 340.353,336.284 L 346.493,340.957 L ' +
         '326.744,346.113 L 328.570,327.046 L 334.102,331.289 C 339.819,326.388 342.676,319.567 ' + '342.676,310.825 L 342.676,310.825 C 342.676,299.620 337.180,290.865 326.190,284.561 L ' +
         '326.190,284.561 L 333.159,271.401 C 339.947,274.590 345.298,279.515 349.205,286.172 L ' + '349.205,286.172 C 352.968,292.550 354.848,299.496 354.848,307.012 L 354.848,307.012 Z M ' +
         '312.581,332.954 L 305.609,346.113 C 298.861,342.931 293.530,338.006 289.623,331.343 L ' + '289.623,331.343 C 285.823,324.971 283.923,318.026 283.923,310.503 L 283.923,310.503 C ' +
         '283.923,304.742 285.158,299.297 287.629,294.175 L 287.629,294.175 C 290.214,288.844 ' + '293.809,284.527 298.418,281.230 L 298.418,281.230 L 292.278,276.504 L 312.027,271.401 L ' +
         '310.201,290.469 L 304.669,286.226 C 298.955,291.133 296.095,297.955 296.095,306.689 L ' + '296.095,306.689 C 296.095,317.902 301.590,326.656 312.581,332.954 L 312.581,332.954 Z ');

Constructor TD2DBNavigator.Create(AOwner: TComponent);
Begin
   Inherited Create(AOwner);
   FCorners := AllCorners;
   FxRadius := 4;
   FyRadius := 4;
   FDataLink := TD2NavDataLink.Create(Self);
   FVisibleButtons := [nbFirst, nbPrior, nbNext, nbLast, nbInsert, nbDelete, nbEdit, nbPost, nbCancel, nbRefresh];
   FDefHints := TStringList.Create;
   FHints := TStringList.Create;
   TStringList(FHints).OnChange := HintsChanged;
   InitButtons;
   InitHints;
   Width := 241;
   Height := 25;
   ButtonWidth := 0;
   FocusedButton := nbFirst;
   FConfirmDelete := True;
End;

Destructor TD2DBNavigator.Destroy;
Begin
   FDefHints.Free;
   FDataLink.Free;
   FHints.Free;
   FDataLink := nil;
   Inherited Destroy;
End;

Procedure TD2DBNavigator.InitButtons;
Var
   I: TD2NavigateBtn;
   Btn: TD2NavButton;
   X: Single;
   ResName: String;
Begin
   MinBtnSize := d2Point(20, 18);
   X := 0;
   For I := Low(Buttons) To High(Buttons) Do
   Begin
      Btn := TD2NavButton.Create(Self);
      Btn.BtnIndex := I;
      Btn.Visible := I In FVisibleButtons;
      Btn.Enabled := True;
      Btn.SetBounds(X, 0, MinBtnSize.X, MinBtnSize.Y);
      FmtStr(ResName, 'dbn_%s', [BtnTypeName[I]]);
      //    Btn.Glyph.LoadFromResourceName(HInstance,ResName);
      //    Btn.NumGlyphs:=2;
      Btn.Enabled := False;
      Btn.Enabled := True;
      Btn.OnClick := ClickHandler;
      Btn.OnMouseDown := BtnMouseDown;
      Btn.Parent := Self;
      Buttons[I] := Btn;
      X := X + MinBtnSize.X;

      Btn.FPath := TD2Path.Create(Self);
      Btn.FPath.Parent := Btn;
      Btn.FPath.Width := 18;
      Btn.FPath.Height := 18;
      Btn.FPath.Align := vaCenter;
      Btn.FPath.HitTest := False;
      Btn.FPath.Locked := True;
      Btn.FPath.Stored := False;
      Btn.FPath.Data.Data := BtnTypePath[I];
      Btn.FPath.WrapMode := d2PathFit;
      Btn.FPath.Stroke.Style := d2BrushNone;
   End;
   Buttons[nbPrior].NavStyle := Buttons[nbPrior].NavStyle + [nsAllowTimer];
   Buttons[nbNext].NavStyle := Buttons[nbNext].NavStyle + [nsAllowTimer];
End;

Procedure TD2DBNavigator.InitHints;
//var
//I:integer;
//J:TD2NavigateBtn;
Begin
(*  if not Assigned(FDefHints) then
  begin
    FDefHints:=TStringList.Create;
    for J:=Low(Buttons) to High(Buttons) do
      FDefHints.Add(LoadResString(BtnHintId[J]));
  end;
  for J:=Low(Buttons) to High(Buttons) do
    Buttons[J].Hint:=FDefHints[Ord(J)];
  J:=Low(Buttons);
  for I:=0 to (FHints.Count - 1) do
  begin
    if FHints.Strings[I] <> '' then Buttons[J].Hint:=FHints.Strings[I];
    if J=High(Buttons) then Exit;
    Inc(J);
  end; *)
End;

Procedure TD2DBNavigator.HintsChanged(Sender: TObject);
Begin
   InitHints;
End;

Procedure TD2DBNavigator.SetHints(Value: TStrings);
Begin
   If Value.Text = FDefHints.Text Then
      FHints.Clear
   Else
      FHints.Assign(Value);
End;

Function TD2DBNavigator.GetHints: TStrings;
Begin
   If (csDesigning In ComponentState) And Not (csWriting In ComponentState) And Not (csReading In ComponentState) And (FHints.Count = 0) Then
      Result := FDefHints
   Else
      Result := FHints;
End;

Procedure TD2DBNavigator.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited Notification(AComponent, Operation);
   If (Operation = opRemove) And (FDataLink <> nil) And (AComponent = DataSource) Then
      DataSource := nil;
End;

Procedure TD2DBNavigator.SetVisible(Value: TD2NavButtonSet);
Var
   I: TD2NavigateBtn;
   W, H: Single;
Begin
   W := Width;
   H := Height;
   FVisibleButtons := Value;
   For I := Low(Buttons) To High(Buttons) Do
      Buttons[I].Visible := I In FVisibleButtons;
   SetSize(W, H);
   If (W <> Width) Or (H <> Height) Then
      SetBounds(Position.X, Position.Y, W, H);
End;

Procedure TD2DBNavigator.CalcMinSize(Var W, H: Single);
Var
   Count: Integer;
   I: TD2NavigateBtn;
Begin
   If (csLoading In ComponentState) Then
      Exit;
   If Buttons[nbFirst] = nil Then
      Exit;

   Count := 0;
   For I := Low(Buttons) To High(Buttons) Do
      If Buttons[I].Visible Then
         Inc(Count);
   If Count = 0 Then
      Inc(Count);

   W := d2MaxFloat(W, Count * MinBtnSize.X);
   H := d2MaxFloat(H, MinBtnSize.Y);

   If Align = vaNone Then
      W := Trunc(W / Count) * Count;
End;

Procedure TD2DBNavigator.SetSize(Var W: Single; Var H: Single);
Var
   Count: Integer;
   I: TD2NavigateBtn;
   Space, Temp, Remain: Single;
   X: Single;
Begin
   If (csLoading In ComponentState) Then
      Exit;
   If Buttons[nbFirst] = nil Then
      Exit;

   CalcMinSize(W, H);

   Count := 0;
   For I := Low(Buttons) To High(Buttons) Do
      If Buttons[I].Visible Then
         Inc(Count);
   If Count = 0 Then
      Inc(Count);

   ButtonWidth := trunc(W / Count);
   Temp := Count * ButtonWidth;
   If Align = vaNone Then
      W := Temp;

   X := 0;
   Remain := W - Temp;
   Temp := Count Div 2;
   For I := Low(Buttons) To High(Buttons) Do
   Begin
      If Buttons[I].Visible Then
      Begin
         If X = 0 Then
            Buttons[I].Corners := [d2CornerTopLeft] * FCorners + [d2CornerBottomLeft] * FCorners
         Else
         If X > Width - (ButtonWidth * 1.5) Then
            Buttons[I].Corners := [d2CornerTopRight] * FCorners + [d2CornerBottomRight] * FCorners
         Else
            Buttons[I].Corners := [];
         Buttons[I].xRadius := FxRadius;
         Buttons[I].yRadius := FyRadius;
         Buttons[I].CornerType := FCornerType;
         Buttons[I].ApplyStyle;

         Space := 0;
         If Remain <> 0 Then
         Begin
            Temp := Temp - Remain;
            If Temp < 0 Then
            Begin
               Temp := Temp + Count;
               Space := 1;
            End;
         End;
         Buttons[I].SetBounds(X, 0, ButtonWidth + Space, Height);
         X := X + ButtonWidth + Space;
      End Else
         Buttons[I].SetBounds(Width + 1, 0, ButtonWidth, Height);
   End;
End;

Procedure TD2DBNavigator.Realign;
Var
   W, H: Single;
Begin
   Inherited;
   W := Width;
   H := Height;
   SetSize(W, H);
End;

Procedure TD2DBNavigator.ClickHandler(Sender: TObject);
Begin
   BtnClick(TD2NavButton(Sender).BtnIndex);
End;

Procedure TD2DBNavigator.BtnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Var
   OldFocus: TD2NavigateBtn;
Begin
   OldFocus := FocusedButton;
   FocusedButton := TD2NavButton(Sender).BtnIndex;
   If IsFocused Then
   Begin
      SetFocus;
   End Else
   If CanFocused And (IsFocused) And (OldFocus <> FocusedButton) Then
   Begin
      Buttons[OldFocus].Repaint;
      Buttons[FocusedButton].Repaint;
   End;
End;

Procedure TD2DBNavigator.BtnClick(Const aIndex: TD2NavigateBtn);
Begin
   If (DataSource <> nil) And (DataSource.State <> dsInactive) Then
   Begin
      If Not (csDesigning In ComponentState) And Assigned(FBeforeAction) Then
         FBeforeAction(Self, aIndex);
      With DataSource.DataSet Do
      Begin
         Case aIndex Of
            nbPrior: Prior;
            nbNext: Next;
            nbFirst: First;
            nbLast: Last;
            nbInsert: Insert;
            nbEdit: Edit;
            nbCancel: Cancel;
            nbPost: Post;
            nbRefresh: Refresh;
            nbDelete:
               If Not FConfirmDelete Or (MessageDlg(TranslateText('Delete record?'), mtConfirmation, mbOKCancel, 0) <> idCancel) Then
                  Delete;
         End;
      End;
   End;
   If Not (csDesigning In ComponentState) And Assigned(FOnNavClick) Then
      FOnNavClick(Self, aIndex);
End;

Procedure TD2DBNavigator.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
//var
// NewFocus:TD2NavigateBtn;
// OldFocus:TD2NavigateBtn;
Begin
(*  OldFocus:=FocusedButton;
  case Key of
    VK_RIGHT:
      begin
        if OldFocus < High(Buttons) then
        begin
          NewFocus:=OldFocus;
          repeat
            NewFocus:=Succ(NewFocus);
          until (NewFocus=High(Buttons)) or (Buttons[NewFocus].Visible);
          if Buttons[NewFocus].Visible then
          begin
            FocusedButton:=NewFocus;
            Buttons[OldFocus].Invalidate;
            Buttons[NewFocus].Invalidate;
          end;
        end;
      end;
    VK_LEFT:
      begin
        NewFocus:=FocusedButton;
        repeat
          if NewFocus > Low(Buttons) then
            NewFocus:=Pred(NewFocus);
        until (NewFocus=Low(Buttons)) or (Buttons[NewFocus].Visible);
        if NewFocus <> FocusedButton then
        begin
          FocusedButton:=NewFocus;
          Buttons[OldFocus].Invalidate;
          Buttons[FocusedButton].Invalidate;
        end;
      end;
    VK_SPACE:
      begin
        if Buttons[FocusedButton].Enabled then
          Buttons[FocusedButton].Click;
      end;
  end; *)
End;

Procedure TD2DBNavigator.DataChanged;
Var
   UpEnable, DnEnable: Boolean;
Begin
   UpEnable := Enabled And FDataLink.Active And Not FDataLink.DataSet.BOF;
   DnEnable := Enabled And FDataLink.Active And Not FDataLink.DataSet.EOF;
   Buttons[nbFirst].Enabled := UpEnable;
   Buttons[nbPrior].Enabled := UpEnable;
   Buttons[nbNext].Enabled := DnEnable;
   Buttons[nbLast].Enabled := DnEnable;
   Buttons[nbDelete].Enabled := Enabled And FDataLink.Active And FDataLink.DataSet.CanModify And Not (FDataLink.DataSet.BOF And FDataLink.DataSet.EOF);
End;

Procedure TD2DBNavigator.EditingChanged;
Var
   CanModify: Boolean;
Begin
   CanModify := Enabled And FDataLink.Active And FDataLink.DataSet.CanModify;
   Buttons[nbInsert].Enabled := CanModify;
   Buttons[nbEdit].Enabled := CanModify And Not FDataLink.Editing;
   Buttons[nbPost].Enabled := CanModify And FDataLink.Editing;
   Buttons[nbCancel].Enabled := CanModify And FDataLink.Editing;
   Buttons[nbRefresh].Enabled := CanModify;
End;

Procedure TD2DBNavigator.ActiveChanged;
Var
   I: TD2NavigateBtn;
Begin
   If Not (Enabled And FDataLink.Active) Then
      For I := Low(Buttons) To High(Buttons) Do
         Buttons[I].Enabled := False
   Else Begin
      DataChanged;
      EditingChanged;
   End;
End;

Procedure TD2DBNavigator.SetDataSource(Value: TDataSource);
Begin
   FDataLink.DataSource := Value;
   If Not (csLoading In ComponentState) Then
      ActiveChanged;
   If Value <> nil Then
      Value.FreeNotification(Self);
End;

Function TD2DBNavigator.GetDataSource: TDataSource;
Begin
   Result := FDataLink.DataSource;
End;

Procedure TD2DBNavigator.Loaded;
Var
   W, H: Single;
Begin
   Inherited Loaded;
   W := Width;
   H := Height;
   SetSize(W, H);
   If (W <> Width) Or (H <> Height) Then
      SetBounds(Position.X, Position.Y, W, H);
   InitHints;
   ActiveChanged;
End;

Procedure TD2DBNavigator.SetCornerType(Const Value: TD2CornerType);
Begin
   If FCornerType <> Value Then
   Begin
      FCornerType := Value;
      Realign;
   End;
End;

Procedure TD2DBNavigator.SetxRadius(Const Value: Single);
Begin
   If FxRadius <> Value Then
   Begin
      FxRadius := Value;
      Realign;
   End;
End;

Procedure TD2DBNavigator.SetyRadius(Const Value: Single);
Begin
   If FyRadius <> Value Then
   Begin
      FyRadius := Value;
      Realign;
   End;
End;

Function TD2DBNavigator.IsCornersStored: Boolean;
Begin
   Result := FCorners <> AllCorners;
End;

Procedure TD2DBNavigator.SetCorners(Const Value: TD2Corners);
Begin
   If FCorners <> Value Then
   Begin
      FCorners := Value;
      Realign;
   End;
End;

//============ TD2NavButton ===================================

Constructor TD2NavButton.Create(AOwner: TComponent);
Begin
   Inherited;
   CanFocused := False;
   FResource := 'CornerButtonStyle';
   Locked := True;
   Stored := False;
End;

Destructor TD2NavButton.Destroy;
Begin
   If FRepeatTimer <> nil Then
      FRepeatTimer.Free;
   Inherited Destroy;
End;

Procedure TD2NavButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited MouseDown(Button, Shift, X, Y);
   If nsAllowTimer In FNavStyle Then
   Begin
      If FRepeatTimer = nil Then
         FRepeatTimer := TD2Timer.Create(Self);

      FRepeatTimer.OnTimer := TimerExpired;
      FRepeatTimer.Interval := cnInitRepeatPause;
      FRepeatTimer.Enabled := True;
   End;
End;

Procedure TD2NavButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   Inherited MouseUp(Button, Shift, X, Y);
   If FRepeatTimer <> nil Then
      FRepeatTimer.Enabled := False;
End;

Procedure TD2NavButton.TimerExpired(Sender: TObject);
Begin
   FRepeatTimer.Interval := cnRepeatPause;
   If (IsPressed) Then
   Begin
      Try
         Click;
      Except
         FRepeatTimer.Enabled := False;
         Raise;
      End;
   End;
End;

Procedure TD2NavButton.ApplyStyle;
Var
   S: TD2Object;
Begin
   Inherited;
   S := FindResource('text');
   If (S <> nil) And (S Is TD2Shape) And (FPath <> nil) Then
      FPath.Fill.Assign(TD2Shape(S).Fill);
End;

//============ TD2NavDataLink ==============================

Constructor TD2NavDataLink.Create(ANav: TD2DBNavigator);
Begin
   Inherited Create;
   FNavigator := ANav;
   VisualControl := True;
End;

Destructor TD2NavDataLink.Destroy;
Begin
   FNavigator := nil;
   Inherited Destroy;
End;

Procedure TD2NavDataLink.EditingChanged;
Begin
   If FNavigator <> nil Then
      FNavigator.EditingChanged;
End;

Procedure TD2NavDataLink.ActiveChanged;
Begin
   If FNavigator <> nil Then
      FNavigator.ActiveChanged;
End;

Procedure TD2NavDataLink.DataSetChanged;
Begin
   If FNavigator <> nil Then
      FNavigator.DataChanged;
End;

//================== TD2DBLabel ==============================

Constructor TD2DBLabel.Create(AOwner: TComponent);
Begin
   Inherited;
   TextAlign := d2TextAlignNear;
   FResource := 'labelstyle';
   FDataLink := TFieldDataLink.Create;
   FDataLink.Control := Self;
   FDataLink.OnDataChange := DataChange;
End;

Procedure TD2DBLabel.DataChange(Sender: TObject);
Begin
   TextW := GetFieldText;
End;

Destructor TD2DBLabel.Destroy;
Begin
   FreeAndNil(FDataLink);
   Inherited;
End;

Procedure TD2DBLabel.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited Notification(AComponent, Operation);
   If (Operation = opRemove) And (FDataLink <> nil) And (AComponent = DataSource) Then
      DataSource := nil;
End;

Function TD2DBLabel.GetFieldText: String;
Begin
   If FDataLink.Field <> nil Then
      Result := FDataLink.Field.DisplayText
   Else
   If csDesigning In ComponentState Then
      Result := Name
   Else
      Result := '';
End;

Function TD2DBLabel.GetDataField: String;
Begin
   Result := FDataLink.FieldName;
End;

Procedure TD2DBLabel.SetDataField(Const Value: String);
Begin
   FDataLink.FieldName := Value;
End;

Function TD2DBLabel.GetDataSource: TDataSource;
Begin
   Result := FDataLink.DataSource;
End;

Procedure TD2DBLabel.SetDataSource(Const Value: TDataSource);
Begin
   If Not (FDataLink.DataSourceFixed And (csLoading In ComponentState)) Then
      FDataLink.DataSource := Value;
   If Value <> nil Then
      Value.FreeNotification(Self);
End;

//============ TD2DBImage ===================================

Constructor TD2DBImage.Create(AOwner: TComponent);
Begin
   Inherited;
   FDataLink := TFieldDataLink.Create;
   FDataLink.Control := Self;
   FDataLink.OnDataChange := DataChange;
   FDataLink.OnUpdateData := UpdateData;
End;

Procedure TD2DBImage.DoBitmapChanged(Sender: TObject);
Begin
   If FDataLink = nil Then
      Exit;

   If (Not Assigned(FDataLink.Field) Or FDataLink.Field.IsBlob) Then
   Begin
      FDataLink.OnDataChange := nil;
      Try
         FDataLink.Edit;
         FDataLink.Modified;
         FDataLink.UpdateRecord;
      Finally
         FDataLink.OnDataChange := DataChange;
      End;
      Repaint;
   End;
End;

Procedure TD2DBImage.UpdateData(Sender: TObject);
Var
   P: TPicture;
Begin
   If FDataLink = nil Then
      Exit;

   If (Not Assigned(FDataLink.Field) Or FDataLink.Field.IsBlob) Then
   Begin
      P := TPicture.Create;
      P.Assign(Bitmap);
      If FDataLink.Field <> nil Then
         FDataLink.Field.Assign(P);
      P.Free;
   End;
End;

Procedure TD2DBImage.DataChange(Sender: TObject);
Begin
   If FDataLink = nil Then
      Exit;

   If Assigned(FDataLink.Field) And FDataLink.Field.IsBlob Then
   Begin
      Bitmap.OnChange := nil;
      Try
         Bitmap.Assign(FDataLink.Field);
      Finally
         Bitmap.OnChange := DoBitmapChanged;
      End;
      Repaint;
   End Else
      Bitmap.Clear();
End;

Destructor TD2DBImage.Destroy;
Begin
   FreeAndNil(FDataLink);
   Inherited;
End;

Procedure TD2DBImage.Paint;
Begin
   Inherited;
   If Assigned(FScene) And FScene.GetDesignTime Then
   Begin
      Canvas.Fill.Style := d2BrushSolid;
      Canvas.Fill.Color := vcGray;

      If FDataLink = nil Then
      Begin
         Canvas.FillText(LocalRect, LocalRect, '[TD2DBImage]', False, AbsoluteOpacity, d2TextAlignCenter);
         exit;
      End;

      If Not Assigned(FDataLink.Field) Then
         Canvas.FillText(LocalRect, LocalRect, '[TD2DBImage]', False, AbsoluteOpacity, d2TextAlignCenter)
      Else
         Canvas.FillText(LocalRect, LocalRect, '[' + FDataLink.FieldName + ']', False, AbsoluteOpacity, d2TextAlignCenter);
   End;
End;

Procedure TD2DBImage.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited Notification(AComponent, Operation);
   If (Operation = opRemove) And (FDataLink <> nil) And (AComponent = DataSource) Then
      DataSource := nil;
End;

Function TD2DBImage.GetFieldText: String;
Begin
   If FDataLink.Field <> nil Then
      Result := FDataLink.Field.DisplayText
   Else
   If csDesigning In ComponentState Then
      Result := Name
   Else
      Result := '';
End;

Function TD2DBImage.GetDataField: String;
Begin
   Result := FDataLink.FieldName;
End;

Procedure TD2DBImage.SetDataField(Const Value: String);
Begin
   FDataLink.FieldName := Value;
End;

Function TD2DBImage.GetDataSource: TDataSource;
Begin
   Result := FDataLink.DataSource;
End;

Procedure TD2DBImage.SetDataSource(Const Value: TDataSource);
Begin
   If Not (FDataLink.DataSourceFixed And (csLoading In ComponentState)) Then
      FDataLink.DataSource := Value;
   If Value <> nil Then
      Value.FreeNotification(Self);
End;

//=============== TD2DBTextBox =====================================

Constructor TD2DBTextBox.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'textboxstyle';
   FDataLink := TFieldDataLink.Create;
   FDataLink.Control := Self;
   FDataLink.OnDataChange := DataChange;
   FDataLink.OnUpdateData := UpdateData;
{  FDataLink.OnEditingChange:=EditingChange;
  FDataLink.OnActiveChange:=ActiveChange;}
End;

Destructor TD2DBTextBox.Destroy;
Begin
   FreeAndNil(FDataLink);
   Inherited;
End;

Procedure TD2DBTextBox.DataChange(Sender: TObject);
Begin
   TextW := GetFieldText;
End;

Procedure TD2DBTextBox.UpdateData(Sender: TObject);
Begin
   FDataLink.Field.Text := Text;
End;

Procedure TD2DBTextBox.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited Notification(AComponent, Operation);
   If (Operation = opRemove) And (FDataLink <> nil) And (AComponent = DataSource) Then
      DataSource := nil;
End;

Function TD2DBTextBox.GetFieldText: String;
Begin
   If FDataLink.Field <> nil Then
      Result := FDataLink.Field.DisplayText
   Else
   If csDesigning In ComponentState Then
      Result := Name
   Else
      Result := '';
End;

Function TD2DBTextBox.GetDataField: String;
Begin
   Result := FDataLink.FieldName;
End;

Procedure TD2DBTextBox.SetDataField(Const Value: String);
Begin
   FDataLink.FieldName := Value;
End;

Function TD2DBTextBox.GetDataSource: TDataSource;
Begin
   Result := FDataLink.DataSource;
End;

Procedure TD2DBTextBox.SetDataSource(Const Value: TDataSource);
Begin
   If Not (FDataLink.DataSourceFixed And (csLoading In ComponentState)) Then
      FDataLink.DataSource := Value;
   If Value <> nil Then
      Value.FreeNotification(Self);
End;

Procedure TD2DBTextBox.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   If (Key = VK_DELETE) Or ((Key = VK_INSERT) And (ssShift In Shift)) Then
      FDataLink.Edit;
   If (KeyChar >= #32) And (FDataLink.Field <> nil) And Not FDataLink.Field.IsValidChar(Char(KeyChar)) Then
   Begin
      KeyChar := #0;
   End;
   Case KeyChar Of
      #27:
      Begin
         FDataLink.Reset;
         SelectAll;
         KeyChar := #0;
      End;
      ^H, ^V, ^X, #32..High(KeyChar):
         If Not FDataLink.Edit Then
            KeyChar := #0;
   End;
   Inherited;
End;

Procedure TD2DBTextBox.Change;
Begin
   Inherited;
   If FDataLink.Editing Then
   Begin
      FDataLink.Modified;
      FDataLink.UpdateRecord;
   End;
End;

Procedure TD2DBTextBox.EnterFocus;
Begin
   Inherited;
   FDataLink.Reset;
End;

Procedure TD2DBTextBox.KillFocus;
Begin
   Inherited;
   FDataLink.Reset;
End;

//=============== TD2DBMemo =====================================

Constructor TD2DBMemo.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'Memostyle';
   FDataLink := TFieldDataLink.Create;
   FDataLink.Control := Self;
   FDataLink.OnDataChange := DataChange;
   FDataLink.OnUpdateData := UpdateData;
{  FDataLink.OnEditingChange:=EditingChange;
  FDataLink.OnActiveChange:=ActiveChange;}
End;

Destructor TD2DBMemo.Destroy;
Begin
   FreeAndNil(FDataLink);
   Inherited;
End;

Procedure TD2DBMemo.DataChange(Sender: TObject);
Begin
   If Assigned(FDataLink.Field) And FDataLink.Field.IsBlob Then
   Begin
      TextW := FDataLink.Field.AsString;
   End Else
      TextW := GetFieldText;
End;

Procedure TD2DBMemo.UpdateData(Sender: TObject);
Begin
   FDataLink.Field.AsString := TextW;
End;

Procedure TD2DBMemo.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited Notification(AComponent, Operation);
   If (Operation = opRemove) And (FDataLink <> nil) And (AComponent = DataSource) Then
      DataSource := nil;
End;

Function TD2DBMemo.GetFieldText: String;
Begin
   If FDataLink.Field <> nil Then
      Result := FDataLink.Field.DisplayText
   Else
   If csDesigning In ComponentState Then
      Result := Name
   Else
      Result := '';
End;

Function TD2DBMemo.GetDataField: String;
Begin
   Result := FDataLink.FieldName;
End;

Procedure TD2DBMemo.SetDataField(Const Value: String);
Begin
   FDataLink.FieldName := Value;
End;

Function TD2DBMemo.GetDataSource: TDataSource;
Begin
   Result := FDataLink.DataSource;
End;

Procedure TD2DBMemo.SetDataSource(Const Value: TDataSource);
Begin
   If Not (FDataLink.DataSourceFixed And (csLoading In ComponentState)) Then
      FDataLink.DataSource := Value;
   If Value <> nil Then
      Value.FreeNotification(Self);
End;

Procedure TD2DBMemo.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);
Begin
   If (Key = VK_DELETE) Or ((Key = VK_INSERT) And (ssShift In Shift)) Then
      FDataLink.Edit;
   If (KeyChar >= #32) And (FDataLink.Field <> nil) And Not FDataLink.Field.IsValidChar(Char(KeyChar)) Then
   Begin
      KeyChar := #0;
   End;
   Case KeyChar Of
      #27:
      Begin
         FDataLink.Reset;
         SelectAll;
         KeyChar := #0;
      End;
      ^H, ^V, ^X, #32..High(KeyChar):
         If Not FDataLink.Edit Then
            KeyChar := #0;
   End;
   Inherited;
End;

Procedure TD2DBMemo.Change;
Begin
   Inherited;
   If FDataLink.Editing Then
   Begin
      FDataLink.Modified;
      FDataLink.UpdateRecord;
   End;
End;

Procedure TD2DBMemo.EnterFocus;
Begin
   Inherited;
   FDataLink.Reset;
End;

Procedure TD2DBMemo.KillFocus;
Begin
   Inherited;
   FDataLink.Reset;
End;

//================== TD2GridDataLink ==============================

Type
   EInvalidGridOperation = Class(Exception);

Procedure RaiseGridError(Const S: String);
Begin
   Raise EInvalidGridOperation.Create(S);
End;

Type
   TIntArraydb = Array[0..MaxMapSize] Of Integer;
   PIntArraydb = ^TIntArraydb;

Constructor TD2GridDataLink.Create(AGrid: TD2DBGrid);
Begin
   Inherited Create;
   FGrid := AGrid;
   VisualControl := True;
End;

Destructor TD2GridDataLink.Destroy;
Begin
   Inherited Destroy;
End;

Function TD2GridDataLink.GetDefaultFields: Boolean;
Var
   I: Integer;
Begin
   Result := True;
   If DataSet <> nil Then
      Result := DataSet.DefaultFields;
   If Result And SparseMap Then
      For I := 0 To FFieldCount - 1 Do
         If FFieldMap[I] < 0 Then
         Begin
            Result := False;
            Exit;
         End;
End;

Function TD2GridDataLink.GetFields(I: Integer): TField;
Begin
   If (0 <= I) And (I < FFieldCount) And (FFieldMap[I] >= 0) Then
      Result := DataSet.Fields[FFieldMap[I]]
   Else
      Result := nil;
End;

Procedure TD2GridDataLink.ActiveChanged;
Begin
   If Active And Assigned(DataSource) Then
      If Assigned(DataSource.DataSet) Then
         If DataSource.DataSet.IsUnidirectional Then
            DatabaseError('Operation not allowed on a unidirectional dataset');
   FGrid.LinkActive(Active);
   FModified := False;
End;

Procedure TD2GridDataLink.Modified;
Begin
   FModified := True;
End;

Procedure TD2GridDataLink.DataSetChanged;
Begin
   FGrid.DataSetChanged;
   FModified := False;
End;

Procedure TD2GridDataLink.DataSetScrolled(Distance: Integer);
Begin
   FGrid.DataChanged;
End;

Procedure TD2GridDataLink.LayoutChanged;
Begin
   Inherited LayoutChanged;
End;

Procedure TD2GridDataLink.FocusControl(Field: TFieldRef);
Begin
   If Assigned(Field) And Assigned(Field^) Then
   Begin
      FGrid.SelectedField := Field^;
   End;
End;

Procedure TD2GridDataLink.EditingChanged;
Begin
   FGrid.EditingChanged;
End;

Procedure TD2GridDataLink.RecordChanged(Field: TField);
Begin
   FGrid.RecordChanged(Field);
   FModified := False;
End;

Procedure TD2GridDataLink.UpdateData;
Begin
   FInUpdateData := True;
   Try
      If FModified Then
         FGrid.UpdateData;
      FModified := False;
   Finally
      FInUpdateData := False;
   End;
End;

Function TD2GridDataLink.GetMappedIndex(ColIndex: Integer): Integer;
Begin
   If (0 <= ColIndex) And (ColIndex < FFieldCount) Then
      Result := FFieldMap[ColIndex]
   Else
      Result := -1;
End;

Procedure TD2GridDataLink.Reset;
Begin
   If FModified Then
      RecordChanged(nil)
   Else
      Dataset.Cancel;
End;

Function TD2GridDataLink.IsAggRow(Value: Integer): Boolean;
Begin
   Result := False;
End;

Procedure TD2GridDataLink.BuildAggMap;
Begin
End;

//================ TD2DBColumn ===============================

Constructor TD2DBColumn.Create(AOwner: TComponent);
Begin
   Inherited;
End;

Destructor TD2DBColumn.Destroy;
Begin
   Inherited;
End;

Function TD2DBColumn.GetField: TField;
Var
   G: TD2DBGrid;
Begin
   G := TD2DBGrid(Grid);
   If (FField = nil) And (Length(FFieldName) > 0) And Assigned(G) And Assigned(G.DataLink.DataSet) Then
      With G.Datalink.Dataset Do
         If Active Or (Not DefaultFields) Then
            SetField(FindField(FieldName));
   Result := FField;
End;

Procedure TD2DBColumn.SetField(Value: TField);
Begin
   If FField = Value Then
      Exit;
   If Assigned(FField) And (Grid <> nil) Then
      FField.RemoveFreeNotification(Grid);
   If Assigned(Value) And (csDestroying In Value.ComponentState) Then
      Value := nil;

   FField := Value;

   If Assigned(Value) Then
   Begin
      If Grid <> nil Then
         FField.FreeNotification(Grid);

      FFieldName := Value.FieldName;
   End;
End;

Procedure TD2DBColumn.SetFieldName(Const Value: String);
Begin
   If FFieldName <> Value Then
   Begin
      FFieldName := Value;
      If Header = '' Then
         Header := FieldName;
      UpdateColumn;
   End;
End;

Function TD2DBColumn.GetData: Variant;
Begin
   Result := Field.Value;
End;

Procedure TD2DBColumn.SetData(Value: Variant);
Begin
   Field.Value := Value;
End;

//=============== TD2DBCheckColumn ==================================

Function TD2DBCheckColumn.CreateCellControl: TD2Control;
Begin
   Result := TD2CheckCell.Create(Self);
   TD2CheckCell(Result).OnChange := DoCheckChanged;
End;

Procedure TD2DBCheckColumn.DoCheckChanged(Sender: TObject);
Begin
   If Grid = nil Then
      Exit;
   If FUpdateColumn Then
      Exit;
   With d2StringToPoint(TD2Object(Sender).TagString) Do
      TD2DBGrid(Grid).SetValue(trunc(x), trunc(y), TD2Control(Sender).Data);
End;

Function TD2DBCheckColumn.GetData: Variant;
Begin
   Result := Field.AsBoolean;
End;

//================= TD2DBPopupColumn ===============================

Constructor TD2DBPopupColumn.Create(AOwner: TComponent);
Begin
   Inherited;
   FItems := TD2WideStringList.Create;
End;

Destructor TD2DBPopupColumn.Destroy;
Begin
   FItems.Free;
   Inherited;
End;

Function TD2DBPopupColumn.CreateCellControl: TD2Control;
Begin
   Result := TD2PopupCell.Create(Self);
   TD2PopupCell(Result).Items.Assign(FItems);
   TD2PopupCell(Result).OnChange := DoPopupChanged;
End;

Procedure TD2DBPopupColumn.SetItems(Const Value: TD2WideStrings);
Begin
   FItems.Assign(Value);
End;

Procedure TD2DBPopupColumn.DoPopupChanged(Sender: TObject);
Begin
   If Grid = nil Then
      Exit;
   If FUpdateColumn Then
      Exit;
   With d2StringToPoint(TD2Object(Sender).TagString) Do
      TD2DBGrid(Grid).SetValue(trunc(x), trunc(y), TD2Control(Sender).Data);
End;

//================ TD2DBImageColumn ==============================

Function TD2DBImageColumn.CreateCellControl: TD2Control;
Begin
   Result := TD2ImageCell.Create(Self);
   TD2ImageCell(Result).OnChange := DoImageChanged;
   TD2ImageCell(Result).EnableOpenDialog := True;
End;

Destructor TD2DBImageColumn.Destroy;
Begin
   If FCurrent <> nil Then
      FCurrent.Free;
   Inherited;
End;

Procedure TD2DBImageColumn.DoImageChanged(Sender: TObject);
Begin
   If Grid = nil Then
      Exit;
   If FUpdateColumn Then
      Exit;
   If Not TD2DBGrid(Grid).CanEditModify Then
      Exit;
   With d2StringToPoint(TD2Object(Sender).TagString) Do
      TD2DBGrid(Grid).SetValue(trunc(x), trunc(y), TD2Control(Sender).Data);
End;

Function TD2DBImageColumn.GetData: Variant;
Begin
   If Assigned(Field) And Field.IsBlob And (TBlobField(Field).BlobSize > 0) Then
   Begin
      If FCurrent = nil Then
         FCurrent := TD2Bitmap.Create(1, 1);
      FCurrent.Assign(Field);
      Result := ObjectToVariant(FCurrent);
   End Else
      Result := NULL;
End;

Procedure TD2DBImageColumn.SetData(Value: Variant);
Var
   P: TPicture;
Begin
   If Assigned(Field) And Field.IsBlob And VarIsObject(Value) Then
   Begin
      P := TPicture.Create;
      P.Assign(TPersistent(VariantToObject(Value)));
      Field.Assign(P);
      P.Free;
   End Else
      Field.Value := NULL;
End;

//=============== TD2DBProgressColumn =======================================

Constructor TD2DBProgressColumn.Create(AOwner: TComponent);
Begin
   Inherited;
   FMax := 100;
End;

Function TD2DBProgressColumn.CreateCellControl: TD2Control;
Begin
   Result := TD2ProgressCell.Create(Self);
   TD2ProgressCell(Result).Min := FMin;
   TD2ProgressCell(Result).Max := FMax;
End;

//================ TD2DBGrid ===============================================

Constructor TD2DBGrid.Create(AOwner: TComponent);
Begin
   Inherited;
   FResource := 'gridstyle';
   FDataLink := TD2GridDataLink.Create(Self);
   RowCount := 0;
End;

Destructor TD2DBGrid.Destroy;
Begin
   FDataLink.Free;
   FDataLink := nil;
   Inherited;
End;

Function TD2DBGrid.ItemClass: String;
Begin
   Result := 'TD2DBColumn;TD2DBCheckColumn;TD2DBPopupColumn;TD2DBImageColumn;TD2DBProgressColumn';
End;

Procedure TD2DBGrid.Loaded;
Begin
   Inherited;
   DataChanged;
End;

Type
   THack = Class(TDataSet);

Procedure TD2DBGrid.UpdateRowCount;
Begin
   If FDataLink.Active Then
   Begin
      FDataLink.BufferCount := VisibleRows * 2;
      If FDataLink.BufferCount < FDataLink.DataSet.RecordCount Then
         RowCount := FDataLink.DataSet.RecordCount - FDataLink.BufferCount + FDataLink.RecordCount
      Else
         RowCount := FDataLink.RecordCount;
   End Else
      RowCount := 0;
End;

Procedure TD2DBGrid.LinkActive(Value: Boolean);
Begin
   UpdateRowCount;
   UpdateSelection;
End;

Procedure TD2DBGrid.EditingChanged;
Begin
   LinkActive(FDataLink.Active);
End;

Procedure TD2DBGrid.RecordChanged(Field: TField);
Begin
   If DataSource.State <> dsInactive Then
      UpdateColumns;
End;

Procedure TD2DBGrid.DataChanged;
Begin
   If (DataSource <> nil) And (DataSource.State <> dsInactive) Then
   Begin
      FDisableMove := True;
      If FDataLink.DataSet.RecNo > 0 Then
         Selected := (FDataLink.DataSet.RecNo - 1);
      FDisableMove := False;
      UpdateColumns;
   End;
End;

Procedure TD2DBGrid.DataSetChanged;
Begin
   If (FDataLink.DataSet <> nil) Then
   Begin
      UpdateRowCount;
      FDisableMove := True;
      If FDataLink.DataSet.RecNo > 0 Then
         Selected := (FDataLink.DataSet.RecNo - 1);
      FDisableMove := False;
      UpdateColumns;
   End;
End;

Function TD2DBGrid.GetValue(Col, Row: Integer): Variant;
Var
   C: TD2DBColumn;
   F, OldActive: Integer;
Begin
   Result := NULL;
   If Assigned(DataLink) And DataLink.Active Then
   Begin
      C := TD2DBColumn(Columns[Col]);
      If (C <> nil) And (C.GetField <> nil) Then
      Begin
         OldActive := FDataLink.ActiveRecord;
         F := (Selected - OldActive);
         Try
            If (Row - F >= 0) And (Row - F < FDataLink.BufferCount) Then
            Begin
               FDatalink.ActiveRecord := Row - F;
               Result := C.GetData;
            End;

         Finally
            FDatalink.ActiveRecord := OldActive;
         End;
      End;
   End;
End;

Procedure TD2DBGrid.SetValue(Col, Row: Integer; Const Value: Variant);
Var
   C: TD2DBColumn;
Begin
   If Assigned(DataLink) And DataLink.Active And Not FDataLink.ReadOnly Then
   Begin
      C := TD2DBColumn(Columns[Col]);
      If (C <> nil) And (C.Field <> nil) And C.Field.CanModify Then
      Begin
         FDataLink.Modified;
         FEditValue := Value;
         FNeedUpdate := True;
         FDatalink.UpdateData;
      End;
   End;
End;

Function TD2DBGrid.GetDataSource: TDataSource;
Begin
   Result := FDataLink.DataSource;
End;

Procedure TD2DBGrid.SetDataSource(Const Value: TDataSource);
Begin
   If Not (FDataLink.DataSourceFixed And (csLoading In ComponentState)) Then
   Begin
      FDataLink.DataSource := Value;
   End;
   If Value <> nil Then
      Value.FreeNotification(Self);
End;

Procedure TD2DBGrid.Notification(AComponent: TComponent; Operation: TOperation);
Begin
   Inherited;
   If (Operation = opRemove) And (FDataLink <> nil) And (AComponent = DataSource) Then
      DataSource := nil;
End;

Procedure TD2DBGrid.UpdateData;
Var
   C: TD2DBColumn;
Begin
   C := TD2DBColumn(Columns[ColumnIndex]);
   If (C <> nil) And FNeedUpdate Then
      TD2DBColumn(Columns[ColumnIndex]).SetData(FEditValue);
   FEditValue := NULL;
   FNeedUpdate := False;
End;

Procedure TD2DBGrid.KeyDown(Var Key: Word; Var KeyChar: System.Widechar; Shift: TShiftState);

   Procedure DoSelection(Select: Boolean; Direction: Integer);
   Begin
      BeginUpdate;
      Try
         FDatalink.MoveBy(Direction);
      Finally
         EndUpdate;
      End;
   End;

   Procedure NextRow(Select: Boolean);
   Begin
      With FDatalink.Dataset Do
      Begin
         If (State = dsInsert) And Not Modified And Not FDatalink.FModified Then
            If FDataLink.EOF Then
               Exit
            Else
               Cancel
         Else
            DoSelection(Select, 1);
         If FDataLink.EOF And CanModify And (Not ReadOnly) Then
            Append;
      End;
   End;

   Procedure PriorRow(Select: Boolean);
   Begin
      With FDatalink.Dataset Do
         If (State = dsInsert) And Not Modified And FDataLink.EOF And Not FDatalink.FModified Then
            Cancel
         Else
            DoSelection(Select, -1);
   End;

Begin
   If (RowCount > 0) And (FDataLink.Active) Then
   Begin
      Case Key Of
         VK_DELETE:
            If (Not ReadOnly) And Not FDataLink.DataSet.IsEmpty And FDataLink.DataSet.CanModify Then
               FDataLink.DataSet.Delete;
         VK_UP: PriorRow(True);
         VK_PRIOR: FDataLink.MoveBy(-VisibleRows);
         VK_HOME:
            If ssCtrl In Shift Then
               FDataLink.DataSet.First
            Else
               ColumnIndex := 0;
         VK_INSERT:
            If FDataLink.DataSet.CanModify And (Not ReadOnly) Then
            Begin
               FDataLink.DataSet.Insert;
            End;
         VK_END:
            If ssCtrl In Shift Then
               FDataLink.DataSet.Last
            Else
               ColumnIndex := ColumnCount - 1;
         VK_DOWN: NextRow(True);
         VK_NEXT: FDataLink.MoveBy(VisibleRows);
         VK_ESCAPE:
         Begin
            FDatalink.Reset;
            SetFocus;
         End;
         Else Inherited;

            Exit;
      End;
      Key := 0;
   End;
   Inherited;
End;

Procedure TD2DBGrid.SetSelected(Const Value: Integer);
Var
   S: Integer;
Begin
   S := Selected;
   If Selected <> Value Then
   Begin
      Inherited;
      If Not FDisableMove Then
      Begin
         If FDataLink.Active Then
            FDataLink.MoveBy(Selected - S);
      End;
      UpdateSelection;
   End;
End;

Function TD2DBGrid.GetSelectedField: TField;
Begin
   Result := TD2DBColumn(Columns[ColumnIndex]).Field;
End;

Procedure TD2DBGrid.SetSelectedField(Const Value: TField);
Var
   i: Integer;
Begin
   For i := 0 To ColumnCount - 1 Do
   Begin
      If TD2DBColumn(Columns[i]).Field = Value Then
      Begin
         ColumnIndex := i;
         Break;
      End;
   End;
End;

Function TD2DBGrid.CanEditAcceptKey(Key: System.Widechar): Boolean;
Begin
   With TD2DBColumn(Columns[ColumnIndex]) Do
      Result := FDatalink.Active And Assigned(Field) And Field.IsValidChar(Char(Key));
End;

Function TD2DBGrid.CanEditModify: Boolean;
Begin
   Result := False;
   If Not ReadOnly And FDatalink.Active And Not FDatalink.ReadOnly Then
      With TD2DBColumn(Columns[ColumnIndex]) Do
         If (Not ReadOnly) And Assigned(Field) And Field.CanModify Then
         Begin
            FDatalink.Edit;
            Result := FDatalink.Editing;
            If Result Then
               FDatalink.Modified;
         End;
End;

Procedure TD2DBGrid.Reset;
Begin
   Inherited;
   If FDatalink.Active Then
      FDatalink.Reset;
End;

Procedure TD2DBGrid.VScrollChange(Sender: TObject);
Begin
   Inherited;
   If FDataLink.Active Then
   Begin
      If TopRow < Selected - FDataLink.ActiveRecord Then
         Selected := TopRow;
      If TopRow + VisibleRows - 1 > Selected - FDataLink.ActiveRecord + FDataLink.BufferCount Then
      Begin
         Selected := TopRow + VisibleRows - 1;
         Selected := Selected;
      End;
   End;
End;

Function TD2DBGrid.GetContentBounds: TD2Rect;
Begin
   Result := Inherited GetContentBounds;
   UpdateRowCount;
End;

//======= TD2CustomBrushDialog ====================================

Constructor TD2CustomBrushDialog.Create(AOwner: TComponent);
Begin
   Inherited;
   FBrush := TD2Brush.Create(d2BrushSolid, $FF808080);
   FShowStyles := [d2BrushNone, d2BrushSolid, d2BrushGradient, d2BrushBitmap, d2BrushResource];
End;

Destructor TD2CustomBrushDialog.Destroy;
Begin
   FBrush.Free;
   Inherited;
End;

Function TD2CustomBrushDialog.Execute: Boolean;
Begin
End;

Procedure TD2CustomBrushDialog.SetBrush(Const Value: TD2Brush);
Begin
   FBrush.Assign(Value);
End;

//====== TD2CustomBitmapEditor ==============================

Procedure TD2CustomBitmapEditor.AssignFromBitmap(B: TD2Bitmap);
Begin
End;

Procedure TD2CustomBitmapEditor.AssignToBitmap(B: TD2Bitmap);
Begin
End;

//====== TD2CustomPathDataDesigner  ========================
Function TD2CustomPathDataDesigner.GetLines: TD2WideStrings;
Begin
   Result := nil;
End;


//------------------------------------------------------------
//------------------------------------------------------------
//------------------------------------------------------------

Procedure __Init_RegisterALLD2Objects;
Begin
   RegisterClasses([TD2CustomScene, TD2Bitmap, TD2Bounds, TD2Position, TD2Gradient, TD2PathData, TD2Brush, TD2GradientPoints, TD2WideStrings, TD2WideStringList, TD2SelectionItem, TD2Thumb,
      TD2ExpanderButton, TD2GradientPoint, TD2Visual, TD2Resources, TD2Object, TD2Content, TD2Control, TD2NavButton, TD2DBColumn, TD2DBCheckColumn, TD2DBPopupColumn, TD2StringColumn, TD2TextCell,
      TD2CheckCell, TD2DBImageColumn, TD2DBProgressColumn, TD2ProgressCell, TD2PopupCell]);

   RegisterD2Objects('0-Animations', [TD2ColorAnimation, TD2GradientAnimation, TD2FloatAnimation, TD2RectAnimation, TD2BitmapAnimation, TD2BitmapListAnimation, TD2ColorKeyAnimation, TD2FloatKeyAnimation, TD2PathAnimation, TD2PathSwitcher]);

   RegisterD2Objects('1-Effects', [TD2ShadowEffect, TD2BlurEffect, TD2GlowEffect, TD2InnerGlowEffect, TD2BevelEffect, TD2ReflectionEffect]);

   RegisterD2Objects('Resources', [TD2BrushObject, TD2PathObject, TD2BitmapObject]);

   RegisterD2Objects('Layout', [TD2Frame, TD2Layout, TD2ScaledLayout, TD2GridLayout, TD2SplitLayout, TD2Nond2Layout]);

   RegisterD2Objects('Popup', [TD2Popup, TD2PopupItem, TD2MessagePopup]);


   RegisterD2Objects('Shapes', [TD2Line, TD2Rectangle, TD2SidesRectangle, TD2BlurRectangle, TD2RoundRect, TD2BlurRoundRect, TD2Ellipse, TD2Circle, TD2Arc, TD2Pie, TD2Text, TD2Path, TD2Image,
      TD2PaintBox, TD2ScrollArrowLeft, TD2ScrollArrowRight, TD2CalloutRectangle]);

   RegisterD2Objects('Design', [TD2Selection, TD2SelectionPoint, TD2DesignFrame, TD2Inspector]);


   RegisterD2Objects('Windows', [TD2Background, TD2SizeGrip, TD2CloseButton]);

   RegisterD2Objects('Boxes', [TD2CheckBox, TD2PathCheckBox, TD2RadioButton, TD2GroupBox, TD2PopupBox, TD2CalendarBox]);

   RegisterD2Objects('Controls', [TD2Panel, TD2CalloutPanel, TD2Label, TD2ValueLabel, TD2ImageControl, TD2ProgressBar, TD2Track, TD2ScrollBar, TD2SmallScrollBar, TD2AniIndicator, TD2Expander,
      TD2TrackBar, TD2Splitter, TD2TabControl, TD2FramedVertScrollBox, TD2ScrollBox, TD2VertScrollBox, TD2FramedScrollBox]);

   RegisterD2Objects('Tool and Status', [TD2StatusBar, TD2ToolBar, TD2ToolButton, TD2ToolPathButton]);

   RegisterD2Objects('Buttons', [TD2Button, TD2RoundButton, TD2CircleButton, TD2BitmapButton, TD2PathButton, TD2SpeedButton, TD2CornerButton, TD2ColorButton, TD2AngleButton, TD2BitmapStateButton, TD2PopupButton]);

   RegisterD2Objects('HUD', [TD2HudPanel, TD2HudWindow, TD2HudButton, TD2HudSpeedButton, TD2HudAngleButton, TD2HudTrack, TD2HudTrackBar, TD2HudScrollBar, TD2HudPopupBox, TD2HudLabel,
      TD2HudCheckBox, TD2HudRadioButton, TD2HudGroupBox, TD2HudCloseButton, TD2HudStatusBar, TD2HudSizeGrip, TD2HudRoundButton, TD2HudCornerButton, TD2HudCircleButton, TD2HudListBox,
      TD2HudHorzListBox, TD2HudComboBox, TD2HudStringListBox, TD2HudStringComboBox, TD2HudImageListBox, TD2HudHorzImageListBox, TD2HudTextBox, TD2HudNumberBox, TD2HudRoundTextBox, TD2HudSpinBox,
      TD2HudComboTextBox, TD2HudComboTrackBar, TD2HudMemo, TD2HudHueTrackBar, TD2HudAlphaTrackBar, TD2HudBWTrackBar, TD2HudComboColorBox, TD2HudTabControl]);


   RegisterD2Objects('Lists', [TD2ListBox, TD2ComboBox, TD2StringListBox, TD2StringComboBox, TD2HorzListBox, TD2ImageListBox, TD2HorzImageListBox]);
   RegisterD2Objects('Items', [TD2ListBoxItem, TD2ImageListBoxItem, TD2TreeViewItem, TD2TabItem, TD2HudTabItem, TD2HeaderItem]);

   RegisterD2Objects('Trees', [TD2TreeView]);


   RegisterD2Objects('Text Edits', [TD2TextBox, TD2RoundTextBox, TD2NumberBox, TD2SpinBox, TD2ComboTextBox, TD2ComboTrackBar, TD2TextBoxClearBtn]);


   RegisterD2Objects('Text Edits', [TD2Memo, TD2CalendarTextBox]);


   RegisterD2Objects('Colors', [TD2HueTrackBar, TD2AlphaTrackBar, TD2BWTrackBar, TD2ColorQuad, TD2ColorPicker, TD2GradientEdit, TD2ColorBox, TD2ColorPanel, TD2ComboColorBox]);

   RegisterD2Objects('Ext. Controls', [TD2IPhoneButton, TD2DockBar, TD2DropTarget, TD2ImageViewer]);

   RegisterD2Objects('Math', [TD2PlotGrid]);

   RegisterD2Objects('Compound', [TD2CompoundTrackBar, TD2CompoundAngleBar, TD2CompoundTextBox, TD2CompoundMemo, TD2CompoundNumberBox, TD2CompoundPopupBox, TD2CompoundColorButton, TD2CompoundImage, TD2Calendar]);


   RegisterD2Objects('Grid', [TD2Grid, TD2StringGrid, TD2Header]);

   RegisterD2Objects('Grid Columns', [TD2Column, TD2CheckColumn, TD2ProgressColumn, TD2PopupColumn, TD2ImageColumn]);

   RegisterD2Objects('DB-Aware', [TD2DBNavigator, TD2DBGrid, TD2DBLabel, TD2DBImage, TD2DBTextBox, TD2DBMemo]);
End;


Procedure __Init_D2Filter_D2Canvas;
Begin
   {$IFDEF ORCA2D_OS_WINDOWS}
     LoadD2D;

     GvarD2DefaultFilterClass := TD2FilterGdiPlus;
     if GvarD2DefaultCanvasClass = nil then GvarD2DefaultCanvasClass := TD2CanvasGdiPlus;
     GvarD2DefaultPrinterCanvasClass := TD2CanvasGdiPlus;
     {$IFNDEF d2_no_init_gdip}
     InitGDIP;
     {$ENDIF}

   {$ENDIF}

   {$IFDEF ORCA2D_USE_CARBON}
      GvarD2DefaultCanvasClass := TD2CanvasDarwin;
      GvarD2DefaultFilterClass := TD2FilterDarwin;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_COCOA}
      GvarD2DefaultCanvasClass := TD2CanvasDarwin;
      GvarD2DefaultFilterClass := TD2FilterDarwin;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK2}
      GvarD2DefaultCanvasClass := TD2CanvasCairo;
      GvarD2DefaultFilterClass := TD2FilterGtk;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK3}
      GvarD2DefaultCanvasClass := TD2CanvasCairo;
      GvarD2DefaultFilterClass := TD2FilterGtk;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_GTK4}
      GvarD2DefaultCanvasClass := TD2CanvasCairo;
      GvarD2DefaultFilterClass := TD2FilterGtk;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_QT4}
      GvarD2DefaultCanvasClass := TD2CanvasQT4;
      GvarD2DefaultFilterClass := TD2FilterQT4;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_QT5}
      GvarD2DefaultCanvasClass := TD2CanvasQT5;
      GvarD2DefaultFilterClass := TD2FilterQT5;
   {$ENDIF}

   {$IFDEF ORCA2D_USE_QT6}
      GvarD2DefaultCanvasClass := TD2CanvasQT6;
      GvarD2DefaultFilterClass := TD2FilterQT6;
   {$ENDIF}
End;

//============================================================== ct2222
//==============================================================
//==============================================================

Initialization

   {$IFDEF ORCA2D_OS_WINDOWS}
  VarUser32Lib := LoadLibrary(User32);
  if VarUser32Lib <> 0 then
  begin
    @VarSetLayeredWindowAttributes := GetProcAddress(VarUser32Lib, 'SetLayeredWindowAttributes');
    @VarUpdateLayeredWindow := GetProcAddress(VarUser32Lib, 'UpdateLayeredWindow');
    @VarPrintWindow := GetProcAddress(VarUser32Lib, 'PrintWindow');
  end;

  OleInitialize(nil);
   {$ENDIF}

   {$IFDEF ORCA2D_USE_CARBON}
  SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);
   {$ENDIF}

   __Init_RegisterALLD2Objects;
   __Init_D2Filter_D2Canvas;

   //============================================================== ct2222
   //==============================================================
   //==============================================================

Finalization

   {$IFDEF ORCA2D_OS_WINDOWS}
   FreeD2D;
   {$IFNDEF d2_no_free_gdip}
    FreeGDIP;
   {$ENDIF}

  if VarUser32Lib <> 0 then FreeLibrary(VarUser32Lib);
   {$ENDIF}

   If varD2PopupList <> nil Then
      FreeAndNil(varD2PopupList);
   If GvarD2aniThread <> nil Then
      FreeAndNil(GvarD2aniThread);
   If GvarD2DefaultStyles <> nil Then
      FreeAndNil(GvarD2DefaultStyles);
   If GvarD2ObjectList <> nil Then
      FreeAndNil(GvarD2ObjectList);
   If varD2ResourceList <> nil Then
      FreeAndNil(varD2ResourceList);
   If varD2CollectLang <> nil Then
      FreeAndNil(varD2CollectLang);
   If varD2Lang <> nil Then
      FreeAndNil(varD2Lang);
   If varD2SceneList <> nil Then
      FreeAndNil(varD2SceneList);
End.
