mwcustomedit.pas

来自「本人买的<<VC++项目开发实例>>源代码配套光盘.」· PAS 代码 · 共 1,920 行 · 第 1/5 页

PAS
1,920 行
字号
{$R mwCustomEdit.RES}

uses mwExport, mwHTMLExport, mwRTFExport;

procedure Register;
begin
  RegisterComponents(MWS_ComponentsPage, [TmwCustomEdit]);
end;

function maxPoint(P1, P2: TPoint): TPoint;
begin
  Result := P1;
  if (P2.y > P1.y) or ((P2.y = P1.y) and (P2.x > P1.x)) then Result := P2;
end;

function minPoint(P1, P2: TPoint): TPoint;
begin
  Result := P1;
  if (P2.y < P1.y) or ((P2.y = P1.y) and (P2.x < P1.x)) then Result := P2;
end;

function Roundoff(X: Extended): Longint;
begin
  if (x >= 0) then begin
    Result := Trunc(x + 0.5)
  end else begin
    Result := Trunc(x - 0.5);
  end;
end;

{ TmwEditList }

function TmwEditList.Add(const S: string): Integer;
begin
  BeginUpdate;
  Result := inherited Add(S);
  if Assigned(FOnAdded) then FOnAdded(Self);
  EndUpdate;
end;

procedure TmwEditList.AddStrings(Strings: TStrings);
var
  I: Integer;
begin
  BeginUpdate;
  try
    for I := 0 to Strings.Count - 1 do
      inherited Add(Strings[I]);
    if Assigned(fOnScanRanges) then
      fOnScanRanges(Self);
  finally
    EndUpdate;
  end;
end;

procedure TmwEditList.Assign(Source: TPersistent);
begin
  BeginLoading;
  try
    inherited Assign(Source);
  finally
    EndLoading;
  end;
end;

procedure TmwEditList.BeginLoading;
begin
  Inc(nLoading);
end;

procedure TmwEditList.Clear;
begin
  if Assigned(fOnCleared) then fOnCleared(Self);
  inherited Clear;
  if (nLoading = 0) and (Count = 0) then Add('');
end;

procedure TmwEditList.Delete(Index: Integer);
begin
  BeginUpdate;
  if (Index = 0) and (Count = 1) then
    inherited Put(0, '')
  else
    inherited Delete(Index);
  if Assigned(FOnDeleted) then fOnDeleted(Index);
  EndUpdate;
end;

procedure TmwEditList.EndLoading;
begin
  if (nLoading > 0) then begin
    Dec(nLoading);
    if (nLoading = 0) and (Count = 0) then Add('');
  end;
end;

procedure TmwEditList.Insert(Index: Integer; const S: string);
begin
  BeginUpdate;
  inherited Insert(Index, S);
  if Assigned(FOnInserted) then fOnInserted(Index);
  EndUpdate;
end;

procedure TmwEditList.LoadFromStream(Stream: TStream);
var
  evtOnAdded: TNotifyEvent;
begin
  BeginLoading;
  BeginUpdate;
  try
    if Assigned(fOnScanRanges) then begin
      evtOnAdded := fOnAdded;
      try
        fOnAdded := nil;
        inherited LoadFromStream(Stream);
        fOnScanRanges(Self);
      finally
        fOnAdded := evtOnAdded;
      end;
    end else
      inherited LoadFromStream(Stream);
  finally
    EndLoading;
    EndUpdate;
  end;
end;

procedure TmwEditList.Put(Index: Integer; const S: string);
begin
  BeginUpdate;
  inherited Put(Index, S);
  if Assigned(FOnPutted) then fOnPutted(Index);
  EndUpdate;
end;

procedure TmwEditList.SetTextStr(const Value: string);
begin
  BeginUpdate;
  inherited SetTextStr(Value);
  if Assigned(FOnLoaded) then FOnLoaded(Self);
  EndUpdate;
end;

{ TmwCustomEdit }

procedure TmwCustomEdit.ComputeCaret(X, Y: Integer);
{$IFDEF MWE_MBCSSUPPORT}
var
  pt: TPoint;
  s: string;
  f: Single;
  {$ENDIF}
begin
  {$IFDEF MWE_MBCSSUPPORT}
  f := (X + LeftChar * fCharWidth - fGutterWidth - 2) / fCharWidth;
  pt := Point(Roundoff(f), Y div fTextHeight + TopLine);
  if ((pt.Y - 1) < Lines.Count) then begin
    s := Lines[pt.Y - 1];
    if (Length(s) >= pt.x) and (ByteType(s, pt.X) = mbTrailByte) then
      if Frac(f) >= 0.5 then
        Dec(pt.X)
      else
        Inc(pt.X);
  end;
  fMBCSStepAside := False;
  CaretXY := pt;
  {$ELSE}
  CaretXY := Point(
    Roundoff((X + LeftChar * fCharWidth - FGutterWidth - 2) / fCharWidth),
    Y div fTextHeight + TopLine
    );
  {$ENDIF}
end;

procedure TmwCustomEdit.DoCopyToClipboard(const SText: string);
var
  Mem: HGLOBAL;
  P: PChar;
  SLen: integer;
  Failed: boolean;
begin
  if SText <> '' then begin
    Failed := TRUE; // assume the worst.
    SLen := Length(SText);
    // Open and Close are the only TClipboard methods we use because TClipboard
    // is very hard (impossible) to work with if you want to put more than one
    // format on it at a time.
    Clipboard.Open;
    try
      // Clear anything already on the clipboard.
      EmptyClipboard;
      // Put it on the clipboard as normal text format so it can be pasted into
      // things like notepad or Delphi.
      Mem := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, SLen + 1);
      if Mem <> 0 then begin
        P := GlobalLock(Mem);
        try
          if P <> nil then begin
            Move(PChar(SText)^, P^, SLen + 1);
            // Put it on the clipboard in text format
            Failed := SetClipboardData(CF_TEXT, Mem) = 0;
          end;
        finally
          GlobalUnlock(Mem);
        end;
      end;
      // Don't free Mem!  It belongs to the clipboard now, and it will free it
      // when it is done with it.
      if not Failed then begin
        // Copy it in our custom format so we know what kind of block it is.
        // That effects how it is pasted in.
        Mem := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, SLen +
          SizeOf(TSelectionMode) + 1);
        P := GlobalLock(Mem);
        try
          if P <> nil then begin
            // Our format:  TSelectionMode value followed by text.
            PSelectionMode(P)^ := SelectionMode;
            inc(P, SizeOf(TSelectionMode));
            Move(PChar(SText)^, P^, SLen + 1);
            Failed := SetClipboardData(mwEditClipboardFormat, Mem) = 0;
          end;
        finally
          GlobalUnlock(Mem);
        end;
        // Don't free Mem!  It belongs to the clipboard now, and it will free it
        // when it is done with it.
      end;
    finally
      Clipboard.Close;
      if Failed then
        raise EmwEditError.Create('Clipboard copy operation failed');
    end;
  end;
end;

procedure TmwCustomEdit.CopyToClipboard;
var
  SText: string;
begin
  if SelAvail then begin
    SText := SelText;
    DoCopyToClipboard(SText);
  end;
end;

procedure TmwCustomEdit.CutToClipboard;
var
  SText: string;
begin
  if SelAvail then begin
    SText := SelText;
    DoCopyToClipboard(SText);
    FUndoList.AddChange(mwcrDelete, fBlockBegin, fBlockEnd, PChar(SText),
      SelectionMode);
    LockUndo;
    SelText := '';
    UnLockUndo;
  end;
end;

constructor TmwCustomEdit.Create(AOwner: TComponent);
begin
  fLines := TmwEditList.Create;
  fFontDummy := TFont.Create;
  fUndoList := TUndoList.Create(self);
  fRedoList := TUndoList.Create(self);
  inherited Create(AOwner);
  {$IFDEF MWE_COMPILER_4_UP}
  DoubleBuffered := false;
  {$ENDIF}
  if not (csDesigning in ComponentState) then fLines.Add('');
  TmwEditList(fLines).OnAdded := ListAdded;
  TmwEditList(fLines).OnCleared := ListCleared;
  TmwEditList(fLines).OnDeleted := ListDeleted;
  TmwEditList(fLines).OnInserted := ListInserted;
  TmwEditList(fLines).OnLoaded := ListLoaded;
  TmwEditList(fLines).OnPutted := ListPutted;
  TmwEditList(fLines).OnScanRanges := ListScanRanges;
  fSelectedColor := TmwSelectedColor.Create;
  fSelectedColor.OnChange := SelectedColorsChanged;
  fBookMarkOpt := TmwBookMarkOpt.Create(Self);
  fBookMarkOpt.OnChange := BookMarkOptionsChanged;
// fRightEdge has to be set before FontChanged is called for the first time
  fRightEdge := 80;
  fGutter := TmwGutter.Create;
  fGutter.OnChange := GutterChanged;
  fGutterWidth := fGutter.Width;
  fTextOffset := fGutterWidth + 2;
  ControlStyle := ControlStyle + [csOpaque, csSetCaption];
  Height := 150;
  Width := 200;
  Cursor := crIBeam;
  Color := clWindow;
  fFontDummy.Name := 'Courier New';
  fFontDummy.Size := 10;
  {$IFDEF MWE_COMPILER_3_UP}
  fFontDummy.CharSet := DEFAULT_CHARSET;
  {$ENDIF}
  fTextDrawer := TheTextDrawer.Create([fsBold], fFontDummy);
  Font.Assign(fFontDummy);
  Font.OnChange := FontChanged;
  FontChanged(nil);
  ParentFont := False;
  ParentColor := False;
  TabStop := True;
  TStringList(Lines).OnChanging := LinesChanging;
  TStringList(Lines).OnChange := LinesChanged;
  fInserting := True;
  fMaxLeftChar := 1024;
  fScrollBars := ssBoth;
  fBorderStyle := bsSingle;
  fInsertCaret := ctVerticalLine;
  fOverwriteCaret := ctBlock;
  FSelectionMode := smNormal;
  fKeystrokes := TmwKeyStrokes.Create(Self);
  fMarkList := TMarkList.Create(self);
  fMarkList.OnChange := MarkListChange;
  SetDefaultKeystrokes;
  fRightEdgeColor := clSilver;
  {$IFDEF MWE_MBCSSUPPORT}
  fImeCount := 0;
  fMBCSStepAside := False;
  {$ENDIF}
  fWantTabs := False;
  fTabWidth := 8;
  // find / replace
  fTSearch := TmwEditSearch.Create;
  fOptions := MWEDIT_DEFAULT_OPTIONS;
end;

procedure TmwCustomEdit.CreateParams(var Params: TCreateParams);
const
  ScrollBar: array[TScrollStyle] of DWORD = (0, WS_HSCROLL, WS_VSCROLL,
    WS_HSCROLL or WS_VSCROLL);
  BorderStyles: array[TBorderStyle] of DWORD = (0, WS_BORDER);
begin
  inherited CreateParams(Params);
  with Params do begin
    Style := Style or ScrollBar[FScrollBars] or BorderStyles[fBorderStyle]
      or WS_CLIPCHILDREN;
    if NewStyleControls and Ctl3D and (fBorderStyle = bsSingle) then begin
      Style := Style and not WS_BORDER;
      ExStyle := ExStyle or WS_EX_CLIENTEDGE;
    end;
  end;
end;

procedure TmwCustomEdit.DecPaintLock;
begin
  Dec(fPaintLock);
  if (fPaintLock = 0) and HandleAllocated then begin
    if mwsfScrollbarChanged in fStateFlags then UpdateScrollbars(FALSE);
    if mwsfCaretChanged in fStateFlags then UpdateCaret;
    if fStatusChanges <> [] then StatusChanged([]);
  end;
end;

destructor TmwCustomEdit.Destroy;
begin
  Highlighter := nil;
  fKeyStrokes.Free;
  fFontDummy.Free;
  Lines.Free;
  fSelectedColor.Free;
  fBookMarkOpt.Free;
  fMarkList.Free;
  fUndoList.Free;
  fRedoList.Free;
  fGutter.Free;
  fTextDrawer.Free;
  fInternalImage.Free;
  fTSearch.Free;
  inherited Destroy;
end;

function TmwCustomEdit.GetBlockBegin: TPoint;
begin
  Result := fBlockBegin;
  if fBlockEnd.Y < fBlockBegin.Y then Result := fBlockEnd else
    if fBlockEnd.Y = fBlockBegin.Y then
      if fBlockEnd.X < fBlockBegin.X then Result := fBlockEnd;
end;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?