home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 October
/
Chip_2001-10_cd1.bin
/
zkuste
/
delphi
/
kolekce
/
d456
/
DCSLIB25.ZIP
/
DCSyntaxMemo.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2001-06-28
|
98KB
|
3,548 lines
{
BUSINESS CONSULTING
s a i n t - p e t e r s b u r g
Components Library for Borland Delphi 4.x, 5.x
Copyright (c) 1998-2000 Alex'EM
Special thanks to Vladimir Pudovkin
}
unit DCSyntaxMemo;
{$I DCConst.inc}
interface
uses
Windows, SysUtils, Messages, Classes, Controls, Consts, Graphics, Forms,
Clipbrd, DCSyntaxData, Dialogs, StdCtrls
{$IFDEF DELPHI_V5UP}, RTLConsts {$ENDIF};
const
CM_MEMO_BASE = WM_USER + $300;
CM_ROW_CHANGED = CM_MEMO_BASE + $00;
CM_ROW_INSERTED = CM_MEMO_BASE + $01;
CM_ROW_DELETED = CM_MEMO_BASE + $02;
CM_UPDATE_STARTED = CM_MEMO_BASE + $03;
CM_UPDATE_FINISHED = CM_MEMO_BASE + $04;
CM_INVALIDATE_ALL = CM_MEMO_BASE + $05;
MEMOSCROLL_IDEVENT = CM_MEMO_BASE + $01;
HTGUTTER = CM_MEMO_BASE + $01;
HTSELECTED = CM_MEMO_BASE + $02;
ngsBreakStop = 0;
ngsBreakInvalid = 1;
ngsBreakSet = 2;
ngsArrow = 3;
ngsEllipse = 4;
ngsBookmark_0 = 5;
ngsBookmark_1 = 6;
ngsBookmark_2 = 7;
ngsBookmark_3 = 8;
ngsBookmark_4 = 9;
ngsBookmark_5 = 10;
ngsBookmark_6 = 11;
ngsBookmark_7 = 12;
ngsBookmark_8 = 13;
ngsBookmark_9 = 14;
type
TTextSelectMode = (smLines, smColumns);
TMemoState = (msSelecting, msMoving);
TMemoStates = set of TMemoState;
TBlockUndoStyle = (buInsert, buDelete);
TDeleteUndoStyle = (duDelete, duBackspace);
TDCMemoStrings = class(TStrings)
private
FCount: Integer;
FCapacity: Integer;
FUpdating: Boolean;
FControl: TCustomControl;
FList: PLineDataItems;
FMessages: boolean;
FDestroing: boolean;
procedure SendControlMessage(AMessage: Cardinal);
procedure Grow;
procedure InsertItem(Index: Integer; const S: string);
function GetLineInfo(Index: Integer): PLineDataItem;
protected
function Get(Index: Integer): string; override;
function GetCapacity: Integer; override;
function GetCount: Integer; override;
procedure Put(Index: Integer; const S: string); override;
procedure SetCapacity(NewCapacity: Integer); override;
procedure SetUpdateState(Updating: Boolean); override;
public
constructor Create(AControl: TCustomControl);
destructor Destroy; override;
function Add(const Value: string): Integer; override;
procedure Clear; override;
procedure Delete(Index: Integer); override;
procedure DeleteLines(Index, ACount: Integer);
procedure InsertLines(Index, ACount: Integer);
procedure GrowTo(ANewCount: Integer);
procedure Insert(Index: Integer; const S: string); override;
property LineInfo[Index: Integer]: PLineDataItem read GetLineInfo;
property Messages: boolean read FMessages write FMessages;
end;
TDCTextArea = class(TObject)
private
function GetRect: TRect;
public
Empty: boolean;
StartPos: TPoint;
EndPos: TPoint;
Mode: TTextSelectMode;
constructor Create;
procedure Add(AStartPos, AEndPos: TPoint);
procedure AddSelection(ANewPos, AOldPos: TPoint);
function WordSelected(ACol, ARow, ALength: integer;
var SelStart, SelEnd: integer): boolean;
procedure Clear;
property Rect: TRect read GetRect;
end;
TDCMemoCoord = record
X: integer;
Y: integer;
end;
TDCMemoPos = packed record
TopLeft: TDCMemoCoord;
Col: integer;
Row: integer;
SelectRect: TRect;
end;
TGutterState = record
HitTest: integer;
MLDown: boolean;
MDPos: TPoint;
MUPos: TPoint;
end;
TBlockType = record
Block: string;
SelectRect: TRect;
DefaultCursor: TCursor;
end;
TDCCustomSyntaxMemo = class;
TDCMemoScroll = class(TPersistent)
private
FControl: TDCCustomSyntaxMemo;
FKind: TScrollBarKind;
FPosition: integer;
FRange: integer;
FIncrement: TScrollBarInc;
FPageSize: integer;
FMax: integer;
public
constructor Create(AControl: TDCCustomSyntaxMemo; AKind: TScrollBarKind);
procedure ScrollMessage(var Message: TWMScroll);
procedure SetPosition(Value: integer);
procedure SetRange(Value: integer);
procedure Update;
end;
TBookmarkNum = 0..9;
TMemoBookmark = packed record
TopLeft: TDCMemoCoord;
Col: integer;
Row: integer;
Toggle: boolean;
end;
TMemoBookmarks = array[TBookmarkNum] of TMemoBookmark;
TCustomUndoAction = class
private
Memo: TDCCustomSyntaxMemo;
CurPos: TDCMemoPos;
NewPos: TDCMemoPos;
public
constructor Create(AMemo: TDCCustomSyntaxMemo; ACurPos, ANewPos: TDCMemoPos);
procedure Undo; dynamic; abstract;
procedure Redo; dynamic; abstract;
end;
TUndoActionList = class(TList)
private
FMemo: TDCCustomSyntaxMemo;
FPosition: integer;
FUpdated: boolean;
function GetCanRedo: boolean;
function GetCanUndo: boolean;
public
constructor Create(AMemo: TDCCustomSyntaxMemo);
procedure AddUndoAction(AUndo: TCustomUndoAction);
procedure Clear; override;
procedure Undo;
procedure Redo;
property CanUndo: boolean read GetCanUndo;
property CanRego: boolean read GetCanRedo;
end;
TColRowChanged = procedure (Sender: TObject; Col, Row: integer) of object;
TGutterClick = procedure (Sender: TObject; MouseDownPos, MouseUpPos: TPoint) of object;
TEditorOptions = (eoDoubleClickLine, eoCursorBeyondEOF, eoAutoIndentMode,
eoPersistentBlocks, eoSmartTab, eoBackspaceUnindents, eoKeepTrailingBlanks);
TEditorOptionsSet = set of TEditorOptions;
TDCCustomSyntaxMemo = class(TCustomControl)
private
FSyntaxDataClass: TSyntaxDataClass;
FLines: TDCMemoStrings;
FSyntaxData: TDCCustomSyntaxData;
FCurrent: TDCMemoCoord;
FColCount: Longint;
FRowCount: Longint;
FHalf: TPoint;
FTopLeft: TDCMemoCoord;
FRowHeight: integer;
FColWidth: integer;
FSelectedArea: TDCTextArea;
FUpdateArea: TDCTextArea;
FChangedArea: TDCTextArea;
FMemoState: TMemoStates;
FInsertMode: Boolean;
FTabSize: integer;
FKeepTrailingBlanks: boolean;
FOnChange: TNotifyEvent;
FScrollTimerHandle: THandle;
FScrollInc: TPoint;
FReadOnly: boolean;
FVScroll: TDCMemoScroll;
FHScroll: TDCMemoScroll;
FUpdateVScroll: boolean;
FGutterWidth: integer;
FRightMargin: integer;
FVisibleGutter: boolean;
FVisibleRightMargin: boolean;
FIndicators: TImageList;
FBookmarks: TMemoBookmarks;
FUpdateCount: integer;
FOnColRowChanged: TColRowChanged;
FOnSetInsertMode: TNotifyEvent;
FUndoList: TUndoActionList;
FGroupUndo: boolean;
FCurPos: TDCMemoPos;
FGutterState: TGutterState;
FMouseMoving: boolean;
FOnGutterClick: TGutterClick;
FMovingBlock: TBlockType;
FLockPaint: integer;
FModified: boolean;
FUndoCount: integer;
FOnModifiedChanged: TNotifyEvent;
FEditorOptions: TEditorOptionsSet;
procedure UpdateMetrics;
procedure UpdateCRCount;
function GetText: string;
procedure SetText(const Value: string);
procedure ResetValues; virtual;
procedure SetCol(Value: integer);
procedure SetRow(Value: integer);
procedure ClearSelection;
procedure DeselectArea;
procedure Backspace;
procedure DeleteChars;
procedure GoEnd;
procedure WordLeft;
procedure WordRight;
procedure TabLeft;
procedure TabRight;
procedure AddSelection(ANewPos, AOldPos: TPoint);
procedure SetTabSize(const Value: integer);
procedure SetLeftCol(const Value: integer);
procedure SetTopRow(const Value: integer);
function GetIndentValue(ACol, ARow: integer): integer;
procedure SetLines(const Value: TDCMemoStrings);
procedure DoChangeText;
procedure StartScrollTimer;
procedure StopScrollTimer;
procedure SetReadOnly(const Value: boolean);
procedure UpdateScrollBars;
procedure DoScroll(X, Y: integer);
procedure DrawRightMargin(ARow: integer);
procedure InvalidateGutter;
procedure BeginUpdate;
procedure EndUpdate(AScroll: boolean = False);
function GetBookmarkState(Index: TBookmarkNum): boolean;
procedure CreateCaretUndo;
procedure SaveCurPos;
procedure MouseSelection(XPos, YPos: integer);
procedure SetMovingParam;
procedure LockPaint;
function UnlockPaint: boolean;
procedure SetModified(Value: boolean);
procedure UpdateModifiedStatus(Value: integer = -1);
function GetSyntaxColors: TDCSyntaxMemoColors;
protected
procedure Paint; override;
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
procedure WndProc(var Message: TMessage); override;
procedure AdjustClientRect(var Rect: TRect); override;
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
MousePos: TPoint): Boolean; override;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
procedure WMNCLButtonDown(var Message: TWMLButtonDown); message WM_NCLBUTTONDOWN;
procedure WMSetCursor(var Message: TWMSetCursor); message WM_SETCURSOR;
procedure WMSize(var Message: TWMSize ); message WM_SIZE;
procedure WMTimer(var Message: TWMTimer); message WM_TIMER;
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
procedure CMCancelMode(var Message: TCMCancelMode); message CM_CANCELMODE;
procedure CMRowChanged(var Message: TMessage); message CM_ROW_CHANGED;
procedure CMRowInserted(var Message: TMessage); message CM_ROW_INSERTED;
procedure CMRowDeleted(var Message: TMessage); message CM_ROW_DELETED;
procedure CMUpdateFinished(var Message: TMessage); message CM_UPDATE_FINISHED;
procedure CMInvalidateAll(var Message: TMessage); message CM_INVALIDATE_ALL;
procedure SetInsertMode(const Value: boolean); virtual;
procedure DrawRow(ARow: integer; AScroll: boolean = False); virtual;
procedure DrawLexem(ACol, ARow: integer; AText: string; Item: TLexemType); virtual;
procedure DrawLexemItem(ACol, ARow: integer; AText: string; BGColor, FGColor: TColor); virtual;
procedure DrawGutter(ARow: integer); virtual;
procedure UpdateGutter(AMode, ARow, ACount: integer); virtual;
function GetBlock(ARect: TRect; SelectMode: TTextSelectMode; AddCR: boolean = True): string;
procedure DeleteBlock(ARect: TRect; SelectMode: TTextSelectMode);
function InsertBlock(ACol, ARow: integer; SelectMode: TTextSelectMode; pBlock: PChar): integer;
procedure InsertChar(ACol, ARow: integer; ASymbol: Char);
procedure SetRowColor(ARow: integer; var BGColor, FGColor: TColor); virtual;
procedure ColRowChanged(ACol, ARow: integer);
property Text: string read GetText write SetText;
property Col: integer read FCurrent.X write SetCol;
property Row: integer read FCurrent.Y write SetRow;
property ReadOnly: boolean read FReadOnly write SetReadOnly;
property OnColRowChanged: TColRowChanged read FOnColRowChanged write FOnColRowChanged;
property OnSetInsertMode: TNotifyEvent read FOnSetInsertMode write FOnSetInsertMode;
property OnGutterClick: TGutterClick read FOnGutterClick write FOnGutterClick;
property Modified: boolean read FModified write SetModified;
property OnModifiedChanged: TNotifyEvent read FOnModifiedChanged write FOnModifiedChanged;
public
constructor Create(AOwner: TComponent;
ASyntaxDataClass: TSyntaxDataClass = nil); reintroduce; overload;
destructor Destroy; override;
procedure Clear;
procedure ClearUndoList;
function CopyToClipboard: string;
function CutToClipboard: string;
procedure DoGutterClick(MouseDownPos, MouseUpPos: TPoint); virtual;
procedure DrawGutterState(AState, ARow, AIndexPos: integer);
function GetCursorPos(ACol, ARow: integer; ACarret: boolean = False): TPoint;
function GetMemoCoord(PosX, PosY: integer): TDCMemoCoord;
procedure GotoBookmark(ABookmark: TBookmarkNum);
procedure InvalidateRect(ARect: TRect);
procedure KeyPress(var Key: Char); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure MoveTopLeft(ALeft, ATop: integer);
procedure RedrawCaret;
procedure PaintText(AScroll: boolean = False);
procedure PasteFromClipboard;
procedure ToggleBookmark(ABookmark: TBookmarkNum);
procedure SelectRect(ARect: TRect);
procedure SetDataClass(Value: TSyntaxDataClass);
procedure UpdateSyntaxData;
function WordAt(ACol, ARow: integer; var APoint: TPoint): boolean;
property Bookmarks[Index: TBookmarkNum]: boolean read GetBookmarkState;
property ColCount: longint read FColCount;
property EditorOptions: TEditorOptionsSet read FEditorOptions write FEditorOptions;
property GroupUndo: boolean read FGroupUndo write FGroupUndo;
property InsertMode: boolean read FInsertMode write SetInsertMode;
property LeftCol: integer read FTopLeft.X write SetLeftCol;
property Lines: TDCMemoStrings read FLines write SetLines;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnKeyPress;
property RowCount: longint read FRowCount;
property SyntaxColors: TDCSyntaxMemoColors read GetSyntaxColors;
property TabSize: integer read FTabSize write SetTabSize;
property TopRow: integer read FTopLeft.Y write SetTopRow;
property OnClick;
property OnDblClick;
property OnDragDrop;
property OnDragOver;
property OnEnter;
property OnExit;
property OnEndDrag;
property OnEndDock;
property OnKeyDown;
property OnKeyUp;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDrag;
property OnStartDock;
end;
TDCSyntaxMemo = class(TDCCustomSyntaxMemo)
published
property Text;
property Col;
property Row;
property OnChange;
property ReadOnly;
property PopupMenu;
property OnColRowChanged;
property OnSetInsertMode;
property OnGutterClick;
property Modified;
property OnModifiedChanged;
end;
implementation
{$R *.RES}
const
gsBreakStop = 'GS_BREAK_STOP';
gsBreakInvalid = 'GS_BREAK_INVALID';
gsBreakSet = 'GS_BREAK_SET';
gsArrow = 'GS_ARROW';
gsEllipse = 'GS_ELLIPSE';
gsBookmark_0 = 'GS_BOOKMARK_0';
gsBookmark_1 = 'GS_BOOKMARK_1';
gsBookmark_2 = 'GS_BOOKMARK_2';
gsBookmark_3 = 'GS_BOOKMARK_3';
gsBookmark_4 = 'GS_BOOKMARK_4';
gsBookmark_5 = 'GS_BOOKMARK_5';
gsBookmark_6 = 'GS_BOOKMARK_6';
gsBookmark_7 = 'GS_BOOKMARK_7';
gsBookmark_8 = 'GS_BOOKMARK_8';
gsBookmark_9 = 'GS_BOOKMARK_9';
type
TCaretUndo = class(TCustomUndoAction)
public
procedure Undo; override;
procedure Redo; override;
end;
TCharUndo = class(TCaretUndo)
private
Value: Char;
BreakCount: integer;
public
constructor Create(AMemo: TDCCustomSyntaxMemo; ACurPos, ANewPos: TDCMemoPos;
AValue: Char; ABreakCount: integer);
procedure Undo; override;
procedure Redo; override;
end;
TBlockUndo = class(TCaretUndo)
private
Style: TBlockUndoStyle;
InsValue: string;
DelValue: string;
SelectMode: TTextSelectMode;
PersistentBlocks: boolean;
BreakCount: integer;
procedure InsertBlock;
function GetTextRect(Left, Top: integer; pSource: PChar): TRect;
public
constructor Create(AMemo: TDCCustomSyntaxMemo; ACurPos, ANewPos: TDCMemoPos;
AStyle: TBlockUndoStyle; AInsValue, ADelValue: string;
ASelectMode: TTextSelectMode; APersistentBlocks: boolean; ABreakCount: integer);
procedure Undo; override;
procedure Redo; override;
end;
TDeleteUndo = class(TCaretUndo)
private
Style: TDeleteUndoStyle;
Value: string;
public
constructor Create(AMemo: TDCCustomSyntaxMemo; ACurPos, ANewPos: TDCMemoPos;
AValue: string; AStyle: TDeleteUndoStyle);
procedure Undo; override;
procedure Redo; override;
end;
function ComparePoints(const P1, P2: TPoint): Integer;
begin
if (P1.Y < P2.Y) or (P1.Y = P2.Y) and (P1.X < P2.X) then
Result := -1
else if (P1.Y = P2.Y) and (P1.X = P2.X) then
Result := 0
else
Result := 1;
end;
function SortPoints(var P1: TPoint; var P2: TPoint): Boolean;
var
P: TPoint;
begin
Result := ComparePoints(P1, P2) = 1;
if Result then
begin
P := P1; P1:= P2; P2:= P;
end
end;
function MemoPos(ATopLeft: TDCMemoCoord; ACol, ARow: integer; ASelectRect: TRect): TDCMemoPos;
begin
with Result do
begin
TopLeft := ATopLeft;
Col := ACol;
Row := ARow;
SelectRect := ASelectRect;
end;
end;
{ TDCMemoStrings }
function TDCMemoStrings.Add(const Value: string): Integer;
var
P, Start: PChar;
S: string;
begin
BeginUpdate;
try
Result := GetCount;
P := Pointer(Value);
if P <> nil then
begin
while P^ <> #0 do
begin
Start := P;
while not (P^ in [#0, #10, #13]) do Inc(P);
SetString(S, Start, P - Start);
inherited Add(S);
if P^ = #13 then Inc(P);
if P^ = #10 then Inc(P);
end
end
else
if Value = '' then inherited Add('');
finally
EndUpdate;
end;
end;
procedure TDCMemoStrings.Clear;
var
i: integer;
begin
if FCount <> 0 then
begin
if not FDestroing then SendControlMessage(CM_UPDATE_STARTED);
for i := 0 to FCount - 1 do
ReallocMem(FList^[I].Lexems, 0);
Finalize(FList^[0], FCount);
FCount := 0;
SetCapacity(0);
if not FDestroing then FControl.Perform(CM_INVALIDATE_ALL, 0, 0);
if not FDestroing then SendControlMessage(CM_UPDATE_FINISHED);
end;
end;
constructor TDCMemoStrings.Create(AControl: TCustomControl);
begin
inherited Create;
FControl := AControl;
FMessages := True;
FDestroing:= False;
end;
procedure TDCMemoStrings.Delete(Index: Integer);
begin
if (Index < 0) or (Index >= FCount) then Error(SListIndexError, Index);
SendControlMessage(CM_UPDATE_STARTED);
ReallocMem(FList^[Index].Lexems, 0);
Finalize(FList^[Index]);
Dec(FCount);
if Index < FCount then
System.Move(FList^[Index + 1], FList^[Index], (FCount - Index)*SizeOf(TLineDataItem));
FControl.Perform(CM_ROW_DELETED, Index, Index);
SendControlMessage(CM_UPDATE_FINISHED);
end;
procedure TDCMemoStrings.DeleteLines(Index, ACount: Integer);
var
i: integer;
begin
if Index < 0 then
Inc(ACount, Index)
else
if Index >= FCount then Exit;
if ACount <= 0 then Exit;
SendControlMessage(CM_UPDATE_STARTED);
i := Index;
while (ACount > 0) and (i < FCount) do
begin
ReallocMem(FList^[I].Lexems,0);
Finalize(FList^[i]);
Inc(i);
Dec(ACount);
end;
if (i < FCount) and (i <> Index) then
System.Move(FList^[i], FList^[Index], (FCount - i)*SizeOf(TLineDataItem));
Dec(FCount, i - Index);
FControl.Perform(CM_ROW_DELETED, Index, i - 1);
SendControlMessage(CM_UPDATE_FINISHED);
end;
destructor TDCMemoStrings.Destroy;
begin
FDestroing := True;
Clear;
inherited;
end;
function TDCMemoStrings.Get(Index: Integer): string;
begin
if (Index < 0) or (Index >= FCount) then Error(SListIndexError, Index);
Result := FList^[Index].FString;
end;
function TDCMemoStrings.GetCapacity: Integer;
begin
Result := FCapacity;
end;
function TDCMemoStrings.GetCount: Integer;
begin
Result := FCount;
end;
function TDCMemoStrings.GetLineInfo(Index: Integer): PLineDataItem;
begin
Result := @(FList^[Index]);
end;
procedure TDCMemoStrings.Grow;
var
Delta: integer;
begin
if FCapacity > 64 then
Delta := FCapacity div 4
else
if FCapacity > 8 then
Delta := 16
else
Delta := 4;
SetCapacity(FCapacity + Delta);
end;
procedure TDCMemoStrings.GrowTo(ANewCount: Integer);
begin
InsertLines(FCount, ANewCount - FCount);
end;
procedure TDCMemoStrings.Insert(Index: Integer; const S: string);
begin
if (Index < 0) or (Index > FCount) then Error(SListIndexError, Index);
InsertItem(Index, S);
end;
procedure TDCMemoStrings.InsertItem(Index: Integer; const S: string);
begin
SendControlMessage(CM_UPDATE_STARTED);
if FCount = FCapacity then Grow;
if Index < FCount then
System.Move(FList^[Index], FList^[Index + 1], (FCount - Index)*SizeOf(TLineDataItem));
FillChar(FList^[Index], SizeOf(TLineDataItem), 0);
FList^[Index].FString := S;
Inc(FCount);
FControl.Perform(CM_ROW_INSERTED, Index, Index);
SendControlMessage(CM_UPDATE_FINISHED);
end;
procedure TDCMemoStrings.InsertLines(Index, ACount: Integer);
var
NewCapacity, i: integer;
begin
if (Index < 0) or (Index > FCount) then Error(SListIndexError, Index);
if ACount <= 0 then Exit;
SendControlMessage(CM_UPDATE_STARTED);
NewCapacity := FCount + ACount;
if NewCapacity > FCapacity then SetCapacity(NewCapacity);
if Index < FCount then
System.Move(FList^[Index], FList^[Index + ACount], (FCount - Index)*SizeOf(TLineDataItem));
for i := 0 to ACount-1 do with FList^[Index+i] do
begin
Pointer(FString) := nil;
FObject := nil;
end;
FillChar(FList^[Index], ACount*SizeOf(TLineDataItem), 0);
Inc(FCount, ACount);
FControl.Perform(CM_ROW_INSERTED, Index, Index + ACount - 1);
SendControlMessage(CM_UPDATE_FINISHED);
end;
procedure TDCMemoStrings.Put(Index: Integer; const S: string);
begin
if (Index < 0) or (Index >= FCount) then Error(SListIndexError, Index);
SendControlMessage(CM_UPDATE_STARTED);
FList^[Index].FString := S;
FControl.Perform(CM_ROW_CHANGED, Index, Index);
SendControlMessage(CM_UPDATE_FINISHED);
end;
procedure TDCMemoStrings.SendControlMessage(AMessage: Cardinal);
begin
if not FUpdating and FMessages then FControl.Perform(AMessage, 0, 0);
end;
procedure TDCMemoStrings.SetCapacity(NewCapacity: Integer);
begin
if FCapacity <> NewCapacity
then begin
FCapacity := NewCapacity;
ReallocMem(FList, FCapacity * SizeOf(TLineDataItem));
end;
end;
procedure TDCMemoStrings.SetUpdateState(Updating: Boolean);
begin
if FUpdating <> Updating
then begin
if FMessages then
begin
if Updating then
FControl.Perform(CM_UPDATE_STARTED, 0, 0)
else
FControl.Perform(CM_UPDATE_FINISHED, 0, 0);
end;
FUpdating := Updating;
end;
end;
{ TDCTextArea }
procedure TDCTextArea.Add(AStartPos, AEndPos: TPoint);
begin
SortPoints(AStartPos, AEndPos);
if Empty then
begin
StartPos := AStartPos;
EndPos := AEndPos;
end
else begin
SortPoints(StartPos, AStartPos);
SortPoints(AEndPos, EndPos);
end;
Empty := ComparePoints(StartPos, EndPos) = 0;
end;
procedure TDCTextArea.AddSelection(ANewPos, AOldPos: TPoint);
begin
if Empty then
Add(ANewPos, AOldPos)
else begin
if ComparePoints(ANewPos, AOldPos) > 0 then
begin
if ComparePoints(AOldPos, EndPos) >= 0 then
EndPos := ANewPos
else
StartPos := ANewPos;
end
else begin
if ComparePoints(AOldPos, StartPos) <= 0 then
StartPos := ANewPos
else
EndPos := ANewPos;
end;
SortPoints(StartPos, EndPos);
Empty := ComparePoints(StartPos, EndPos) = 0;
end;
end;
procedure TDCTextArea.Clear;
begin
Empty := True;
Mode := smLines;
StartPos := Point(0, 0);
EndPos := Point(0, 0);
end;
constructor TDCTextArea.Create;
begin
Clear;
end;
function TDCTextArea.GetRect: TRect;
begin
Result.TopLeft := StartPos;
Result.BottomRight := EndPos;
end;
function TDCTextArea.WordSelected(ACol, ARow, ALength: integer;
var SelStart, SelEnd: integer): boolean;
var
AEndPos: integer;
begin
Result := False;
if Empty or (ARow < StartPos.Y) or (ARow > EndPos.Y) or (ACol < 0) then Exit;
AEndPos := ACol + ALength;
case Mode of
smLines:
begin
if (ARow = StartPos.Y) then
begin
if AEndPos < StartPos.X then Exit;
if ACol > StartPos.X then
SelStart := ACol
else
SelStart := StartPos.X;
end
else
SelStart := ACol;
if (ARow = EndPos.Y) then
begin
if ACol > EndPos.X then Exit;
if AEndPos < EndPos.X then
SelEnd := AEndPos
else
SelEnd := EndPos.X;
end
else
SelEnd := AEndPos;
Result := True;
end;
smColumns:
begin
if (AEndPos < StartPos.X) or (ACol > EndPos.Y) then Exit;
if ACol > StartPos.X then
SelStart := ACol
else
SelStart := StartPos.X;
if AEndPos < EndPos.X then
SelEnd := AEndPos
else
SelEnd := AEndPos;
Result := True;
end;
end;
end;
{ TDCCustomSyntaxMemo }
procedure TDCCustomSyntaxMemo.CMFontChanged(var Message: TMessage);
begin
inherited;
Canvas.Font := Font;
UpdateMetrics;
UpdateCRCount;
Invalidate;
RedrawCaret;
end;
constructor TDCCustomSyntaxMemo.Create(AOwner: TComponent;
ASyntaxDataClass: TSyntaxDataClass = nil);
var
ABitmap: TBitmap;
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csFramed];
Font.Name := 'Courier New';
Font.Size := 10;
Font.Charset := RUSSIAN_CHARSET;
FLines := TDCMemoStrings.Create(Self);
FSyntaxDataClass := ASyntaxDataClass;
if FSyntaxDataClass = nil then FSyntaxDataClass := TDCDelphiSyntaxData;
FSyntaxData := FSyntaxDataClass.Create;
FSelectedArea := TDCTextArea.Create;
FUpdateArea := TDCTextArea.Create;
FChangedArea := TDCTextArea.Create;
InsertMode := True;
FTabSize := 8;
FMemoState := [];
FEditorOptions := [{eoDoubleClickLine, }eoCursorBeyondEOF, eoAutoIndentMode,
{eoPersistentBlocks,} eoSmartTab, eoBackspaceUnindents{, eoKeepTrailingBlanks}];
FHScroll := TDCMemoScroll.Create(Self, sbHorizontal);
FVScroll := TDCMemoScroll.Create(Self, sbVertical);
FHScroll.FRange := 924;
FHScroll.FMax := MaxInt;
FUpdateVScroll := True;
FRightMargin := 80;
FGutterWidth := 42;
FVisibleGutter := True;
FUpdateCount := 0;
FVisibleRightMargin := True;
FGroupUndo := True;
FUndoList := TUndoActionList.Create(Self);
FLockPaint := 0;
Modified := False;
ResetValues;
ABitmap := TBitmap.Create;
try
ABitmap.LoadFromResourceName(HInstance, gsBreakStop);
FIndicators := TImageList.CreateSize(ABitmap.Width, ABitmap.Height);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBreakInvalid);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBreakSet);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsArrow);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsEllipse);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_0);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_1);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_2);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_3);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_4);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_5);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_6);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_7);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_8);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
ABitmap.LoadFromResourceName(HInstance, gsBookmark_9);
FIndicators.AddMasked(ABitmap, ABitmap.Canvas.Pixels[0,0]);
finally
ABitmap.Free;
end;
end;
function TDCCustomSyntaxMemo.GetMemoCoord(PosX,
PosY: integer): TDCMemoCoord;
var
AColCount, ARowCount: integer;
begin
with Result do
begin
if FVisibleGutter then Dec(PosX, FGutterWidth);
X := MulDiv(PosX, 1, FColWidth);
Y := Trunc(PosY * 1.0 / FRowHeight);
AColCount := FColCount - FHalf.X;
ARowCount := FRowCount - FHalf.Y;
if X > AColCount then X := AColCount;
if Y > ARowCount then Y := ARowCount;
Inc(X, LeftCol);
Inc(Y, TopRow);
end;
end;
function TDCCustomSyntaxMemo.GetCursorPos(ACol, ARow: integer; ACarret: boolean = False): TPoint;
begin
with Result do
begin
X := (ACol - FTopLeft.X) * FColWidth;
Y := (ARow - FTopLeft.Y) * FRowHeight;
if ACarret and (X < 0) then Exit;
if FVisibleGutter then Inc(X, FGutterWidth);
end;
end;
procedure TDCCustomSyntaxMemo.RedrawCaret;
var
AWidth, AHeight: integer;
P: TPoint;
begin
if HandleAllocated then
begin
AWidth := 2;
AHeight := FRowHeight;
if Focused then
begin
HideCaret(Handle);
CreateCaret(Handle, 0, AWidth, AHeight);
P := GetCursorPos(FCurrent.X, FCurrent.Y, True);
if P.X > 0 then Dec(P.X);
SetCaretPos(P.X, P.Y);
ShowCaret(Handle);
end
else
HideCaret(Handle);
end;
end;
procedure TDCCustomSyntaxMemo.UpdateCRCount;
var
AClientWidth, AClientHeight: integer;
begin
if HandleAllocated then
begin
if FVisibleGutter then
AClientWidth := ClientWidth - FGutterWidth
else
AClientWidth := ClientWidth;
AClientHeight := ClientHeight;
FColCount := AClientWidth div FColWidth;
FRowCount := AClientHeight div FRowHeight;
FHalf.X := 0;
FHalf.Y := 0;
if AClientWidth mod FColWidth <> 0 then Inc(FHalf.X);
if AClientHeight mod FRowHeight <> 0 then Inc(FHalf.Y);
end;
end;
procedure TDCCustomSyntaxMemo.UpdateMetrics;
var
TextMetric: TTextMetric;
begin
if HandleAllocated then
begin
if GetTextMetrics(Canvas.Handle, TextMetric) then
begin
FColWidth := TextMetric.tmAveCharWidth;
FRowHeight := TextMetric.tmHeight;
end;
end;
end;
procedure TDCCustomSyntaxMemo.WMSize(var Message: TWMSize);
begin
inherited;
UpdateMetrics;
UpdateCRCount;
with FVScroll do
begin
FPageSize := FRowCount;
if FLines.Count > 1 then
FRange := FLines.Count + FRowCount - 2
else
FRange := FRowCount;
FMax := FRange;
end;
with FHScroll do
begin
FPageSize := FColCount;
end;
UpdateScrollBars;
end;
procedure TDCCustomSyntaxMemo.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
inherited;
end;
procedure TDCCustomSyntaxMemo.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
with Message do
Result := Result or DLGC_WANTARROWS or DLGC_WANTTAB or DLGC_WANTALLKEYS;
end;
procedure TDCCustomSyntaxMemo.WMSetCursor(var Message: TWMSetCursor);
begin
with Message do
begin
case FGutterState.HitTest of
HTCLIENT:
SetCursor(Screen.Cursors[crIBeam]);
HTGUTTER:
inherited;
HTSELECTED:
begin
if msMoving in FMemoState then
SetCursor(Screen.Cursors[crDrag])
else
inherited;
end;
else
inherited;
end;
end;
end;
procedure TDCCustomSyntaxMemo.WndProc(var Message: TMessage);
begin
with Message do
begin
case Msg of
WM_SETFOCUS:
RedrawCaret;
WM_KILLFOCUS:
RedrawCaret;
WM_ERASEBKGND:
Exit;
WM_MOUSEACTIVATE:
SetFocus;
end;
end;
inherited;
end;
procedure TDCCustomSyntaxMemo.WMNCHitTest(var Message: TWMNCHitTest);
var
APoint: TPoint;
SelStart, SelEnd: integer;
MemoCoord: TDCMemoCoord;
begin
inherited;
with Message do
begin
APoint := ScreenToClient(Point(XPos, YPos));
if FVisibleGutter then
begin
if (APoint.X < FGutterWidth) and (Result = HTCLIENT) then Result := HTGUTTER;
if not FSelectedArea.Empty then
begin
with FSelectedArea, APoint do
begin
MemoCoord := GetMemoCoord(X, Y);
if WordSelected(MemoCoord.X, MemoCoord.Y, 0, SelStart, SelEnd) then
begin
FGutterState.HitTest := HTSELECTED;
Exit;
end;
end;
end;
end;
FGutterState.HitTest := Result;
end;
end;
procedure TDCCustomSyntaxMemo.Paint;
begin
FUpdateArea.Add(Point(LeftCol, TopRow),
Point(LeftCol + FColCount + FHalf.X, TopRow + FRowCount + FHalf.Y));
PaintText;
end;
procedure TDCCustomSyntaxMemo.PaintText(AScroll: boolean = False);
var
i: integer;
begin
if FLockPaint = 0 then
begin
if not FUpdateArea.Empty then with FUpdateArea do
begin
if StartPos.Y < FTopLeft.Y then StartPos.Y := FTopLeft.Y;
HideCaret(Handle);
for i := StartPos.Y to EndPos.Y do DrawRow(i, AScroll);
Clear;
end;
RedrawCaret;
FUpdateArea.Clear;
end;
end;
procedure TDCCustomSyntaxMemo.DrawRow(ARow: integer; AScroll: boolean = False);
var
AText, AValue: String;
LineItem: PLineDataItem;
LexemItem: TLexemItem;
i, AColPos, ADrawing: integer;
begin
if (ARow < FTopLeft.Y) or (ARow > FTopLeft.Y + FRowCount + FHalf.Y) then Exit;
if (FLines.Count = 0) or (ARow >= FLines.Count) then
begin
AText := ' ';
for i := 1 to FColCount +FHalf.X - 1 do AText := AText + ' ';
DrawLexem(FTopLeft.X, ARow, AText, lxWhitespace);
end
else begin
LineItem := FLines.LineInfo[ARow];
AText := LineItem.FString;
AColPos := 0;
i := 0;
while (i < LineItem.Count) and (AColPos < FTopLeft.X + FColCount + FHalf.X) do
begin
LexemItem := LineItem.Lexems[i];
ADrawing := AColPos;
if AColPos + LexemItem.Length >= FTopLeft.X then
begin
if AColPos >= FTopLeft.X then
AValue := Copy(AText, AColPos+1, LexemItem.Length)
else begin
AValue := Copy(AText, FTopLeft.X+1, LexemItem.Length);
ADrawing := FTopLeft.X;
end;
DrawLexem(ADrawing, ARow, AValue, LexemItem.Item);
end;
AColPos := AColPos + LexemItem.Length;
Inc(i);
end;
if AColPos < FTopLeft.X + FColCount + FHalf.X
then begin
AText := ' ';
if AColPos > FTopLeft.X then
ADrawing := AColPos
else
ADrawing := FTopLeft.X;
for i := ADrawing to FTopLeft.X + FColCount + FHalf.X - 1 do AText := AText + ' ';
DrawLexem(ADrawing, ARow, AText, lxWhitespace);
end;
end;
if FVisibleRightMargin and (LeftCol <= FRightMargin) then DrawRightMargin(ARow);
if FVisibleGutter and not AScroll then DrawGutter(ARow);
end;
procedure TDCCustomSyntaxMemo.DrawLexem(ACol, ARow: integer; AText: string;
Item: TLexemType);
var
ALength, SelStart, SelEnd: Integer;
Selected: Boolean;
begin
Canvas.Font.Style := SyntaxColors.Items[Item].FontStyle;
ALength := Length(AText);
Selected := FSelectedArea.WordSelected(ACol, ARow, ALength, SelStart, SelEnd);
with SyntaxColors.Items[Item] do
begin
if not Selected then
DrawLexemItem(ACol, ARow, AText, BGColor, FGColor)
else begin
DrawLexemItem(ACol, ARow, Copy(AText, 1 , SelStart - ACol), BGColor, FGColor);
DrawLexemItem(SelStart, ARow,
Copy(AText, SelStart - ACol + 1 , SelEnd - SelStart), clHighlight, clHighlightText);
if ACol + ALength > SelEnd then
DrawLexemItem(SelEnd, ARow,
Copy(AText, SelEnd - ACol + 1, ACol + ALength - SelEnd), BGColor, FGColor);
end;
end;
end;
procedure TDCCustomSyntaxMemo.DrawLexemItem(ACol, ARow: integer;
AText: string; BGColor, FGColor: TColor);
var
APoint: TPoint;
begin
with Canvas do
begin
SetRowColor(ARow, BGColor, FGColor);
Font.Color := FGColor;
Brush.Color := BGColor;
APoint := GetCursorPos(ACol, ARow);
TextOut(APoint.X, APoint.Y, AText);
end;
end;
destructor TDCCustomSyntaxMemo.Destroy;
begin
FUndoList.Free;
FIndicators.Free;
FHScroll.Free;
FVScroll.Free;
FSyntaxData.Free;
FSelectedArea.Free;
FUpdateArea.Free;
FChangedArea.Free;
FLines.Free;
inherited;
end;
procedure TDCCustomSyntaxMemo.CMInvalidateAll(var Message: TMessage);
begin
FChangedArea.Clear;
FChangedArea.Add(Point(0, 0), Point(0, TopRow + FRowCount + FHalf.Y));
end;
procedure TDCCustomSyntaxMemo.CMRowChanged(var Message: TMessage);
begin
with Message do
FChangedArea.Add(Point(0, WParam),Point(0, LParam + 1));
if not FUndoList.FUpdated then Modified := True;
DoChangeText;
end;
procedure TDCCustomSyntaxMemo.CMRowDeleted(var Message: TMessage);
begin
with Message do
begin
FChangedArea.Add(Point(0, WParam), Point(0, LParam + 1));
FUpdateArea.Add(Point(0, WParam), Point(0, TopRow + FRowCount + FHalf.X));
UpdateGutter(CM_ROW_DELETED, WParam, LParam + 1 - WParam);
end;
if not FUndoList.FUpdated then Modified := True;
DoChangeText;
end;
procedure TDCCustomSyntaxMemo.CMRowInserted(var Message: TMessage);
begin
with Message do
begin
FChangedArea.Add(Point(0, WParam), Point(0, LParam + 1));
FUpdateArea.Add(Point(0, WParam), Point(0, TopRow + FRowCount + FHalf.X));
UpdateGutter(CM_ROW_INSERTED, WParam, LParam + 1 - WParam);
end;
if not FUndoList.FUpdated then Modified := True;
DoChangeText;
end;
procedure TDCCustomSyntaxMemo.CMUpdateFinished(var Message: TMessage);
var
i: Integer;
APrevComment, AComment: DWORD;
pLineItem: PLineDataItem;
sLine: string;
begin
if FChangedArea.Empty then Exit;
FLines.Messages := False;
i := FChangedArea.StartPos.Y;
if i < FLines.Count then
begin
if i > 0 then
begin
pLineItem := FLines.LineInfo[i-1];
APrevComment := pLineItem.Comment;
end
else
APrevComment := 0;
while i < FLines.Count do
begin
pLineItem := FLines.LineInfo[i];
AComment := pLineItem.Comment;
pLineItem.PrevComment := APrevComment;
if not FKeepTrailingBlanks then
begin
sLine := TrimRight(FLines[i]);
if AnsiCompareStr(sLine, FLines[i]) <> 0 then FLines[i] := sLine;
end;
FSyntaxData.ParseLine(pLineItem);
Inc(i);
if (pLineItem.Comment <> APrevComment) or (pLineItem.Comment <> AComment)then
FChangedArea.Add(Point(0, i + 1), Point(0, i + 1));
APrevComment := pLineItem.Comment;
if i >= FChangedArea.EndPos.Y then break;
end;
end;
FLines.Messages := True;
FUpdateArea.Add(FChangedArea.StartPos, FChangedArea.EndPos);
PaintText;
FChangedArea.Clear;
with FVScroll do
begin
if FLines.Count > 1 then
FRange := FLines.Count + FRowCount - 2
else
FRange := FRowCount;
FMax := FRange;
if FUpdateVScroll then Update;
end;
end;
function TDCCustomSyntaxMemo.GetText: string;
begin
Result := FLines.Text;
end;
procedure TDCCustomSyntaxMemo.SetText(const Value: string);
begin
FLines.Text := Value;
FUndoList.Clear;
ResetValues;
end;
procedure TDCCustomSyntaxMemo.ResetValues;
begin
FUndoList.FUpdated := True;
BeginUpdate;
FCurrent.X := 0;
FCurrent.Y := 0;
FTopLeft.X := 0;
FTopLeft.Y := 0;
EndUpdate;
FUndoList.FUpdated := False;
end;
function TDCCustomSyntaxMemo.InsertBlock(ACol, ARow: integer;
SelectMode: TTextSelectMode; pBlock: PChar): integer;
var
BreakCount, CurRow, CurCol, i, nCount: integer;
pValue: PChar;
Break: boolean;
AText, ABuf, ABuf1, ABuf2, AEndText: string;
begin
Result := 0;
if pBlock^ = #0 then Exit;
FLines.BeginUpdate;
BeginUpdate;
nCount := 0;
if SelectMode = smLines then
begin
if ARow > FLines.Count then nCount := ARow - FLines.Count;
FLines.GrowTo(ARow + 1);
BreakCount := 0;
pValue := pBlock;
while (pValue^ <> #0) do
begin
if (pValue^ = #13) or (pValue^ = #10) then
begin
Inc(BreakCount);
Inc(pValue);
if pValue^ = #10 then Inc(pValue);
end
else
Inc(pValue);
end;
if BreakCount > 0 then
FLines.InsertLines(ARow +1, BreakCount);
pValue := pBlock;
CurRow := ARow;
CurCol := ACol;
while pValue^ <> #0 do
begin
while not ((pValue^ = #0) or (pValue^ = #13) or (pValue^ = #10)) do
Inc(pValue);
Break := pValue^ <> #0;
SetString(AText, pBlock, pValue - pBlock);
if CurRow = ARow then
begin
ABuf := FLines[CurRow];
if (ACol > 0) and (ACol <= Length(ABuf)) then
begin
ABuf1 := Copy(ABuf, 1, ACol);
ABuf2 := Copy(ABuf, ACol+1, Length(ABuf));
end
else begin
if (ACol > Length(ABuf)) and (ACol > 0) then
begin
ABuf1 := ABuf;
for i := Length(ABuf) to ACol - 1 do ABuf1 := ABuf1 + ' ';
ABuf2 := '';
end
else begin
ABuf1 := '';
ABuf2 := ABuf;
end;
end;
if Break then
begin
AEndText := ABuf2;
FLines[CurRow] := ABuf1 + AText;
CurCol := 0;
end
else begin
FLines[CurRow] := ABuf1 + AText + ABuf2;
CurCol := ACol + Length(AText);
end;
end
else begin
FLines[CurRow] := AText;
CurCol := Length(AText);
end;
if Break then
begin
Inc(CurRow);
Inc(pValue);
CurCol := 0;
if pValue^ = #10 then Inc(pValue);
end;
pBlock := pValue;
end;
if (CurRow <> ARow) and (AEndText <> '') then
begin
CurCol := Length(FLines[CurRow]);
FLines[CurRow] := FLines[CurRow] + AEndText;
end;
Col := CurCol;
Row := CurRow;
end
else begin
{}
end;
EndUpdate;
FLines.EndUpdate;
Result := nCount;
end;
procedure TDCCustomSyntaxMemo.SetCol(Value: integer);
begin
if FCurrent.X <> Value then
begin
BeginUpdate;
if Value < 0 then Value := 0;
FCurrent.X := Value;
if FCurrent.X < LeftCol then LeftCol := FCurrent.X;
if FCurrent.X >= (LeftCol + FColCount -1) then
LeftCol := FCurrent.X - FColCount + 1;
EndUpdate;
end;
end;
procedure TDCCustomSyntaxMemo.SetRow(Value: integer);
begin
if FCurrent.Y <> Value then
begin
BeginUpdate;
try
if Value < 0 then Value := 0;
FCurrent.Y := Value;
if FCurrent.Y < TopRow then TopRow := FCurrent.Y;
if FCurrent.Y > (TopRow + FRowCount -1) then TopRow := FCurrent.Y - FRowCount + 1;
if FCurrent.Y > FLines.Count - 1 then
begin
if msSelecting in FMemoState then
begin
FCurrent.Y := FLines.Count - 1;
if FCurrent.Y < 0 then FCurrent.Y := 0;
GoEnd;
Exit;
end;
if not(eoCursorBeyondEOF in EditorOptions) then
begin
FCurrent.Y := FLines.Count - 1;
if FCurrent.Y < 0 then FCurrent.Y := 0;
end;
end;
finally
EndUpdate;
end;
end;
end;
procedure TDCCustomSyntaxMemo.KeyPress(var Key: Char);
begin
inherited;
if (Key < #32) then Exit;
if not FReadOnly then
begin
if not( eoPersistentBlocks in EditorOptions) then ClearSelection;
InsertChar(Col, Row, Key);
end;
end;
procedure TDCCustomSyntaxMemo.PasteFromClipboard;
var
ACol, ARow, BreakCount: integer;
DeletedText, AText: string;
begin
if not FReadOnly and Clipboard.HasFormat(CF_TEXT) then
begin
SaveCurPos;
AText := ClipBoard.AsText;
BeginUpdate;
if not(eoPersistentBlocks in EditorOptions) then
begin
FUpdateVScroll := False;
DeletedText := GetBlock(FSelectedArea.Rect, FSelectedArea.Mode, False);
ClearSelection;
BreakCount := InsertBlock(Col, Row, FSelectedArea.Mode, PChar(AText));
FUpdateVScroll := True;
FVScroll.Update;
end
else begin
DeselectArea;
ACol := Col;
ARow := Row;
DeletedText := '';
BreakCount := InsertBlock(Col, Row, FSelectedArea.Mode, PChar(AText));
FSelectedArea.Add(Point(ACol, ARow), Point(Col, Row));
FUpdateArea.Add(Point(ACol, ARow), Point(Col, Row));
end;
TBlockUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect),
buInsert, AText, DeletedText, FSelectedArea.Mode, eoPersistentBlocks in EditorOptions,
BreakCount);
EndUpdate;
end;
end;
procedure TDCCustomSyntaxMemo.ClearSelection;
begin
if not FSelectedArea.Empty then
begin
DeleteBlock(FSelectedArea.Rect, FSelectedArea.Mode);
Col := FSelectedArea.StartPos.X;
Row := FSelectedArea.StartPos.Y;
DeselectArea;
end;
end;
procedure TDCCustomSyntaxMemo.DeleteBlock(ARect: TRect;
SelectMode: TTextSelectMode);
var
LinePart1, LinePart2, AText: string;
i, j: integer;
begin
if ARect.Top >= FLines.Count then Exit;
FLines.BeginUpdate;
if SelectMode = smLines then
begin
LinePart1 := Copy(FLines[ARect.Top], 1, ARect.Left);
if ARect.Bottom < FLines.Count then
begin
AText := FLines[ARect.Bottom];
if ARect.Right > 0 then
LinePart2 := Copy(AText, ARect.Right + 1, Length(AText) - ARect.Right)
else
LinePart2 := AText;
if (ARect.Top = ARect.Bottom) and
(ARect.Right >= Length(AText) + 1) and
(ARect.Top < FLines.Count-1) then
begin
LinePart2 := FLines[ARect.Top + 1];
FLines.Delete(ARect.Top);
end
end
else
LinePart2 := '';
FLines.DeleteLines(ARect.Top, ARect.Bottom - ARect.Top);
if Length(LinePart1) < ARect.Left then
begin
j := Length(LinePart1);
AText := '';
for i := j to ARect.Left - 1 do AText := AText + ' ';
FLines[ARect.Top] := LinePart1 + AText + LinePart2;
end
else
FLines[ARect.Top] := LinePart1 + LinePart2;
end
else begin
end;
FLines.EndUpdate;
end;
function TDCCustomSyntaxMemo.GetBlock(ARect: TRect;
SelectMode: TTextSelectMode; AddCR: boolean = True): string;
var
i: Integer;
AText: string;
ALength, Size: integer;
pValue: PChar;
begin
Result := '';
if (ARect.Top = ARect.Bottom) and (ARect.Top < FLines.Count) then
begin
Result := Copy(FLines[ARect.Top], ARect.Left + 1, ARect.Right - ARect.Left);
if (Result = '') and AddCR then Result := #13#10;
Exit
end;
if SelectMode = smLines then
begin
Size := 0;
for i := ARect.Top to ARect.Bottom do
begin
if i < FLines.Count then
begin
if i = ARect.Top then
Inc(Size, Length(FLines[i]) - ARect.Left + 2)
else
if i = ARect.Bottom then
Inc(Size, ARect.Right)
else
Inc(Size, Length(FLines[i]) + 2);
end;
end;
SetString(Result, nil, Size);
pValue := Pointer(Result);
for i := ARect.Top to ARect.Bottom do
begin
if i < FLines.Count then
AText := FLines[i]
else
AText := '';
if i = ARect.Top then
AText := Copy(AText, ARect.Left + 1, 9000)
else
if i = ARect.Bottom then AText := Copy(AText, 1, ARect.Right);
ALength := Length(AText);
if ALength <> 0 then
begin
System.Move(Pointer(AText)^, pValue^, ALength);
Inc(pValue, ALength);
end;
if i <> ARect.Bottom then
begin
pValue^ := #13;
Inc(pValue);
pValue^ := #10;
Inc(pValue);
end;
end;
end
else begin
end;
end;
procedure TDCCustomSyntaxMemo.KeyDown(var Key: Word; Shift: TShiftState);
var
AText: string;
ACol, ARow, i, j: integer;
MovingKey: boolean;
KeyState: TKeyboardState;
begin
inherited;
ACol := Col;
ARow := Row;
SaveCurPos;
MovingKey := Key in [VK_DOWN, VK_UP, VK_LEFT, VK_RIGHT, VK_PRIOR, VK_NEXT, VK_END, VK_HOME];
BeginUpdate;
try
if MovingKey then
begin
if ssShift in Shift then
begin
if not (msSelecting in FMemoState) and not FSelectedArea.Empty then
with FSelectedArea do
begin
if not((StartPos.X = Col) and (StartPos.Y = Row) or
(EndPos.X = Col) and (EndPos.Y = Row)) then
begin
FUpdateArea.Add(StartPos, EndPos);
Clear;
end;
end;
FMemoState := FMemoState + [msSelecting]
end
else begin
FMemoState := FMemoState - [msSelecting];
with FSelectedArea do
begin
if not(eoPersistentBlocks in EditorOptions) then
begin
FUpdateArea.Add(StartPos, EndPos);
Clear;
end;
end
end;
end;
case Key of
VK_DOWN:
if ssCtrl in Shift then TopRow := TopRow + 1 else Row := Row + 1;
VK_UP:
if ssCtrl in Shift then TopRow := TopRow - 1 else Row := Row - 1;
VK_LEFT:
if not ((msSelecting in FMemoState) and (Row > FLines.Count)) then
begin
if ssCtrl in Shift then WordLeft else Col := Col - 1;
end;
VK_RIGHT:
if not ((msSelecting in FMemoState) and (Row > FLines.Count)) then
begin
if ssCtrl in Shift then WordRight else Col := Col + 1;
end;
VK_PRIOR:
if ssCtrl in Shift then
Row := TopRow
else begin
if TopRow > 0 then
begin
if TopRow > FRowCount then
begin
FCurrent.Y := FCurrent.Y - FRowCount + 1;
TopRow := TopRow - FRowCount + 1;
end
else begin
FCurrent.Y := FCurrent.Y - TopRow;
TopRow := 0;
end;
end
else
Row := 0;
end;
VK_NEXT:
if ssCtrl in Shift then
Row := TopRow + FRowCount - 1
else begin
if TopRow < FLines.Count then
begin
FCurrent.Y := FCurrent.Y + FRowCount -1;
TopRow := TopRow + FRowCount -1;
end
else
Row := Row + FRowCount;
end;
VK_END:
begin
if ssCtrl in Shift then Row := FLines.Count - 1;
GoEnd;
end;
VK_HOME:
begin
if ssCtrl in Shift then
begin
Col := 0;
Row := 0;
end
else Col := 0;
end;
VK_BACK:
Backspace;
VK_DELETE:
begin
if ssShift in Shift then
begin
CutToClipboard;
FMemoState := FMemoState - [msSelecting];
end
else
DeleteChars;
end;
VK_INSERT:
begin
if Shift = [] then
InsertMode := not InsertMode
else
if not FSelectedArea.Empty and (ssCtrl in Shift) then
CopyToClipboard
else
if ssShift in Shift then PasteFromClipboard;
end;
VK_RETURN:
begin
if not FReadOnly then
begin
if not(eoPersistentBlocks in EditorOptions) then ClearSelection;
AText := #13#10;
if FInsertMode then
begin
if eoAutoIndentMode in EditorOptions then
begin
i := GetIndentValue(Col, Row);
for j := 1 to i do AText := AText + ' ';
end;
InsertBlock(Col, Row, smLines, PChar(AText))
end
else
if Row < FLines.Count-1 then
begin
Row := Row + 1;
Col := GetIndentValue(Col, Row);
end
else begin
AText := #13#10;
InsertBlock(Col, Row, smLines, PChar(AText))
end;
TBlockUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect),
buInsert, AText, '', FSelectedArea.Mode, eoPersistentBlocks in EditorOptions, 0);
end;
end;
VK_TAB:
begin
if not(ssShift in Shift) then
TabRight
else
TabLeft
end;
VK_ESCAPE:
begin
GetKeyboardState(KeyState);
if KeyState[VK_LBUTTON] and $80 <> 0 then
begin
FMemoState := FMemoState - [msSelecting, msMoving];
with FSelectedArea do
begin
FUpdateArea.Add(StartPos, EndPos);
Clear;
end;
end;
end;
$30..$39:
begin
if [ssCtrl,ssShift] * Shift = [ssCtrl,ssShift] then
ToggleBookmark(Key - $30)
else
if [ssCtrl,ssShift] * Shift = [ssCtrl] then GotoBookmark(Key - $30);
end;
$43: {C}
if not FSelectedArea.Empty and (ssCtrl in Shift) then CopyToClipboard;
$56: {V}
if ssCtrl in Shift then PasteFromClipboard;
$58: {X}
if ssCtrl in Shift then
begin
CutToClipboard;
FMemoState := FMemoState - [msSelecting];
end;
$5A: {Z}
begin
if [ssCtrl,ssShift] * Shift = [ssCtrl,ssShift] then
FUndoList.Redo
else
if [ssCtrl,ssShift] * Shift = [ssCtrl] then FUndoList.Undo;
end;
end;
finally
if MovingKey then
begin
if msSelecting in FMemoState then AddSelection(Point(Col, Row), Point(ACol, ARow));
CreateCaretUndo;
end;
EndUpdate;
end;
end;
function TDCCustomSyntaxMemo.CopyToClipboard: string;
begin
Result := GetBlock(FSelectedArea.Rect, FSelectedArea.Mode, False);
Clipboard.SetTextBuf(PChar(Result));
end;
function TDCCustomSyntaxMemo.CutToClipboard: string;
begin
SaveCurPos;
Result := CopyToClipboard;
ClearSelection;
TBlockUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect), buDelete, '',
Result, FSelectedArea.Mode, eoPersistentBlocks in EditorOptions, 0)
end;
procedure TDCCustomSyntaxMemo.DeselectArea;
begin
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
FSelectedArea.Clear;
end;
procedure TDCCustomSyntaxMemo.AddSelection(ANewPos, AOldPos: TPoint);
procedure CheckPosBounds(var APos: TPoint);
begin
if APos.Y > FLines.Count - 1 then APos.Y := FLines.Count - 1;
if APos.X > Length(FLines[APos.Y]) then APos.X := Length(FLines[APos.Y]);
end;
begin
if FSelectedArea.Mode = smLines then
begin
if FLines.Count = 0 then Exit;
CheckPosBounds(ANewPos);
CheckPosBounds(AOldPos);
end;
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
FUpdateArea.Add(ANewPos, AOldPos);
FSelectedArea.AddSelection(ANewPos, AOldPos);
end;
procedure TDCCustomSyntaxMemo.InsertChar(ACol, ARow: integer; ASymbol: Char);
var
i, BreakCount: integer;
ABuf, ABuf1, ABuf2: string;
begin
SaveCurPos;
FLines.BeginUpdate;
if ARow > FLines.Count then
BreakCount := ARow - FLines.Count
else
BreakCount := 0;
FLines.GrowTo(ARow + 1);
ABuf := FLines[ARow];
if (ACol > 0) and (ACol <= Length(ABuf)) then
begin
ABuf1 := Copy(ABuf, 1, ACol);
ABuf2 := Copy(ABuf, ACol+1, Length(ABuf));
end
else begin
if (ACol > Length(ABuf)) and (ACol > 0) then
begin
ABuf1 := ABuf;
for i := Length(ABuf) to ACol - 1 do ABuf1 := ABuf1 + ' ';
ABuf2 := '';
end
else begin
ABuf1 := '';
ABuf2 := ABuf;
end;
end;
if not InsertMode then ABuf2 := Copy(ABuf2, 2, Length(ABuf2)-1);
FLines[ARow] := ABuf1 + ASymbol + ABuf2;
Col := Length(ABuf1)+1;
Row := ARow;
FLines.EndUpdate;
if not FUndoList.FUpdated then
TCharUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect),
ASymbol, BreakCount);
end;
procedure TDCCustomSyntaxMemo.SetInsertMode(const Value: boolean);
begin
FInsertMode := Value;
if Assigned(FOnSetInsertMode) then FOnSetInsertMode(Self);
end;
procedure TDCCustomSyntaxMemo.SetTabSize(const Value: integer);
begin
FTabSize := Value;
end;
procedure TDCCustomSyntaxMemo.Backspace;
var
AText, DeletedText: string;
LineData: PLineDataItem;
ARow, ACol: integer;
begin
DeletedText := '';
if Row < FLines.Count
then begin
LineData := FLines.LineInfo[Row];
if (eoBackspaceUnindents in EditorOptions) and ((LineData.Count = 0) or
(LineData.Count = 1) and (LineData.Lexems[0].Item = lxWhitespace)) then
begin
if (Row > 0) and (Col > 0)then
begin
ARow := Row - 1;
ACol := -1;
while (ACol = -1) and (ARow >= 0) do
begin
LineData := FLines.LineInfo[ARow];
if (LineData.Count = 0) then
ACol := -1
else begin
if (LineData.Lexems[0].Item = lxWhitespace) and (LineData.Lexems[0].Length < Col) then
ACol := LineData.Lexems[0].Length;
if (ACol = -1) and (LineData.Lexems[0].Item <> lxWhitespace) then
ACol := 0;
end;
Dec(ARow);
end;
if ACol = -1 then Col := 0 else Col := ACol;
end
else
if Col > 0 then
Col := 0
else begin
SaveCurPos;
FLines.BeginUpdate;
if (Col = 0) and (Row > 0) then
begin
Row := Row - 1;
GoEnd;
FLines[Row] := FLines[Row] + FLines[Row + 1];
FLines.Delete(Row+1);
DeletedText := #13#10;
end;
FLines.EndUpdate;
TDeleteUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect), DeletedText, duBackspace);
end;
end
else begin
SaveCurPos;
FLines.BeginUpdate;
if (Col = 0) and (Row > 0) then
begin
Row := Row - 1;
GoEnd;
FLines[Row] := FLines[Row] + FLines[Row + 1];
FLines.Delete(Row+1);
DeletedText := #13#10;
end
else
if Col > 0 then
begin
if Col <= Length(FLines[Row]) then
begin
AText := FLines[Row];
DeletedText := Copy(AText, Col, 1);
Delete(AText, Col, 1);
FLines[Row] := AText;
end;
Col := Col - 1;
end;
FLines.EndUpdate;
TDeleteUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect), DeletedText, duBackspace);
end;
end
else begin
if Col = 0 then
begin
Row := Row - 1;
GoEnd;
end
else
Col := Col - 1;
end;
end;
procedure TDCCustomSyntaxMemo.GoEnd;
begin
if (Row < FLines.Count) and (Row >= 0) then
Col := Length(FLines[Row])
else
Col := 0
end;
procedure TDCCustomSyntaxMemo.TabLeft;
begin
FCurrent.X := (Col div FTabSize - 1) * FTabSize;
if FCurrent.X < 0 then FCurrent.X := 0;
end;
procedure TDCCustomSyntaxMemo.TabRight;
var
ARow, ACol, i: integer;
AText: string;
function NextWord(iCol, iRow: integer): integer;
var
pSource, pValue: PChar;
begin
pSource := PChar(FLines[iRow]);
if integer(StrLen(pSource)) > iCol then
begin
pValue := pSource + iCol;
{
while (pValue^ <> #0) and not FSyntaxData.IsDelimiter(pValue^) do Inc(pValue);
while (pValue^ <> #0) and FSyntaxData.IsDelimiter(pValue^) do Inc(pValue);
}
while (pValue^ <> #0) and not(pValue^ = #32) do Inc(pValue);
while (pValue^ <> #0) and (pValue^ = #32) do Inc(pValue);
Result := pValue - pSource;
end
else begin
if iCol < Integer(StrLen(pSource)) then
Result := StrLen(pSource)
else
Result := iCol;
end;
end;
begin
if FInsertMode and not FReadOnly and (eoSmartTab in EditorOptions) then
begin
if Row > 0 then
begin
ARow := Row;
ACol := Col;
while (ACol <= Col) and (ARow > 0) do
begin
ACol := NextWord(ACol, ARow-1);
Dec(ARow);
end;
if ACol > Col then
begin
SaveCurPos;
AText := '';
for i := Col to ACol-1 do AText := AText + ' ';
InsertBlock(Col, Row, smLines, PChar(AText));
TBlockUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect),
buInsert, AText, '', FSelectedArea.Mode, eoPersistentBlocks in EditorOptions, 0);
end;
end;
end
else begin
FCurrent.X := (Col div FTabSize + 1) * FTabSize;
end;
end;
procedure TDCCustomSyntaxMemo.WordLeft;
var
pSource, pValue: PChar;
begin
if Row > FLines.Count - 1 then Exit;
pSource := PChar(FLines[Row]);
pValue := pSource + Col;
if pValue > StrEnd(pSource) then pValue:= StrEnd(pSource);
Dec(pValue);
while (pValue >= pSource) and FSyntaxData.IsDelimiter(pValue^) do Dec(pValue);
if (pValue < pSource) then
begin
if Row > 0 then
begin
Row := Row - 1;
GoEnd;
end
end
else begin
while (pValue >= pSource) and not FSyntaxData.IsDelimiter(pValue^) do Dec(pValue);
Col := pValue - pSource + 1;
end;
end;
procedure TDCCustomSyntaxMemo.WordRight;
var
pSource, pValue: PChar;
begin
if Row > FLines.Count - 1 then Exit;
pSource := PChar(FLines[Row]);
pValue := pSource + Col;
if pValue > StrEnd(pSource) then pValue:= StrEnd(pSource);
while (pValue^ <> #0) and not FSyntaxData.IsDelimiter(pValue^) do Inc(pValue);
while (pValue^ <> #0) and FSyntaxData.IsDelimiter(pValue^) do Inc(pValue);
if (pValue^ = #0) then
begin
if Col < pValue - pSource then
GoEnd
else
if Row < FLines.Count - 1 then
begin
Row := Row + 1;
Col := 0;
pSource := PChar(FLines[Row]);
if FSyntaxData.IsDelimiter(pSource^) then WordRight;
end;
end
else
Col := pValue - pSource;
end;
procedure TDCCustomSyntaxMemo.MoveTopLeft(ALeft, ATop: integer);
var
OffsX, OffsY: integer;
R: TRect;
ALeftCol, ATopRow: integer;
begin
if (FTopLeft.X <> ALeft) or (FTopLeft.Y <> ATop) then
begin
BeginUpdate;
try
if Col < ALeft then FCurrent.X := ALeft;
if ALeft < 0 then ALeft := 0;
if Col > ALeft + FColCount then FCurrent.X := ALeft + FColCount;
if ATop > FLines.Count - 1 then ATop := FLines.Count -1;
if ATop < 0 then ATop := 0;
if Row > ATop + FRowCount then FCurrent.Y := ATop + FRowCount;
OffsX := FTopLeft.X - ALeft;
OffsY := FTopLeft.Y - ATop;
FTopLeft.X := ALeft;
FTopLeft.Y := ATop;
R := Rect(0, 0, ClientWidth, ClientHeight);
ALeftCol := LeftCol + FColCount + FHalf.X;
ATopRow := TopRow + FRowCount + FHalf.Y;
if OffsX <> 0 then
begin
if Abs(OffsX) < FColCount shr 2 then
//ScrollWindow(Handle, OffsX * FColWidth, 0, @R, @R)
else
OffsX := FColCount;
if OffsX > 0 then
FUpdateArea.Add(Point(LeftCol, TopRow), Point(LeftCol + OffsX, ATopRow))
else
if OffsX < 0 then
FUpdateArea.Add(Point(ALeftCol + OffsX, TopRow), Point(ALeftCol, ATopRow))
else
FUpdateArea.Add(Point(LeftCol, TopRow), Point(ALeftCol, ATopRow));
end;
if OffsY <> 0 then
begin
if Abs(OffsY) < FRowCount shr 2 then
ScrollWindow(Handle, 0, OffsY * FRowHeight, @R, @R)
else
OffsY := FRowCount;
if OffsY > 0 then
FUpdateArea.Add(Point(LeftCol, TopRow), Point(ALeftCol, TopRow + OffsY))
else
if OffsY < 0 then
FUpdateArea.Add(Point(LeftCol, ATopRow), Point(ALeftCol, ATopRow + OffsY))
else
FUpdateArea.Add(Point(LeftCol, TopRow), Point(ALeftCol, ATopRow));
end;
if FCurrent.X < FTopLeft.X then FCurrent.X := FTopLeft.X;
if FCurrent.Y < FTopLeft.Y then FCurrent.Y := FTopLeft.Y;
if FCurrent.X > FTopLeft.X + FColCount - 1 then FCurrent.X := FTopLeft.X + FColCount - 1;
if not(eoCursorBeyondEOF in EditorOptions) then
if FCurrent.Y > FLines.Count - 1 then FCurrent.Y := FLines.Count - 1 else
else
if FCurrent.Y > FTopLeft.Y + FRowCount - 1 then FCurrent.Y := FTopLeft.Y + FRowCount - 1;
FHScroll.FPosition := FTopLeft.X;
FVScroll.FPosition := FTopLeft.Y;
finally
EndUpdate;
UpdateScrollBars;
end;
end;
end;
procedure TDCCustomSyntaxMemo.SetLeftCol(const Value: integer);
begin
if LeftCol <> Value then MoveTopLeft(Value, TopRow);
end;
procedure TDCCustomSyntaxMemo.SetTopRow(const Value: integer);
begin
if TopRow <> Value then MoveTopLeft(LeftCol, Value);
end;
function TDCCustomSyntaxMemo.GetIndentValue(ACol, ARow: integer): integer;
var
pString, pValue: PChar;
begin
Result := 0;
if ARow > FLines.Count-1 then Exit;
pString := PChar(FLines[ARow]);
pValue := pString;
while (pValue^ = #32) and (pValue^ <> #0) do Inc(pValue);
Result := pValue - pString;
if Result > ACol then Result := ACol
end;
procedure TDCCustomSyntaxMemo.UpdateSyntaxData;
var
AText: string;
begin
AText := FLines.Text;
FLines.BeginUpdate;
FLines.Clear;
FLines.Text := AText;
FLines.EndUpdate;
end;
procedure TDCCustomSyntaxMemo.SetDataClass(Value: TSyntaxDataClass);
var
AText: string;
begin
if Value <> FSyntaxDataClass then
begin
AText := FLines.Text;
FLines.BeginUpdate;
FLines.Clear;
FSyntaxData.Free;
FSyntaxData := Value.Create;
FLines.Text := AText;
FLines.EndUpdate;
end;
end;
procedure TDCCustomSyntaxMemo.WMMouseMove(var Message: TWMMouseMove);
begin
if not FMouseMoving and (msSelecting in FMemoState) then
begin
with FSelectedArea do
begin
if not((StartPos.X = Col) and (StartPos.Y = Row) or
(EndPos.X = Col) and (EndPos.Y = Row)) then
begin
FUpdateArea.Add(StartPos, EndPos);
Clear;
end;
end;
end;
if not FMouseMoving and (msMoving in FMemoState) then
begin
SetMovingParam;
end;
FMouseMoving := True;
with Message, FGutterState do
begin
if (HitTest = HTGUTTER) and MLDown then
begin
if MDPos.Y <> YPos then
begin
if MDPos.X <> -1 then
begin
MDPos.X := -1;
DeselectArea;
end;
MouseSelection(FGutterWidth, YPos);
end;
end
else
if MK_LBUTTON and Keys = MK_LBUTTON then MouseSelection(XPos, YPos);
end;
inherited;
end;
procedure TDCCustomSyntaxMemo.WMLButtonDown(var Message: TWMLButtonDown);
var
MemoCoord: TDCMemoCoord;
ShiftState: TShiftState;
begin
FMouseMoving := False;
with Message do
begin
MemoCoord := GetMemoCoord(XPos, YPos);
ShiftState := KeysToShiftState(Keys);
if FGutterState.HitTest = HTSELECTED then
FMemoState := FMemoState + [msMoving]
else
FMemoState := FMemoState + [msSelecting];
if (ssShift in ShiftState) then
begin
MouseSelection(XPos, YPos);
end
else begin
if not(eoPersistentBlocks in EditorOptions) and not(msMoving in FMemoState) then DeselectArea;
if (MemoCoord.X <> Col) or (MemoCoord.Y <> Row) then
begin
SaveCurPos;
BeginUpdate;
FCurrent := MemoCoord;
EndUpdate;
CreateCaretUndo;
end;
end;
PaintText;
end;
inherited;
end;
procedure TDCCustomSyntaxMemo.WMLButtonUp(var Message: TWMLButtonUp);
var
GutterAreaDown: boolean;
begin
StopScrollTimer;
if msMoving in FMemoState then
begin
FMemoState := FMemoState - [msMoving];
SetMovingParam;
end;
FMemoState := FMemoState - [msSelecting];
GutterAreaDown := (FGutterState.HitTest = HTGUTTER) and FGutterState.MLDown;
if not FMouseMoving {and (eoPersistentBlocks in EditorOptions)} and
not GutterAreaDown then
begin
DeselectArea;
end;
with Message do
begin
if GutterAreaDown then
begin
FGutterState.MUPos := Point(XPos, YPos);
if FGutterState.MDPos.X > -1 then
begin
DoGutterClick(FGutterState.MDPos, FGutterState.MUPos);
end;
end;
FGutterState.MLDown := False;
end;
FMouseMoving := False;
PaintText;
inherited;
end;
procedure TDCCustomSyntaxMemo.KeyUp(var Key: Word; Shift: TShiftState);
begin
inherited;
if not(ssShift in Shift) then FMemoState := FMemoState - [msSelecting];
end;
procedure TDCCustomSyntaxMemo.SetLines(const Value: TDCMemoStrings);
begin
FLines.Assign(Value);
end;
procedure TDCCustomSyntaxMemo.DoChangeText;
begin
if (FLines.FUpdating = False) and Assigned(FOnChange) then FOnChange(Self);
end;
procedure TDCCustomSyntaxMemo.WMTimer(var Message: TWMTimer);
var
ACol, ARow: integer;
begin
inherited;
if Message.TimerID = MEMOSCROLL_IDEVENT then
begin
ACol := Col;
ARow := Row;
if FScrollInc.Y < 0 then
ARow := Row;
if (ARow + FScrollInc.Y) < FLines.Count then
Row := ARow + FScrollInc.Y
else
Row := FLines.Count - 1;
Col := ACol + FScrollInc.X;
if msSelecting in FMemoState then
AddSelection(Point(Col, Row), Point(ACol, ARow));
PaintText;
end;
end;
procedure TDCCustomSyntaxMemo.StartScrollTimer;
begin
FScrollTimerHandle := SetTimer(Handle, MEMOSCROLL_IDEVENT, 100, nil);
end;
procedure TDCCustomSyntaxMemo.StopScrollTimer;
begin
if FScrollTimerHandle <> 0 then
begin
KillTimer(Handle, FScrollTimerHandle);
FScrollTimerHandle := 0;
FScrollInc := Point(0, 0);
end;
end;
procedure TDCCustomSyntaxMemo.SetReadOnly(const Value: boolean);
begin
FReadOnly := Value;
end;
procedure TDCCustomSyntaxMemo.AdjustClientRect(var Rect: TRect);
begin
inherited AdjustClientRect(Rect);
with Rect do
begin
Bottom := Bottom - 15;
Right := Right - 15;
end;
end;
procedure TDCCustomSyntaxMemo.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
Style := Style or WS_HSCROLL or WS_VSCROLL;
end;
end;
procedure TDCCustomSyntaxMemo.UpdateScrollBars;
begin
if HandleAllocated then
begin
if FUpdateVScroll then FVScroll.Update;
FHScroll.Update;
end;
end;
procedure TDCCustomSyntaxMemo.WMHScroll(var Message: TWMHScroll);
begin
FHScroll.ScrollMessage(Message);
end;
procedure TDCCustomSyntaxMemo.WMVScroll(var Message: TWMVScroll);
begin
FVScroll.ScrollMessage(Message);
end;
procedure TDCCustomSyntaxMemo.DoScroll(X, Y: integer);
var
ACurrent: TDCMemoCoord;
ATopRow: integer;
begin
BeginUpdate;
ACurrent := FCurrent;
ATopRow := TopRow;
MoveTopLeft(LeftCol - X, TopRow - Y);
FCurrent := ACurrent;
EndUpdate(ATopRow = TopRow);
end;
function TDCCustomSyntaxMemo.DoMouseWheel(Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint): Boolean;
var
ACurrent: TDCMemoCoord;
ADelta: integer;
begin
Result := inherited DoMouseWheel(Shift, WheelDelta, MousePos);
if not Result then
begin
BeginUpdate;
ADelta := WheelDelta div WHEEL_DELTA;
if ssCtrl in Shift then
begin
ACurrent := FCurrent;
TopRow := TopRow - ADelta * FRowCount;
FCurrent := ACurrent;
Result := True;
end
else
if ssShift in Shift then
begin
Row := Row - ADelta;
Result := True;
end
else begin
ACurrent := FCurrent;
TopRow := TopRow - ADelta;
FCurrent := ACurrent;
end;
EndUpdate;
end;
end;
procedure TDCCustomSyntaxMemo.DrawRightMargin(ARow: integer);
var
ABegPoint, AEndPoint: TPoint;
begin
with Canvas do
begin
Pen.Color := clBtnFace;
ABegPoint := GetCursorPos(FRightMargin, ARow);
AEndPoint := GetCursorPos(FRightMargin, ARow+1);
MoveTo(ABegPoint.X, ABegPoint.Y);
LineTo(AEndPoint.X, AEndPoint.Y);
end;
end;
procedure TDCCustomSyntaxMemo.DrawGutter(ARow: integer);
var
ARect: TRect;
ABegPoint, AEndPoint: TPoint;
i: integer;
begin
with Canvas do
begin
ABegPoint := GetCursorPos(0, ARow);
AEndPoint := GetCursorPos(0, ARow+1);
ARect := Rect(0, ABegPoint.Y, FGutterWidth - 4, AEndPoint.Y);
Brush.Color := clBtnFace;
FillRect(ARect);
MoveTo(FGutterWidth - 4, ABegPoint.Y);
Pen.Color := clWhite;
LineTo(FGutterWidth - 4, AEndPoint.Y);
MoveTo(FGutterWidth - 3, ABegPoint.Y);
Pen.Color := clBtnShadow;
LineTo(FGutterWidth - 3, AEndPoint.Y);
Brush.Color := clWhite;
ARect := Rect(FGutterWidth - 2, ABegPoint.Y, FGutterWidth, AEndPoint.Y);
FillRect(ARect);
end;
for i := Low(TBookmarkNum) to High(TBookmarkNum) do
begin
if (FBookMarks[i].Toggle) and (FBookMarks[i].Row = ARow) then
begin
DrawGutterState(ngsBookMark_0 + i, ARow, 1);
Exit;
end;
end;
end;
procedure TDCCustomSyntaxMemo.DrawGutterState(AState, ARow,
AIndexPos: integer);
var
APoint: TPoint;
begin
APoint := GetCursorPos(0, ARow);
APoint.X := 2 + AIndexPos * FIndicators.Width;
if APoint.X + FIndicators.Width < FGutterWidth then
FIndicators.Draw(Canvas, APoint.X, APoint.Y, AState);
end;
procedure TDCCustomSyntaxMemo.SetRowColor(ARow: integer; var BGColor,
FGColor: TColor);
begin
{}
end;
procedure TDCCustomSyntaxMemo.InvalidateGutter;
var
i: integer;
begin
if FVisibleGutter then
begin
HideCaret(Handle);
i := TopRow;
while i < TopRow + FRowCount do
begin
DrawGutter(i);
inc(i);
end;
RedrawCaret;
end;
end;
procedure TDCCustomSyntaxMemo.GotoBookmark(ABookmark: TBookmarkNum);
begin
with FBookmarks[ABookmark] do
begin
if Toggle then
begin
if (Col < LeftCol) or (Col > (LeftCol + FColCount)) or
(Row < TopRow) or (Row > (TopRow + FRowCount)) then
MoveTopLeft(TopLeft.X, TopLeft.Y);
Self.Row := Row;
Self.Col := Col;
end;
end;
end;
procedure TDCCustomSyntaxMemo.ToggleBookmark(ABookmark: TBookmarkNum);
begin
if Row < FLines.Count then
begin
with FBookmarks[ABookmark] do
begin
if not Toggle then
Toggle := True
else
Toggle := not(Row = FCurrent.Y);
if Toggle then
begin
BeginUpdate;
TopLeft := FTopLeft;
Col := FCurrent.X;
Row := FCurrent.Y;
EndUpdate;
end;
end;
InvalidateGutter;
end;
end;
procedure TDCCustomSyntaxMemo.UpdateGutter(AMode, ARow, ACount: integer);
var
i, j: integer;
begin
for i := Low(TBookmarkNum) to High(TBookmarkNum) do
if FBookMarks[i].Toggle then
case AMode of
CM_ROW_DELETED:
begin
j := FBookMarks[i].Row;
if FBookMarks[i].Row > ARow then
begin
if FBookMarks[i].Row > (ARow + ACount) then
FBookMarks[i].Row := FBookMarks[i].Row - ACount
else
FBookMarks[i].Row := ARow;
DrawGutter(j);
end
else if (FBookMarks[i].Row = ARow) and (ARow = FLines.Count) then
begin
FBookMarks[i].Row := FLines.Count - 1;
DrawGutter(j);
end;
end;
CM_ROW_INSERTED:
begin
j := FBookMarks[i].Row;
if ((FBookMarks[i].Row = ARow-1) and (Col = 0)) or
(FBookMarks[i].Row > ARow-1) then
begin
FBookMarks[i].Row := FBookMarks[i].Row + ACount;
DrawGutter(j);
end;
end;
end;
end;
procedure TDCCustomSyntaxMemo.BeginUpdate;
begin
Inc(FUpdateCount);
LockPaint;
end;
procedure TDCCustomSyntaxMemo.EndUpdate(AScroll: boolean = False);
begin
Dec(FUpdateCount);
if UnlockPaint then PaintText(AScroll);
if FUpdateCount = 0 then
begin
ColRowChanged(Col, Row);
end;
end;
procedure TDCCustomSyntaxMemo.ColRowChanged(ACol, ARow: integer);
begin
if Assigned(FOnColRowChanged) then FOnColRowChanged(Self, ACol, ARow);
end;
function TDCCustomSyntaxMemo.GetBookmarkState(Index: TBookmarkNum): boolean;
begin
Result := FBookmarks[Index].Toggle;
end;
procedure TDCCustomSyntaxMemo.CreateCaretUndo;
begin
if ((FCurPos.Col <> Col) or (FCurPos.Row <> Row)) and
not FUndoList.FUpdated then
TCaretUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect));
end;
procedure TDCCustomSyntaxMemo.SaveCurPos;
begin
FCurPos := MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect)
end;
procedure TDCCustomSyntaxMemo.DeleteChars;
var
ARect: TRect;
DeletedText: string;
begin
SaveCurPos;
if (eoPersistentBlocks in EditorOptions) or FSelectedArea.Empty then
begin
ARect := Rect(Col, Row, Col+1, Row);
DeletedText := GetBlock(ARect, smLines);
DeleteBlock(ARect, smLines);
TDeleteUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect),
DeletedText, duDelete);
end
else begin
DeletedText := GetBlock(FSelectedArea.Rect, FSelectedArea.Mode);
ClearSelection;
TBlockUndo.Create(Self, FCurPos, MemoPos(FTopLeft, Col, Row, FSelectedArea.Rect),
buDelete, '', DeletedText, FSelectedArea.Mode, eoPersistentBlocks in EditorOptions, 0)
end;
end;
procedure TDCCustomSyntaxMemo.SelectRect(ARect: TRect);
begin
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
FSelectedArea.Clear;
FSelectedArea.Add(ARect.TopLeft, ARect.BottomRight);
FUpdateArea.Add(ARect.TopLeft, ARect.BottomRight);
PaintText;
end;
procedure TDCCustomSyntaxMemo.InvalidateRect(ARect: TRect);
begin
with FUpdateArea do
begin
Clear;
Add(ARect.TopLeft, ARect.BottomRight);
PaintText;
end;
end;
procedure TDCCustomSyntaxMemo.WMNCLButtonDown(var Message: TWMLButtonDown);
var
APoint: TPoint;
KeyState: TKeyboardState;
WParam: Word;
begin
inherited;
with Message do
begin
if (FGutterState.HitTest = HTGUTTER) then
begin
APoint := ScreenToClient(Point(XPos, YPos));
FGutterState.MLDown := True;
FGutterState.MDPos := APoint;
GetKeyboardState(KeyState);
WParam := MK_LBUTTON;
if KeyState[VK_SHIFT] and $80 <> 0 then WParam := WParam or MK_SHIFT;
if KeyState[VK_CONTROL] and $80 <> 0 then WParam := WParam or MK_CONTROL;
Perform(WM_LBUTTONDOWN, WParam, MakeLParam(FGutterWidth, APoint.Y));
end
else
FGutterState.MLDown := False;
end;
end;
procedure TDCCustomSyntaxMemo.CMCancelMode(var Message: TCMCancelMode);
begin
if Message.Sender <> Self then FGutterState.MLDown := False;
inherited;
end;
procedure TDCCustomSyntaxMemo.MouseSelection(XPos, YPos: integer);
var
MemoCoord: TDCMemoCoord;
MousePos, BoundsPos: TPoint;
BoundsRect: TRect;
function SetInc(APos: integer; APoint: TPoint; ADiv: integer): integer;
begin
if APos < APoint.X then
Result := - ((APoint.X - APos - 1) div ADiv + 1)
else
if APos > APoint.Y then
Result := (APos - APoint.Y - 1) div ADiv + 1
else
Result := 0;
end;
begin
BoundsPos := ClientToScreen(Point(0, 0));
BoundsRect := ClientRect;
BoundsRect.Right := BoundsRect.Left + (FColCount - FHalf.X) * FColWidth;
BoundsRect.Bottom := BoundsRect.Top + (FRowCount - FHalf.Y) * FRowHeight;
OffsetRect(BoundsRect, BoundsPos.X, BoundsPos.Y);
Windows.GetCursorPos(MousePos);
MemoCoord := GetMemoCoord(XPos, YPos);
SaveCurPos;
if PtInRect(BoundsRect, MousePos) then
begin
StopScrollTimer;
BeginUpdate;
Row := MemoCoord.Y;
Col := MemoCoord.X;
if msSelecting in FMemoState then
AddSelection(Point(Col, Row), Point(FCurPos.Col, FCurPos.Row));
EndUpdate;
end
else begin
with BoundsRect do
begin
FScrollInc.X := SetInc(MousePos.X, Point(Left, Right), FColWidth);
FScrollInc.Y := SetInc(MousePos.Y, Point(Top, Bottom), FRowHeight);
if FScrollInc.X = 0 then FCurrent.X := MemoCoord.X;
if FScrollInc.Y = 0 then FCurrent.Y := MemoCoord.Y;
if msSelecting in FMemoState then
AddSelection(Point(Col, Row), Point(FCurPos.Col, FCurPos.Row));
end;
if FScrollTimerHandle = 0 then StartScrollTimer;
end;
CreateCaretUndo;
end;
procedure TDCCustomSyntaxMemo.WMLButtonDblClk(
var Message: TWMLButtonDblClk);
var
APoint: TPoint;
begin
inherited;
if Row < FLines.Count then
begin
BeginUpdate;
SaveCurPos;
DeselectArea;
if eoDoubleClickLine in EditorOptions then
begin
FCurPos.Col := 0;
if Row < FLines.Count - 1 then
begin
Row := Row + 1;
Col := 0;
end
else
GoEnd;
FSelectedArea.Add(Point(FCurPos.Col, FCurPos.Row), Point(Col, Row));
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
end
else begin
WordAt(Col, Row, APoint);
FSelectedArea.Add(Point(APoint.X, Row), Point(APoint.Y, Row));
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
Col := APoint.Y;
end;
EndUpdate;
end;
end;
procedure TDCCustomSyntaxMemo.DoGutterClick(MouseDownPos,
MouseUpPos: TPoint);
begin
if Assigned(FonGutterClick) then FonGutterClick(Self, MouseDownPos, MouseUpPos);
end;
procedure TDCCustomSyntaxMemo.SetMovingParam;
begin
with FMovingBlock do
begin
if msMoving in FMemoState then
begin
DefaultCursor := Screen.Cursor;
SelectRect := FSelectedArea.Rect;
Block := GetBlock(SelectRect, FSelectedArea.Mode, False);
Screen.Cursor := crDrag;
end
else begin
Screen.Cursor := DefaultCursor;
SetLength(Block, 0);
SetRectEmpty(SelectRect);
DefaultCursor := crDefault;
end;
end;
end;
function TDCCustomSyntaxMemo.WordAt(ACol, ARow: integer;
var APoint: TPoint): boolean;
var
pSource, pValue, pValueEnd: PChar;
WordAtLeft: boolean;
begin
pSource := PChar(FLines[ARow]);
if integer(StrLen(pSource)) > ACol then
begin
pValue := pSource + ACol;
Result := True;
end
else begin
pValue := StrEnd(pSource);
Result := False;
end;
if FSyntaxData.IsDelimiter(pValue^) then
begin
while (pValue >= pSource) and FSyntaxData.IsDelimiter(pValue^) do
Dec(pValue);
Result := False;
end;
if pValue >= pSource then
WordAtLeft := True
else
WordAtLeft := False;
while (pValue >= pSource) and not FSyntaxData.IsDelimiter(pValue^) do
Dec(pValue);
if pValue < pSource then
begin
pValue := pSource;
if not WordAtLeft then
while (pValue^ <> #0) and FSyntaxData.IsDelimiter(pValue^) do
Inc(pValue);
end;
if (pValue >= pSource) and (pValue^ <> #0) then
begin
if FSyntaxData.IsDelimiter(pValue^) then Inc(pValue);
pValueEnd := pValue;
while (pValueEnd^ <> #0) and not FSyntaxData.IsDelimiter(pValueEnd^) do
Inc(pValueEnd);
APoint := Point(pValue - pSource, pValueEnd - pSource);
end
else begin
APoint := Point(0, StrLen(pSource));
Result := False;
end;
end;
procedure TDCCustomSyntaxMemo.LockPaint;
begin
Inc(FLockPaint);
end;
function TDCCustomSyntaxMemo.UnlockPaint: boolean;
begin
Dec(FLockPaint);
Result := FLockPaint = 0;
end;
procedure TDCCustomSyntaxMemo.SetModified(Value: boolean);
begin
if FModified <> Value then
begin
FModified := Value;
if not FModified then FUndoCount := 0;
if Assigned(FOnModifiedChanged) then FOnModifiedChanged(Self)
end;
end;
procedure TDCCustomSyntaxMemo.ClearUndoList;
begin
FUndoList.Clear;
end;
procedure TDCCustomSyntaxMemo.UpdateModifiedStatus(Value: integer = -1);
begin
FUndoCount := FUndoCount + Value;
Modified := not (FUndoCount = 0);
end;
procedure TDCCustomSyntaxMemo.Clear;
begin
FLines.Clear;
end;
procedure TDCCustomSyntaxMemo.CreateWnd;
begin
inherited;
end;
function TDCCustomSyntaxMemo.GetSyntaxColors: TDCSyntaxMemoColors;
begin
Result := FSyntaxData.SyntaxColors;
end;
{ TDCMemoScroll }
constructor TDCMemoScroll.Create(AControl: TDCCustomSyntaxMemo;
AKind: TScrollBarKind);
begin
inherited Create;
FControl := AControl;
FIncrement := 1;
FPosition := 0;
FKind := AKind;
end;
procedure TDCMemoScroll.ScrollMessage(var Message: TWMScroll);
var
APosition: integer;
begin
APosition := FPosition;
with Message do
case ScrollCode of
SB_LINEUP:
APosition := FPosition - FIncrement;
SB_LINEDOWN:
APosition := FPosition + FIncrement;
SB_PAGEUP:
APosition := FPosition - FPageSize;
SB_PAGEDOWN:
APosition := FPosition + FPageSize;
SB_THUMBPOSITION:
APosition := Pos;
SB_THUMBTRACK:
APosition := Pos;
SB_TOP:
APosition := 0;
SB_BOTTOM:
APosition := FMax;
SB_ENDSCROLL:
;
end;
SetPosition(APosition);
end;
procedure TDCMemoScroll.SetPosition(Value: integer);
var
BarCode: Word;
Form: TCustomForm;
APosition: Integer;
begin
if csReading in FControl.ComponentState then
FPosition := Value
else begin
BarCode := SB_HORZ;
if Value < 0 then Value := 0;
if Value > FMax then Value := FMax;
case FKind of
sbHorizontal:
BarCode := SB_HORZ;
sbVertical:
BarCode := SB_VERT;
end;
if Value <> FPosition then
begin
APosition := FPosition;
FPosition := Value;
case FKind of
sbHorizontal:
FControl.DoScroll(APosition - Value, 0);
sbVertical:
FControl.DoScroll(0, APosition - Value);
end;
if csDesigning in FControl.ComponentState then
begin
Form := GetParentForm(FControl);
if (Form <> nil) and (Form.Designer <> nil) then Form.Designer.Modified;
end;
end;
if GetScrollPos(FControl.Handle, BarCode) <> FPosition then
SetScrollPos(FControl.Handle, BarCode, FPosition, True);
end;
end;
procedure TDCMemoScroll.SetRange(Value: integer);
begin
FRange := Value;
FControl.UpdateScrollBars;
end;
procedure TDCMemoScroll.Update;
var
BarCode: Word;
ScrollInfo: TScrollInfo;
begin
BarCode := SB_HORZ;
case FKind of
sbHorizontal:
BarCode := SB_HORZ;
sbVertical:
BarCode := SB_VERT;
end;
ScrollInfo.cbSize := SizeOf(ScrollInfo);
ScrollInfo.fMask := SIF_ALL;
ScrollInfo.nMin := 0;
ScrollInfo.nMax := FRange;
ScrollInfo.nPage := FPageSize;
ScrollInfo.nPos := FPosition;
ScrollInfo.nTrackPos := FPosition;
SetScrollInfo(FControl.Handle, BarCode, ScrollInfo, True);
end;
{ TCustomUndoAction }
constructor TCustomUndoAction.Create(AMemo: TDCCustomSyntaxMemo;
ACurPos, ANewPos: TDCMemoPos);
begin
inherited Create;
Memo := AMemo;
CurPos := ACurPos;
NewPos := ANewPos;
Memo.FUndoList.AddUndoAction(Self);
end;
{ TUndoActionList }
procedure TUndoActionList.AddUndoAction(AUndo: TCustomUndoAction);
begin
while (Count > 0) and (FPosition < Count - 1) do
begin
TCustomUndoAction(Items[FPosition + 1]).Free;
Delete(FPosition + 1);
end;
Add(AUndo);
FPosition := Count - 1;
end;
procedure TUndoActionList.Clear;
var
i: integer;
begin
for i := 0 to Count - 1 do TCustomUndoAction(Items[i]).Free;
FPosition := 0;
inherited;
end;
constructor TUndoActionList.Create(AMemo: TDCCustomSyntaxMemo);
begin
FMemo := AMemo;
FPosition := -1;
FUpdated := False;
end;
function TUndoActionList.GetCanRedo: boolean;
begin
Result := FPosition < (Count - 1);
end;
function TUndoActionList.GetCanUndo: boolean;
begin
Result := FPosition > -1;
end;
procedure TUndoActionList.Redo;
begin
FUpdated := True;
if (FPosition < Count - 1) then
begin
Inc(FPosition);
TCustomUndoAction(Items[FPosition]).Redo;
end;
FUpdated := False;
end;
procedure TUndoActionList.Undo;
var
UndoAction: TCustomUndoAction;
UndoClassType: TClass;
begin
FUpdated := True;
if (FPosition <= Count -1) and (FPosition >= 0) then
begin
UndoAction := TCustomUndoAction(Items[FPosition]);
if FMemo.FGroupUndo then
begin
UndoClassType := UndoAction.ClassType;
while (UndoClassType = UndoAction.ClassType) and
((FPosition > 0) or (UndoAction.ClassType <> TCaretUndo)) do
begin
UndoAction.Undo;
Dec(FPosition);
UndoAction := TCustomUndoAction(Items[FPosition]);
end;
end
else begin
if (FPosition > 0) or (UndoAction.ClassType <> TCaretUndo) then
begin
UndoAction.Undo;
Dec(FPosition);
end;
end;
end;
FUpdated := False;
end;
{ TCaretUndo }
procedure TCaretUndo.Redo;
begin
if (NewPos.Col < Memo.LeftCol) or
(NewPos.Col > Memo.LeftCol + Memo.FColCount) or
(NewPos.Row < Memo.TopRow) or
(NewPos.Row > Memo.TopRow + Memo.FRowCount)
then
Memo.MoveTopLeft(NewPos.TopLeft.X, NewPos.TopLeft.Y);
Memo.Row := NewPos.Row;
Memo.Col := NewPos.Col;
with Memo do
begin
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
FSelectedArea.Clear;
FSelectedArea.Add(NewPos.SelectRect.TopLeft, NewPos.SelectRect.BottomRight);
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
end;
end;
procedure TCaretUndo.Undo;
begin
if (CurPos.Col < Memo.LeftCol) or
(CurPos.Col > Memo.LeftCol + Memo.FColCount) or
(CurPos.Row < Memo.TopRow) or
(CurPos.Row > Memo.TopRow + Memo.FRowCount)
then
Memo.MoveTopLeft(CurPos.TopLeft.X, CurPos.TopLeft.Y);
Memo.Row := CurPos.Row;
Memo.Col := CurPos.Col;
with Memo do
begin
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
FSelectedArea.Clear;
FSelectedArea.Add(CurPos.SelectRect.TopLeft, CurPos.SelectRect.BottomRight);
FUpdateArea.Add(FSelectedArea.StartPos, FSelectedArea.EndPos);
end;
end;
{ TCharUndo }
constructor TCharUndo.Create(AMemo: TDCCustomSyntaxMemo;
ACurPos, ANewPos: TDCMemoPos; AValue: Char; ABreakCount: integer);
begin
inherited Create(AMemo, ACurPos, ANewPos);
Value := AValue;
BreakCount := ABreakCount;
Inc(Memo.FUndoCount);
end;
procedure TCharUndo.Redo;
begin
inherited;
Memo.InsertChar(CurPos.Col, CurPos.Row, Value);
Memo.UpdateModifiedStatus(1);
end;
procedure TCharUndo.Undo;
begin
Memo.FLines.BeginUpdate;
Memo.DeleteBlock(Rect(CurPos.Col, CurPos.Row, NewPos.Col, NewPos.Row), smLines);
if BreakCount > 0 then
Memo.FLines.DeleteLines(CurPos.Row - BreakCount, BreakCount);
Memo.FLines.EndUpdate;
Memo.UpdateModifiedStatus;
inherited;
end;
{ TBlockUndo }
constructor TBlockUndo.Create(AMemo: TDCCustomSyntaxMemo; ACurPos, ANewPos: TDCMemoPos;
AStyle: TBlockUndoStyle; AInsValue, ADelValue: string;
ASelectMode: TTextSelectMode; APersistentBlocks: boolean; ABreakCount: integer);
begin
inherited Create(AMemo, ACurPos, ANewPos);
Style := AStyle;
InsValue := AInsValue;
DelValue := ADelValue;
SelectMode := ASelectMode;
PersistentBlocks := APersistentBlocks;
BreakCount := ABreakCount;
Inc(Memo.FUndoCount);
end;
function TBlockUndo.GetTextRect(Left, Top: integer; pSource: PChar): TRect;
begin
Result := Rect(Left, Top , Left, Top);
while pSource^ <> #0 do
begin
Result.Right := Result.Right + 1;
if pSource^ = #13 then
begin
Result.Right := 0;
Result.Bottom := Result.Bottom + 1;
Inc(pSource);
if pSource^ = #10 then Inc(pSource);
end
else
Inc(pSource);
end;
end;
procedure TBlockUndo.InsertBlock;
begin
Memo.InsertBlock(CurPos.SelectRect.Left, CurPos.SelectRect.Top, SelectMode, PChar(DelValue));
end;
procedure TBlockUndo.Redo;
begin
case Style of
buInsert:
with CurPos.SelectRect do
begin
if DelValue <> '' then
Memo.DeleteBlock(CurPos.SelectRect, SelectMode);
if (TopLeft.X = BottomRight.X) and (TopLeft.Y = BottomRight.Y) or PersistentBlocks then
Memo.InsertBlock(CurPos.Col, CurPos.Row, SelectMode, PChar(InsValue))
else
Memo.InsertBlock(Left, Top, SelectMode, PChar(InsValue))
end;
buDelete:
begin
Memo.DeleteBlock(CurPos.SelectRect, SelectMode);
Memo.DeselectArea;
end;
end;
Memo.UpdateModifiedStatus(1);
inherited;
end;
procedure TBlockUndo.Undo;
var
TextRect: TRect;
AText: string;
ALeft, ATop: integer;
begin
case Style of
buInsert:
with CurPos.SelectRect do
begin
Memo.FLines.BeginUpdate;
if (TopLeft.X = BottomRight.X) and (TopLeft.Y = BottomRight.Y) or PersistentBlocks then
begin
ALeft := CurPos.Col;
ATop := CurPos.Row;
end
else begin
ALeft := Left;
ATop := Top;
end;
AText := InsValue;
if BreakCount > 0 then
Memo.FLines.DeleteLines(ATop - BreakCount, BreakCount);
TextRect := GetTextRect(ALeft, ATop - BreakCount, PChar(AText));
Memo.DeleteBlock(TextRect, SelectMode);
if DelValue <> '' then InsertBlock;
Memo.FLines.EndUpdate;
end;
buDelete:
InsertBlock;
end;
Memo.UpdateModifiedStatus;
inherited;
end;
{ TDeleteUndo }
constructor TDeleteUndo.Create(AMemo: TDCCustomSyntaxMemo;
ACurPos, ANewPos: TDCMemoPos; AValue: string; AStyle: TDeleteUndoStyle);
begin
inherited Create(AMemo, ACurPos, ANewPos);
Value := AValue;
Style := AStyle;
Inc(Memo.FUndoCount);
end;
procedure TDeleteUndo.Redo;
begin
case Style of
duDelete:
begin
inherited;
with Memo do DeleteBlock(Rect(Col, Row, Col + 1, Row), smLines);
end;
duBackspace:
begin
Memo.Backspace;
end;
end;
Memo.UpdateModifiedStatus(1);
end;
procedure TDeleteUndo.Undo;
begin
case Style of
duDelete:
begin
Memo.InsertBlock(CurPos.Col, CurPos.Row, smLines, PChar(Value));
end;
duBackspace:
begin
Memo.InsertBlock(NewPos.Col, NewPos.Row, smLines, PChar(Value));
end;
end;
Memo.UpdateModifiedStatus;
inherited;
end;
end.