分享

Editor Window ece simple code Editor

 quasiceo 2013-05-01
http://ece./svn-history/r129/src/EditorWindow.pas
// ************************************************************
//
// Editor Window
// Copyright (c) 2010  zeDevel
//
// 朽琊噌铗麒? 岭嚆钿囵邂 赔沐龛? ze0nni@gmail.com
//
// ************************************************************


unit EditorWindow;
// test comments line
{$IFDEF fpc}{$MODE delphi}{$ENDIF}

interface

uses
  Windows,
  Messages,
  Classes,
  SysUtils,
  iece,
  zeWndControls,
  DocumentWindow,
  BaseFile,
  math,
  eceSynParser;

type
  TGutter = class;

  TLine = class;

  TTokenClass = class;

  TTokenClassList = class;

  TToken = class;

  TCaret = class;

  /// <summary>
  /// 孰囫?- 铌磬 疱潢囹铕?觐溧
  /// </summary>

  TEceEditorState = (esEdit, esPanaram);

  TEceEditorWindow = class(TEceDocumentWindow, IDispatch, IEceDocument,
    IEceEditor)
  private
    FFileName: string;
    FFonts: array [0 .. 3] of HFont;
    FFontExtraSpace: array [0 .. 3] of integer;
    FLineModificationChecker: integer;
    FCharSize: TSize;
    FGutter: TGutter;
    FCaret: TCaret;
    FBackBuffer: HBitmap;
    FLines: TList;
    FVisibleLines: TList;
    FUpdateLockCount: integer;
    FOffsetX: integer;
    FOffsetY: integer;

    FTokens: TTokenClassList;

    FPlugins: TInterfaceList;
    FTextColor: integer;
    FBackgroundColor: integer;

    FSyntaxParser: TEceSynParser;
    FState: TEceEditorState;
    FPanaramModeMousePt: TPoint; // 项腩驽龛?禧 镳?镟磬疣扈痤忄龛?

    Function GetCount: integer;
    Function GetStrings(const Index: integer): string;
    procedure SetStrings(const Index: integer; const value: string);
    function GetLines(const index: integer): TLine;
    function GetVisibleLines(const index: integer): TLine;
    procedure SetOffsetX(const value: integer);
    procedure SetOffsetY(const value: integer);
    function GetEditorRect: TRect;
    function GetCharsInHeight: integer;
    function GetCharsInWidth: integer;
    procedure LineModification;
    procedure SetBackgroundColor(const value: integer);
    procedure SetTextColor(const value: integer);
    procedure SetState(const value: TEceEditorState);
  protected
    function CreateCaret: TCaret; virtual;
    function CreateLine: TLine; virtual;
  protected
    procedure CreateParams(var Param: CreateStruct); override;
    procedure wmPaint(var msg: TMessage);
    message WM_PAINT;
    procedure wmSize(var msg: TWMSize);
    message WM_SIZE;
    procedure wmGetDlgCode(var msg: TWmGetDlgCode);
    message WM_GETDLGCODE;
    procedure wmKeyDown(var msg: TWmKeyDown);
    message WM_KEYDOWN;
    procedure wmChar(var msg: TWmChar);
    message WM_CHAR;
    procedure wmSetCursor(var msg: TWmSetCursor);
    message WM_SETCURSOR;

    procedure wmLbuttonDown(var msg: TWmLbuttonDown);
    message WM_LBUTTONDOWN;
    procedure wmLbuttonUp(var msg: TWMLButtonUp);
    message WM_LBUTTONUP;
    procedure wmMouseMove(var msg: TWMMouseMove);
    message WM_MOUSEMOVE;
    procedure wmMButtonDown(var msg: TWmLbuttonDown);
    message WM_MBUTTONDOWN;
    procedure wmMButtonUp(var msg: TWMLButtonUp);
    message WM_MBUTTONUP;

    procedure wmMouseWheel(var msg: TWMMouseWheel);
    message WM_MOUSEWHEEL;

    procedure onVscroll(pos: integer; EndScroll: boolean); override;
    procedure onHscroll(pos: integer; EndScroll: boolean); override;

    function GetDocumentFileName: string; override;
    function GetDocumentTitle: string; override;
  protected
    procedure _BeginUpdate; safecall;
    procedure _EndUpdate; safecall;
    function _GetLinesCount: integer; safecall;
    function _GetLines(AIndex: integer): IEceLine; safecall;
    function _GetGutter: IGutter; safecall;
    function _GetCaret: ICaret; safecall;
    function _AddLine: IEceLine; safecall;
    function _InsertLine(Index: integer): IEceLine; safecall;
    procedure _DeleteLine(Index: integer); safecall;
  public
    Constructor Create(Parent: Cardinal; AApplication: IEceApplication);
    Destructor Destroy; override;

    /// <summary>玉蜞磬怆桠屐 玎溧眄 梏?玎溧眄钽?疣珈屦?/summary>
    procedure SetFont(AFont: String; Size: integer);
    /// <summary>吾眍忤螯 铌眍 疱溧牝铕?/summary>
    procedure Invalidate;
    /// <summary>
    /// 杨猁蜩 忸珥桕噱?镳?镱塍麇龛?镱蝈痍 纛痍?纛牦襦, 磬耠邃篁?
    /// 铗 觌囫襦 TzeWndControl
    /// </summary>
    procedure SetFocus; override;
    procedure KillFocus; override;

    /// <summary>
    /// 橡铞邃箴?潆 黩屙?/青镨耔 羿殡钼
    /// 亦?驽 铐?篑蜞磬怆桠帼 疋铋耱忸 FileName
    /// </summary>
    procedure SaveToFile(AFileName: string); override;
    procedure LoadFromFile(AFileName: string); override;
    /// <summary>
    /// 泽黻鲨 玎沭箸觇 鲡弪钼铋 聃屐?
    /// </summary>
    procedure LoadColorTheme(AFileName: string);

    property State: TEceEditorState read FState write SetState;

    property Caret: TCaret Read FCaret;
    property Gutter: TGutter read FGutter;

    property Tokens: TTokenClassList Read FTokens;
    Property Count: integer Read GetCount;
    property Strings[const index: integer]
      : string Read GetStrings Write SetStrings;
    property Lines[const index: integer]: TLine read GetLines;
    property VisibleLines[const index: integer]: TLine read GetVisibleLines;
    procedure Clear;
    function AddString(const ANewString: string): integer;
    function InsertString(Const ANewString: string; AIndex: integer): integer;

    function AddLine: TLine;
    function InsertLine(AIndex: integer): TLine;
    procedure DeleteLine(const index: integer);

    Procedure BeginUpdate;
    Procedure EndUpdate;

    property BackgroundColor: integer read FBackgroundColor write
      SetBackgroundColor;
    property TextColor: integer read FTextColor write SetTextColor;

    property CharWidth: integer read FCharSize.Cx;
    property CharHeight: integer read FCharSize.Cy;
    property OffsetX: integer Read FOffsetX Write SetOffsetX;
    property OffsetY: integer Read FOffsetY Write SetOffsetY;

    property EditorRect: TRect Read GetEditorRect;
    property CharsInHeight: integer Read GetCharsInHeight;
    property CharsInWidth: integer Read GetCharsInWidth;

    procedure LoadPlugin(AFileName: string);
  end;

  TGutter = class(TEceInterfacedObject, IDispatch, IGutter)
  private
    FSize: Cardinal;
    FEditor: TEceEditorWindow;
    Function GetSize: Cardinal;
  public
    Constructor Create(AEditor: TEceEditorWindow);
    Procedure Draw(DC: HDC; Rt: TRect);
    Property Size: Cardinal read GetSize;
  end;

  // 项赅玎蝈朦 蝾泐, 赅?恹溴脲磬 耱痤赅
  TLineSelectionMode = (selNone, // 湾 恹溴脲磬
    selFull, // 项腠铖
    selStart, // 秧圜嚯?
    selEnd, // ?觐眦?
    selMiddle // ?皴疱滂礤
    );

  TLine = class(TEceInterfacedObject, IDispatch, IEceLine)
  private
    FText: String;
    FVisible: boolean;
    { 箨噻噱?磬 耱痤牦, 漕 觐蝾痤? 犭铌 疋屦眢?}
    FRolllUpFor: TLine;
    { 扬桉铌 忮痦篁 耱痤?}
    FRollUpLines: TList;
    { 耱痤赅, 觐蝾疣 镱耢咫?疋屦眢螯 }
    FRollUpIn: TLine;
    FLineIndex: integer;
    FVisibleIndex: integer;
    FLineModificationChecker: integer;
    FEditor: TEceEditorWindow;
    FisRollUp: boolean;
    FLevel: integer;
    Function GetText: String;
    Procedure SetText(const value: String);
    procedure SetVisible(const value: boolean);
    function GetLineIndex: integer;
    function GetLength: integer;
    Function GetisRollBlock: boolean;
    Procedure SetIsRollUp(const value: boolean);
    function GetVisibleIndex: integer;
    procedure UpdateLinesIndex;
    function GetisEndInLevel: boolean;
    procedure RestorStyle(DC: HDC);
  protected
    { 扬桉铌 蝾赍眍?}
    FTokens: TList;

    function _GetText: string; safecall;
    function _SetText(Text: string): integer; safecall;
    function _GetIndex: integer; safecall;
    procedure UpdateSyn; virtual;
  public
    Constructor Create(AEditor: TEceEditorWindow);
    Destructor Destroy; override;
    Procedure Insert(const AString: String; AChar: integer);
    procedure Delete(AChar, ACount: integer);
    Procedure BreakLine(AChar: integer);
    procedure Draw(DC: HDC; Cx, Cy, StartChar: integer);
    Procedure Invalidate;
    property Editor: TEceEditorWindow read FEditor;
    property isRollBlock: boolean read GetisRollBlock;
    property isRollUp: boolean read FisRollUp write SetIsRollUp;
    property Text: String Read GetText write SetText;
    property Visible: boolean read FVisible Write SetVisible;
    property LineIndex: integer read GetLineIndex;
    property VisibleIndex: integer read GetVisibleIndex;
    property Length: integer read GetLength;
    property isEndInLevel: boolean read GetisEndInLevel;
    property Level: integer read FLevel;
  end;

  // 孰囫?蝾赍磬
  TTokenClassType = (ttSeparator, // 朽玟咫栩咫? 祛驽?猁螯 蝾朦觐 耔焘铍
    ttWord, // 橡铖蝾 耠钼?
    ttBeginRegion, // 袜鬣腩 疱汨铐?
    ttEndRegion, // 暑礤?疱汨铐?
    // 岭铌, ?铗腓麒?铗 疱汨铐?礤 桁邋?
    // 忤珞嚯 镳桤磬觐?
    ttBeginBlock, // 袜鬣腩 犭铌?
    ttEndBlock); // 暑礤?犭铌?

  // 翌赍? 赅?徉珙恹?觌囫?
  TTokenClass = class
  private
    FEditor: TEceEditorWindow;
    FName: string;
    FTokenType: TTokenClassType;
    FTextColor: integer;
    FFontStyle: integer;
    FUnderline: boolean;
    FBkColor: integer;
    FStrick: boolean;

    // 扬桉觐 蝾赍眍?忭篁痂 觐蝾瘥?祛驽?磬躅滂螯? 铗
    FInclueIn: TList;
    // 谚耜?蝾赍眍? 忭篁痂 觐蝾瘥?礤 祛驽?磬躅滂螯?
    FExceptOut: TList;

    procedure SetBkColor(const value: integer);
    procedure SetFontStyle(const value: integer);
    procedure SetStrick(const value: boolean);
    procedure SetTextColor(const value: integer);
    procedure SetUnderline(const value: boolean);
  public
    constructor Create(AEditor: TEceEditorWindow);
    property Name: string read FName Write FName;
    property TokenType: TTokenClassType read FTokenType;
    property Editor: TEceEditorWindow read FEditor;
    // 妈珞嚯铄 铘铕祀屙桢
    property FontStyle: integer read FFontStyle write SetFontStyle;
    property Underline: boolean read FUnderline write SetUnderline;
    property Strick: boolean read FStrick write SetStrick;
    property TextColor: integer read FTextColor write SetTextColor;
    property BkColor: integer read FBkColor write SetBkColor;
  end;

  // 扬桉铌 怦艴 觌囫耦?蝾赍眍?漕牦戾眚?
  TTokenClassList = class
  private
    FEditor: TEceEditorWindow;
    FTokens: TStringList;
    function GetTokens(const ATokenKey: string): TTokenClass;
  public
    constructor Create(AEditor: TEceEditorWindow);
    destructor Destroy; override;

    procedure Clear;

    function NewToken(AName: string; AType: TTokenClassType): TTokenClass;
    property Tokens[const ATokenKey: string]: TTokenClass read GetTokens;
    default;
  end;

  // 燕?蝾赍?
  TToken = class
  private
    FTokenClass: TTokenClass;
    FLength: Cardinal;
    FFirstChar: Cardinal;
    procedure SetFirstChar(const value: Cardinal);
    procedure SetLength(const value: Cardinal);

  public
    constructor Create(ATokenClass: TTokenClass);
    property TokenClass: TTokenClass read FTokenClass;
    property FirstChar: Cardinal read FFirstChar write SetFirstChar;
    property Length: Cardinal read FLength write SetLength;
    procedure ApplyStyle(DC: HDC);
  end;

  TCaretStyle = (csNormal, csClassic);

  TSelectionRange = record
    selStart: TPoint;
    selEnd: TPoint;
  end;

  TCaret = class(TEceInterfacedObject, IDispatch, ICaret)
  private
    FEditor: TEceEditorWindow;
    Fx, Fy: integer;
    FStyle: TCaretStyle;
    FSelection: boolean;
    FSelStartX: integer;
    FSelStartY: integer;
    procedure SetStyle(Const value: TCaretStyle);
    function GetLine: integer;
    function GetSelectionMode: boolean;
    function GetHaveSelection: boolean;
    function GetSelectionRange: TSelectionRange;
  protected
    Procedure SetX(Const value: integer); virtual;
    Procedure SetY(Const value: integer); virtual;
    function _GetX: integer; safecall;
    function _GetY: integer; safecall;
    function _SetX(value: integer): integer; safecall;
    function _SetY(value: integer): integer; safecall;
  public
    Constructor Create(AEditor: TEceEditorWindow);
    property Editor: TEceEditorWindow read FEditor;
    property X: integer read Fx Write SetX;
    property Y: integer read Fy write SetY;
    procedure SetXY(const Ax, Ay: integer);
    // 杨铗忮蜮簋?疱嚯 耱痤赅?
    property SelStartX: integer read FSelStartX;
    property SelStartY: integer read FSelStartY;
    property SelectionMode: boolean read GetSelectionMode;
    property HaveSelection: boolean read GetHaveSelection;
    property Line: integer Read GetLine;
    property Style: TCaretStyle read FStyle Write SetStyle;
    property SelectionRange: TSelectionRange read GetSelectionRange;
    procedure Show;
    procedure Hide;
    procedure Update;
  end;

implementation

function isKeyDown(key: integer): boolean;
begin
  Result := (GetKeyState(key) and 128) = 128
end;

{ TEceEditorWindow }

procedure TEceEditorWindow.CreateParams(var Param: CreateStruct);
begin
  inherited;
  Param.Style := Param.Style or WS_VSCROLL or WS_HSCROLL;
  Param.dwExStyle := WS_EX_CLIENTEDGE;
end;

procedure TEceEditorWindow.wmPaint(var msg: TMessage);
var
  Ps: TPaintStruct;
  Rt: TRect;
  CDC: HDC;
  LineStart, LineEnd, i, j, lleft: integer;
  LineTop, LineLen: integer;
  LineO: TLine;
  Line: Pchar;
  Brush: HBrush;
  ClipRgn : HRGN;
begin
  BeginPaint(Handle, Ps);
  CDC := CreateCompatibleDC(Ps.HDC);
  SelectObject(CDC, FBackBuffer);

  // 砚屦躞 怦邈?钽?徨珙狃噻? 痂耋屐 泱蝌屦
  {DONE 1 -oOnni -cBug : 软蝈疱耥 汶, 羼腓 镥鬣蜞螯 蝈犟?磬 镱耠邃礤?耱痤赍,
  蝾 镳?镥鬣蜩 15 ?16 耔焘铍?磬犭帼蝰 镳钺脲禧 ?铗痂耦怅铋, 蒡钽?礤 镳铊聃钿栩 羼腓
  皿蝌屦 痂耋弪? 漕, 珥圜栩 眢骓?痂耦忄螯 泱蝌屦 漕 ?桎咫囹?ClipRect}
  FGutter.Draw(CDC, Ps.rcPaint);
  GetClientRect(Handle, Rt);
  ClipRgn := CreateRectRgn(Gutter.Size, 0, Rt.Right, rt.Bottom);
  SelectClipRgn(CDC, ClipRgn);

  { TODO -oOnni -cGeneral : 项耱?眄?耦玟帼蝰 狃帙?}
  Brush := CreateSolidBrush(BackgroundColor);
  FillRect(CDC, EditorRect, Brush);
  DeleteObject(Brush);

  LineStart := OffsetY;
  LineEnd := LineStart + (EditorRect.Bottom div CharHeight) + 1;
  if LineEnd > FVisibleLines.Count - 1 then
    LineEnd := Count - 1;

  LineTop := 0;
  Rt := EditorRect;
  Rt.Bottom := CharHeight;
  for i := LineStart to LineEnd do
  begin
    if i > FVisibleLines.Count - 1 then
      continue;
    LineO := TLine(FVisibleLines[i]);
    if LineO = nil then
      continue;
    lleft := Rt.Left;
    LineO.Draw(CDC, Rt.Left, Rt.Top, OffsetX);
    OffsetRect(Rt, 0, CharHeight);
  end;

  (*
  // 砚屦躞 怦邈?钽?徨珙狃噻? 痂耋屐 泱蝌屦
  FGutter.Draw(CDC, Ps.rcPaint);
  *)

  BitBlt(Ps.HDC, Ps.rcPaint.Left, Ps.rcPaint.Top,
    Ps.rcPaint.Right - Ps.rcPaint.Left, Ps.rcPaint.Bottom - Ps.rcPaint.Top,
    CDC, Ps.rcPaint.Left, Ps.rcPaint.Top, SRCCOPY);
  SelectClipRgn(CDC, 0);
  DeleteObject(ClipRgn);
  DeleteDc(CDC);
  EndPaint(Handle, Ps);
end;

procedure TEceEditorWindow.wmSize(var msg: TWMSize);
var
  Rt: TRect;
begin
  if FUpdateLockCount <> 0 then
    exit;

  // 杨玟噱?玎漤栝 狍翦?
  DeleteObject(FBackBuffer);

  GetWindowRect(Handle, Rt);
  FBackBuffer := CreateBitmap(Rt.Right, Rt.Bottom, 1, 32, nil);

  // 如戾?屐 疣珈屦 耱疣龛鳆 耜痤腚?

  VScroll.Page := EditorRect.Bottom div CharHeight;
  HScroll.Page := (EditorRect.Right - EditorRect.Left) div CharWidth;

  onVscroll(VScroll.pos, true);
  onHscroll(HScroll.pos, true);

  InvalidateRect(Handle, nil, false);
end;

function TEceEditorWindow._AddLine: IEceLine;
begin
  Result := AddLine;
end;

procedure TEceEditorWindow._BeginUpdate;
begin
  BeginUpdate;
end;

procedure TEceEditorWindow._DeleteLine(Index: integer);
begin
  DeleteLine(Index);
end;

procedure TEceEditorWindow._EndUpdate;
begin
  EndUpdate;
end;

function TEceEditorWindow._GetCaret: ICaret;
begin
  Result := Caret;
end;

function TEceEditorWindow._GetGutter: IGutter;
begin
  Result := Gutter;
end;

function TEceEditorWindow._GetLines(AIndex: integer): IEceLine;
begin
  Result := Lines[AIndex];
end;

function TEceEditorWindow._GetLinesCount: integer;
begin
  Result := FLines.Count
end;

function TEceEditorWindow._InsertLine(Index: integer): IEceLine;
begin
  Result := InsertLine(Index);
end;

procedure TEceEditorWindow.wmGetDlgCode(var msg: TWmGetDlgCode);
begin
  msg.Result :=
    DLGC_HASSETSEL or DLGC_WANTALLKEYS or DLGC_WANTARROWS or DLGC_WANTCHARS or
    DLGC_WANTMESSAGE or DLGC_WANTTAB;
end;

procedure TEceEditorWindow.wmKeyDown(var msg: TWmKeyDown);
begin
  case msg.CharCode of
    VK_UP:
      Caret.Y := Caret.Y - 1;
    VK_DOWN:
      Caret.Y := Caret.Y + 1;
    VK_LEFT:
      Caret.X := Caret.X - 1;
    VK_RIGHT:
      Caret.X := Caret.X + 1;
    VK_HOME:
      begin
        if isKeyDown(VK_CONTROL) then
          Caret.Y := 0;
        Caret.X := 0;
      end;
    VK_END:
      begin
        if isKeyDown(VK_CONTROL) then
          Caret.Y := Count - 1;
        Caret.X := Lines[Caret.Line].Length;
      end;
    VK_DELETE:
      begin
        Lines[Caret.Line].Delete(Caret.X + 1, 1);
      end;
  end;
end;

procedure TEceEditorWindow.wmChar(var msg: TWmChar);
var
  LIndex: integer;
begin
  inherited;
  try
    case msg.CharCode of
      VK_RETURN:
        begin
          { 蝮?磬漕 妁?皲咫囹?噔蝾铗耱箫??怦?蜞觐?}
          Lines[Caret.Line].BreakLine(Caret.X + 1);
          Caret.X := 0;
          Caret.Y := Caret.Y + 1;
        end;
      VK_BACK:
        begin
          if Caret.X > 0 then
          begin
            // 愉嚯屐 耔焘铍
            Lines[Caret.Line].Delete(Caret.X, 1);
            Caret.X := Caret.X - 1;
          end
          else
          begin
            if Caret.Y > 0 then
            begin
              LIndex := Caret.Line;
              Caret.X := Lines[LIndex - 1].Length;
              Caret.Y := Caret.Y - 1;
              // todo: 橡?钺疣龛??疋屦眢蝾?耱痤赍 铐?漕腈磬 疣玮屦眢螯?
              Lines[LIndex - 1].Text := Lines[LIndex - 1].Text + Lines[LIndex]
                .Text;
              DeleteLine(LIndex);
              LineModification;
              Invalidate;
            end;
          end;
        end;
      VK_ESCAPE:
        begin

        end;
      VK_TAB:
        begin
          Lines[Caret.Line].Insert('  ', Caret.X + 1);
          Caret.X := Caret.X + 2;
        end;
    else
      begin
        Lines[Caret.Line].Insert(Char(msg.CharCode), Caret.X + 1);
        Caret.X := Caret.X + 1;
      end;
    end;
  except
  end;
end;

procedure TEceEditorWindow.wmSetCursor(var msg: TWmSetCursor);
var
  pt: TPoint;
begin
  GetCursorPos(pt);
  ScreenToClient(Handle, pt);

  if msg.Hittest = 1 then
  begin
    if pt.X < Gutter.Size then
    begin
      SetCursor(LoadCursor(0, IDC_ARROW));
    end
    else
    begin
      case State of
        esEdit:
          SetCursor(LoadCursor(0, IDC_IBEAM));
        esPanaram:
          SetCursor(LoadCursor(0, IDC_SIZEALL));
      end;
    end;
  end
  else
    inherited;
end;

procedure TEceEditorWindow.wmLbuttonDown(var msg: TWmLbuttonDown);
var
  LineO: TLine;
begin
  if (msg.xPos < Gutter.Size) and (msg.xPos > Gutter.Size - CharHeight) then
  { 袜驵腓 镱 觏铒赍 疋铕圜桠囗? 犭铌?}
  begin
    try
      LineO := TLine(FVisibleLines[(msg.yPos div CharHeight) + OffsetY]);
      if LineO.isRollBlock then
        LineO.isRollUp := not LineO.isRollUp;
    except

    end;
  end
  else if (msg.xPos > Gutter.Size) then
  { 袜驵腓 镱 镱膻 }
  begin
    SetCapture(Handle);
    Caret.SetXY( { X }
      ((msg.xPos + (CharWidth div 2) - Gutter.Size) div CharWidth) + OffsetX,
      { Y }
      (msg.yPos div CharHeight) + OffsetY);
  end;
  SetFocus;
end;

procedure TEceEditorWindow.wmLbuttonUp(var msg: TWMLButtonUp);
begin
  ReleaseCapture;
end;

procedure TEceEditorWindow.wmMButtonDown(var msg: TWmLbuttonDown);
begin
  State := esPanaram;
  SendMessage(Handle, WM_SETCURSOR, 0, MakeWParam(1, 0));
  FPanaramModeMousePt.X := msg.xPos;
  FPanaramModeMousePt.Y := msg.yPos;
  SetCapture(Handle);
end;

procedure TEceEditorWindow.wmMButtonUp(var msg: TWMLButtonUp);
begin
  State := esEdit;
  ReleaseCapture;
end;

procedure TEceEditorWindow.wmMouseMove(var msg: TWMMouseMove);
var
  Rt: TRect;
  pt: TPoint;
begin
  case State of
    esEdit:
{$REGION '拟桄屙桢 禧?眍痨嚯铎 疱骅戾'}
      if GetCapture = Handle then
      begin
        Caret.SetXY(((msg.xPos + (CharWidth div 2) - Gutter.Size)
              div CharWidth) + OffsetX, (msg.yPos div CharHeight) + OffsetY);
      end;
{$ENDREGION}
    esPanaram:
{$REGION '拟桄屙桢 ?疱骅戾 镟磬疣扈痤忄龛'}
      if GetCapture = Handle then
      begin
        // 妙痂珙眚嚯?
        if abs(FPanaramModeMousePt.X - msg.xPos) > CharWidth then
        begin
          OffsetX := OffsetX + (FPanaramModeMousePt.X - msg.xPos) div CharWidth;
          FPanaramModeMousePt.X := msg.xPos;
        end;
        // 洛痱桕嚯?
        if abs(FPanaramModeMousePt.Y - msg.yPos) > CharHeight then
        begin
          OffsetY := OffsetY + (FPanaramModeMousePt.Y - msg.yPos)
            div CharHeight;
          FPanaramModeMousePt.Y := msg.yPos;
        end;
        (*
          GetClientRect(Handle, Rt);
          //妙痂珙眚嚯?
          if msg.xPos < 0 then
          begin
          GetCursorPos(pt);
          pt.X := pt.X + Rt.Right;
          SetCursorPos(pt.X, pt.X);
          FPanaramModeMousePt.X := msg.xPos + Rt.Right;
          end;
          if msg.xPos > Rt.Right then
          begin
          GetCursorPos(pt);
          pt.X := pt.X - Rt.Right;
          SetCursorPos(pt.X, pt.X);
          FPanaramModeMousePt.X := msg.xPos - Rt.Right;
          end;
          //洛痱桕嚯?
          if msg.yPos < 0 then
          begin
          GetCursorPos(pt);
          pt.Y := pt.Y + Rt.Bottom;
          SetCursorPos(pt.X, pt.Y);
          FPanaramModeMousePt.Y := msg.yPos + Rt.Bottom;
          end;
          if msg.yPos > Rt.Bottom then
          begin
          GetCursorPos(pt);
          pt.Y := pt.Y - Rt.Bottom;
          SetCursorPos(pt.X, pt.Y);
          FPanaramModeMousePt.Y := msg.yPos - Rt.Bottom;
          end;
          *)
      end;
{$ENDREGION}
  end;
end;

procedure TEceEditorWindow.wmMouseWheel(var msg: TWMMouseWheel);
begin
  if msg.Keys = 0 then
  begin
    if msg.WheelDelta < 0 then
      OffsetY := OffsetY + 3
    else
      OffsetY := OffsetY - 3
  end
  else if (msg.Keys and MK_CONTROL) = MK_CONTROL then
  begin
    if msg.WheelDelta < 0 then
      OffsetY := OffsetY + VScroll.Page
    else
      OffsetY := OffsetY - VScroll.Page
  end
  else if (msg.Keys and MK_SHIFT) = MK_SHIFT then
  begin
    if msg.WheelDelta < 0 then
      Caret.Y := Caret.Y + 1
    else
      Caret.Y := Caret.Y - 1;
  end;
end;

procedure TEceEditorWindow.SetBackgroundColor(const value: integer);
begin
  FBackgroundColor := value;
end;

procedure TEceEditorWindow.SetFocus;
begin
  Inherited;
  Caret.Show;
  Caret.Update;
end;

procedure TEceEditorWindow.KillFocus;
begin
  Inherited;
  Caret.Hide;
end;

procedure TEceEditorWindow.SaveToFile(AFileName: string);
var
  i: integer;
  f: TextFile;
begin
  AssignFile(f, AFileName);
  Rewrite(f);
  for i := 0 to Count - 1 do
    WriteLn(f, Strings[i]);
  CloseFile(f);
end;

procedure TEceEditorWindow.LoadColorTheme(AFileName: string);
var
  bf: TBaseFile;
  procedure ReadStyle(AStyle: string);
  var
    Tk: TTokenClass;
    FontStyle: String;
  begin
    Tk := Tokens.NewToken(AStyle, ttWord);
    Tk.BkColor := bf.IntValue(AStyle + '.Background.Color',
      Self.BackgroundColor);
    Tk.TextColor := bf.IntValue(AStyle + '.Text.Color', Self.TextColor);
    FontStyle := bf.StrValue(AStyle + '.Text.Style', 'Normal');
    if pos('Bold', FontStyle) <> 0 then
      Tk.FontStyle := Tk.FontStyle or 1;
    if pos('Italic', FontStyle) <> 0 then
      Tk.FontStyle := Tk.FontStyle or 2;
  end;

begin
  // 俞桊噱?怦?黩?猁腩 疣礤?
  Tokens.Clear;
  // 灭箸桁 桤 羿殡?桦?桤 蝾泐 黩?磬殇屐 =)
  bf := TBaseFile.Create;
  if FileExists(AFileName) then
    bf.LoadFromFile(AFileName); // 软圜?狍溴玎沭箸赅 耦 珥圜屙?扈 镱 溴纛腧?
  BackgroundColor := bf.IntValue('Normal.Background.Color', $FFFFFF);
  TextColor := bf.IntValue('Normal.Text.Color', $000000);
  // 灭箸桁 镱 铟屦邃?
  ReadStyle('Normal');
  ReadStyle('Comments');
  ReadStyle('Comments.Line');
  ReadStyle('Comments.Block');
  ReadStyle('Keywords');
  ReadStyle('Commands');
  ReadStyle('Strings');
  ReadStyle('Symbols');
  ReadStyle('Symbols.Hover');
  ReadStyle('Numbers');
  ReadStyle('Numbers.Integer');
  ReadStyle('Numbers.Float');
  ReadStyle('Numbers.Bin');
  ReadStyle('Numbers.Oct');
  ReadStyle('Numbers.Hex');
  ReadStyle('StdIn');
  ReadStyle('StdOut');
  ReadStyle('StdErr');
  bf.Free;
end;

procedure TEceEditorWindow.LoadFromFile(AFileName: string);
var
  f: TextFile;
  ln: String;
  Len, MaxLen: integer;
  index: integer;
begin
  try
    BeginUpdate;
    DocumentState := DsLoading;
    Clear;
    AssignFile(f, AFileName);
    Reset(f);

    MaxLen := 0;
    While not eof(f) do
    begin
      ReadLn(f, ln);
      ln := StringReplace(ln, #9, #32#32, [rfReplaceAll]);
      index := AddString(ln);
      Lines[index].UpdateSyn;

      Len := Length(ln);
      if Len > MaxLen then
        MaxLen := Len;
    end;
    CloseFile(f);

    { 砚铕圜桠噱?疋铊 驵腙桢 犭铌?}
    // Lines[4].FRolllUpFor := Lines[12];
    // Lines[5].FLevel := 1;
    // Lines[6].FLevel := 1;
    // Lines[7].FLevel := 1;
    // Lines[8].FLevel := 1;
    // Lines[9].FLevel := 1;
    // Lines[10].FLevel := 1;
    // Lines[11].FLevel := 1;
    // Lines[12].FLevel := 1;
    // Lines[14].FRolllUpFor := Lines[225];

    HScroll.Max := MaxLen;
  finally
    DocumentState := DsReady;
    EndUpdate;
  end;

  FFileName := AFileName;
  Application._UpdateCaption;
end;

procedure TEceEditorWindow.LoadPlugin(AFileName: string);
type
  PGetPlugin = function: IEceEditorPlugin; safecall;
var
  hPlugin: HMODULE;
  LoadProc: PGetPlugin;
  Plugin: IEceEditorPlugin;
begin
  hPlugin := LoadLibrary(Pchar(AFileName));
  if hPlugin = 0 then
    raise Exception.Create('湾 箐嚯铖?玎沭箸栩?祛潴朦 ' + AFileName);

  LoadProc := GetProcAddress(hPlugin, 'GetPlugin');
  if @LoadProc = nil then
  begin
    FreeLibrary(hPlugin);
    raise Exception.Create('GetPlugin 礤 磬殇屙 ?蜞犭桷?耧铕蜞 祛潴? ' +
        AFileName);
  end;

  Plugin := LoadProc;
  Plugin.Load(Self);

  FPlugins.Add(Plugin);
end;

Constructor TEceEditorWindow.Create(Parent: Cardinal;
  AApplication: IEceApplication);
begin
  Inherited;
  // 玉蜞磬怆桠噱?梏?
  SetFont('Lucida Console', 16);
  // 杨玟噱?耱痤觇
  FLines := TList.Create;
  FVisibleLines := TList.Create;
  AddLine;

  FGutter := TGutter.Create(Self);
  // FCaret := TCaret.Create(Self);
  FCaret := CreateCaret;

  FTokens := TTokenClassList.Create(Self);

  //
  FSyntaxParser := TEceSynParser.Create;

  // ?鲥??镳铘桦嚓蜩觇
  SendMessage(Handle, WM_SIZE, 0, 0);

  FPlugins := TInterfaceList.Create;
end;

function TEceEditorWindow.CreateCaret: TCaret;
begin
  Result := TCaret.Create(Self);
end;

function TEceEditorWindow.CreateLine: TLine;
begin
  Result := TLine.Create(Self);
end;

Destructor TEceEditorWindow.Destroy;
var
  i: integer;
begin
  if Assigned(FCaret) then
    FCaret.Free;
  if Assigned(FGutter) then
    FGutter.Free;
  if Assigned(FVisibleLines) then
    FVisibleLines.Free;

  if Assigned(FLines) then
  begin
    for i := 0 to FLines.Count - 1 do
      TLine(FLines[i]).Free;
    FLines.Free;
  end;

  if Assigned(FPlugins) then
  begin
    FPlugins.Clear;
    FPlugins.Free;
  end;

  if Assigned(FTokens) then
  begin
    FTokens.Free;
  end;

  if Assigned(FSyntaxParser) then
  begin
    FSyntaxParser.Free
  end;

  inherited;
end;

procedure TEceEditorWindow.Invalidate;
begin
  InvalidateRect(Handle, Nil, false);
end;

Function TEceEditorWindow.GetCount: integer;
begin
  Result := FLines.Count;
end;

function TEceEditorWindow.GetDocumentFileName: string;
begin
  Result := FFileName;
end;

function TEceEditorWindow.GetDocumentTitle: string;
begin
  Result := ExtractFileName(FFileName);
end;

Function TEceEditorWindow.GetStrings(const Index: integer): string;
begin
  Result := Lines[index].Text;
end;

function TEceEditorWindow.GetVisibleLines(const index: integer): TLine;
begin
  if (index < 0) or (index > FVisibleLines.Count - 1) then
    raise Exception.Create('湾忮痦 桧溴犟 耱痤觇');
  Result := TLine(FVisibleLines[index]);
end;

procedure TEceEditorWindow.SetState(const value: TEceEditorState);
begin
  FState := value;
end;

procedure TEceEditorWindow.SetStrings(const Index: integer;
  const value: string);
begin
  Lines[index].Text := value;
  { todo: 耱痤赅 襦爨 筲邃铎栩 磬??蝾? 黩?桤戾龛豚顸 }
end;

procedure TEceEditorWindow.SetTextColor(const value: integer);
begin
  FTextColor := value;
end;

function TEceEditorWindow.GetLines(const index: integer): TLine;
begin
  if (index < 0) or (index > Count - 1) then
    raise Exception.Create('湾忮痦 桧溴犟 耱痤觇');
  Result := TLine(FLines[index]);
end;

procedure TEceEditorWindow.Clear;
begin
  while Count <> 0 do
    DeleteLine(0);
end;

function TEceEditorWindow.AddString(const ANewString: String): integer;
begin
  Result := InsertString(ANewString, FLines.Count);
end;

function TEceEditorWindow.InsertString(Const ANewString: string;
  AIndex: integer): integer;
begin
  with InsertLine(AIndex) do
    Text := ANewString;
  Result := AIndex;
end;

function TEceEditorWindow.AddLine: TLine;
var
  index: integer;
begin
  Result := InsertLine(FLines.Count);
end;

function TEceEditorWindow.InsertLine(AIndex: integer): TLine;
var
  vIndex: integer;
begin
  Result := CreateLine;
  try
    FLines.Insert(AIndex, Result);
    { 殷?禧 礤 镳铖蝾 怦蜞怆屐, ?妁??溴豚屐 镳钼屦牦
      - 忤漤?腓 耱痤牦 磬??镱?礤?}
    FVisibleLines.Insert(AIndex, Result);
  except
    Result.Free;
    raise Exception.Create('湾忮痦 桧溴犟 耱痤觇');
  end;

  if FUpdateLockCount > 0 Then
    exit;
  LineModification;
  SendMessage(Handle, WM_SIZE, 0, 0);
end;

procedure TEceEditorWindow.DeleteLine(const index: integer);
var
  Line: TLine;
  LIndex: integer;
begin
  Line := Lines[index]; // 丸 赅觇?镳钼屦铌. ?怦塍鬣?麇泐-蝮?桉觌屙桢

  LIndex := FVisibleLines.IndexOf(Line);
  if LIndex <> -1 then
  begin
    FVisibleLines.Delete(LIndex);
  end;

  FLines.Delete(index);
  Line.Free;
  LineModification;
end;

Procedure TEceEditorWindow.BeginUpdate;
begin
  inc(FUpdateLockCount)
end;

Procedure TEceEditorWindow.EndUpdate;
begin
  dec(FUpdateLockCount);
  if FUpdateLockCount < 0 then
    FUpdateLockCount := 0
  else if FUpdateLockCount = 0 then
  begin
    LineModification;
    SendMessage(Handle, WM_SIZE, 0, 0);
    Caret.Update;
    Invalidate;
  end;
end;

procedure TEceEditorWindow.SetFont(AFont: String; Size: integer);
var
  BoldVal: integer;
  DC: HDC;
  i: integer;
  Sz: TSize;
begin
  BoldVal := 600;

  for i := 0 to 3 do
    DeleteObject(FFonts[i]);

  // 眍痨嚯, 纣痦,牦瘃桠, ?骅痦 牦瘃桠. 镱 镱?潢?
  FFonts[0] := CreateFont(Size, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0,
    0, Pchar(AFont));
  FFonts[1] := CreateFont(Size, 0, 0, 0, BoldVal, 0, 0, 0, DEFAULT_CHARSET, 0,
    0, 0, 0, Pchar(AFont));
  FFonts[2] := CreateFont(Size, 0, 0, 0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0,
    0, Pchar(AFont));
  FFonts[3] := CreateFont(Size, 0, 0, 0, BoldVal, 1, 0, 0, DEFAULT_CHARSET, 0,
    0, 0, 0, Pchar(AFont));

  DC := CreateCompatibleDC(0);

  SelectObject(DC, FFonts[0]);
  GetTextExtentPoint32(DC, '#', 1, FCharSize);

  FFontExtraSpace[0] := 0;

  // 蔓麒螓忄屐 皲忤?潆 耔焘铍钼 赅驿钽?桤 磬麇痱囗栝
  for i := 1 to 3 do
  begin
    SelectObject(DC, FFonts[i]);
    GetTextExtentPoint32(DC, '#', 1, Sz);
    FFontExtraSpace[i] := FCharSize.Cx - Sz.Cx;
  end;

  DeleteDc(DC);
end;

procedure TEceEditorWindow.SetOffsetX(const value: integer);
var
  OffS: integer;
  Rt: TRect;
  CDC: HDC;
begin

  if FOffsetX = value then
    exit;

  if (value < 0) and (FOffsetX = 0) then
    exit;
  if (value >= HScroll.Max - CharsInWidth + 1) and
    (FOffsetX = HScroll.Max - CharsInWidth + 1) then
    exit;

  OffS := FOffsetX - value;
  Rt := EditorRect;
  ScrollWindow(Handle, OffS * CharWidth, 0, nil, @Rt);

  FOffsetX := value;
  HScroll.pos := FOffsetX;

  // 昨?猁 礤 恹脲珧?玎 沭囗桷??怦?耔眭痤龛玷痤忄螯
  if HScroll.pos <> FOffsetX then
  begin
    HScroll.pos := FOffsetX;
    OffsetX := HScroll.pos;
  end;
end;

procedure TEceEditorWindow.SetOffsetY(const value: integer);
var
  OffS: integer;
  Rt: TRect;
begin
  if FOffsetY = value then
    exit;

  if (value < 0) and (FOffsetY = 0) then
    exit;

  if (value > Count - CharsInHeight + 1)and
       (FOffsetY = Count - CharsInHeight + 1) then
    exit;

  OffS := FOffsetY - value;
  Rt := EditorRect;
  Rt.Left := 0;
  ScrollWindow(Handle, 0, OffS * CharHeight, nil, nil);
  FOffsetY := value;
  VScroll.pos := FOffsetY;
  // 昨?猁 礤 恹脲珧?玎 沭囗桷??怦?耔眭痤龛玷痤忄螯
  if VScroll.pos <> FOffsetY then
  begin
    VScroll.pos := FOffsetY;
    OffsetY := VScroll.pos;
  end;
end;

procedure TEceEditorWindow.onVscroll(pos: integer; EndScroll: boolean);
begin
  OffsetY := pos;
end;

procedure TEceEditorWindow.onHscroll(pos: integer; EndScroll: boolean);
begin
  OffsetX := pos;
end;

function TEceEditorWindow.GetEditorRect: TRect;
begin
  GetClientRect(Handle, Result);
  if not Assigned(FGutter) then
    exit;
  Result.Left := Gutter.Size;
end;

function TEceEditorWindow.GetCharsInHeight: integer;
begin
  Result := EditorRect.Bottom div CharHeight;
end;

function TEceEditorWindow.GetCharsInWidth: integer;
begin
  with EditorRect do
    Result := (Right - Left) div CharWidth;
end;

procedure TEceEditorWindow.LineModification;
begin
  inc(FLineModificationChecker);
  // 如戾?屐 ?疣珈屦 耜痤腚?
  VScroll.Max := FVisibleLines.Count;
end;

{ TGutter }

Constructor TGutter.Create(AEditor: TEceEditorWindow);
begin
  FEditor := AEditor;
end;

function GetDecCound(i: integer): Cardinal;
begin
  Result := 1;
  if i < 10 then
    exit;
  repeat
    inc(Result);
    i := i div 10;
  until i < 10;
end;

Procedure TGutter.Draw(DC: HDC; Rt: TRect);
var
  Brush: HBrush;
  Pen: HPen;

  Font: HFont;
  FontColor: Cardinal;
  bkMode: Cardinal;
  i: integer;
  LineO: TLine;
  LineNum: integer;
  Text: String;
  FormatStr: String;
  ert: TRect;
  Cx, Cy: integer;
begin
  // 昨?猁 礤 痂耦忄螯 蝾, 黩?礤 眢骓?
  if Rt.Left > Size Then
    exit;

  Rt.Left := 0;
  Rt.Right := Size;

  Brush := GetSysColorBrush(COLOR_BTNFACE);
  FillRect(DC, Rt, Brush);
  DeleteObject(Brush);

  bkMode := GetBkMode(DC);
  SelectObject(DC, FEditor.FFonts[0]);
  FontColor := GetTextColor(DC);

  Pen := SelectObject(DC, CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)));
  MoveToEx(DC, Rt.Right - 1, Rt.Top, nil);
  LineTo(DC, Rt.Right - 1, Rt.Bottom);

  FormatStr := '%0' + IntToStr(GetDecCound(FEditor.FLines.Count)) + 'd';
  for i := (Rt.Top div FEditor.CharHeight) to
    (Rt.Bottom div FEditor.CharHeight) do
  begin
    SetTextColor(DC, GetSysColor(COLOR_BTNSHADOW));
    SetBkMode(DC, TRANSPARENT);

    LineNum := i + FEditor.OffsetY;
    if LineNum < FEditor.FVisibleLines.Count then
    begin
      LineO := TLine(FEditor.FVisibleLines[LineNum]);
      LineNum := LineO.LineIndex
    end
    else
      continue;

    Text := Format(FormatStr, [LineNum + 1]);
    TextOut(DC, Rt.Left, i * FEditor.CharHeight, Pchar(Text), Length(Text));

    { 殷?蜩镟 +/- 潆 疋铕圜桠噱禧?犭铌钼 }
    if LineO.isRollBlock then
    begin
      if LineO.isRollUp then
        Text := '+'
      else
        Text := '-';
      SetTextColor(DC, FEditor.TextColor);
      // SetBkColor(dc, GetSysColor(COLOR_WINDOW));
      // SetBkMode(Dc, OPAQUE);
      Cx := Size - FEditor.CharWidth - 2;
      Cy := i * FEditor.CharHeight;
      ert := Rect(Cx, Cy, Cx + FEditor.CharWidth + 2,
        Cy + FEditor.CharHeight - 1);

      Brush := CreateSolidBrush(FEditor.BackgroundColor);
      FillRect(DC, ert, Brush);
      DeleteObject(Brush);

      MoveToEx(DC, Cx + FEditor.CharWidth + 1, Cy, nil);
      LineTo(DC, Cx, Cy);
      LineTo(DC, Cx, Cy + FEditor.CharHeight - 1);
      LineTo(DC, Cx + FEditor.CharWidth + 1, Cy + FEditor.CharHeight - 1);
      // DrawEdge(DC, ert,
      // BDR_RAISEDOUTER,
      // BF_RECT and not BF_RIGHT or BF_MIDDLE or BF_MONO);
      TextOut(DC, Cx, Cy, Pchar(Text), 1)
    end
    else
    { 桧圜?痂耋屐 忮痱桕嚯 腓龛?}
    begin
      if LineO.Level > 0 then
      begin
        // Pen := SelectObject(Dc, CreatePen(PS_DOT, 1, GetSysColor(COLOR_BTNSHADOW)));
        Cx := (Size - FEditor.CharWidth - 2) + (FEditor.CharWidth div 2);
        Cy := i * FEditor.CharHeight;
        MoveToEx(DC, Cx, Cy, nil);
        LineTo(DC, Cx, Cy + FEditor.CharHeight);

        if LineO.isEndInLevel then
        begin
          { todo: 襄疱躅?磬 箴钼屙?龛驽 }
          MoveToEx(DC, Cx, Cy + (FEditor.CharHeight div 2), nil);
          LineTo(DC, Cx + (FEditor.CharWidth div 2) + 1, Cy +
              (FEditor.CharHeight div 2))
        end;
      end;
    end;
  end;

  DeleteObject(SelectObject(DC, Pen));
  SetTextColor(DC, FontColor);
  SetBkMode(DC, bkMode);
  SelectObject(DC, Font);
end;

Function TGutter.GetSize: Cardinal;
begin
  { todo: 痂磬 泱蝌屦?- 怦?耔焘铍?+ 2 磬 犷痄 + 1 耢忸?磬 觏铒牦 "砚屦眢螯" }
  Result := 2 + FEditor.CharWidth + (GetDecCound(FEditor.FLines.Count))
    * FEditor.CharWidth;
  if FSize <> Result then
  begin
    FSize := Result;
    FEditor.Invalidate;
  end;
end;

{ TLine }

Constructor TLine.Create(AEditor: TEceEditorWindow);
begin
  inherited Create;
  FEditor := AEditor;
  FVisible := true;
  FLineIndex := -1;
  // 镱蝾?耦玟噤桁
  // FRollUpLines := TList.Create;
  FTokens := TList.Create;
end;

Destructor TLine.Destroy;
var
  i: integer;
begin
  if Assigned(FRollUpLines) then
  begin
    { todo: 朽玮屦眢螯 黩?玎忮痦箅?}
    FRollUpLines.Free;
  end;
  if Assigned(FTokens) then
  begin
    for i := 0 to FTokens.Count - 1 do
      TToken(FTokens[i]).Free;
    FTokens.Free;
  end;
  inherited;
end;

procedure TLine.Draw(DC: HDC; Cx, Cy, StartChar: integer);
var
  i, Count: integer;
  Pen: HPen;
  // Brush : HBRUSH;
  Char: Pchar;
  ChWidth: integer;
  Tk: TToken;
  // bid : TLogBrush;
begin
  Count := Length - StartChar;
  if Count <> 0 then
  begin
    Char := Pchar(FText) + StartChar;
    ChWidth := FEditor.CharWidth;
    // 蔓忸滂?怦?耔焘铍?
    if FTokens.Count = 0 then
    begin
{$REGION '蔓忸?徨?篦弪?镱漶忮蜿?}
      for i := 0 to Count - 1 do
      begin
        SelectObject(DC, FEditor.FFonts[0]);
        SetBkMode(DC, TRANSPARENT);
        SetTextColor(DC, FEditor.TextColor);

        TextOut(DC, Cx, Cy, Char, 1);
        inc(Char);
        inc(Cx, ChWidth);
      end;
{$ENDREGION}
    end
    else
    begin
{$REGION '蔓忸? 耦汶囫眍 溧眄 蝾赍磬'}
      for i := 0 to FTokens.Count - 1 do
      begin
        Tk := TToken(FTokens[i]);
        // if StartChar > tk.FirstChar + tk.Length  then
        // continue;
        { TODO -oOnni -cDraw : 项蜩扈玷痤忄螯 }
        Tk.ApplyStyle(DC);
        Char := @FText[Tk.FirstChar + 1];
        TextOut(DC, Cx - FEditor.OffsetX * FEditor.CharWidth, Cy, Char,
          Tk.Length);
        inc(Cx, Tk.Length * FEditor.CharWidth);
      end;
      RestorStyle(DC);
{$ENDREGION}
    end;
  end;

  // 篷腓 犭铌 疋屦眢?
  if isRollUp then
  begin
    inc(Count, 3);
    if Count < 4 then
      Count := 4;
    Pen := SelectObject(DC, CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW))
      );
    MoveToEx(DC, FEditor.Gutter.Size, Cy + FEditor.CharHeight - 1, nil);
    LineTo(DC, FEditor.Gutter.Size + 1 + (FEditor.CharsInWidth + 1) * ChWidth,
      Cy + FEditor.CharHeight - 1);
  end;
end;

procedure TLine.RestorStyle(DC: HDC);
begin
  SetTextCharacterExtra(DC, 0);
  SetBkMode(DC, TRANSPARENT);
  SetTextColor(DC, 0);
end;

Function TLine.GetText: String;
begin
  Result := FText;
end;

function TLine.GetVisibleIndex: integer;
begin
  if FLineModificationChecker <> FEditor.FLineModificationChecker then
    UpdateLinesIndex;
  Result := FVisibleIndex;
end;

Procedure TLine.SetText(const value: String);
begin
  FText := value;
  UpdateSyn;
  { todo: 如忮耱栩?钺 桤戾礤龛?}
end;

procedure TLine.SetVisible(const value: boolean);
begin
  FVisible := value;
  { todo: 如忮耱栩?钺 桤戾礤龛?}
end;

function TLine._GetIndex: integer;
begin
  Result := LineIndex;
end;

function TLine._GetText: string;
begin
  Result := Text;
end;

function TLine._SetText(Text: string): integer;
begin
  Self.Text := Text;
end;

function TLine.GetLineIndex: integer;
begin
  if FLineModificationChecker <> FEditor.FLineModificationChecker then
    UpdateLinesIndex;
  Result := FLineIndex;
end;

function TLine.GetLength: integer;
begin
  Result := System.Length(FText);
end;

function TLine.GetisEndInLevel: boolean;
var
  Index: integer;
begin
  Index := LineIndex;
  if Index > FEditor.Count - 2 then
    exit(false);
  Result := (FEditor.Lines[index + 1].Level < Level);
end;

Function TLine.GetisRollBlock: boolean;
begin
  Result := FRolllUpFor <> nil;
end;

Procedure TLine.SetIsRollUp(const value: boolean);
var
  index: integer;
  i: integer;
  EndIndex: integer;
  LineO: TLine;
begin
  if FisRollUp = value then
    exit;
  if isRollBlock then
  begin
    FisRollUp := value;
    if isRollUp then
    begin
      { 砚铕圜桠噱?}
      Index := FEditor.FVisibleLines.IndexOf(Self) + 1;
      EndIndex := FEditor.FLines.IndexOf(FRolllUpFor);
      if not Assigned(FRollUpLines) then
        FRollUpLines := TList.Create;
      repeat
        LineO := TLine(FEditor.FVisibleLines[Index]);
        if (LineO.LineIndex <= EndIndex) then
        begin
          LineO.FRollUpIn := Self;
          FRollUpLines.Add(LineO);
          FEditor.FVisibleLines.Delete(Index);
        end;
      until (LineO = nil) or (LineO.LineIndex > EndIndex);
    end
    else
    begin
      { 朽玮铕圜桠噱?}
      Index := FEditor.FVisibleLines.IndexOf(Self) + 1;
      for i := FRollUpLines.Count - 1 downto 0 do
      begin
        LineO := TLine(FRollUpLines[i]);
        LineO.FRollUpIn := nil;
        FEditor.FVisibleLines.Insert(index, LineO);
      end;
      FRollUpLines.Clear;
      if Assigned(FRollUpLines) then
        FRollUpLines.Free;
      FRollUpLines := nil;
    end;
    FEditor.Invalidate;
    FEditor.LineModification;
  end
  else
    FisRollUp := false;
end;

Procedure TLine.Insert(const AString: String; AChar: integer);
begin
  { 念徉怆屙桢 镳钺咫钼, 羼腓 恹躅滂?玎 沭囗桷?耱痤昴钺噔脲龛?镳钺咫钼, 羼腓 恹躅滂?玎 沭囗桷?耱痤念徉怆屙桢 镳钺咫钼, 羼腓 恹躅滂?玎 沭囗桷?耱痤昴钺噔脲龛?镳钺咫钼, 羼腓 恹躅滂?玎 沭囗桷?耱痤觇桕梃 }
  while (System.Length(FText) < AChar - 1) do
    FText := FText + ' ';

  System.Insert(AString, FText, AChar);
  Invalidate;
end;

procedure TLine.Delete(AChar, ACount: integer);
begin
  System.Delete(FText, AChar, ACount);
  Invalidate;
end;

Procedure TLine.BreakLine(AChar: integer);
var
  LineAfter: string;
begin
  LineAfter := Copy(FText, AChar, Length);
  System.Delete(FText, AChar, Length);
  FEditor.InsertString(LineAfter, LineIndex + 1);
  Invalidate;
  { Invalidate; }
  FEditor.Invalidate;
end;

Procedure TLine.Invalidate;
var
  Rt: TRect;
begin
  UpdateSyn;
  if FEditor.FUpdateLockCount <> 0 then
    exit;
  GetClientRect(FEditor.Handle, Rt);
  Rt.Left := FEditor.FGutter.Size;
  Rt.Top := (VisibleIndex - FEditor.OffsetY) * FEditor.CharHeight;
  Rt.Bottom := Rt.Top + FEditor.CharHeight;
  InvalidateRect(FEditor.Handle, @Rt, false);
end;

procedure TLine.UpdateLinesIndex;
begin
  { DONE: 徨珙狃噻桢 }
  { 蒡?趔黻鲨 狍溴?镳钼屦螯 - 耦铗忮蝰蜮箦?腓 邋 镱耠邃龛?桧溴犟 怵屐屙?
    桤戾礤龛 麒耠?耱痤?蝈牦祗 怵屐屙?镱耠邃礤泐 桤戾礤龛 麒耠?耱痤?
    疱溧牝铕? ?羼腓 铐?礤 耦怙噤帼? 蝾 狍溴?镱塍鬣螯 桧溴犟 镱-眍忸?
    镱耠?麇泐 钺眍忤?疋铋 桧溴犟 镱耠邃礤泐 桤戾礤龛 麒耠?耱痤?}
  FLineIndex := FEditor.FLines.IndexOf(Self);
  FVisibleIndex := FEditor.FVisibleLines.IndexOf(Self);
  FLineModificationChecker := FEditor.FLineModificationChecker;
end;

procedure TLine.UpdateSyn;
var
  index: integer;
  Tk: TToken;
  i: integer;
begin
  for i := 0 to FTokens.Count - 1 do
    TToken(FTokens[i]).Free;
  FTokens.Clear;

  index := pos('//', Text);
  if index <> 0 then
  begin
    Tk := TToken.Create(FEditor.FTokens['normal']);
    FTokens.Add(Tk);
    Tk.FFirstChar := 0;
    Tk.Length := index - 1;

    Tk := TToken.Create(FEditor.FTokens['Comments']);
    FTokens.Add(Tk);
    Tk.FFirstChar := index - 1;
    Tk.Length := Length - index + 1;
  end;
end;

{ TCaret }

Constructor TCaret.Create(AEditor: TEceEditorWindow);
begin
  FEditor := AEditor;
  Update;
end;

procedure TCaret.SetX(const value: integer);
begin
  Fx := value;
  Update;
end;

procedure TCaret.SetXY(const Ax, Ay: integer);
begin
  Fx := Ax;
  Fy := Ay;
  Update;
end;

procedure TCaret.SetY(const value: integer);
begin
  Fy := value;
  if Fy < 0 then
    Fy := 0;
  if Fy > FEditor.FVisibleLines.Count - 1 then
    Fy := FEditor.FVisibleLines.Count - 1;

  Update;
end;

procedure TCaret.SetStyle(Const value: TCaretStyle);
begin
  FStyle := value;
  FEditor.Invalidate;
  Update;
  Hide;
  Show;
end;

procedure TCaret.Show;
begin
  case Style of
    csNormal:
      CreateCaret(FEditor.Handle, 0, 2, FEditor.CharHeight);
    csClassic:
      CreateCaret(FEditor.Handle, 0, FEditor.CharWidth, 2);
  end;
  ShowCaret(FEditor.Handle);
end;

function TCaret.GetHaveSelection: boolean;
begin
  Result :=
  { TODO -oOnni -cBug : 骂珈铈睇 觐?觇 潆 疋孱眢螓?犭铌钼 }
  (SelStartX <> X) or (SelStartY <> Y);
end;

function TCaret.GetLine: integer;
begin
  try
    Result := TLine(FEditor.FVisibleLines[Y]).LineIndex;
  except
    Result := -1;
  end;
end;

function TCaret.GetSelectionMode: boolean;
begin
  Result := isKeyDown(VK_SHIFT) or isKeyDown(VK_LBUTTON);
end;

function TCaret.GetSelectionRange: TSelectionRange;
begin
  Result.selStart.X := Min(X, SelStartX);
  Result.selStart.Y := Min(Y, SelStartY);
  Result.selEnd.X := Max(X, SelStartX);
  Result.selEnd.Y := Max(Y, SelStartY);
end;

procedure TCaret.Hide;
begin
  HideCaret(FEditor.Handle);
  DestroyCaret;
end;

procedure TCaret.Update;
var
  Cx, Cy: integer;
begin
  if Fx < 0 then
    Fx := 0;
  if Fy < 0 then
    Fy := 0;
  if Fy > FEditor.FVisibleLines.Count - 1 then
    Fy := FEditor.Count - 1;
  // 篷腓 赅疱蜿?恹躅滂?玎 沭囗桷?铌磬 - 镳铌痼麒忄屐
  with FEditor do
  begin
    if Fx < OffsetX then
      OffsetX := Fx;
    if Fy < OffsetY then
      OffsetY := Fy;

    if Fx > HScroll.Max then
      HScroll.Max := Fx;
    if Fx - CharsInWidth > OffsetX - 1 then
      OffsetX := Fx - CharsInWidth + 1;
    if Fy - CharsInHeight > OffsetY - 1 then
      OffsetY := Fy - CharsInHeight + 1;
  end;
  // 蔓溴?屐 桦?礤?
  if not SelectionMode then
  begin
    FSelStartX := Fx;
    FSelStartY := Fy;
  end;
  // 吾眍怆屐 镱腩驽龛 赅疱蜿?
  case Style of
    csNormal:
      begin
        Cx := FEditor.FGutter.Size + (Fx - FEditor.OffsetX) * FEditor.CharWidth;
        Cy := (Fy - FEditor.OffsetY) * FEditor.CharHeight;
      end;
    csClassic:
      begin
        Cx := FEditor.FGutter.Size + (Fx - FEditor.OffsetX) * FEditor.CharWidth;
        Cy := (Fy - FEditor.OffsetY + 1) * FEditor.CharHeight - 2;
      end;
  end;
  SetCaretPos(Cx, Cy);
end;

function TCaret._GetX: integer;
begin
  Result := X;
end;

function TCaret._GetY: integer;
begin
  Result := Y;
end;

function TCaret._SetX(value: integer): integer;
begin
  Result := X;
  X := value;
end;

function TCaret._SetY(value: integer): integer;
begin
  Result := Y;
  Y := value;
end;

{ TTokenClass }

constructor TTokenClass.Create(AEditor: TEceEditorWindow);
begin
  inherited Create;
  FEditor := AEditor;
end;

procedure TTokenClass.SetBkColor(const value: integer);
begin
  FBkColor := value;
end;

procedure TTokenClass.SetFontStyle(const value: integer);
begin
  FFontStyle := value;
end;

procedure TTokenClass.SetStrick(const value: boolean);
begin
  FStrick := value;
end;

procedure TTokenClass.SetTextColor(const value: integer);
begin
  FTextColor := value;
end;

procedure TTokenClass.SetUnderline(const value: boolean);
begin
  FUnderline := value;
end;

{ TTokenClassList }

procedure TTokenClassList.Clear;
var
  i: integer;
begin
  for i := 0 to FTokens.Count - 1 do
    FTokens.Objects[i].Free;
end;

constructor TTokenClassList.Create(AEditor: TEceEditorWindow);
begin
  inherited Create;
  FEditor := AEditor;
  FTokens := TStringList.Create;
end;

destructor TTokenClassList.Destroy;
begin
  if Assigned(FTokens) then
  begin
    Clear;
    FTokens.Free;
  end;
  inherited;
end;

function TTokenClassList.GetTokens(const ATokenKey: string): TTokenClass;
var

  Index: integer;
begin
  index := FTokens.IndexOf(ATokenKey);
  if Index = -1 then
    raise Exception.Create(Format('翌赍?"%s" 礤 磬殇屙.', [ATokenKey]));
  Result := TTokenClass(FTokens.Objects[index]);
end;

function TTokenClassList.NewToken(AName: string; AType: TTokenClassType)
  : TTokenClass;
begin
  Result := TTokenClass.Create(FEditor);
  Result.Name := AName;
  Result.FTokenType := AType;

  FTokens.AddObject(AName, Result);
  FTokens.Sort;
  FTokens.Sorted := true;
end;

{ TToken }

procedure TToken.ApplyStyle(DC: HDC);
begin
  if TokenClass.BkColor <> -1 then
  begin
    SetBkMode(DC, OPAQUE);
    SetBkColor(DC, TokenClass.BkColor)
  end
  else
    SetBkMode(DC, TRANSPARENT);

  SelectObject(DC, TokenClass.Editor.FFonts[TokenClass.FontStyle]);
  SetTextCharacterExtra(DC, TokenClass.Editor.FFontExtraSpace
      [TokenClass.FontStyle]);
  SetTextColor(DC, TokenClass.TextColor);
end;

constructor TToken.Create(ATokenClass: TTokenClass);
begin
  inherited Create;
  FTokenClass := ATokenClass;
end;

procedure TToken.SetFirstChar(const value: Cardinal);
begin
  FFirstChar := value;
end;

procedure TToken.SetLength(const value: Cardinal);
begin
  FLength := value;
end;

end.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约