unit synMemo;

interface

uses
  Messages, Windows, SysUtils, Classes, controls, Graphics, forms,
  stdctrls, extctrls, Mask, Buttons, ComCtrls, db, dbctrls;

type
  eSynMemoError = class(exception);

  tSynMemoScrollEnum = (smAutoScroll, smVertical, smHoriz, smBoth, smNoScroll);
  tSynMargins = record left, right : word; end;

  TSynCustomMemo = class(TCustomMemo)
  private
    fScrolling : tSynMemoScrollEnum;
    fUseFontMargin : boolean;
    oldwinproc : pointer;
    newWinProc : pointer;
    fSubClassDone : boolean;
    procedure SubClassParentProc(var Msg : tmessage);

    function getScrollOption : tsynMemoScrollEnum;
    procedure setScrollOption(aOption : tSynMemoScrollEnum);
    function getSize: integer;
    function getLinesShowing : integer;
    procedure setLinesShowing(numLines : integer);
    function getTopLine: integer;
    procedure setTopLine(aline : integer);
    function getRow: integer;
    procedure setRow(aRow : integer);
    function getCol: integer;
    procedure setCol(aCol : integer);
    function getLineHeight : integer;
    function getLineWidth(aline: integer): integer;
    function getRowCol : tpoint;
    procedure setRowCol(at : tpoint);
    function getLeftMargin: word;
    procedure setLeftMargin(amargin : word);
    function getrightMargin: word;
    procedure setrightMargin(amargin : word);
    Procedure UseFontMargin(abool : boolean);
    procedure WMERASEBKGND(var Message: TMessage); message WM_ERASEBKGND;
    procedure WMSIZE(var message: tmessage); message WM_SIZE;
    procedure TurnOnSubClassing;
    Procedure TurnOffSubClassing;
  protected
    procedure CheckScrolling; virtual;
    property ScrollOption : tsynMemoScrollEnum read getSCrollOption write setScrollOption default smAutoScroll;
    property LinesShowing : integer read getLinesShowing write setLinesShowing default 3;
    property MarginLeft : word read getLeftMargin write setLeftMargin;
    Property MarginRight : word read getRightMargin write setRightMargin;
    property MarginFont : boolean read fUseFontMargin write UseFontMargin;
  public
    constructor create(aOwner : tcomponent); override;
    destructor destroy; override;
    procedure SetToTop(focusit: boolean);
    function LongestLine : integer;
    property Row : integer read getRow write setRow;
    property Col : integer read GetCol write setCol;
    property RowCol : tpoint read getRowCol write setRowCol;
    property LineHeight : integer read GetLineHeight;
    property LineWidth[aline : integer] : integer read GetLineWidth;
    property TopLine : integer read getTopLine write setTopline;
    Property NumBytes : integer read GetSize;
  published
  end;

TsynMemo = class(tSynCustomMemo)
  published
    property ScrollOption;
    property LinesShowing;
    property MarginLeft;
    Property MarginRight;
    property MarginFont;
    property Align;
    property Alignment;
    property BorderStyle;
    property Color;
    property Ctl3D;
    property DragCursor;
    property DragMode;
    property Enabled;
    property Font;
    property HideSelection;
    property Lines;
    property MaxLength;
    property OEMConvert;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly;
//    property ScrollBars;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;
    property WantReturns;
    property WantTabs;
    property WordWrap;
    property OnChange;
    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
  end;

  TSynDBMemo = class(TsynCustomMemo)
  private
    FDataLink: TFieldDataLink;
    FAutoDisplay: Boolean;
    FFocused: Boolean;
    FMemoLoaded: Boolean;
    FPaintControl: TPaintControl;
    procedure DataChange(Sender: TObject);
    procedure EditingChange(Sender: TObject);
    function GetDataField: string;
    function GetDataSource: TDataSource;
    function GetField: TField;
    function GetReadOnly: Boolean;
    procedure SetDataField(const Value: string);
    procedure SetDataSource(Value: TDataSource);
    procedure SetReadOnly(Value: Boolean);
    procedure SetAutoDisplay(Value: Boolean);
    procedure SetFocused(Value: Boolean);
    procedure UpdateData(Sender: TObject);
    procedure WMCut(var Message: TMessage); message WM_CUT;
    procedure WMPaste(var Message: TMessage); message WM_PASTE;
    procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
    procedure CMExit(var Message: TCMExit); message CM_EXIT;
    procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    procedure CMGetDataLink(var Message: TMessage); message CM_GETDATALINK;
  protected
    procedure CheckScrolling; override;
    procedure Change; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyPress(var Key: Char); override;
    procedure Loaded; override;
    procedure Notification(AComponent: TComponent;
      Operation: TOperation); override;
    procedure WndProc(var Message: TMessage); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure LoadMemo;
    property Field: TField read GetField;
  published
    property ScrollOption;
    property LinesShowing;
    property MarginLeft;
    Property MarginRight;
    property MarginFont;
    property Align;
    property Alignment;
    property AutoDisplay: Boolean read FAutoDisplay write SetAutoDisplay default True;
    property BorderStyle;
    property Color;
    property Ctl3D;
    property DataField: string read GetDataField write SetDataField;
    property DataSource: TDataSource read GetDataSource write SetDataSource;
    property DragCursor;
    property DragMode;
    property Enabled;
    property Font;
    property ImeMode;
    property ImeName;
    property MaxLength;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly: Boolean read GetReadOnly write SetReadOnly default False;
//    property ScrollBars;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;
    property WantTabs;
    property WordWrap;
    property OnChange;
    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDrag;
  end;

Procedure Register;

implementation
resourcestring
  rsMemoSubClassFail = 'Failed to subclass synMemo Parent, no AutoScroll available.';
const
  cCopyright = 'Synature, copyright 1997: TSynMemo by Brandon C. Smith';

{============= support functions ============}
function maxOf(const first, second : integer):integer;
begin
  if first > second
    then result := first
    else result := second;
end;


{============ the memo itself ==========}

constructor TsynCustomMemo.create(aOwner : tcomponent);
begin
  inherited create(aOwner);
  fScrolling := smAutoScroll;
  fsubClassDone := false;
end;

Destructor TsynCustomMemo.destroy;
begin
  FreeObjectInstance(NewWinProc);
  inherited destroy;
end;

procedure TsynCustomMemo.WMERASEBKGND(var Message: TMessage);
begin
  inherited;
  if not fSubclassDone
    then TurnOnSubclassing;
end;

procedure TsynCustomMemo.wmsize(var message: tMessage);
begin
  inherited;
  if showing then checkScrolling;
end;

Procedure TsynCustomMemo.TurnOnSubclassing;
begin
  if ( not (csDesigning in ComponentState))
     and (not (csLoading in ComponentState))
     and HandleAllocated
     and (not fsubclassDone)
       then begin
         if NewWinProc <> nil
           then FreeObjectInstance(newWinProc);
         NewWinProc := MakeObjectInstance(SubClassParentProc);
         OldWinProc := Pointer(setWindowLong(parent.handle, GWL_WNDPROC, longint(NewWinProc)));
         if OldWinProc = nil
           then raise eSynMemoError.create(rsMemoSubClassFail);
         fSubClassDone := true;
         end;
end;

Procedure TsynCustomMemo.TurnOffSubclassing;
begin
  SetWindowLong(parent.handle, GWL_WNDPROC, longint(OldWinProc));
  fSubclassDone := false;
end;

function TsynCustomMemo.getScrollOption : tsynMemoScrollEnum;
begin result := fScrolling end;

procedure TsynCustomMemo.setScrollOption(aOption : tSynMemoScrollEnum);
begin
  fSCrolling := aOption;
  CheckScrolling;
end;

Procedure TsynCustomMemo.CheckScrolling;
var here : tpoint;
    tmpScroll : tSCrollStyle;
  function Translate(customScrollEnum : tSynMemoScrollEnum ): tscrollstyle;
  begin
    result := ssNone;
    case CustomScrollEnum of
      smAutoScroll : result := ssNone;
      smVertical   : result := ssVertical;
      smHoriz      : result := ssHorizontal;
      smBoth       : result := ssBoth;
      smNoScroll   : result := ssNone;
      end;
    end;

begin
  tmpScroll := translate(fScrolling);
  here := GetRowCol;
  if fScrolling = smAutoScroll
    then begin
      if not fSubClassDone
        then TurnOnSubClassing;
      if lines.count*lineheight > clientheight
           then tmpScroll := ssVertical
           else tmpScroll := ssNone;
       if WordWrap = false
         then if (longestline > clientwidth) and (getLinesShowing > 1)
             then begin
                if TmpScroll = ssVertical
                   then TmpScroll := ssBoth
                   else TmpScroll := ssHorizontal;
                end
             else begin
                if TmpScroll = ssBoth
                   then TmpScroll := ssVertical
                   else if lines.count*lineheight > clientheight
                     then TmpScroll := ssVertical
                     else TmpScroll := ssNone;
                end;
      end
    else TurnOffSubClassing;
  case fSCrolling of
    smAutoSCroll : if scrollbars <> TmpScroll then scrollbars := tmpScroll;
    smVertical : if scrollbars <> ssVertical then scrollbars := ssVertical;
    smHoriz : if scrollbars <> ssHorizontal then scrollbars := ssHorizontal;
    smBoth  : if scrollbars <> ssBoth then scrollbars := ssboth;
    smNoScroll : if scrollbars <> ssNone then scrollbars := ssnone;
    end;
  SetRowCol(here);
  invalidate;
end;

procedure TsynCustomMemo.SubClassParentProc(var Msg : tmessage);
begin
  with msg do begin
    Result := CallWindowProc(OldWinProc, Parent.handle, Msg, wParam, lParam);
    if (msg = WM_COMMAND) and (lparam = handle)
      then if WParamHi = en_change then CheckSCrolling;
    if msg = WM_DESTROY
      then   SetWindowLong(parent.handle, GWL_WNDPROC, longint(OldWinProc));
    end;
end;

(*procedure TsynCustomMemo.CMRecreateWnd(var Message: TMessage);
var oldhandle : hwnd;
begin
  oldHandle := handle;
  inherited;
  if oldHandle <> handle
    then begin
      lines.add('old = '+intTostr(oldhandle));
      lines.add('new = '+IntToStr(handle));
      end
    else begin
      lines.add('Apparently still the same');
      end;
end;
*)

function TsynCustomMemo.getRow: integer;
begin  result := Perform(EM_LineFromChar, $FFFF, 0); end;

procedure TsynCustomMemo.setRow(aRow : integer);
begin  selstart := perform(EM_LineIndex, arow, 0); end;

function TsynCustomMemo.getCol: integer;
begin  result := SelStart - perform(EM_lineIndex, $FFFF, 0); end;

procedure TsynCustomMemo.setCol(aCol : integer);
var fCol, frow : integer;
begin
  frow := getRow;
  fCol := perform(EM_lineLength, perform(EM_lineIndex, fRow, 0), 0);
  if fCol > acol
    then fCol := aCol;
  selStart := perform(em_lineIndex, fRow, 0)+fCol;
end;

function TsynCustomMemo.getRowCol : tpoint;
begin
  result.x := GetRow;
  result.y := getCol;
end;

procedure TsynCustomMemo.setRowCol(at : tpoint);
begin
  autoselect := false;
  SetRow(at.x);
  SetCol(at.y);
  perform(EM_SCROLLCARET, 0, 0);
//  setfocus;
end;

Procedure TsynCustomMemo.SetToTop(focusit : boolean);
var here : tpoint;
begin
  here.x := 0; here.y := 0;
  setRowCol(here);
  if focusit
    then setfocus;
end;

function TsynCustomMemo.getLineHeight : integer;
begin result := abs(font.height) + height - clientheight + 2 end;
(*
Var
  oldfont: HFont;  {the old font}
  dc: THandle;     {a dc handle}
  tm: TTextMetric; {text metric structure}
  textSize : tSize;
const junk : pchar = 'X';
begin
//  result := (height - clientheight);  This gives us a good number sometimes, but not consistently...
  dc := GetDC(handle); {Get the Dc for the memo}
  oldFont := SelectObject(dc, Font.handle); {now make sure we have the memo's font}
    {if I don't do the line above, then the text size is 2 pixels too big...}
  GetTextMetrics(dc, tm); {Get the text metric info}
  GetTextExtentPoint32(dc, junk, 1, textSize); {and get the height in this font}
  result := textsize.cy + 2*tm.tmExternalLeading;
  SelectObject(dc, oldfont); {Select the old font -- I'm not sure if or why we need this, but Lloyd's file said so...}
  ReleaseDC(handle, dc); {Release the Dc}
end;
*)

function TsynCustomMemo.getLineWidth(aline: integer): integer;
var
  oldfont: HFont;  {the old font}
  dc: THandle;     {a dc handle}
  textSize : tSize;
begin
   dc := GetDC(handle);
  oldFont := SelectObject(dc, Font.handle); {Select the memo's font}
   GetTextExtentPoint32(dc, pchar(lines[aline]), length(lines[aline]), textSize);
   result := textsize.cx;
  SelectObject(dc, oldfont); {Select the old font}
  ReleaseDC(handle, dc); {Release the Dc}
end;

function TsynCustomMemo.getTopLine : integer;
begin result := perform(EM_GETFIRSTVISIBLELINE, 0, 0); end;

procedure TsynCustomMemo.setTopLine(aline : integer);
var numToScroll, i : integer;
begin
  NumToScroll := GetRow;
  for i := 1 to NumToScroll do
    perform(EM_SCROLL,  SB_LINEUP, 0);
  For i := 1 to aline do
    perform(EM_SCROLL,  SB_LINEDOWN, 0)
end;


function TsynCustomMemo.LongestLine: integer;
var i : integer;
begin
  result := 0;
  for i := 0 to lines.count - 1 do
    result := maxof(result, LineWidth[i]);
end;

function TsynCustomMemo.getLinesShowing : integer;
begin
  result := (Height - (height - clientHeight+2)) div lineheight;
end;

procedure TsynCustomMemo.setLinesShowing(numLines : integer);
begin
  height := (maxof(numlines,0))*lineheight + (Height - ClientHeight+2);
end;


function TsynCustomMemo.getLeftMargin: word;
var margins : longint;
begin
  margins := perform(EM_GETMARGINS,0,0);
  result := tsynMargins(Margins).left;
end;

procedure TsynCustomMemo.setLeftMargin(amargin : word);
var margins : tsynMargins;
begin
  Margins.left := amargin;
  perform(EM_SETMARGINS, EC_LEFTMARGIN, MakeLong(margins.left, margins.right));
end;

function TsynCustomMemo.getrightMargin: word;
var margins : tsynMargins;
begin
  longint(Margins) := perform(EM_GETMARGINS,0,0);
  result := Margins.right;
end;

procedure TsynCustomMemo.setrightMargin(amargin : word);
var margins : tsynMargins;
begin
  Margins.right := amargin;
  perform(EM_SETMARGINS, EC_rightMARGIN, MakeLong(margins.right, margins.right));
end;

procedure TsynCustomMemo.UseFontMargin(abool : boolean);
begin
  fUseFontMargin := abool;
  if abool
    then Perform(EM_SETMARGINS, EC_USEFONTINFO, 0);
end;

function TsynCustomMemo.getSize: integer;
begin result := GetWindowTextLength(handle); end;

(*
procedure TsynCustomMemo.FillFromTable(fromField : tfield);
  procedure GetMemo;
  var
    BS: TBlobStream;
  begin
    BS := TBlobStream.Create(TMemoField(fromField), bmRead);
    try
     lines.LoadFromStream(BS);
    finally
      BS.Free;
    end;
  end;
begin
  if fromField is tStringField
    then text := fromField.asString;
  if fromField is tMemoField
    then getMemo;
end;
*)

constructor TsynDBmemo.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  inherited ReadOnly := True;
  ControlStyle := ControlStyle + [csReplicatable];
  FAutoDisplay := True;
  FDataLink := TFieldDataLink.Create;
  FDataLink.Control := Self;
  FDataLink.OnDataChange := DataChange;
  FDataLink.OnEditingChange := EditingChange;
  FDataLink.OnUpdateData := UpdateData;
  FPaintControl := TPaintControl.Create(Self, 'EDIT');
end;

destructor TsynDBmemo.Destroy;
begin
  FPaintControl.Free;
  FDataLink.Free;
  FDataLink := nil;
  inherited Destroy;
end;

procedure TsynDBmemo.Loaded;
begin
  inherited Loaded;
  if (csDesigning in ComponentState) then DataChange(Self);
end;

procedure TsynDBmemo.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (FDataLink <> nil) and
    (AComponent = DataSource) then DataSource := nil;
end;

procedure TsynDBmemo.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited KeyDown(Key, Shift);
  if FMemoLoaded then
  begin
    if (Key = VK_DELETE) or ((Key = VK_INSERT) and (ssShift in Shift)) then
      FDataLink.Edit;
  end;
end;

procedure TsynDBmemo.KeyPress(var Key: Char);
begin
  inherited KeyPress(Key);
  if FMemoLoaded then
  begin
    if (Key in [#32..#255]) and (FDataLink.Field <> nil) and
      not FDataLink.Field.IsValidChar(Key) then
    begin
      MessageBeep(0);
      Key := #0;
    end;
    case Key of
      ^H, ^I, ^J, ^M, ^V, ^X, #32..#255:
        FDataLink.Edit;
      #27:
        FDataLink.Reset;
    end;
  end else
  begin
    if Key = #13 then LoadMemo;
    Key := #0;
  end;
end;

procedure TsynDBmemo.Change;
begin
  if FMemoLoaded then FDataLink.Modified;
  FMemoLoaded := True;
  inherited Change;
end;

function TsynDBmemo.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

procedure TsynDBmemo.SetDataSource(Value: TDataSource);
begin
  FDataLink.DataSource := Value;
  if Value <> nil then Value.FreeNotification(Self);
end;

function TsynDBmemo.GetDataField: string;
begin
  Result := FDataLink.FieldName;
end;

procedure TsynDBmemo.SetDataField(const Value: string);
begin
  FDataLink.FieldName := Value;
end;

function TsynDBmemo.GetReadOnly: Boolean;
begin
  Result := FDataLink.ReadOnly;
end;

procedure TsynDBmemo.SetReadOnly(Value: Boolean);
begin
  FDataLink.ReadOnly := Value;
end;

function TsynDBmemo.GetField: TField;
begin
  Result := FDataLink.Field;
end;

procedure TsynDBmemo.LoadMemo;
begin
  if not FMemoLoaded and Assigned(FDataLink.Field) and FDataLink.Field.IsBlob then
  begin
    try
      Lines.Text := FDataLink.Field.AsString;
      FMemoLoaded := True;
    except
      { Memo too large }
      on E:EInvalidOperation do
        Lines.Text := Format('(%s)', [E.Message]);
    end;
    EditingChange(Self);
  end;
end;

procedure TsynDBmemo.DataChange(Sender: TObject);
begin
  if FDataLink.Field <> nil then
    if FDataLink.Field.IsBlob then
    begin
      if FAutoDisplay or (FDataLink.Editing and FMemoLoaded) then
      begin
        FMemoLoaded := False;
        LoadMemo;
      end else
      begin
        Text := Format('(%s)', [FDataLink.Field.DisplayLabel]);
        FMemoLoaded := False;
      end;
    end else
    begin
      if FFocused and FDataLink.CanModify then
        Text := FDataLink.Field.Text
      else
        Text := FDataLink.Field.DisplayText;
      FMemoLoaded := True;
    end
  else
  begin
    if csDesigning in ComponentState then Text := Name else Text := '';
    FMemoLoaded := False;
  end;
  if HandleAllocated then
    RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_ERASE or RDW_FRAME);
  CheckScrolling;
end;


Procedure TsynDBMemo.CheckScrolling;
var here : tpoint;
    tmpScroll : tSCrollStyle;

  function Translate(customScrollEnum : tSynMemoScrollEnum ): tscrollstyle;
  begin
    result := ssNone;
    case CustomScrollEnum of
      smAutoScroll : result := ssNone;
      smVertical   : result := ssVertical;
      smHoriz      : result := ssHorizontal;
      smBoth       : result := ssBoth;
      smNoScroll   : result := ssNone;
      end;
    end;

begin
  tmpScroll := translate(fScrolling);
  here := GetRowCol;
  if fScrolling = smAutoScroll
    then begin
      if lines.count > GetlinesShowing  // lines.count*lineheight > (clientheight-1)
           then tmpScroll := ssVertical
           else tmpScroll := ssNone;
       if WordWrap = false
         then if (longestline > clientwidth) and (getLinesShowing > 1)
             then begin
                if TmpScroll = ssVertical
                   then TmpScroll := ssBoth
                   else TmpScroll := ssHorizontal;
                end
             else begin
                if TmpScroll = ssBoth
                   then TmpScroll := ssVertical
                   else if lines.count*lineheight > clientheight
                     then TmpScroll := ssVertical
                     else TmpScroll := ssNone;
                end;
      end;
  case fSCrolling of
    smAutoSCroll : if scrollbars <> TmpScroll then scrollbars := tmpScroll;
    smVertical : if scrollbars <> ssVertical then scrollbars := ssVertical;
    smHoriz : if scrollbars <> ssHorizontal then scrollbars := ssHorizontal;
    smBoth  : if scrollbars <> ssBoth then scrollbars := ssboth;
    smNoScroll : if scrollbars <> ssNone then scrollbars := ssnone;
    end;
  SetRowCol(here);
  invalidate;
end;


procedure TsynDBmemo.EditingChange(Sender: TObject);
begin
  inherited ReadOnly := not (FDataLink.Editing and FMemoLoaded);
end;

procedure TsynDBmemo.UpdateData(Sender: TObject);
begin
  FDataLink.Field.AsString := Text;
end;

procedure TsynDBmemo.SetFocused(Value: Boolean);
begin
  if FFocused <> Value then
  begin
    FFocused := Value;
    if not Assigned(FDataLink.Field) or not FDataLink.Field.IsBlob then
      FDataLink.Reset;
  end;
end;

procedure TsynDBmemo.WndProc(var Message: TMessage);
begin
  with Message do
    if (Msg = WM_CREATE) or (Msg = WM_WINDOWPOSCHANGED) or
      (Msg = CM_FONTCHANGED) then FPaintControl.DestroyHandle;
  inherited;
end;

procedure TsynDBmemo.CMEnter(var Message: TCMEnter);
begin
  SetFocused(True);
  inherited;
  if SysLocale.FarEast and FDataLink.CanModify then
    inherited ReadOnly := False;
end;

procedure TsynDBmemo.CMExit(var Message: TCMExit);
begin
  try
    FDataLink.UpdateRecord;
  except
    SetFocus;
    raise;
  end;
  SetFocused(False);
  inherited;
end;

procedure TsynDBmemo.SetAutoDisplay(Value: Boolean);
begin
  if FAutoDisplay <> Value then
  begin
    FAutoDisplay := Value;
    if Value then LoadMemo;
  end;
end;

procedure TsynDBmemo.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
  if not FMemoLoaded then LoadMemo else inherited;
end;

procedure TsynDBmemo.WMCut(var Message: TMessage);
begin
  FDataLink.Edit;
  inherited;
end;

procedure TsynDBmemo.WMPaste(var Message: TMessage);
begin
  FDataLink.Edit;
  inherited;
end;

procedure TsynDBmemo.CMGetDataLink(var Message: TMessage);
begin
  Message.Result := Integer(FDataLink);
end;

procedure TsynDBmemo.WMPaint(var Message: TWMPaint);
var
  S: string;
begin
  if not (csPaintCopy in ControlState) then inherited else
  begin
    if FDataLink.Field <> nil then
      if FDataLink.Field.IsBlob then
      begin
        if FAutoDisplay then
          S := AdjustLineBreaks(FDataLink.Field.AsString) else
          S := Format('(%s)', [FDataLink.Field.DisplayLabel]);
      end else
        S := FDataLink.Field.DisplayText;
    SendMessage(FPaintControl.Handle, WM_SETTEXT, 0, Integer(PChar(S)));
    SendMessage(FPaintControl.Handle, WM_ERASEBKGND, Message.DC, 0);
    SendMessage(FPaintControl.Handle, WM_PAINT, Message.DC, 0);
  end;
end;


procedure Register;
var x : string;
begin
  x := copy(cCopyright,1,8);
  RegisterComponents(x, [TsynMemo, TsynDBMemo]);
end;

end.

