{*********************************************************}
{*                   O32FLXED.PAS 4.06                   *}
{*********************************************************}

{* ***** BEGIN LICENSE BLOCK *****                                            *}
{* Version: MPL 1.1                                                           *}
{*                                                                            *}
{* The contents of this file are subject to the Mozilla Public License        *}
{* Version 1.1 (the "License"); you may not use this file except in           *}
{* compliance with the License. You may obtain a copy of the License at       *}
{* http://www.mozilla.org/MPL/                                                *}
{*                                                                            *}
{* 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.                                                                   *}
{*                                                                            *}
{* The Original Code is TurboPower Orpheus                                    *}
{*                                                                            *}
{* The Initial Developer of the Original Code is TurboPower Software          *}
{*                                                                            *}
{* Portions created by TurboPower Software Inc. are Copyright (C)1995-2002    *}
{* TurboPower Software Inc. All Rights Reserved.                              *}
{*                                                                            *}
{* Contributor(s):                                                            *}
{*                                                                            *}
{* ***** END LICENSE BLOCK *****                                              *}

{$I OVC.INC}

{$B-} {Complete Boolean Evaluation}
{$I+} {Input/Output-Checking}
{$P+} {Open Parameters}
{$T-} {Typed @ Operator}
{.W-} {Windows Stack Frame}
{$X+} {Extended Syntax}
{$J+} {Writable constants}

unit o32flxed;
  {OvcFlexEdit and support classes - Introduced in Orpheus 4.0}

interface

uses
  {$IFNDEF LCL} Windows, Messages, {$ELSE} LclIntf, LMessages, Types, LclType, MyMisc, {$ENDIF} 
  Classes, Controls, Forms, SysUtils, StdCtrls, Buttons,
  OvcData, O32Editf, OvcEF, Graphics, O32SR, O32bordr, O32Vldtr,
  O32VlOp1, o32ovldr, {$IFNDEF LCL} o32pvldr, {$ENDIF} o32rxvld, Dialogs;

type
  {Forward Declaration}
  TO32CustomFlexEdit = class;

  TO32PopupAnchor = (paLeft, paRight);


//  TO32FlexEditDataType = (feString, feFloat, feInteger, feDateTime, feExtended,
//    feStDate, feStTime, feLogical);

  TO32FEButton = class(TBitBtn)
  public
     procedure Click; override;
  end;

  TO32feButtonClickEvent =
    procedure(Sender: TO32CustomFlexEdit; PopupPoint: TPoint) of object;
  TFEUserValidationEvent =
    procedure(Sender : TObject; var ValidEntry : Boolean) of object;
  TFEValidationErrorEvent =
    procedure(Sender : TObject; ErrorCode : Word; ErrorMsg : string) of object;

  TFlexEditValidatorOptions = class(TValidatorOptions)
  published
    property InputRequired;
  end;

  TO32EditLines = class(TPersistent)
  protected{private}
    FlexEdit       : TO32CustomFlexEdit;
    FMaxLines      : Integer;
    FDefaultLines  : Integer;
    FFocusedLines  : Integer;
    FMouseOverLines: Integer;
    procedure SetDefaultLines(Value: Integer);
    procedure SetMaxLines(Value: Integer);
    procedure SetFocusedLines(Value: Integer);
    procedure SetMouseOverLines(Value: Integer);
  public
    constructor Create; virtual;
    destructor  Destroy; override;
  published
    property MaxLines: Integer       read FMaxLines       write SetMaxLines
      default 3;
    property DefaultLines: Integer   read FDefaultLines   write SetDefaultLines
      default 1;
    property FocusedLines: Integer   read FFocusedLines   write SetFocusedLines
      default 3;
    property MouseOverLines: Integer read FMouseOverLines write SetMouseOverLines
      default 3;
  end;

  TFlexEditStrings = class(TStrings)
  protected {private}
    FCapacity: Integer;
    function  Get(Index: Integer): string; override;
    function  GetCount: Integer; override;
    function  GetTextStr: string; override;
    procedure Put(Index: Integer; const S: string); override;
    procedure SetTextStr(const Value: string); override;
    procedure SetUpdateState(Updating: Boolean); override;

  public
    FlexEdit: TCustomEdit;
    procedure Clear; override;
    procedure SetCapacity(NewCapacity: Integer); override;
    procedure Delete(Index: Integer); override;
    procedure Insert(Index: Integer; const S: string); override;
  end;

  TO32CustomFlexEdit = class(TO32CustomEdit)
  protected {private}
    FAlignment        : TAlignment;
    FBorders          : TO32Borders;
    FButton           : TO32FEButton;
    FButtonGlyph      : TBitmap;
    FCanvas           : TControlCanvas;
//    FDataType         : TO32FlexEditDataType;
    FEditLines        : TO32EditLines;
    FEFColors         : TOvcEFColors;
    FDisplayedLines   : Integer;
    FMaxLines         : Integer;
    FStrings          : TFlexEditStrings;
//    FPasswordChar     : Char;
    FPopupAnchor      : TO32PopupAnchor;
    FShowButton       : Boolean;
    FWordWrap         : Boolean;
    FWantReturns      : Boolean;
    FWantTabs         : Boolean;
    FMouseInControl   : Boolean;
    FValidation       : TFlexEditValidatorOptions;
    FValidator        : TO32BaseValidator;
    FValidationError  : Integer;

    FOnButtonClick    : TO32feButtonClickEvent;
    FOnUserValidation : TFEUserValidationEvent;
    FOnValidationError: TFEValidationErrorEvent;
    FBeforeValidation : TNotifyEvent;
    FAfterValidation  : TNotifyEvent;

    {Internal Variables}
    FSaveEdit        : String;     {saved copy of edit string}
    FCreating        : Boolean;

    FColor           : TColor;
    FFontColor       : TColor;

    FUpdating        : Integer;
    feValid          : Boolean;


    {Property Methods}
    function  GetButtonGlyph          : TBitmap;
    procedure SetButtonGlyph(Value    : TBitmap);
    procedure SetShowButton (Value    : Boolean);
    function  GetBoolean              : Boolean;
    function  GetYesNo                : Boolean;
    function  GetDateTime             : TDateTime;
    function  GetDouble               : Double;
    function  GetExtended             : Extended;
    function  GetInteger              : Integer;
    function  GetStrings              : TStrings;
    function  GetVariant              : Variant;
    function  GetText                 : String;
    function  GetColor                : TColor; virtual;

    procedure SetBoolean       (Value : Boolean);
    procedure SetYesNo         (Value : Boolean);
    procedure SetDateTime      (Value : TDateTime);
//    procedure SetDataType      (Value : TO32FlexEditDataType);
    procedure SetDouble        (Value : Double);
    procedure SetExtended      (Value : Extended);
    procedure SetInteger       (Value : Integer);
    procedure SetStrings       (Value : TStrings);
    procedure SetVariant       (Value : Variant);
    procedure SetDisplayedLines(Value : Integer);
    procedure SetWordWrap      (Value : Boolean);
    procedure SetWantReturns   (Value : Boolean);
    procedure SetWantTabs      (Value : Boolean);
    procedure SetText          (const Value : String);
    procedure SetColor         (Value : TColor); virtual;

    {Message Handlers}
    procedure WMGetDlgCode (var Message : TWMGetDlgCode); message WM_GETDLGCODE;
    procedure CMMouseEnter (var Message : TMessage);      message CM_MOUSEENTER;
    procedure CMMouseLeave (var Message : TMessage);      message CM_MOUSELEAVE;
{$IFNDEF LCL}
    procedure CMGotFocus   (var Message : TMessage);      message WM_SETFOCUS;
    procedure CMLostFocus  (var Message : TMessage);      message WM_KILLFOCUS;
{$ELSE}
    procedure CMGotFocus   (var Message : TLMSetFocus);   message WM_SETFOCUS;
    procedure CMLostFocus  (var Message : TLMKillFocus);  message WM_KILLFOCUS;
{$ENDIF}

    { - added}
    procedure CMFontChanged(var Message: TMessage);       message CM_FONTCHANGED;

    procedure WMNCPaint    (var Message : TWMNCPaint);    message WM_NCPAINT;

{ - was commented out in 4.02 and re-written in 4.06}
    procedure WMPaint      (var Message : TWMPaint);      message WM_PAINT;

    procedure OMValidate   (var Message : TMessage);      message OM_VALIDATE;
    procedure OMRecreateWnd(var Message : TMessage);      message OM_RECREATEWND;

    {Internal Methods}
    procedure KeyPress(var Key: Char); override;
    procedure CreateParams(var Params : TCreateParams); override;
    procedure SetParent(Value: TWinControl); override;
    procedure CreateWnd; override;
{$IFNDEF LCL}
    procedure CreateWindowHandle(const Params: TCreateParams); override;
{$ENDIF}
    procedure AdjustHeight;
    procedure GlyphChanged; dynamic;
    procedure Loaded; override;
    procedure SetAlignment(Value: TAlignment);
    function  MultiLineEnabled: Boolean;
    function  GetButtonWidth : Integer;
    function  GetButtonEnabled : Boolean; dynamic;
    procedure SetMaxLines(Value: Integer);
    function  ValidateSelf: Boolean; virtual;
    procedure SaveEditString;

    procedure DoOnChange; virtual;

{$IFDEF LCL}
    function ChildClassAllowed(ChildClass: TClass): Boolean; override;
{$ENDIF}

  public
    constructor Create(AOwner : TComponent); override;
    destructor  Destroy; override;

    procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
    procedure ButtonClick; dynamic;
    procedure Restore; virtual;

    procedure BeginUpdate;
    procedure EndUpdate;

    {properties}
    property  Alignment: TAlignment
      read FAlignment   write SetAlignment default taLeftJustify;
//    property DataType: TO32FlexEditDataType
//      read FDataType write SetDataType default feString;
    property  Borders: TO32Borders
      read FBorders     write FBorders;
    property  Color: TColor
      read GetColor write SetColor default clWindow;
    property  EfColors: TOvcEfColors
      read FEFColors    write FEFColors;
    property  EditLines: TO32EditLines
      read FEditLines   write FEditLines;
//    property PasswordChar: char
//      read FPasswordChar write SetPwdChar;
    property  PopupAnchor : TO32PopupAnchor
      read FPopupAnchor write FPopupAnchor;
    property  ShowButton : Boolean
      read FShowButton  write SetShowButton;
    property Validation: TFlexEditValidatorOptions
      read FValidation write FValidation;
    property  WantReturns: Boolean
      read FWantReturns write SetWantReturns default False;
    property  WantTabs: Boolean
      read FWantTabs    write SetWantTabs default False;
    property  WordWrap: Boolean
      read FWordWrap    write SetWordWrap default False;
    property  Text: String
      read GetText      write SetText;
    property  Strings: TStrings
      read GetStrings     write SetStrings;

    property  AsBoolean: Boolean
      read GetBoolean   write SetBoolean;
    property  AsYesNo: Boolean
      read GetYesNo     write SetYesNo;
    property  AsDateTime: TDateTime
      read GetDateTime  write SetDateTime;
    property  AsFloat: Double
      read GetDouble    write SetDouble;
    property  AsExtended: Extended
      read GetExtended  write SetExtended;
    property  AsInteger: Integer
      read GetInteger   write SetInteger;
    property  AsVariant: Variant
      read GetVariant   write SetVariant;
    property  ButtonGlyph : TBitmap
      read GetButtonGlyph write SetButtonGlyph;
    property Canvas :TControlCanvas
      read FCanvas;
    property OnButtonClick : TO32feButtonClickEvent
      read FOnButtonClick write FOnButtonClick;
    property OnUserValidation: TFEUserValidationEvent
      read FOnUserValidation write FOnUserValidation;
    property OnValidationError: TFEValidationErrorEvent
      read FOnValidationError write FOnValidationError;
    property BeforeValidation : TNotifyEvent
      read FBeforeValidation write FBeforeValidation;
    property AfterValidation : TNotifyEvent
      read FAfterValidation write FAfterValidation;
  end;

  {O32FlexEdit}
  TO32FlexEdit = class(TO32CustomFlexEdit)
  published
    {$IFDEF VERSION4}
    property Alignment;
    property Anchors;
{$IFNDEF LCL}
    property BiDiMode;
    property ParentBiDiMode;
{$ENDIF}
    property DragKind;
    property DragMode;
    property OnEndDock;
    property OnStartDock;
    {$ENDIF}
    property AutoSize default False;
    property About;
{$IFNDEF LCL}
    property AutoSelect;  // Added recently to LCL, but leave out for now
{$ENDIF}    
    property Borders;
    property ButtonGlyph;
    property CharCase;
    property Color;
    property Cursor;
    property DragCursor;
    property EditLines;
    property EfColors;
    property Enabled;
    property Font;
{$IFNDEF LCL}
    property HideSelection;
    property ImeMode;
    property ImeName;
{$ENDIF}
    property LabelInfo;
    property MaxLength;
{$IFNDEF LCL}
    property OEMConvert;
{$ENDIF}
    property ParentFont;
    property ParentShowHint;
    property PasswordChar;
    property PopupAnchor default paLeft;
    property PopupMenu;
    property ReadOnly;
    property ShowButton default False;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Text;
    property Validation;
    property Visible;
    property WantReturns;
    property WantTabs;
    property WordWrap;

    property AfterValidation;
    property BeforeValidation;
    property OnButtonClick;
    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;
    property OnUserValidation;
    property OnValidationError;
  end;

implementation

// The optional button inside a TO32FlexEdit originally did not work with LCL,
//  so excluded all button-related code. 
// Note now seems to work with win32 and carbon as of 0.9.28. To enable,
//  delete the dot in the two DEFINE's.
// For more information, see Lazarus bug 7097.
{$IFNDEF LCL}
 {$DEFINE ButtonOkay}
{$ELSE}
 {.$DEFINE ButtonOkay}
 {$IFNDEF MSWINDOWS}
  {.$DEFINE ButtonNotChild}  //Manually reposition relative to edit control
 {$ENDIF}
{$ENDIF}

{===== TO32FEButton ==================================================}

procedure TO32FEButton.Click;
begin
  TO32FlexEdit(Parent).SetFocus;
  TO32FlexEdit(Parent).ButtonClick;
end;

{===== TO32EditLines =================================================}

constructor TO32EditLines.Create;
begin
  inherited Create;
  FMaxLines       := 3{1};
  FFocusedLines   := 3{1};
  FMouseOverLines := 3{1};
  FDefaultLines   := 1;
end;
{=====}

destructor  TO32EditLines.Destroy;
begin
  inherited Destroy;
end;
{=====}

procedure TO32EditLines.SetDefaultLines(Value: Integer);
begin
  if FDefaultLines <> Value then
    FDefaultLines := Value;
  if FDefaultLines > FMaxLines then
    FDefaultLines := FMaxLines;
end;
{=====}

procedure TO32EditLines.SetMaxLines(Value: Integer);
begin
  if FMaxLines <> Value then begin
    FMaxLines := Value;
    TO32CustomFlexEdit(FlexEdit).SetMaxLines(FMaxLines);
  end;
end;
{=====}

procedure TO32EditLines.SetFocusedLines(Value: Integer);
begin
  if FFocusedLines <> Value then
    FFocusedLines := Value;
  if FFocusedLines > FMaxLines then
    FFocusedLines := FMaxLines;
end;
{=====}

procedure TO32EditLines.SetMouseOverLines(Value: Integer);
begin
  if FMouseOverLines <> Value then
    FMouseOverLines := Value;
  if FMouseOverLines > FMaxLines then
    FMouseOverLines := FMaxLines;
end;


{===== TFlexEditStrings ==============================================}

function TFlexEditStrings.GetCount: Integer;
begin
  Result := 0;
  if FlexEdit.HandleAllocated then
  begin
    Result := SendMessage(FlexEdit.Handle, EM_GETLINECOUNT, 0, 0);
    if SendMessage(FlexEdit.Handle, EM_LINELENGTH, SendMessage(FlexEdit.Handle,
      EM_LINEINDEX, Result - 1, 0), 0) = 0 then Dec(Result);
  end;
end;
{=====}

function TFlexEditStrings.Get(Index: Integer): string;
var
  Text: array[0..4095] of Char;
begin
  Word((@Text)^) := SizeOf(Text);
  SetString(Result, Text, SendMessage(FlexEdit.Handle, EM_GETLINE, Index,
    Longint(@Text)));
end;
{=====}

procedure TFlexEditStrings.Put(Index: Integer; const S: string);
var
  SelStart: Integer;
begin
  SelStart := SendMessage(FlexEdit.Handle, EM_LINEINDEX, Index, 0);
  if SelStart >= 0 then
  begin
    SendMessage(FlexEdit.Handle, EM_SETSEL, SelStart, SelStart +
      SendMessage(FlexEdit.Handle, EM_LINELENGTH, SelStart, 0));
    SendMessage(FlexEdit.Handle, EM_REPLACESEL, 0, Longint(PChar(S)));
  end;
end;
{=====}

procedure TFlexEditStrings.Insert(Index: Integer; const S: string);
var
  SelStart, LineLen: Integer;
  Line: string;
begin
  if Count = FCapacity then exit;

  if Index >= 0 then
  begin
    SelStart := SendMessage(FlexEdit.Handle, EM_LINEINDEX, Index, 0);
    if SelStart >= 0 then Line := S + #13#10 else
    begin
      SelStart := SendMessage(FlexEdit.Handle, EM_LINEINDEX, Index - 1, 0);
      if SelStart < 0 then Exit;
      LineLen := SendMessage(FlexEdit.Handle, EM_LINELENGTH, SelStart, 0);
      if LineLen = 0 then Exit;
      Inc(SelStart, LineLen);
      Line := #13#10 + s;
    end;
    SendMessage(FlexEdit.Handle, EM_SETSEL, SelStart, SelStart);
    SendMessage(FlexEdit.Handle, EM_REPLACESEL, 0, Longint(PChar(Line)));
  end;
end;
{=====}

procedure TFlexEditStrings.Delete(Index: Integer);
const
  Empty: PChar = '';
var
  SelStart, SelEnd: Integer;
begin
  SelStart := SendMessage(FlexEdit.Handle, EM_LINEINDEX, Index, 0);
  if SelStart >= 0 then
  begin
    SelEnd := SendMessage(FlexEdit.Handle, EM_LINEINDEX, Index + 1, 0);
    if SelEnd < 0 then SelEnd := SelStart +
      SendMessage(FlexEdit.Handle, EM_LINELENGTH, SelStart, 0);
    SendMessage(FlexEdit.Handle, EM_SETSEL, SelStart, SelEnd);
    SendMessage(FlexEdit.Handle, EM_REPLACESEL, 0, Longint(Empty));
  end;
end;
{=====}

procedure TFlexEditStrings.Clear;
begin
  FlexEdit.Clear;
end;
{=====}

procedure TFlexEditStrings.SetUpdateState(Updating: Boolean);
begin
  if FlexEdit.HandleAllocated then
  begin
    SendMessage(FlexEdit.Handle, WM_SETREDRAW, Ord(not Updating), 0);
    if not Updating then
    begin   // WM_SETREDRAW causes visibility side effects in memo controls
      FlexEdit.Perform(CM_SHOWINGCHANGED,0,0); // This reasserts the visibility we want
      FlexEdit.Refresh;
    end;
  end;
end;
{=====}

function TFlexEditStrings.GetTextStr: string;
begin
  Result := FlexEdit.Text;
end;
{=====}

procedure TFlexEditStrings.SetCapacity(NewCapacity: Integer);
begin
  {Sets line-limit and destructively removes any lines that exceed the limit.}
  if FCapacity <> NewCapacity then begin
    FCapacity := NewCapacity;
    while Count > FCapacity do Delete(FCapacity);
  end;
end;
{=====}

procedure TFlexEditStrings.SetTextStr(const Value: string);
var
  NewText: string;
begin
  NewText := AdjustLineBreaks(Value);
  if (Length(NewText) <> FlexEdit.GetTextLen) or (NewText <> FlexEdit.Text) then
  begin
{$IFNDEF LCL}
    if SendMessage(FlexEdit.Handle, WM_SETTEXT, 0, Longint(NewText)) = 0 then
      raise EInvalidOperation.Create(RSTooManyBytes);
    FlexEdit.Perform(CM_TEXTCHANGED, 0, 0);
{$ELSE}  //Previous SendMessage always returns 0 (error) with LCL
    FlexEdit.SetTextBuf(PAnsiChar(NewText));
{$ENDIF}
  end;
end;

{===== TO32CustomFlexEdit ============================================}

constructor TO32CustomFlexEdit.Create(AOwner : TComponent);
begin
  FCreating := True;

  inherited Create(AOwner);

  FWordWrap := False;
  FWantReturns := False;
  FWantTabs := False;
  Width := 185;
  AutoSize := False;

  {create support classes}
  FCanvas := TControlCanvas.Create;
  TControlCanvas(FCanvas).Control := Self;

  feValid := true;
  FShowButton := False;
{$IFDEF ButtonOkay}
  FButton := TO32FEButton.Create(Self);
  FButton.Visible := True;
  FButton.Parent := Self;
  FButton.Caption := '';
  FButton.TabStop := False;
 {$IFNDEF LCL}
  FButton.Style := bsNew;
 {$ENDIF}
{$ENDIF}

  FButtonGlyph := TBitmap.Create;

  FBorders := TO32Borders.Create(Self);

  FEFColors := TOvcEfColors.Create;

  FStrings := TFlexEditStrings.Create;
  TFlexEditStrings(FStrings).FlexEdit := Self;

  FEditLines := TO32EditLines.Create;
  TO32EditLines(FEditLines).FlexEdit := self;

  { now set in TO32EditLines.Create:
  EditLines.MaxLines := 3;
  EditLines.DefaultLines := 1;
  EditLines.FocusedLines := 3;
  EditLines.MouseOverLines := 3;
  }

  FDisplayedLines := FEditLines.FDefaultLines;
  TFlexEditStrings(FStrings).Capacity := FMaxLines;

  FMouseInControl := false;

  FSaveEdit := '';

  Height := 80;
  AdjustHeight;

  Validation := TFlexEditValidatorOptions.Create(self);
  FColor := Color;
  FFontColor := Font.Color;
  FCreating := False;
end;
{=====}

destructor TO32CustomFlexEdit.Destroy;
begin
  {Free support classes}
{$IFDEF ButtonOkay}
  FButton.Free;
{$ENDIF}  
  FEFColors.Free;
  FEditLines.Free;
  FStrings.Free;
  FButtonGlyph.Free;
  FCanvas.Free;
  FBorders.Free;
  FValidation.Free;

  inherited Destroy;
end;
{=====}

procedure TO32CustomFlexEdit.CreateParams(var Params: TCreateParams);
const
  Passwords: array[Boolean] of DWORD = (0, ES_PASSWORD);
  Alignments: array[Boolean, TAlignment] of DWORD =
    ((ES_LEFT, ES_RIGHT, ES_CENTER),(ES_RIGHT, ES_LEFT, ES_CENTER));
  WordWraps: array[Boolean] of DWORD = (0, ES_AUTOHSCROLL);
begin
  inherited CreateParams(Params);
  with Params do
  begin
  { - begin}
    if MultilineEnabled then
      Style := Style and not WordWraps[FWordWrap] or ES_MULTILINE
        {$IFDEF VERSION4}
{$IFNDEF LCL}
        or Alignments[UseRightToLeftAlignment, FAlignment]
{$ELSE}
        or Alignments[False, FAlignment]
{$ENDIF}
        {$ENDIF}
        or WS_CLIPCHILDREN
    else
      Style := Style and not WordWraps[FWordWrap]
        or Passwords[PasswordChar <> #0]
        {$IFDEF VERSION4}
{$IFNDEF LCL}
        or Alignments[UseRightToLeftAlignment, FAlignment]
{$ELSE}
        or Alignments[False, FAlignment]
{$ENDIF}
        {$ENDIF}
        or WS_CLIPCHILDREN
  { - end}
  end;
end;
{=====}

procedure TO32CustomFlexEdit.SetParent(Value: TWinControl);
begin
  inherited;
  {$IFNDEF VERSION4}
    {$IFDEF CBuilder}
      HandleNeeded; {BCB3 needs a handle here}
    {$ENDIF}
  {$ENDIF}
end;
{=====}

procedure TO32CustomFlexEdit.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
  inherited;
  if FWantTabs then Message.Result := Message.Result or DLGC_WANTTAB
  else Message.Result := Message.Result and not DLGC_WANTTAB;
  if not FWantReturns then
    Message.Result := Message.Result and not DLGC_WANTALLKEYS;
end;
{=====}

procedure TO32CustomFlexEdit.WMNCPaint(var Message: TWMNCPaint);
var
  DC: HDC;
begin
  if (FUpdating > 0) then exit;

  inherited;

  if Borders.Active then begin
    DC := GetWindowDC(Handle);
    FCanvas.Handle := DC;
    try
      FBorders.DrawBorders(FCanvas, Color);
    finally
      FCanvas.Handle := 0;
      ReleaseDC( Handle, DC );
    end;
    Message.Result := 0;
  end;
end;
{=====}

{WMPaint had been completely commented out in 4.02.  It is now }
{ re introduced as follows....                                 }
{ - Re-written}
procedure TO32CustomFlexEdit.WMPaint(var Message: TWMPaint);
begin
  { known limitation, The ancestor is overriding the font color when the }
  { control is disabled }
  if not Enabled then begin
    inherited Color := efColors.Disabled.BackColor;
    Font.Color := efColors.Disabled.TextColor;
  end else begin
    if feValid then begin
      inherited Color := FColor;
      Font.Color := FFontColor;
    end else if Validation.SoftValidation then begin
      inherited Color := EFColors.Error.BackColor;
      Font.Color := EFColors.Error.TextColor;
    end;
  end;
  inherited;
end;

{ - Not necessary }
(*
procedure TO32CustomFlexEdit.WMPaint(var Message: TWMPaint);
var
  Rct: TRect;
  Str: string;
  DC: HDC;
  PS: TPaintStruct;
begin
  if (FUpdating > 0) then exit;

  if ((FAlignment = taLeftJustify ) or Focused)
    and not (csPaintCopy in ControlState)
  then inherited
  else begin
    DC := Message.DC;
    if DC = 0 then DC := BeginPaint(Handle, PS);
    FCanvas.Handle := DC;
    try
      if (FAlignment = taRightJustify) then begin
        FCanvas.Font := Font;
        with FCanvas do begin
          Rct := ClientRect;
          Brush.Color := Color;
          Str := Text;
          TextRect(Rct, Rct.Right - TextWidth(Str) - 2, 2, Str);
        end;
      end;
    finally
      FCanvas.Handle := 0;
      if Message.DC = 0 then EndPaint( Handle, PS );
    end;
  end;
end;
{=====}
*)

procedure TO32CustomFlexEdit.OMValidate (var Message : TMessage);
begin
  if not ValidateSelf then begin
    if Assigned(FOnValidationError) then
// TurboPower bug: With vtUser, FValidator is nil here.
//      FOnValidationError(Self, FValidator.ErrorCode, 'Invalid input');
      FOnValidationError(Self, FValidationError, 'Invalid input');  //Fixed
    Message.Result := FValidationError;
    if (Validation.ValidationEvent  = veOnChange) then begin
      Validation.BeginUpdate;
      Restore;
      Validation.EndUpdate;
    end;
  end else begin
    Message.Result := 0;
  end;
end;
{=====}

function TO32CustomFlexEdit.ValidateSelf: Boolean;
begin
  result := true;
  if (FUpdating > 0) then exit;

  case Validation.ValidationType of

    vtNone: begin
      {User can specify that the field be non-empty even if he is specifying no
       custom validation}
      if Validation.InputRequired and (Text = '') then
        result := false;
      exit;
    end;

    vtUser: begin
      if (Text = '') then begin
        if Validation.InputRequired then
          result := false
        else
          result := true;
      end;  { else  }  //Commented out; otherwise our validation handler never 
                       // gets called if Text is blank - TurboPower bug?.
        if Assigned(FOnUserValidation) then
          FOnUserValidation(Self, result);

      if not Result then
        FValidationError := 1;
    end; {vtUser}

    vtValidator: begin

      if (text = '') then begin
        if Validation.InputRequired then begin
          {Fail Validation for an empty, required field}
          result := false;
          FValidationError := 1;
        end else begin
          {Pass validation for a non-required, empty field}
          result := true;
          FValidationError := 0;
        end;
      end

      else if Validation.Mask = '' then begin
        result := true;
        FValidationError := 0;
        exit;
      end

      else if FValidation.ValidatorClass = nil then begin
        result := false;
        raise(Exception.Create('Error: Unknown validator Class.'));
      end

      else begin
        FValidator := FValidation.ValidatorClass.Create(Self);
        try
          FValidator.Mask := Validation.Mask;
          FValidator.Input := Text;

          if Assigned(FBeforeValidation) then
            FBeforeValidation(self);

          result := FValidator.IsValid;
          FValidationError := FValidator.ErrorCode;

          if Assigned(FAfterValidation) then
            FAfterValidation(self);

        finally
          FValidator.Free;
        end; {try}
      end; {if}
    end; {VtValidator}

  end; {case}

{ - begin}
  feValid := result;
  if result then
    SaveEditString
  else
    if Validation.BeepOnError then
      MessageBeep(0);

  { Invalidate; } { !!.04 - Commented out - Causes flicker } {!!!!}
{ - end}
end;
{=====}

procedure TO32CustomFlexEdit.OMRecreateWnd(var Message : TMessage);
begin
  if (FUpdating > 0) then exit;
{$IFNDEF LCL}
  RecreateWnd;
{$ELSE}
  MyMisc.RecreateWnd(Self);
{$ENDIF}
end;
{=====}

procedure TO32CustomFlexEdit.CMMouseEnter(var Message : TMessage);
begin
  inherited;
  if Enabled and MultiLineEnabled and (not FMouseInControl)
  and (not Focused) then begin
    if FDisplayedLines <> FEditLines.FMouseOverLines then begin
      SetDisplayedLines(FEditLines.FMouseOverLines);
      AdjustHeight;
    end;
  end;
  FMouseInControl := True;
end;
{=====}

procedure TO32CustomFlexEdit.CMMouseLeave(var Message : TMessage);
begin
  inherited;
{$IFDEF ButtonOkay}
  if FButton.Focused then exit;
{$ENDIF}
  if Enabled and FMouseInControl and MultiLineEnabled then begin
    if (Focused) then begin
      if FDisplayedLines <> FEditLines.FocusedLines then
        SetDisplayedLines(FEditLines.FocusedLines);
    end else begin
      if FDisplayedLines <> FEditLines.DefaultLines then
        SetDisplayedLines(FEditLines.DefaultLines);
    end;
    AdjustHeight;
  end;
  FMouseInControl := False;
end;
{=====}

{$IFNDEF LCL}
procedure TO32CustomFlexEdit.CMGotFocus(var Message : TMessage);
{$ELSE}
procedure TO32CustomFlexEdit.CMGotFocus(var Message : TLMSetFocus);
{$ENDIF}
begin
  inherited;
  if Enabled and MultiLineEnabled then begin
    if FDisplayedLines <> FEditLines.FocusedLines then begin
      SetDisplayedLines(FEditLines.FocusedLines);
      AdjustHeight;
    end;
  end;
{$IFNDEF LCL}  // AutoSelect is False by default in LCL TEdit
  if AutoSelect then
    SelectAll;
{$ENDIF}    
  SaveEditString;
end;
{=====}

{$IFNDEF LCL}
procedure TO32CustomFlexEdit.CMLostFocus(var Message : TMessage);
{$ELSE}
procedure TO32CustomFlexEdit.CMLostFocus(var Message : TLMKillFocus);
{$ENDIF}
begin
  inherited;
  if Enabled and MultiLineEnabled then begin
    if FMouseInControl then begin
      if FDisplayedLines <> FEditLines.MouseOverLines then
        SetDisplayedLines(FEditLines.MouseOverLines);
    end else begin
      if FDisplayedLines <> FEditLines.DefaultLines then
        SetDisplayedLines(FEditLines.DefaultLines);
    end;
    AdjustHeight;
  end;
end;
{=====}

{ - added}
procedure TO32CustomFlexEdit.CMFontChanged(var Message: TMessage);
begin
  inherited;
  AdjustHeight;
end;
{=====}

function TO32CustomFlexEdit.GetText: String;
begin
  result := FStrings.Text;
end;
{=====}

procedure TO32CustomFlexEdit.SetText(const Value: String);
var
  buffer: String;
  i  : Integer;
begin
  buffer := Value;

  if buffer <> '' then begin
    if not MultiLineEnabled then
    {strip out cr and lf's}
//      for i := Length(buffer) downto 0 do  <== TurboPower bug!
      for i := Length(buffer) downto 1 do
        if (buffer[i] = #13) or (buffer[i] = #10) then begin
          Delete(buffer, i, 1);
          if ((buffer[i - 1] <> ' ') and (buffer[i - 1] <> #10)
          and (buffer[i - 1] <> #13))and (buffer[i] <> ' ') then
            Insert(' ', buffer, i);
        end;
  end;

  FStrings.Text := buffer;

  SetTextBuf(PAnsiChar(buffer));

  if Borders.Active then Borders.RedrawControl;
end;
{=====}

function TO32CustomFlexEdit.GetColor: TColor;
begin
  Result := inherited Color;
end;
{=====}

procedure TO32CustomFlexEdit.SetColor( Value: TColor );
begin
  if Color <> Value then begin
    inherited Color := Value;
    FColor := Value;
    if Borders.Active then Borders.RedrawControl;
  end;
end;
{=====}

procedure TO32CustomFlexEdit.KeyPress(var Key: Char);
begin
  inherited KeyPress(Key);
  if (Key = Char(VK_RETURN)) then
    if not FWantReturns then
      Key := #0
    else begin
      if TFlexEditStrings(FStrings).Count >= FMaxLines then
        Key := #0;
    end;
end;
{=====}

procedure TO32CustomFlexEdit.CreateWnd;
begin
  if (FUpdating > 0) then exit;

  inherited CreateWnd;

  {force button placement}
  SetBounds(Left, Top, Width, Height);

{$IFDEF ButtonOkay}
  FButton.Enabled := GetButtonEnabled;
{$ENDIF}
  AdjustHeight;

  if Validation <> nil then
    Validation.AttachTo(Self);
end;

{$IFNDEF LCL}  //With LCL, will never be called since not in ancestor
procedure TO32CustomFlexEdit.CreateWindowHandle(const Params: TCreateParams);
begin
  if (FUpdating > 0) then exit;

  if not HandleAllocated then begin
    if (csDesigning in ComponentState) then
      inherited
    else begin
      with Params do
        WindowHandle := CreateWindowEx(ExStyle, WinClassName, '', Style,
          X, Y, Width, Height, WndParent, 0, WindowClass.HInstance,
          Param);
      SendMessage(WindowHandle, WM_SETTEXT, 0, Longint(Caption));
    end;
  end;
end;
{$ENDIF}
{=====}

procedure TO32CustomFlexEdit.AdjustHeight;
var
  DC: HDC;
  SaveFont: HFont;
  I: Integer;
  SysMetrics, Metrics: TTextMetric;
  Str: String;
begin
  if (FUpdating > 0) then exit;

  if FCreating then exit;

  DC := GetDC(0);
  GetTextMetrics(DC, SysMetrics);
  SaveFont := SelectObject(DC, Font.Handle);
  GetTextMetrics(DC, Metrics);
  SelectObject(DC, SaveFont);
  ReleaseDC(0, DC);
  if NewStyleControls then
  begin
    if Ctl3D then I := 8 else I := 6;
    I := GetSystemMetrics(SM_CYBORDER) * I;
  end else
  begin
    I := SysMetrics.tmHeight;
    if I > Metrics.tmHeight then I := Metrics.tmHeight;
    I := I div 4 + GetSystemMetrics(SM_CYBORDER) * 4;
  end;
  Height := (Metrics.tmHeight * FDisplayedLines) + I;
  if Borders.Active and not FCreating then begin
    Str := Text;
    Borders.RedrawControl;
    Text := Str;
  end;
end;
{=====}

function TO32CustomFlexEdit.GetButtonEnabled : Boolean;
begin
  result := (not ReadOnly);
end;
{=====}

procedure TO32CustomFlexEdit.SetMaxLines(Value: Integer);
var
  buffer: String;
begin
  if Value <> FMaxLines then begin
    FMaxLines := Value;
    TFlexEditStrings(FStrings).Capacity := FMaxLines;
    buffer := FStrings.Text;
    if buffer <> '' then
      while (buffer[Length(buffer)] = #13) or (buffer[Length(buffer)] = #10) do
        Delete(buffer, Length(buffer), 1);
    FStrings.Text := buffer;
  end;
end;
{=====}

procedure TO32CustomFlexEdit.SaveEditString;
begin
  if (Text <> FSaveEdit) then
    FSaveEdit := Text;
end;
{=====}

procedure TO32CustomFlexEdit.DoOnChange;
begin
  if Assigned(OnChange) then
    OnChange(Self);
end;
{=====}

function TO32CustomFlexEdit.GetButtonWidth : Integer;
begin
  if FShowButton then begin
    Result := GetSystemMetrics(SM_CXHSCROLL);
    if Assigned(FButtonGlyph) and not FButtonGlyph.Empty then
      if FButtonGlyph.Width + 4 > Result then
        Result := FButtonGlyph.Width + 4;
  end else
    Result := 0;
end;
{=====}

function TO32CustomFlexEdit.GetButtonGlyph : TBitmap;
begin
  if not Assigned(FButtonGlyph) then
    FButtonGlyph := TBitmap.Create;

  Result := FButtonGlyph
end;
{=====}

procedure TO32CustomFlexEdit.GlyphChanged;
begin
end;
{=====}

procedure TO32CustomFlexEdit.Loaded;
begin
  inherited Loaded;

{$IFDEF ButtonOkay}
  if Assigned(FButtonGlyph) then
    FButton.Glyph.Assign(FButtonGlyph);
{$ENDIF}
end;
{=====}

{ - rewritten to solve the "Text disappearing at alignment change" bug. }
procedure TO32CustomFlexEdit.SetAlignment(Value: TAlignment);
var
  Str: string;
begin
{$IFDEF LCL}
  if Value <> taLeftJustify then
    Exit;  {taCenter and taRightJustify not supported and crash IDE, so ignore}
{$ENDIF}
  if FAlignment <> Value then
  begin
    Str := Text;
    FAlignment := Value;
{$IFNDEF LCL}
    RecreateWnd;
{$ELSE}
    MyMisc.RecreateWnd(Self);
{$ENDIF}
    Text := Str;
  end;
end;
{=====}

function TO32CustomFlexEdit.MultiLineEnabled: Boolean;
begin
  { The control is only multi-line able if either WordWrap or WantReturns is }
  { set and Password char is not being used }
  result := (FWantReturns or FWordWrap) and (PasswordChar = #0);
end;
{=====}

procedure TO32CustomFlexEdit.ButtonClick;
var
  P: TPoint;
begin
  if (Assigned(FOnButtonClick)) then begin
    {Get the screen coordinates of the bottom-left or bottom-right corner of
    the control.}
    if PopupAnchor = paLeft then
      P := Point(Left, Top + Height)
    else
      P := Point(Left + Width, Top + Height);

    {Call the user defined event handler, passing the desired popup point}
    FOnButtonClick(Self, P);
  end;
end;
{=====}

procedure TO32CustomFlexEdit.Restore;
  {-restore the previous contents of the FlexEdit}
var
  CursorPos: Integer;
begin
  BeginUpdate;
  CursorPos := SelStart;
  Text := FSaveEdit;
  Repaint;
  DoOnChange;
  SelStart := CursorPos;
  EndUpdate;
end;
{=====}

procedure TO32CustomFlexEdit.BeginUpdate;
begin
  Inc(FUpdating);
  Validation.BeginUpdate;
end;
{=====}

procedure TO32CustomFlexEdit.EndUpdate;
begin
  Dec(FUpdating);
  Validation.EndUpdate;
  if (FUpdating < 0) then
    FUpdating := 0;
end;
{=====}

procedure TO32CustomFlexEdit.SetBounds(ALeft, ATop, AWidth, AHeight : Integer);
var
  CHgt : Integer;
begin
  if (FUpdating > 0) then exit;

  inherited SetBounds(ALeft, ATop, AWidth, AHeight);

  if not HandleAllocated then
    Exit;

{$IFDEF ButtonOkay}
  if not FShowButton then begin
    FButton.Height := 0;
    FButton.Width := 0;
    Exit;
  end;

  CHgt := ClientHeight;
  if BorderStyle = bsNone then begin
    FButton.Height := CHgt;
    FButton.Width := GetButtonWidth;
    FButton.Left := Width - FButton.Width;
    FButton.Top := 0;
  end else if Ctl3D then begin
    FButton.Height := CHgt;
    FButton.Width := GetButtonWidth;
    FButton.Left := Width - FButton.Width - 4;
    FButton.Top := 0;
  end else begin
    FButton.Height := CHgt - 2;
    FButton.Width := GetButtonWidth;
{$IFNDEF ButtonNotChild}
    FButton.Left := Width - FButton.Width - 1;
    FButton.Top := 1;
{$ELSE}
    FButton.Left := Left + Width - FButton.Width - 1;
    FButton.Top := Top + 1;
{$ENDIF}
  end;
{$ENDIF}
end;
{=====}

procedure TO32CustomFlexEdit.SetButtonGlyph(Value : TBitmap);
begin
  if not Assigned(FButtonGlyph) then
    FButtonGlyph := TBitmap.Create;

  if not Assigned(Value) then begin
    FButtonGlyph.Free;
    FButtonGlyph := TBitmap.Create;
  end else
    FButtonGlyph.Assign(Value);

  GlyphChanged;

{$IFDEF ButtonOkay}
  FButton.Glyph.Assign(FButtonGlyph);
{$ENDIF}
  SetBounds(Left, Top, Width, Height);
end;
{=====}

procedure TO32CustomFlexEdit.SetShowButton(Value : Boolean);
begin
  if Value <> FShowButton then begin
    FShowButton := Value;
    {force resize and redisplay of button}
    SetBounds(Left, Top, Width, Height);
  end;
end;
{=====}

function TO32CustomFlexEdit.GetBoolean: Boolean;
begin
 result := (AnsiUpperCase(Text) = AnsiUppercase(RSTrue));
end;
{=====}

function TO32CustomFlexEdit.GetYesNo: Boolean;
begin
  result := (AnsiUpperCase(Text) = AnsiUppercase(RSYes));
end;
{=====}

function TO32CustomFlexEdit.GetDateTime: TDateTime;
begin
  try
    result := StrToDateTime(Text);
  except
    result := 0;
  end;
end;
{=====}

function TO32CustomFlexEdit.GetDouble: Double;
begin
  try
    result := StrToFloat(Text);
  except
    result := -1;
  end;
end;
{=====}

function TO32CustomFlexEdit.GetExtended: Extended;
begin
  {Note: StrToFloat returns an Extended}
  try
    result := StrToFloat(Text);
  except
    result := -1;
  end;
end;
{=====}

function TO32CustomFlexEdit.GetInteger: Integer;
begin
  result := StrToIntDef(Text, -1);
end;
{=====}

function TO32CustomFlexEdit.GetStrings: TStrings;
begin
  result := TStrings(FStrings);
end;
{=====}

function TO32CustomFlexEdit.GetVariant: Variant;
begin
  result := Text;
end;
{=====}

procedure TO32CustomFlexEdit.SetBoolean(Value: Boolean);
begin
  if Value then
    Text := 'True'
  else
    Text := 'False';
end;
{=====}

procedure TO32CustomFlexEdit.SetYesNo(Value: Boolean);
begin
  if Value then
    Text := 'Yes'
  else
    Text := 'No';
end;
{=====}

procedure TO32CustomFlexEdit.SetDateTime(Value: TDateTime);
begin
  Text := DateTimeToStr(Value);
end;
{=====}

(*
procedure TO32CustomFlexEdit.SetDataType(Value : TO32FlexEditDataType);
begin
 { TODO : Implement }
end;
{=====}
*)

procedure TO32CustomFlexEdit.SetDouble(Value: Double);
begin
  Text := FloatToStr(Value);
end;
{=====}

procedure TO32CustomFlexEdit.SetExtended(Value: Extended);
begin
  Text := FloatToStr(Value);
end;
{=====}

procedure TO32CustomFlexEdit.SetInteger(Value: Integer);
begin
  Text := IntToStr(Value);
end;
{=====}

procedure TO32CustomFlexEdit.SetStrings(Value: TStrings);
begin
  FStrings.Assign(Value);
  Invalidate;
end;
{=====}

procedure TO32CustomFlexEdit.SetVariant(Value: Variant);
begin
  try
    Text := Value;
  except
    Text := '';
  end;
end;
{=====}

procedure TO32CustomFlexEdit.SetDisplayedLines(Value: Integer);
var
  buffer: String;
begin
  if Value <> FDisplayedLines then begin
    buffer := Text;
    FDisplayedLines := Value;
    AdjustHeight;
    Text:= Buffer;
  end;
end;
{=====}

procedure TO32CustomFlexEdit.SetWordWrap(Value: Boolean);
var
  buffer: String;
begin
  if Value <> FWordWrap then
  begin
    buffer := Text;
    FWordWrap := Value;
{$IFNDEF LCL}
    RecreateWnd;
{$ELSE}
    MyMisc.RecreateWnd(Self);
{$ENDIF}    
    Text:= buffer;
  end;
end;
{=====}

procedure TO32CustomFlexEdit.SetWantReturns (Value : Boolean);
var
  buffer: String;
begin
  if Value <> FWantReturns then
  begin
    buffer := Text;
    FWantReturns := Value;
{$IFNDEF LCL}
    RecreateWnd;
{$ELSE}
    MyMisc.RecreateWnd(Self);
{$ENDIF}    
    Text:= buffer;
  end;
end;
{=====}

procedure TO32CustomFlexEdit.SetWantTabs(Value : Boolean);
var
  buffer: String;
begin
  if Value <> FWantTabs then
  begin
    buffer := Text;
    FWantTabs := Value;
{$IFNDEF LCL}
    RecreateWnd;
{$ELSE}
    MyMisc.RecreateWnd(Self);
{$ENDIF}
    Text:= buffer;
  end;
end;
    
{$IFDEF LCL}
// Eliminates LCL "[Type1] can not have [Type2] as child" runtime message,
//  but button still doesn't work with all widgetsets. See Lazarus bug 7097.
function TO32CustomFlexEdit.ChildClassAllowed(ChildClass: TClass): Boolean;
begin
  Result := True;
end;
{$ENDIF}


end.
