
{******************************************}
{                                          }
{             FastReport v4.0              }
{            Designer controls             }
{                                          }
{         Copyright (c) 1998-2008          }
{         by Alexander Tzyganenko,         }
{            Fast Reports Inc.             }
{                                          }
{******************************************}

unit frxDesgnCtrls;

interface

{$I frx.inc}

uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ExtCtrls, ComCtrls, ToolWin, ImgList, frxClass,
  frxPictureCache
{$IFDEF Delphi6}
, Variants
{$ENDIF};


type
  TfrxRulerUnits = (ruCM, ruInches, ruPixels, ruChars);

  TfrxRuler = class(TPanel)
  private
    FOffset: Integer;
    FScale: Extended;
    FStart: Integer;
    FUnits: TfrxRulerUnits;
    FPosition: Extended;
    FSize: Integer;
    procedure SetOffset(const Value: Integer);
    procedure SetScale(const Value: Extended);
    procedure SetStart(const Value: Integer);
    procedure SetUnits(const Value: TfrxRulerUnits);
    procedure SetPosition(const Value: Extended);
    procedure WMEraseBackground(var Message: TMessage); message WM_ERASEBKGND;
    procedure SetSize(const Value: Integer);
  public
    constructor Create(AOwner: TComponent); override;
    procedure Paint; override;
  published
    property Offset: Integer read FOffset write SetOffset;
    property Scale: Extended read FScale write SetScale;
    property Start: Integer read FStart write SetStart;
    property Units: TfrxRulerUnits read FUnits write SetUnits default ruPixels;
    property Position: Extended read FPosition write SetPosition;
    property Size: Integer read FSize write SetSize;
  end;

  TfrxScrollBox = class(TScrollBox)
  protected
    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure KeyPress(var Key: Char); override;
  end;

  TfrxCustomSelector = class(TPanel)
  private
    FclWidth: Integer;
    FclHeight: Integer;
    procedure WMEraseBackground(var Message: TMessage); message WM_ERASEBKGND;
  protected
    procedure DrawEdge(X, Y: Integer; IsDown: Boolean); virtual; abstract;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  public
    procedure Paint; override;
    constructor Create(AOwner: TComponent); override;
  end;

  TfrxColorSelector = class(TfrxCustomSelector)
  private
    FColor: TColor;
    FOnColorChanged: TNotifyEvent;
  protected
    procedure DrawEdge(X, Y: Integer; IsDown: Boolean); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure Paint; override;
    property Color: TColor read FColor write FColor;
    property OnColorChanged: TNotifyEvent read FOnColorChanged write FOnColorChanged;
  end;

  TfrxLineSelector = class(TfrxCustomSelector)
  private
    FStyle: Byte;
    FOnStyleChanged: TNotifyEvent;
  protected
    procedure DrawEdge(X, Y: Integer; IsDown: Boolean); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    procedure Paint; override;
    property Style: Byte read FStyle;
    property OnStyleChanged: TNotifyEvent read FOnStyleChanged write FOnStyleChanged;
  end;

  TfrxUndoBuffer = class(TObject)
  private
    FPictureCache: TfrxPictureCache;
    FRedo: TList;
    FUndo: TList;
    function GetRedoCount: Integer;
    function GetUndoCount: Integer;
    procedure SetPictureFlag(ReportComponent: TfrxComponent; Flag: Boolean);
    procedure SetPictures(ReportComponent: TfrxComponent);
  public
    constructor Create;
    destructor Destroy; override;
    procedure AddUndo(ReportComponent: TfrxComponent);
    procedure AddRedo(ReportComponent: TfrxComponent);
    procedure GetUndo(ReportComponent: TfrxComponent);
    procedure GetRedo(ReportComponent: TfrxComponent);
    procedure ClearUndo;
    procedure ClearRedo;
    property UndoCount: Integer read GetUndoCount;
    property RedoCount: Integer read GetRedoCount;
    property PictureCache: TfrxPictureCache read FPictureCache write FPictureCache;
  end;

  TfrxClipboard = class(TObject)
  private
    FDesigner: TfrxCustomDesigner;
    FPictureCache: TfrxPictureCache;
    function GetPasteAvailable: Boolean;
  public
    constructor Create(ADesigner: TfrxCustomDesigner);
    procedure Copy;
    procedure Paste;
    property PasteAvailable: Boolean read GetPasteAvailable;
    property PictureCache: TfrxPictureCache read FPictureCache write FPictureCache;
  end;


implementation

uses
  frxDMPClass, frxPopupForm, frxDsgnIntf, frxCtrls, frxXMLSerializer, Clipbrd,
  frxUtils, frxXML;

const
  Colors: array[0..47] of TColor =
    (clNone, clWhite, clBlack, clMaroon, clGreen, clOlive, clNavy, clPurple,
     clGray, clSilver, clTeal, clRed, clLime, clYellow, clBlue, clFuchsia,
     $CCCCCC, $E4E4E4, clAqua, $00CCFF, $00CC98, $98FFFF, $FFCC00, $FF98CC,
     $D8D8D8, $F0F0F0, $FFFFDC, $CAE4FF, $CCFFCC, $CCFFFF, $FFF4CC, $CC98FF,
     clBtnFace, $46DAFF, $9BEBFF, $00A47B, $FDBD97, $FED3BA, $6ACFFF, $FFF4CC,
     clBtnFace, clBtnFace, clBtnFace, clBtnFace, clBtnFace, clBtnFace, clBtnFace, clBtnFace);

type
  THackControl = class(TWinControl);


{ TfrxRuler }

constructor TfrxRuler.Create(AOwner: TComponent);
begin
  inherited;
  FScale := 1;
  DoubleBuffered := True;
end;

procedure TfrxRuler.WMEraseBackground(var Message: TMessage);
begin
//
end;

procedure TfrxRuler.Paint;
var
  fh, oldfh: HFont;
  sz: Integer;

  function CreateRotatedFont(Font: TFont): HFont;
  var
    F: TLogFont;
  begin
    GetObject(Font.Handle, SizeOf(TLogFont), @F);
    F.lfEscapement := 90 * 10;
    F.lfOrientation := 90 * 10;
    Result := CreateFontIndirect(F);
  end;

  procedure Line(x, y, dx, dy: Integer);
  begin
    Canvas.MoveTo(x, y);
    Canvas.LineTo(x + dx, y + dy);
  end;

  procedure DrawLines;
  var
    i, dx, maxi: Extended;
    i1, h, w, w5, w10, maxw, ofs: Integer;
    s: String;
  begin
    with Canvas do
    begin
      Pen.Color := clBlack;
      Brush.Style := bsClear;
      w5 := 5;
      w10 := 10;
      if FUnits = ruCM then
        dx := fr01cm * FScale
      else if FUnits = ruInches then
        dx := fr01in * FScale
      else if FUnits = ruChars then
      begin
        if Align = alLeft then
          dx := fr1CharY * FScale / 10 else
          dx := fr1CharX * FScale / 10
      end
      else
      begin
        dx := FScale;
        w5 := 50;
        w10 := 100;
      end;

      if FSize = 0 then
      begin
        if Align = alLeft then
          maxi := Height + FStart else
          maxi := Width + FStart;
      end
      else
        maxi := FSize;

      if FUnits = ruPixels then
        s := IntToStr(FStart + Round(maxi / dx)) else
        s := IntToStr((FStart + Round(maxi / dx)) div 10);

      maxw := TextWidth(s);
      ofs := FOffset - FStart;
      if FUnits = ruChars then
      begin
        if Align = alLeft then
          Inc(ofs, Round(fr1CharY * FScale / 2)) else
          Inc(ofs, Round(fr1CharX * FScale / 2))
      end;

      i := 0;
      i1 := 0;
      while i < maxi do
      begin
        h := 0;
        if i1 = 0 then
          h := 0
        else if i1 mod w10 = 0 then
          h := 6
        else if i1 mod w5 = 0 then
          h := 4
        else if FUnits <> ruPixels then
          h := 2;

        if (h = 2) and (dx * w10 < 41) then
          h := 0;
        if (h = 4) and (dx * w10 < 21) then
          h := 0;

        w := 0;
        if h = 6 then
        begin
          if maxw > dx * w10 * 1.5 then
            w := w10 * 4
          else if maxw > dx * w10 * 0.7 then
            w := w10 * 2
          else
            w := w10;
        end;

        if FUnits = ruPixels then
          s := IntToStr(i1) else
          s := IntToStr(i1 div 10);

        if (w <> 0) and (i1 mod w = 0) and (ofs + i >= FOffset) then
          if Align = alLeft then
            TextOut(Width - TextHeight(s) - 6, ofs + Round(i) + TextWidth(s) div 2 + 1, s) else
            TextOut(ofs + Round(i) - TextWidth(s) div 2 + 1, 4, s)
        else if (h <> 0) and (ofs + i >= FOffset) then
          if Align = alLeft then
            Line(3 + (13 - h) div 2, ofs + Round(i), h, 0) else
            Line(ofs + Round(i), 3 + (13 - h) div 2, 0, h);

        i := i + dx;
        Inc(i1);
      end;

      i := FPosition * dx;
      if FUnits <> ruPixels then
        i := i * 10;
      if ofs + i >= FOffset then
        if Align = alLeft then
          Line(3, ofs + Round(i), 13, 0) else
          Line(ofs + Round(i), 3, 0, 13);
    end;
  end;

begin
  fh := 0; oldfh := 0;
  with Canvas do
  begin
    Brush.Color := clBtnFace;
    Brush.Style := bsSolid;
    FillRect(Rect(0, 0, Width, Height));
    Brush.Color := clWindow;

    Font.Name := 'Arial';
    Font.Size := 7;
    if Align = alLeft then
    begin
      if FSize = 0 then
        sz := Height
      else
        sz := FSize + FOffset;
      FillRect(Rect(3, FOffset, Width - 5, sz));
      fh := CreateRotatedFont(Font);
      oldfh := SelectObject(Handle, fh);
    end
    else
    begin
      if FSize = 0 then
        sz := Width
      else
        sz := FSize + FOffset;
      FillRect(Rect(FOffset, 3, sz, Height - 5));
    end;
  end;

  DrawLines;

  if Align = alLeft then
  begin
    SelectObject(Canvas.Handle, oldfh);
    DeleteObject(fh);
  end;
end;

procedure TfrxRuler.SetOffset(const Value: Integer);
begin
  FOffset := Value;
  Invalidate;
end;

procedure TfrxRuler.SetPosition(const Value: Extended);
begin
  FPosition := Value;
  Invalidate;
end;

procedure TfrxRuler.SetScale(const Value: Extended);
begin
  FScale := Value;
  Invalidate;
end;

procedure TfrxRuler.SetStart(const Value: Integer);
begin
  FStart := Value;
  Invalidate;
end;

procedure TfrxRuler.SetUnits(const Value: TfrxRulerUnits);
begin
  FUnits := Value;
  Invalidate;
end;

procedure TfrxRuler.SetSize(const Value: Integer);
begin
  FSize := Value;
  Invalidate;
end;


{ TfrxScrollBox }

procedure TfrxScrollBox.KeyDown(var Key: Word; Shift: TShiftState);
var
  i: Integer;
begin
  inherited;
  for i := 0 to ControlCount - 1 do
    if Controls[i] is TWinControl then
      THackControl(Controls[i]).KeyDown(Key, Shift);
end;

procedure TfrxScrollBox.KeyPress(var Key: Char);
var
  i: Integer;
begin
  inherited;
  for i := 0 to ControlCount - 1 do
    if Controls[i] is TWinControl then
      THackControl(Controls[i]).KeyPress(Key);
end;

procedure TfrxScrollBox.KeyUp(var Key: Word; Shift: TShiftState);
var
  i: Integer;
begin
  inherited;
  for i := 0 to ControlCount - 1 do
    if Controls[i] is TWinControl then
      THackControl(Controls[i]).KeyUp(Key, Shift);
end;

procedure TfrxScrollBox.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  Message.Result := DLGC_WANTARROWS or DLGC_WANTTAB;
end;


{ TfrxCustomSelector }

constructor TfrxCustomSelector.Create(AOwner: TComponent);
var
  f: TfrxPopupForm;
  p: TPoint;
begin
  f := TfrxPopupForm.Create(nil);

  inherited Create(f);
  Width :=   FclWidth;
  Height :=   FclHeight;
  Parent := f;
  DoubleBuffered := True;
  f.AutoSize := True;
  Tag := AOwner.Tag;

  with TControl(AOwner) do
    p := ClientToScreen(Point(0, Height + 2));
  f.SetBounds(p.X, p.Y, 20, 20);
  f.Show;
end;

procedure TfrxCustomSelector.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  DrawEdge(X, Y, True);
end;

procedure TfrxCustomSelector.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  DrawEdge(X, Y, False);
end;

procedure TfrxCustomSelector.Paint;
begin
  with Canvas do
  begin
    Pen.Color := clBtnShadow;
    Brush.Color := clWindow;
    Rectangle(0, 0, ClientWidth, ClientHeight);
  end;
end;

procedure TfrxCustomSelector.WMEraseBackground(var Message: TMessage);
begin
//

end;


{ TfrxColorSelector }

constructor TfrxColorSelector.Create(AOwner: TComponent);
begin
  FclWidth := 155;
  FclHeight := 143;
  inherited Create(AOwner);
end;

procedure TfrxColorSelector.DrawEdge(X, Y: Integer; IsDown: Boolean);
var
  r: TRect;
begin
  X := (X - 5) div 18;
  if X >= 8 then
    X := 7;
  Y := (Y - 5) div 18;

  Repaint;
  if Y < 6 then
    r := Rect(X * 18 + 5, Y * 18 + 5, X * 18 + 23, Y * 18 + 23) else
    r := Rect(5, 113, Width - 6, Height - 6);

  with Canvas do
  begin
    Brush.Style := bsClear;
    Pen.Color := $C56A31;
    Rectangle(r.Left, r.Top, r.Right, r.Bottom);
    InflateRect(r, -1, -1);
    Pen.Color := $E8E6E2;
    Rectangle(r.Left, r.Top, r.Right, r.Bottom);
    InflateRect(r, -1, -1);
    Rectangle(r.Left, r.Top, r.Right, r.Bottom);
  end;
end;

procedure TfrxColorSelector.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
var
  cd: TColorDialog;

  procedure AddCustomColor;
  var
    i: Integer;
    Found: Boolean;
    Empty: Integer;
  begin
    Found := False;
    Empty := 0;
    for i := 0 to 47 do
    begin
      if Colors[i] = FColor then
        Found := True;
      if (i > 37) and (Colors[i] = clBtnFace) and (Empty = 0) then
        Empty := i;
    end;

    if Found then exit;

    if Empty = 0 then
    begin
      for i := 40 to 46 do
        Colors[i] := Colors[i + 1];
      Empty := 47;
    end;
    Colors[Empty] := FColor
  end;

begin
  X := (X - 5) div 18;
  if X >= 8 then
    X := 7;
  Y := (Y - 5) div 18;

  if Y < 6 then
    FColor := Colors[X + Y * 8]
  else
  begin
    TForm(Parent).AutoSize := False;
    Parent.Height := 0;
    cd := TColorDialog.Create(Self);
    cd.Options := [cdFullOpen];
    cd.Color := FColor;
    if cd.Execute then
      FColor := cd.Color else
      Exit;

    AddCustomColor;
  end;

  Repaint;
  if Assigned(FOnColorChanged) then
    FOnColorChanged(Self);
  Parent.Hide;
end;

procedure TfrxColorSelector.Paint;
var
  i, j: Integer;
  s: String;
begin
  inherited;

  with Canvas do
  begin
    for j := 0 to 5 do
      for i := 0 to 7 do
      begin
        if (i = 0) and (j = 0) then
          Brush.Color := clWhite else
          Brush.Color := Colors[i + j * 8];
        Pen.Color := clGray;
        Rectangle(i * 18 + 8, j * 18 + 8, i * 18 + 20, j * 18 + 20);
        if (i = 0) and (j = 0) then
        begin
          MoveTo(i * 18 + 10, j * 18 + 10);
          LineTo(i * 18 + 18, j * 18 + 18);
          MoveTo(i * 18 + 17, j * 18 + 10);
          LineTo(i * 18 + 9, j * 18 + 18);
        end;
      end;

    Pen.Color := clGray;
    Brush.Color := clBtnFace;
    Rectangle(8, 116, Width - 9, Height - 9);
    s := 'Other...';
    Font := Self.Font;
    TextOut((Width - TextWidth(s)) div 2, 118, s);
  end;
end;


{ TfrxLineSelector }

constructor TfrxLineSelector.Create(AOwner: TComponent);
begin
  FclWidth := 98;
  FclHeight := 106;
  inherited;
end;

procedure TfrxLineSelector.DrawEdge(X, Y: Integer; IsDown: Boolean);
var
  r: TRect;
begin
  Y := (Y - 5) div 16;
  if Y > 5 then
    Y := 5;

  Repaint;

  r := Rect(5, Y * 16 + 5, Width - 5, Y * 16 + 21);
  if IsDown then
    Frame3D(Canvas, r, clBtnShadow, clBtnShadow, 2) else
    Frame3D(Canvas, r, clBtnShadow, clBtnShadow, 1);
end;

procedure TfrxLineSelector.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  Y := (Y - 5) div 16;
  if Y > 5 then
    Y := 5;

  FStyle := Y;

  Repaint;
  if Assigned(FOnStyleChanged) then
    FOnStyleChanged(Self);
  Parent.Hide;
end;

procedure TfrxLineSelector.Paint;
var
  i: Integer;

  procedure DrawLine(Y, Style: Integer);
  begin
    if Style = 5 then
    begin
      Style := 0;
      DrawLine(Y - 2, Style);
      Inc(Y, 2);
    end;

    with Canvas do
    begin
      Pen.Color := clBlack;
      Pen.Style := TPenStyle(Style);
      MoveTo(7, Y);
      LineTo(Width - 8, Y);
      MoveTo(7, Y + 1);
      LineTo(Width - 8, Y + 1);
    end;
  end;

begin
  inherited;

  for i := 0 to 5 do
    DrawLine(12 + i * 16, i);
end;


{ TfrxUndoBuffer }

constructor TfrxUndoBuffer.Create;
begin
  FRedo := TList.Create;
  FUndo := TList.Create;
end;

destructor TfrxUndoBuffer.Destroy;
begin
  ClearUndo;
  ClearRedo;
  FUndo.Free;
  FRedo.Free;
  inherited;
end;

procedure TfrxUndoBuffer.AddUndo(ReportComponent: TfrxComponent);
var
  m: TMemoryStream;
begin
  m := TMemoryStream.Create;
  FUndo.Add(m);
  SetPictureFlag(ReportComponent, False);
  try
    ReportComponent.SaveToStream(m);
  finally
    SetPictureFlag(ReportComponent, True);
  end;
end;

procedure TfrxUndoBuffer.AddRedo(ReportComponent: TfrxComponent);
var
  m: TMemoryStream;
begin
  m := TMemoryStream.Create;
  FRedo.Add(m);
  SetPictureFlag(ReportComponent, False);
  try
    ReportComponent.SaveToStream(m);
  finally
    SetPictureFlag(ReportComponent, True);
  end;
end;

procedure TfrxUndoBuffer.GetUndo(ReportComponent: TfrxComponent);
var
  m: TMemoryStream;
  IsReport: Boolean;
begin
  IsReport := False;
  if ReportComponent is TfrxReport then
    isReport := True;
  m := FUndo[FUndo.Count - 2];
  m.Position := 0;
  if IsReport then
    TfrxReport(ReportComponent).Reloading := True;
  try
    ReportComponent.LoadFromStream(m);
  finally
  if IsReport then
    TfrxReport(ReportComponent).Reloading := False;
  end;
  SetPictures(ReportComponent);

  m := FUndo[FUndo.Count - 1];
  m.Free;
  FUndo.Delete(FUndo.Count - 1);
end;

procedure TfrxUndoBuffer.GetRedo(ReportComponent: TfrxComponent);
var
  m: TMemoryStream;
  IsReport: Boolean;
begin
  IsReport := False;
  if ReportComponent is TfrxReport then
    isReport := True;
  m := FRedo[FRedo.Count - 1];
  m.Position := 0;
  if IsReport then
    TfrxReport(ReportComponent).Reloading := True;
  try
    ReportComponent.LoadFromStream(m);
  finally
    if IsReport then
      TfrxReport(ReportComponent).Reloading := False;
  end;
  SetPictures(ReportComponent);

  m.Free;
  FRedo.Delete(FRedo.Count - 1);
end;

procedure TfrxUndoBuffer.ClearUndo;
begin
  while FUndo.Count > 0 do
  begin
    TMemoryStream(FUndo[0]).Free;
    FUndo.Delete(0);
  end;
end;

procedure TfrxUndoBuffer.ClearRedo;
begin
  while FRedo.Count > 0 do
  begin
    TMemoryStream(FRedo[0]).Free;
    FRedo.Delete(0);
  end;
end;

function TfrxUndoBuffer.GetRedoCount: Integer;
begin
  Result := FRedo.Count;
end;

function TfrxUndoBuffer.GetUndoCount: Integer;
begin
  Result := FUndo.Count;
end;

procedure TfrxUndoBuffer.SetPictureFlag(ReportComponent: TfrxComponent; Flag: Boolean);
var
  i: Integer;
  l: TList;
  c: TfrxComponent;
begin
  l := ReportComponent.AllObjects;
  for i := 0 to l.Count - 1 do
  begin
    c := l[i];
    if c is TfrxPictureView then
    begin
      TfrxPictureView(c).IsPictureStored := Flag;
      TfrxPictureView(c).IsImageIndexStored := not Flag;
    end;
  end;
end;

procedure TfrxUndoBuffer.SetPictures(ReportComponent: TfrxComponent);
var
  i: Integer;
  l: TList;
  c: TfrxComponent;
begin
  l := ReportComponent.AllObjects;
  for i := 0 to l.Count - 1 do
  begin
    c := l[i];
    if c is TfrxPictureView then
      FPictureCache.GetPicture(TfrxPictureView(c));
  end;
end;


{ TfrxClipboard }

constructor TfrxClipboard.Create(ADesigner: TfrxCustomDesigner);
begin
  FDesigner := ADesigner;
end;

procedure TfrxClipboard.Copy;
var
  c, c1: TfrxComponent;
  i, j: Integer;
  text: String;
  minX, minY: Extended;
  List: TList;
  Flag: Boolean;

  procedure Write(c: TfrxComponent);
  var
    c1: TfrxComponent;
    Writer: TfrxXMLSerializer;
  begin
    c1 := TfrxComponent(c.NewInstance);
    c1.Create(FDesigner.Page);

    if c is TfrxPictureView then
    begin
      TfrxPictureView(c).IsPictureStored := False;
      TfrxPictureView(c).IsImageIndexStored := True;
    end;

    try
      c1.Assign(c);
    finally
      if c is TfrxPictureView then
      begin
        TfrxPictureView(c).IsPictureStored := True;
        TfrxPictureView(c).IsImageIndexStored := False;
        TfrxPictureView(c1).IsImageIndexStored := True;
      end;
    end;

    c1.Left := c1.Left - minX;
    c1.Top := c.AbsTop - minY;

    Writer := TfrxXMLSerializer.Create(nil);
    Writer.Owner := c1.Report;
    text := text + '<' + c1.ClassName + ' Name="' + c.Name + '"' + Writer.ObjToXML(c1) + '/>';
    Writer.Free;

    c1.Free;
  end;

begin
  text := '#FR3 clipboard#' + #10#13;

  minX := 100000;
  minY := 100000;
  for i := 0 to FDesigner.SelectedObjects.Count - 1 do
  begin
    c := FDesigner.SelectedObjects[i];
    if c.AbsLeft < minX then
      minX := c.AbsLeft;
    if c.AbsTop < minY then
      minY := c.AbsTop;
  end;

  List := FDesigner.Page.AllObjects;
  for i := 0 to List.Count - 1 do
  begin
    c := List[i];
    if FDesigner.SelectedObjects.IndexOf(c) <> -1 then
    begin
      Write(c);
      if c is TfrxBand then
      begin
        Flag := False;
        for j := 0 to c.Objects.Count - 1 do
        begin
          c1 := c.Objects[j];
          if FDesigner.SelectedObjects.IndexOf(c1) <> -1 then
            Flag := True;
        end;

        if not Flag then
          for j := 0 to c.Objects.Count - 1 do
            Write(c.Objects[j]);
      end;
    end;
  end;

  Clipboard.AsText := text;
end;

function TfrxClipboard.GetPasteAvailable: Boolean;
begin
  try
    Result := Clipboard.HasFormat(CF_TEXT) and
      (Pos('#FR3 clipboard#', Clipboard.AsText) = 1);
  except
    Result := False;
  end;
end;

procedure TfrxClipboard.Paste;
var
  c: TfrxComponent;
  sl: TStrings;
  s: TStream;
  List: TList;
  NewCompName: string;
  NewComp: TfrxComponent;

  function ReadComponent_(AReader: TfrxXMLSerializer; Root: TfrxComponent): TfrxComponent;
  var
    rd: TfrxXMLReader;
    RootItem: TfrxXMLItem;
  begin
    rd := TfrxXMLReader.Create(AReader.Stream);
    RootItem := TfrxXMLItem.Create;

    try
      rd.ReadRootItem(RootItem, False);
      Result := AReader.ReadComponentStr(Root, RootItem.Name + ' ' + RootItem.Text);

      NewCompName := RootItem.Prop['Name'];
    finally
      rd.Free;
      RootItem.Free;
    end;
  end;

  function ReadComponent: TfrxComponent;
  var
    Reader: TfrxXMLSerializer;
  begin
    Reader := TfrxXMLSerializer.Create(s);
    Result := ReadComponent_(Reader, FDesigner.Report);
    Reader.Free;
  end;

  function FindBand(Band: TfrxComponent): Boolean;
  var
    i: Integer;
  begin
    Result := False;
    for i := 0 to FDesigner.Page.Objects.Count - 1 do
      if (FDesigner.Page.Objects[i] <> Band) and
        (TObject(FDesigner.Page.Objects[i]) is Band.ClassType) then
        Result := True;
  end;

  function CanInsert(c: TfrxComponent): Boolean;
  begin
    Result := True;
    if (c is TfrxDialogControl) and (FDesigner.Page is TfrxReportPage) then
      Result := False;
    if not (c is TfrxDialogComponent) and not (c is TfrxDialogControl) and
      (FDesigner.Page is TfrxDialogPage) then
      Result := False;
    if ((c is TfrxDMPMemoView) or (c is TfrxDMPLineView) or (c is TfrxDMPCommand)) and
      not (FDesigner.Page is TfrxDMPPage) then
      Result := False;
    if not ((c is TfrxBand) or (c is TfrxDMPMemoView) or (c is TfrxDMPLineView) or
      (c is TfrxDMPCommand)) and (FDesigner.Page is TfrxDMPPage) then
      Result := False;
    if not ((c is TfrxCustomLineView) or (c is TfrxCustomMemoView) or
      (c is TfrxShapeView) or (c is TfrxDialogComponent)) and
      (FDesigner.Page is TfrxDataPage) then
      Result := False;
  end;

  procedure FindParent(c: TfrxComponent);
  var
    i: Integer;
    Found: Boolean;
    c1: TfrxComponent;
  begin
    Found := False;
    if not (c is TfrxBand) then
      for i := List.Count - 1 downto 0 do
      begin
        c1 := List[i];
        if c1 is TfrxBand then
          if (c.Top >= c1.Top) and (c.Top < c1.Top + c1.Height) then
          begin
            c.Parent := c1;
            c.Top := c.Top - c1.Top;
            Found := True;
            break;
          end;
      end;
    if not Found then
      c.Parent := FDesigner.Page;
  end;

begin
  FDesigner.SelectedObjects.Clear;

  sl := TStringList.Create;
  sl.Text := Clipboard.AsText;
  sl.Delete(0);

  s := TMemoryStream.Create;
  sl.SaveToStream(s{$IFDEF Delphi12} ,TEncoding.UTF8 {$ENDIF});
  sl.Free;
  s.Position := 0;

  List := TList.Create;

  while s.Position < s.Size do
  begin
    c := ReadComponent;
    if c = nil then break;

    if (((c is TfrxReportTitle) or (c is TfrxReportSummary) or
       (c is TfrxPageHeader) or (c is TfrxPageFooter) or
       (c is TfrxColumnHeader) or (c is TfrxColumnFooter)) and FindBand(c)) or
       not CanInsert(c) then
      c.Free
    else
    begin
      if c is TfrxPictureView then
        FPictureCache.GetPicture(TfrxPictureView(c));
      List.Add(c);
      FindParent(c);
      if FDesigner.IsPreviewDesigner then
        NewComp := FDesigner.Report.FindObject(NewCompName) as TfrxComponent
      else
        NewComp := FDesigner.Report.FindComponent(NewCompName) as TfrxComponent;
      if ((NewComp <> nil) and (NewComp <> c)) or (NewCompName = '') then
        c.CreateUniqueName
      else
        c.Name := NewCompName;
      c.GroupIndex := 0;
      FDesigner.Objects.Add(c);
      if c.Parent = FDesigner.Page then
        FDesigner.SelectedObjects.Add(c);
      c.OnPaste;
    end;
  end;

  if FDesigner.SelectedObjects.Count = 0 then
    FDesigner.SelectedObjects.Add(FDesigner.Page);

  List.Free;
  s.Free;
end;


end.





