
{*******************************************************}
{                                                       }
{       ScaleRichView                                   }
{       TEdit replacement for ScaleRichView.            }
{                                                       }
{       Copyright (c) Ilya Zelenskiy                    }
{       Ilya.Zelenskiy@gmail.com                        }
{       http://www.trichview.com                        }
{                                                       }
{*******************************************************}

unit SRVEdit;

interface
{$I RV_Defs.inc}
uses
  Windows, Messages, Forms, Graphics, Controls, Classes, SysUtils,
  SRVControl, RVUni, RVScroll, RVItem, RVTypes, StdCtrls, SRVSkinManager, RVGrIn;

const
  WM_REPAINT_CONTROL = WM_USER + 1985;

  SRVCB_FACE_START_BUTTON = $00EEEEEE;
  SRVCB_FACE_END_BUTTON = $00B7BBBF;
  SRVCB_FACE_START_VALUE = $00EEEEEE;
  SRVCB_FACE_END_VALUE = $00B7BBBF;
  SRVCB_FACE_START_FON = $00FFFFFF;
  SRVCB_FACE_END_FON = $00E7EBEF;

type
  TSRVEditCharCase = (srvecNormal, srvecUpperCase, srvecLowerCase);
  TSRVHintStyle = (hsDropDownList, hsOwnerDrawFixed, hsOwnerDrawVariable);

  TSRVEditHintsEvent = procedure (Sender: TSRVEditControl;
    Text: String; EditHints: TStringList; var ItemIndex : Integer) of object;
  TFormHintsClose = procedure(Sender: TObject; FormWidth, FormHeight : Integer; var NewText : String) of object;

  TSRVEdit = class(TSRVEditControl)
  private
    indexPos1 : Integer;
    indexPos2 : Integer;
    iSel1 : Integer;
    iSel2 : Integer;
    OffX : Integer;
    OffY : Integer;
    imgTopButton : TBitmap;
    imgBottomButton : TBitmap;
    imgValue : TBitmap;
    imgTopButtonDown : TBitmap;
    imgBottomButtonDown : TBitmap;
    imgValueDown : TBitmap;
    imgFon : TBitmap;
    SBButtonHeight : Integer;
    FOnHintDropDown: TNotifyEvent;
    FHintDropingDown : Boolean;
    FHintItemHeight : Integer;
    FOnDrawItem: TDrawItemEvent;
    FOnMeasureItem: TMeasureItemEvent;
    FHintStyle : TSRVHintStyle;
    FHintImageList : TImageList;
    EditHints: TStringList;
    FDropDownHints: Integer;
    FAutoSize: Boolean;
    FMaxLength: Integer;
    FPasswordChar: Char;
    FReadOnly: Boolean;
    FCharCase: TSRVEditCharCase;
    FCreating: Boolean;
    FModified: Boolean;
    FOnChange: TNotifyEvent;
    FStartX: Integer;
    FOnEditHints : TSRVEditHintsEvent;
    FOnCloseHints : TFormHintsClose;
    FGraphicInterface: TRVGraphicInterface;
    ClickState: Integer;
    function GetModified: Boolean;
    function GetCanUndo: Boolean;
    procedure SetAutoSize(NewAutoSize: Boolean); reintroduce;
    procedure SetCharCase(NewCharCase: TSRVEditCharCase);
    procedure SetMaxLength(NewMaxLength: Integer);
    procedure SetModified(NewModified: Boolean);
    procedure SetPasswordChar(NewPasswordChar: Char);
    procedure SetReadOnly(NewReadOnly: Boolean);
    procedure SetSelText(const Value: string);
    procedure SetDropDownHints(const Value: Integer);
    procedure UpdateHeight;
    procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
    procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
    procedure WMCaptureChanged(var Message: TWMPaint); message WM_CAPTURECHANGED;
    procedure GetBoundPos;
    procedure UpdateFormHints;
    procedure EditHintsClose(Sender: TObject; var Action: TCloseAction);
    function GetScrollBarHeight : Integer;
    function CalculateDrawItemCount(FirstItem, Height, HintItemHeight : Integer) : Integer;
    procedure CheckListPosition;
  protected
    lockUpdate : Integer;
    HintsItemIndex : Integer;
    procedure CreateParams(var Params: TCreateParams); override;
    procedure CreateWindowHandle(const Params: TCreateParams); override;
    procedure CreateWnd; override;
    procedure DestroyWnd; override;
    procedure DoSetMaxLength(Value: Integer); virtual;
    function GetSelText: string; virtual;
    function  DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint) : Boolean; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure WMKillFocus(var Message: TMessage); message WM_KILLFOCUS;
  public
    RFormSize : TSize;
    RequiredCanvasOff : Boolean;
    BGTextColor : TColor;
    TextColor : TColor;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DefaultHandler(var Message); override;
    procedure Clear; virtual;
    procedure Change; dynamic;
    procedure ClearSelection;
    procedure CopyToClipboard;
    procedure CutToClipboard;
    procedure PasteFromClipboard;
    procedure Undo;
    procedure ClearUndo;
    procedure BeginUpdate;
    procedure EndUpdate;
    procedure ShowHints;
    function  GetCaretPos: TPoint; override;
    function  GetCaretHeight: Integer; override;
    function  GetSelected: Boolean; override;
    function  GetSelTextBuf(Buffer: PChar; BufSize: Integer): Integer; virtual;
    procedure SetSelTextBuf(Buffer: PChar);
    property CanUndo: Boolean read GetCanUndo;
    property Modified: Boolean read GetModified write SetModified;
    property SelText: string read GetSelText write SetSelText;
    property CaretPos: TPoint read GetCaretPos;
    procedure PaintTo(DC : THandle);
  published
    property Anchors;
    property Align;
    property AutoSize: Boolean read FAutoSize write SetAutoSize default True;
    property SkinManager;
    property SkinSchemeIndex;
    property ScrollBarSkinSchemeIndex;
    property ItemSkinSchemeIndex;
    property EditSkinSchemeIndex;
    property Font;
    property Hint;
    property Enabled;
    property Color default clWhite;
    property EnabledBorderColor;
    property EnabledBorderWidth;
    property DisabledBorderColor;
    property DisabledBorderWidth;
    property MouseInBorderColor;
    property MouseInBorderWidth;
    property FocusedBorderColor;
    property FocusedBorderWidth;
    property DrawOnPrint;
    property CharCase: TSRVEditCharCase read FCharCase write SetCharCase default srvecNormal;
    property MaxLength: Integer read FMaxLength write SetMaxLength default 0;
    property PasswordChar: Char read FPasswordChar write SetPasswordChar default #0;
    property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
    property DropDownHints: Integer read FDropDownHints write SetDropDownHints default 10;
    property Height default 25;
    property ShowHint;
    property Text;
    property Visible;
    property TabStop default True;
    property TabOrder;
    property Width default 121;
    property OnClick;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property OnCloseHints : TFormHintsClose read FOnCloseHints write FOnCloseHints;
    property OnEditHints : TSRVEditHintsEvent read FOnEditHints write FOnEditHints;
    property OnMouseMove;
    property OnMouseDown;
    property OnMouseUp;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnEnter;
    property OnExit;
    property OnDblClick;
    {$IFDEF RICHVIEWDEF5}
    property OnContextPopup;
    {$ENDIF}
    property OnMouseEnter;
    property OnMouseLeave;
  end;

implementation
uses Math, SRVHints, RVStyle, RVWinGrIn;

constructor TSRVEdit.Create(AOwner: TComponent);
const
  EditStyle = [csClickEvents, csDoubleClicks, csFixedHeight];
begin
  inherited Create(AOwner);
  FGraphicInterface := TRVWinGraphicInterface.Create;

//  NewStyleControls := False;
  ControlStyle := EditStyle;
  Color := clWhite;
  BGTextColor := clNone;
  TextColor := clNone;
  lockUpdate := 0;
  indexPos1 := 0;
  indexPos2 := 0;
  RequiredCanvasOff := False;
  FHintDropingDown := False;
  FDropDownHints := 10;
  EditHints := nil;
  OffX := 3;
  OffY := 2;
  SBButtonHeight := 20;
  FHintStyle := hsDropDownList;
  FHintItemHeight := 20;
  imgTopButton := TBitmap.Create;
  imgBottomButton := TBitmap.Create;
  imgValue := TBitmap.Create;
  imgTopButtonDown := TBitmap.Create;
  imgBottomButtonDown := TBitmap.Create;
  imgValueDown := TBitmap.Create;
  imgFon := TBitmap.Create;

  Width := 121;
  Height := 25;
  FAutoSize := True;
  TabStop := True;
  AdjustHeight;
end;

destructor TSRVEdit.Destroy;
begin
  imgTopButton.Free;
  imgBottomButton.Free;
  imgValue.Free;
  imgTopButtonDown.Free;
  imgBottomButtonDown.Free;
  imgValueDown.Free;
  imgFon.Free;
  EditHints.Free;
  FGraphicInterface.Free;
  inherited;
end;

procedure TSRVEdit.DoSetMaxLength(Value: Integer);
begin
  SendMessage(Handle, EM_LIMITTEXT, Value, 0)
end;

procedure TSRVEdit.SetAutoSize(NewAutoSize: Boolean);
begin
  if NewAutoSize <> FAutoSize then begin
    FAutoSize := NewAutoSize;
    UpdateHeight;
  end;
end;

procedure TSRVEdit.SetCharCase(NewCharCase: TSRVEditCharCase);
begin
  if NewCharCase <> FCharCase then
  begin
    FCharCase := NewCharCase;
    RecreateWnd;
  end;
end;

procedure TSRVEdit.SetMaxLength(NewMaxLength: Integer);
var S: string;
begin
  if NewMaxLength <> FMaxLength then
    begin
      FMaxLength := NewMaxLength;
      if HandleAllocated then DoSetMaxLength(NewMaxLength);
      if (MaxLength <= 0) or (Length(Text) <= MaxLength) then
        Exit;
      S := Text;
      SetLength(S, MaxLength);
      Text := S;
    end;
end;

function TSRVEdit.GetModified: Boolean;
begin
  Result := FModified;
  if HandleAllocated then Result := SendMessage(Handle, EM_GETMODIFY, 0, 0) <> 0;
end;

function TSRVEdit.GetCanUndo: Boolean;
begin
  Result := False;
  if HandleAllocated then Result := SendMessage(Handle, EM_CANUNDO, 0, 0) <> 0;
end;

procedure TSRVEdit.SetModified(NewModified: Boolean);
begin
  if HandleAllocated then
    SendMessage(Handle, EM_SETMODIFY, Byte(NewModified), 0) else
    FModified := NewModified;
end;

procedure TSRVEdit.SetPasswordChar(NewPasswordChar: Char);
begin
  if NewPasswordChar <> FPasswordChar then
  begin
    FPasswordChar := NewPasswordChar;
    if HandleAllocated then
    begin
      SendMessage(Handle, EM_SETPASSWORDCHAR, Ord(FPasswordChar), 0);
      SetTextBuf(PChar(Text));
    end;
  end;
end;

procedure TSRVEdit.SetReadOnly(NewReadOnly: Boolean);
begin
  if NewReadOnly <> FReadOnly then
  begin
    FReadOnly := NewReadOnly;
    if HandleAllocated then
      SendMessage(Handle, EM_SETREADONLY, Ord(NewReadOnly), 0);
  end;
end;

procedure TSRVEdit.Clear;
begin
  SetWindowText(Handle, '');
end;

procedure TSRVEdit.ClearSelection;
begin
  SendMessage(Handle, WM_CLEAR, 0, 0);
end;

procedure TSRVEdit.CopyToClipboard;
begin
  SendMessage(Handle, WM_COPY, 0, 0);
end;

procedure TSRVEdit.CutToClipboard;
begin
  SendMessage(Handle, WM_CUT, 0, 0);
end;

procedure TSRVEdit.PasteFromClipboard;
begin
  SendMessage(Handle, WM_PASTE, 0, 0);
end;

procedure TSRVEdit.Undo;
begin
  SendMessage(Handle, WM_UNDO, 0, 0);
end;

procedure TSRVEdit.ClearUndo;
begin
  SendMessage(Handle, EM_EMPTYUNDOBUFFER, 0, 0);
end;

function TSRVEdit.GetSelTextBuf(Buffer: PChar; BufSize: Integer): Integer;
var
  P: PChar;
  StartPos: Integer;
begin
  StartPos := Self.SelStart;
  Result := Self.SelLength;
  P := StrAlloc(GetTextLen + 1);
  try
    GetTextBuf(P, StrBufSize(P));
    if Result >= BufSize then Result := BufSize - 1;
    StrLCopy(Buffer, P + StartPos, Result);
  finally
    StrDispose(P);
  end;
end;

procedure TSRVEdit.SetSelTextBuf(Buffer: PChar);
begin
  SendMessage(Handle, EM_REPLACESEL, 0, LongInt(Buffer));
end;

function TSRVEdit.GetSelText: string;
var
  P: PChar;
  SelStart, Len: Integer;
begin
  SelStart := Self.SelStart;
  Len := Self.SelLength;
  SetString(Result, PChar(nil), Len);
  if Len <> 0 then
  begin
    P := StrAlloc(GetTextLen + 1);
    try
      GetTextBuf(P, StrBufSize(P));
      Move(P[SelStart], Pointer(Result)^, Len);
    finally
      StrDispose(P);
    end;
  end;
end;

procedure TSRVEdit.SetSelText(const Value: String);
begin
  SendMessage(Handle, EM_REPLACESEL, 0, Longint(PChar(Value)));
end;

procedure TSRVEdit.CreateParams(var Params: TCreateParams);
const
  Passwords: array[Boolean] of DWORD = (0, ES_PASSWORD);
  ReadOnlys: array[Boolean] of DWORD = (0, ES_READONLY);
  CharCases: array[TSRVEditCharCase] of DWORD = (0, ES_UPPERCASE, ES_LOWERCASE);
  HideSelections: array[Boolean] of DWORD = (ES_NOHIDESEL, 0);
  OEMConverts: array[Boolean] of DWORD = (0, ES_OEMCONVERT);
begin
  inherited CreateParams(Params);

  if not (csDesigning in ComponentState) then begin
    CreateSubClass(Params, 'EDIT');
    with Params do begin
       Style := Style or (ES_AUTOHSCROLL or ES_AUTOVSCROLL) or
         Passwords[FPasswordChar <> #0] or
         ReadOnlys[FReadOnly] or CharCases[FCharCase];
    end;
  end;
end;

procedure TSRVEdit.CreateWindowHandle(const Params: TCreateParams);
var
  P: TCreateParams;
begin
  if SysLocale.FarEast and (Win32Platform <> VER_PLATFORM_WIN32_NT) and
    ((Params.Style and ES_READONLY) <> 0) then
  begin
    // Work around Far East Win95 API/IME bug.
    P := Params;
    P.Style := P.Style and (not ES_READONLY);
    inherited CreateWindowHandle(P);
    if WindowHandle <> 0 then
      SendMessage(WindowHandle, EM_SETREADONLY, Ord(True), 0);
  end
  else
    inherited CreateWindowHandle(Params);
end;

procedure TSRVEdit.CreateWnd;
begin
  FCreating := True;
  try
    inherited CreateWnd;
  finally
    FCreating := False;
  end;
  DoSetMaxLength(FMaxLength);
  Modified := FModified;
  if FPasswordChar <> #0 then
    SendMessage(Handle, EM_SETPASSWORDCHAR, Ord(FPasswordChar), 0);
  UpdateHeight;
end;

procedure TSRVEdit.DestroyWnd;
begin
  FModified := Modified;
  inherited DestroyWnd;
end;

procedure TSRVEdit.UpdateHeight;
begin
  if FAutoSize then begin
    ControlStyle := ControlStyle - [csFixedHeight];
    AdjustHeight;
  end else
    ControlStyle := ControlStyle + [csFixedHeight];
end;

procedure TSRVEdit.Change;
begin
  inherited Changed;
  if (lockUpdate = 0) then
    ShowHints;
  if Assigned(FOnChange) then FOnChange(Self);
  GetBoundPos;
end;

procedure TSRVEdit.DefaultHandler(var Message);
begin
  case TMessage(Message).Msg of
    WM_SETFOCUS:
      if (Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and
        not IsWindow(TWMSetFocus(Message).FocusedWnd) then
        TWMSetFocus(Message).FocusedWnd := 0;
  end;
  if not ((TMessage(Message).Msg = WM_CHAR) and
    ((TMessage(Message).WParam = 27) or (TMessage(Message).WParam = 13))) then
    inherited;
end;

procedure TSRVEdit.CNCommand(var Message: TWMCommand);
begin
  if (Message.NotifyCode = EN_CHANGE) and not FCreating then Change;
end;

procedure TSRVEdit.CMTextChanged(var Message: TMessage);
begin
  inherited;
  if not HandleAllocated or (GetWindowLong(Handle, GWL_STYLE) and
    ES_MULTILINE <> 0) then Change;
end;

procedure TSRVEdit.PaintTo(DC : THandle);
var
     editDC, DCScreen: HDC;
     R, rSel: TRect;
     SZ: TSize;
     w, i
     //,FontSize
     : Integer;
     Canvas, CanvasScreen: TCanvas;
     s: String;
     sraw: TRVRawByteString;
     ItemOptions: TRVItemOptions;
     DoSkin : Boolean;
begin
  if (Parent = nil) then
    Exit;

  If ParentSRV and (DC = 0) then begin
    editDC := GetWindowDC(Handle);
    ReleaseDC(Handle, editDC);
    if Parent <> nil then
      PostMessage(Parent.Handle, WM_REPAINT_CONTROL, 0, 0);
    exit;
  end;

  if DC = 0 then
    editDC := GetWindowDC(Handle)
  else
    editDC := DC;

  Canvas := TCanvas.Create;
  CanvasScreen := TCanvas.Create;
  GetWindowRect(Handle, R);
  OffsetRect(R, -R.Left, -R.Top);
  If RequiredCanvasOff then
    OffsetRect(R, CanvasOffX, CanvasOffY)
  else begin
    CanvasOffX := 0;
    CanvasOffY := 0;
  end;
  DCScreen := CreateCompatibleDC(0);

  try
    Canvas.Handle := editDC;

{    Canvas.Brush.Style := bsSolid;
    Canvas.Brush.Color := Color;
    Canvas.FillRect(R);}

    DrawBoxBG(Canvas, R, SkinManager, EditSkinSchemeIndex, True,
      MouseIn, ClickState <> 0, Enabled, Focused, 1, Color);

    CanvasScreen.Handle := DCScreen;
    CanvasScreen.Font.PixelsPerInch := 96;

    Canvas.Font.Assign(Font);
    If TextColor <> clNone then
      Canvas.Font.Color := TextColor;
    {
    FontSize := Canvas.Font.Size;
    Canvas.Font.PixelsPerInch := RVFORMATCANVASRESOLUTION;
    Canvas.Font.Size := FontSize;
    Canvas.Font.PixelsPerInch := 96;
    }
    CanvasScreen.Font.Assign(Canvas.Font);

    s := '';
    if PasswordChar = '' then
      s := Copy(Text, indexPos1 + 1, indexPos2 - indexPos1 + 1)
    else
      for i:= 1 to indexPos2 - indexPos1 + 1 do
       s := s + PasswordChar;

    {$IFDEF RVUNICODESTR}
    sraw := RVU_GetRawUnicode(s);
    ItemOptions := [rvioUnicode];
    {$ELSE}
    sraw := s;
    ItemOptions := [];
    {$ENDIF}
    SZ := CanvasScreen.TextExtent(s);
    w := (Width - ClientWidth) div 2 + FStartX;
    If BGTextColor <> clNone then
      Canvas.Brush.Color := BGTextColor;
    if not RVU_DrawSelectedTextEx(w + CanvasOffX, BorderWidth + CanvasOffY, SZ.cx, sraw,
      Canvas, CanvasScreen,
      1, Length(s), ItemOptions, rvbdLeftToRight, False, FGraphicInterface) then begin
      //Canvas.Font.Size := FontSize;
      Canvas.Brush.Style := bsClear;
      Canvas.TextRect(R, BorderWidth + CanvasOffX, BorderWidth + CanvasOffY, s);
      Canvas.Brush.Style := bsSolid;
      {
      Canvas.Font.PixelsPerInch := RVFORMATCANVASRESOLUTION;
      Canvas.Font.Size := FontSize;
      Canvas.Font.PixelsPerInch := 96;
      }
    end;

    if not Printing and (iSel2 > iSel1) then begin
      rSel := Rect(w + Canvas.TextWidth(Copy(Text, indexPos1+1, iSel1 - indexPos1)),
                   BorderWidth,
                   w + Canvas.TextWidth(Copy(Text, indexPos1+1, iSel2 - indexPos1)),
                   Min(BorderWidth + Canvas.TextHeight(Text), R.Bottom));
      Canvas.Brush.Style := bsSolid;
      Canvas.Brush.Color := clHighlight;
      Canvas.Font.Color := clWhite;
      if not RVU_DrawSelectedTextEx(w + CanvasOffX, BorderWidth + CanvasOffY,
        SZ.cx, sraw, Canvas, CanvasScreen,
        -indexPos1+iSel1+1, -indexPos1+iSel2, ItemOptions, rvbdLeftToRight, False,
        FGraphicInterface) then begin
        //Canvas.Font.Size := FontSize;
        Canvas.Brush.Style := bsClear;
        Canvas.TextRect(rSel, rSel.Left + CanvasOffX, rSel.Top + CanvasOffY,
                        Copy(Text, iSel1+1, iSel2 - iSel1 + 1));
        Canvas.Brush.Style := bsSolid;
      end;
    end;

    DoSkin := DrawBoxBorder(Canvas, R, FSkinManager, EditSkinSchemeIndex, True,
      MouseIn, ClickState <> 0, Enabled, Focused, 1);

    if not DoSkin then begin
      if Enabled = True then
        if MouseIn then
          Frame3D(Canvas, R, MouseInBorderColor, MouseInBorderColor, MouseInBorderWidth)
        else if Focused then
          Frame3D(Canvas, R, FocusedBorderColor, FocusedBorderColor, FocusedBorderWidth)
        else
          Frame3D(Canvas, R, EnabledBorderColor, EnabledBorderColor, EnabledBorderWidth)
      else
        Frame3D(Canvas, R, DisabledBorderColor, DisabledBorderColor, DisabledBorderWidth);
      If Color <> clNone then
        Frame3D(Canvas, R, Color, Color, 1);
    end;
  finally
    DeleteDC(DCScreen);
    if DC = 0 then
      ReleaseDC(Handle, editDC);
    Canvas.Handle := 0;
    Canvas.Free;
    CanvasScreen.Handle := 0;
    CanvasScreen.Free;
  end;
  CheckListPosition;
end;

procedure TSRVEdit.WMPaint(var Message: TWMPaint);
begin
  PaintTo(Message.DC);

  if Message.DC = 0 then
    Inherited;
end;

function TSRVEdit.GetCaretPos: TPoint;
begin
  Result := Point(-1, -1);
  if Focused then
    begin
      Windows.GetCaretPos(Result);
//      Result.x := Result.x + BorderWidth*2;
//      Result.y := Result.y + BorderWidth*2;
      Result.x := Result.x + BorderWidth;
      Result.y := Result.y + BorderWidth;
      if (Result.x > Width-BorderWidth) or (Result.y > Height-BorderWidth) then
        Result := Point(-1, -1);
      GetBoundPos;
    end;
end;

function TSRVEdit.GetCaretHeight: Integer;
begin
  Result := -1;
  If Focused then
    Result := Abs(Font.Height);
end;

function TSRVEdit.GetSelected: Boolean;
begin
  Result := False;
  If Focused then
    Result := SelLength > 0;
end;

procedure TSRVEdit.GetBoundPos;
var
     tmp, i : Integer;
     f: Boolean;
begin
  // indexPos1 := SendMessage(Handle, EM_CHARFROMPOS, 0, MAKELPARAM(BorderWidth, 1));
  // indexPos2 := SendMessage(Handle, EM_CHARFROMPOS, 0, MAKELPARAM(ClientWidth - 1, 1));

  indexPos1 := 0;
  indexPos2 := Length(Text)-1;

  f := False;
  for i := 0 to Length(Text)-1 do begin
    tmp := SendMessage(Handle, EM_POSFROMCHAR, i, 0);
    if (tmp and $FF00)<>$FF00 then begin
      if not f then begin
        indexPos1 := i;
        FStartX := tmp and $FF;
        f := True;
        end
      else if f then begin
        if tmp and $FF>ClientWidth then begin
          indexPos2 := i-1;
          break;
        end;
      end;
      end
    else if f then begin
      indexPos2 := i-1;
      break;
    end;
  end;


  iSel1:= 0;
  iSel2:= 0;
  if (SelLength > 0) then begin
    iSel1:= Max(indexPos1, SelStart);
    iSel2:= Min(indexPos2+1, SelStart + SelLength);
    if iSel1 > iSel2 then begin
      tmp := iSel1;
      iSel1 := iSel2;
      iSel2 := tmp;
    end;
  end;
end;

procedure TSRVEdit.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
  if (csDesigning in ComponentState) then
    exit;

  inherited;
    GetBoundPos;
end;

procedure TSRVEdit.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  if (csDesigning in ComponentState) then
    exit;

  inherited;
  if Button=mbLeft then begin
    ClickState := 1;
    if ParentSRV then
      ReleaseCapture;
  end;
end;

procedure TSRVEdit.WMCaptureChanged(var Message: TWMPaint);
begin
  if ClickState=1 then
    ClickState := 2
  else begin
    inherited;
    ClickState := 0;
  end;
  GetBoundPos;
end;

procedure TSRVEdit.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  if (csDesigning in ComponentState) then
    exit;

  GetBoundPos;
  if ClickState=2 then
    SendMessage(Handle, WM_CAPTURECHANGED, 0, 0);
  inherited;
end;

procedure TSRVEdit.SetDropDownHints(const Value: Integer);
begin
  FDropDownHints := Value;
  UpdateFormHints;
end;

procedure TSRVEdit.UpdateFormHints;
var
     CountShowItem, MaxPos, PageSize, ListHeight, SBHeight,
     FormWidth, FormHeight, W, H, edtH : Integer;
     p : TPoint;
begin
  if (lockUpdate <> 0) or not Assigned(EditHints) or not Assigned(FOnEditHints) or
     (FDropDownHints <= 0) then
    Exit;

  if RFormSize.cx = 0 then
    FormWidth := Round(Width * ScaleX) + 2
  else
    FormWidth := Round(RFormSize.cx * ScaleX);

//  if Assigned(FSRVHints) then
//    FormHeight := Round(RFormSize.cy * ScaleY)
//  else
    FormHeight := 0;

  if not Assigned(FSRVHints) then
    Application.CreateForm(TFSRVHints, FSRVHints);

  FSRVHints.EditHints := EditHints;    
  FSRVHints.SkinManager := SkinManager;
  FSRVHints.SkinSchemeIndex := SkinSchemeIndex;
  FSRVHints.ScrollBarSkinSchemeIndex := ScrollBarSkinSchemeIndex;
  FSRVHints.ItemSkinSchemeIndex := ItemSkinSchemeIndex;

  FSRVHints.CalculateDrawItemCount := CalculateDrawItemCount;

  FSRVHints.ImgList := FHintImageList;
  FSRVHints.ScaleX := ScaleX;
  FSRVHints.ScaleY := ScaleY;
  FSRVHints.OffX := OffX;
  FSRVHints.OffY := OffY;
  FSRVHints.OnClose := EditHintsClose;
  FSRVHints.Font := Font;

  FSRVHints.SBButtonHeight := SBButtonHeight;
  FSRVHints.ItemIndex := -1;
  FSRVHints.MouseMoveIndex := HintsItemIndex;
  FSRVHints.DropDownHints := FDropDownHints;
  FSRVHints.FirstItem := HintsItemIndex - (FDropDownHints div 2);
  FSRVHints.FirstItem := Min(Max(FSRVHints.FirstItem, 0), Max(EditHints.Count - FDropDownHints, 0));

  if (FHintStyle = hsOwnerDrawFixed) or (FHintStyle = hsOwnerDrawVariable) then
    FSRVHints.FOnDrawItem := FOnDrawItem
  else
    FSRVHints.FOnDrawItem := nil;

  if (FHintStyle = hsOwnerDrawVariable) and Assigned(FOnMeasureItem) then begin
    FSRVHints.OnMeasureItem := FOnMeasureItem;
    FSRVHints.HintItemHeight := FHintItemHeight;
    if Assigned(FOnMeasureItem) then
      SBHeight := GetScrollBarHeight
    else
      SBHeight := Min(EditHints.Count, FDropDownHints) * FHintItemHeight;
  end else begin
    FSRVHints.OnMeasureItem := nil;
    FSRVHints.HintItemHeight := FSRVHints.Canvas.TextHeight('Text') + FSRVHints.OffY;
    If Assigned(FHintImageList) then
      FSRVHints.HintItemHeight := Max(FHintImageList.Height, FSRVHints.HintItemHeight);
    FSRVHints.HintItemHeight := FSRVHints.HintItemHeight + FSRVHints.OffY;
    SBHeight :=
      Min(EditHints.Count, FDropDownHints) * FSRVHints.HintItemHeight + FSRVHints.OffY;
  end;

  if FormHeight <> 0 then
    SBHeight := FormHeight;

  FSRVHints.ScrollBar.IsSkin := True;
  FSRVHints.ScrollBar.SkinManager := SkinManager;
  FSRVHints.ScrollBar.SkinSchemeIndex := ScrollBarSkinSchemeIndex;

  CountShowItem := SBHeight div FHintItemHeight;
  if SBHeight mod FHintItemHeight > 0 then
    CountShowItem := CountShowItem + 1;
  MaxPos := EditHints.Count;
  PageSize := MaxPos - Max(CountShowItem, 1);
  PageSize := Max(PageSize, MaxPos - FDropDownHints);
  PageSize := Max(PageSize, 0);
  FSRVHints.ScrollBar.PageSize := 0;
  FSRVHints.ScrollBar.Max := MaxPos;
  FSRVHints.ScrollBar.PageSize := FSRVHints.ScrollBar.Max - PageSize + 1;

  FSRVHints.ScrollBar.Visible := PageSize > 0;
  FSRVHints.ScrollBar.Height := Max(SBHeight - FSRVHints.Image1.Height, 0);

  if FSRVHints.ScrollBar.Visible then begin
    FSRVHints.ScrollBar.SmallChange := 1;
    FSRVHints.ScrollBar.LargeChange := FDropDownHints-1;
    FSRVHints.FirstItem := Min(Max(FSRVHints.FirstItem, 0), FSRVHints.ScrollBar.Max);
    FSRVHints.ScrollBar.Position := FSRVHints.FirstItem;
  end;

  if FormHeight = 0 then
    ListHeight := Max(Round(SBHeight * ScaleY), FHintItemHeight) + 2
  else
    ListHeight := FormHeight;

  if ParentSRV or (ScreenOffX <> 0) or (ScreenOffY <> 0) then
    p := Point(ScreenOffX, ScreenOffY)
  else
    p := ClientToScreen(Point(0, 0));

  W := Round(FormWidth);
  H := ListHeight;
  edtH := Round(Height * ScaleY);
{$IFDEF RICHVIEWDEF6}
  if p.x + W > Screen.WorkAreaRect.Right then
    p.x := Screen.WorkAreaRect.Right - W;
  if p.y + edtH + H > Screen.WorkAreaRect.Bottom then
{$ELSE}
  if p.x + W > Screen.DesktopLeft + Screen.DesktopWidth then
    p.x := Screen.DesktopLeft + Screen.DesktopWidth - W;
  if p.y + edtH + H > Screen.DesktopTop + Screen.DesktopHeight then
{$ENDIF}
    p.y := p.y - H - 3
  else
    p.y := p.y + edtH;

  FSRVHints.SetBounds(p.x, p.y, W, H);
  FSRVHints.ScrollBar.Height := Round(H / ScaleY) - 2;
  FSRVHints.ScrollBar.Left := -FSRVHints.ScrollBar.Width;

  FHintDropingDown := True;

  FSRVHints.Reset;
  if not FSRVHints.Visible then
    FSRVHints.Show
  else
    FSRVHints.Paint;

  if Visible and Enabled and not Focused then begin
    _CanSelectAll := False;
    SetFocus;
    _CanSelectAll := True;
  end;

  Repaint;
  if Assigned(FOnHintDropDown) then
    FOnHintDropDown(Self);
end;

procedure TSRVEdit.ShowHints;
begin
  if (lockUpdate <> 0) then Exit;
  // заполняем список подсказаками
  if not Assigned(EditHints) then
    EditHints := TStringList.Create;

  if EditHints.Count > 0 then
    EditHints.Clear;

  HintsItemIndex := -1;
  if Assigned(FSRVHints) then
    FSRVHints.ItemIndex := -1;
  if Assigned(FOnEditHints) then
    FOnEditHints(Self, Text, EditHints, HintsItemIndex);

  if EditHints.Count = 0 then begin
    if Assigned(FSRVHints) then
      FSRVHints.Close;
    EditHints.Free;
    EditHints := nil;
    end
  else
    UpdateFormHints;
end;

procedure TSRVEdit.EditHintsClose(Sender: TObject; var Action: TCloseAction);
var
     NewText : String;
begin
  FHintDropingDown := False;
  if FSRVHints.ItemIndex >= 0 then
    NewText := EditHints[FSRVHints.ItemIndex]
  else
    NewText := Text;

  if Assigned(FOnCloseHints) then
    FOnCloseHints(Self, FSRVHints.Width, FSRVHints.Height, NewText);
  RFormSize.cx := Round(FSRVHints.Width / ScaleX);
  RFormSize.cy := Round(FSRVHints.Height / ScaleY);
  if FSRVHints.ItemIndex >= 0 then begin
    BeginUpdate;
    Text := NewText;
    SelLength := 0;
    SelStart := Length(NewText);
    EndUpdate;
    if Visible and Enabled then
      SetFocus;
  end;
end;

function TSRVEdit.GetScrollBarHeight : Integer;
var
     index, NewItemHeight : Integer;
begin
  Result := 0;
  for index := 0 to FDropDownHints-1 do begin
    NewItemHeight := FHintItemHeight;
    if Assigned(FOnMeasureItem) then
      FOnMeasureItem(Self, FSRVHints.FirstItem + index, NewItemHeight);
    Inc(Result, NewItemHeight);
  end;
end;

function TSRVEdit.CalculateDrawItemCount(FirstItem, Height, HintItemHeight : Integer) : Integer;
var
     i, Y, H : Integer;
begin
  Result := 0;
  if not Assigned(EditHints) then
    exit;

  Y := OffY;
  for i:=FirstItem to EditHints.Count-1 do begin
    H := HintItemHeight;
    if (FHintStyle = hsOwnerDrawVariable) and Assigned(FOnMeasureItem) then
      FOnMeasureItem(Self, i, H);
    Y := Y + H;
    if (Y > Height) then
      Break;
    Result := i - FirstItem + 1;
  end;
end;

procedure TSRVEdit.KeyDown(var Key: Word; Shift: TShiftState);
begin
//  if (Key <> VK_DELETE) and (Key <> VK_BACK) then
//    isUpdateAutoText := True;
  if (Key = VK_F4) and not FHintDropingDown then
    ShowHints
  else if Assigned(FSRVHints) and FSRVHints.Visible and
    ((Key = VK_UP) or (Key = VK_DOWN) or
     (Key = VK_PRIOR) or (Key = VK_NEXT) or
     (Key = VK_HOME) or (Key = VK_END) or
     ((Key = VK_SPACE) and (ssCtrl in Shift)) or (Key = VK_F4) or
     (Key = VK_RETURN) or (Key = VK_ESCAPE)) then begin
    FSRVHints.FormKeyDown(nil, Key, Shift);
    Key := 0;
  end else
    inherited;

  If ParentSRV and (Parent <> nil) and (Parent.ClassName <> 'TSRichViewEdit') then begin
    GetBoundPos;
    PostMessage(Parent.Handle, WM_REPAINT_CONTROL, 0, 0);
  end;

{  if (lockUpdate = 0) then
    Repaint;}
end;

procedure TSRVEdit.KeyUp(var Key: Word; Shift: TShiftState);
begin
  inherited;

  If ParentSRV and (Parent <> nil) and (Parent.ClassName <> 'TSRichViewEdit') then begin
    GetBoundPos;
    PostMessage(Parent.Handle, WM_REPAINT_CONTROL, 0, 0);
  end;
end;

procedure TSRVEdit.WMKillFocus(var Message: TMessage);
begin
  if Assigned(FSRVHints) and FSRVHints.Visible and
    (FSRVHints.Handle <> THandle(Message.WParam)) and
    (Handle <> THandle(Message.WParam)) then begin
    FSRVHints.Close;
  end;
  inherited;
end;

procedure TSRVEdit.BeginUpdate;
begin
  Inc(lockUpdate);
end;

procedure TSRVEdit.EndUpdate;
begin
  Dec(lockUpdate);
end;

function  TSRVEdit.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint) : Boolean;
begin
  inherited DoMouseWheel(Shift, WheelDelta, MousePos);

  if Assigned(FSRVHints) and FSRVHints.Visible then
    FSRVHints.FormMouseWheel(Self, Shift, WheelDelta, MousePos, Result);

  Result := True;
end;

procedure TSRVEdit.CheckListPosition;
var
     p : TPoint;
begin
  if not FHintDropingDown then
    Exit;
  if ParentSRV then
    p := Point(ScreenOffX, ScreenOffY)
  else
    p := ClientToScreen(Point(0, 0));
  if (FSRVHints.Left <> p.x) or (FSRVHints.Top <> p.y + Round(Height * ScaleY)) then
    UpdateFormHints;
end;


end.
