mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-06-06 02:38:14 +02:00

Basic graphic primitives split into GraphType package, so that we can reference it from interface (GTK, Win32) units. New Frame3d canvas method that uses native (themed) drawing (GTK only). New overloaded Canvas.TextRect method. LCLLinux and Graphics was split, so a bunch of files had to be modified. git-svn-id: trunk@653 -
1095 lines
32 KiB
ObjectPascal
1095 lines
32 KiB
ObjectPascal
{-------------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: SynEditTextBuffer.pas, released 2000-04-07.
|
|
The Original Code is based on parts of mwCustomEdit.pas by Martin Waldenburg,
|
|
part of the mwEdit component suite.
|
|
Portions created by Martin Waldenburg are Copyright (C) 1998 Martin Waldenburg.
|
|
All Rights Reserved.
|
|
|
|
Contributors to the SynEdit and mwEdit projects are listed in the
|
|
Contributors.txt file.
|
|
|
|
Alternatively, the contents of this file may be used under the terms of the
|
|
GNU General Public License Version 2 or later (the "GPL"), in which case
|
|
the provisions of the GPL are applicable instead of those above.
|
|
If you wish to allow use of your version of this file only under the terms
|
|
of the GPL and not to allow others to use your version of this file
|
|
under the MPL, indicate your decision by deleting the provisions above and
|
|
replace them with the notice and other provisions required by the GPL.
|
|
If you do not delete the provisions above, a recipient may use your version
|
|
of this file under either the MPL or the GPL.
|
|
|
|
$Id$
|
|
|
|
You may retrieve the latest version of this file at the SynEdit home page,
|
|
located at http://SynEdit.SourceForge.net
|
|
|
|
Known Issues:
|
|
-------------------------------------------------------------------------------}
|
|
|
|
unit SynEditTextBuffer;
|
|
|
|
{$I synedit.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
{$IFDEF SYN_LAZARUS}
|
|
LCLLinux,
|
|
LCLType,
|
|
{$ELSE}
|
|
Windows,
|
|
{$ENDIF}
|
|
SynEditTypes, SynEditMiscProcs; //mh 2000-10-19
|
|
|
|
type
|
|
{begin} //mh 2000-10-10
|
|
TSynEditRange = pointer;
|
|
|
|
{begin} //mh 2000-10-19
|
|
TSynEditStringFlag = (sfHasTabs, sfHasNoTabs, sfExpandedLengthUnknown);
|
|
TSynEditStringFlags = set of TSynEditStringFlag;
|
|
{end} //mh 2000-10-19
|
|
|
|
PSynEditStringRec = ^TSynEditStringRec;
|
|
TSynEditStringRec = record
|
|
fString: string;
|
|
fObject: TObject;
|
|
fRange: TSynEditRange;
|
|
{begin} //mh 2000-10-19
|
|
fExpandedLength: integer;
|
|
fFlags: TSynEditStringFlags;
|
|
{end} //mh 2000-10-19
|
|
end;
|
|
|
|
const
|
|
SynEditStringRecSize = SizeOf(TSynEditStringRec);
|
|
MaxSynEditStrings = MaxInt div SynEditStringRecSize;
|
|
|
|
NullRange = TSynEditRange(-1);
|
|
|
|
type
|
|
PSynEditStringRecList = ^TSynEditStringRecList;
|
|
TSynEditStringRecList = array[0..MaxSynEditStrings - 1] of TSynEditStringRec;
|
|
|
|
TStringListIndexEvent = procedure(Index: Integer) of object;
|
|
|
|
TSynEditStringList = class(TStrings)
|
|
private
|
|
fList: PSynEditStringRecList;
|
|
fCount: integer;
|
|
fCapacity: integer;
|
|
fDosFileFormat: boolean;
|
|
{begin} //mh 2000-10-19
|
|
fConvertTabsProc: TConvertTabsProcEx;
|
|
{$IFDEF SYN_LAZARUS}
|
|
fSimulateConvertTabsProc: TSimulateConvertTabsProcEx;
|
|
{$ENDIF}
|
|
fIndexOfLongestLine: integer;
|
|
fTabWidth: integer;
|
|
{end} //mh 2000-10-19
|
|
fOnChange: TNotifyEvent;
|
|
fOnChanging: TNotifyEvent;
|
|
{begin} //mh 2000-10-19
|
|
function ExpandedString(Index: integer): string;
|
|
{$IFDEF SYN_LAZARUS}
|
|
function ExpandedStringLength(Index: integer): integer;
|
|
{$ENDIF}
|
|
function GetExpandedString(Index: integer): string;
|
|
function GetLengthOfLongestLine: integer;
|
|
{end} //mh 2000-10-19
|
|
function GetRange(Index: integer): TSynEditRange;
|
|
procedure Grow;
|
|
procedure InsertItem(Index: integer; const S: string);
|
|
procedure PutRange(Index: integer; ARange: TSynEditRange);
|
|
protected
|
|
fLongestLineIndex: integer; //mh 2000-10-19
|
|
fOnAdded: TStringListIndexEvent;
|
|
fOnCleared: TNotifyEvent;
|
|
fOnDeleted: TStringListIndexEvent;
|
|
fOnInserted: TStringListIndexEvent;
|
|
fOnPutted: TStringListIndexEvent;
|
|
function Get(Index: integer): string; override;
|
|
function GetCapacity: integer;
|
|
{$IFDEF SYN_COMPILER_3_UP} override; {$ENDIF} //mh 2000-10-18
|
|
function GetCount: integer; override;
|
|
function GetObject(Index: integer): TObject; override;
|
|
procedure Put(Index: integer; const S: string); override;
|
|
procedure PutObject(Index: integer; AObject: TObject); override;
|
|
procedure SetCapacity(NewCapacity: integer);
|
|
{$IFDEF SYN_COMPILER_3_UP} override; {$ENDIF} //mh 2000-10-18
|
|
procedure SetTabWidth(Value: integer); //mh 2000-10-19
|
|
procedure SetUpdateState(Updating: Boolean); override;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function Add(const S: string): integer; override;
|
|
procedure AddStrings(AStrings: TStrings); override;
|
|
procedure Clear; override;
|
|
procedure Delete(Index: integer); override;
|
|
procedure DeleteLines(Index, NumLines: integer); // DJLP 2000-11-01
|
|
procedure Exchange(Index1, Index2: integer); override;
|
|
procedure Insert(Index: integer; const S: string); override;
|
|
procedure InsertLines(Index, NumLines: integer); // DJLP 2000-11-01
|
|
procedure InsertStrings(Index: integer; NewStrings: TStrings); // DJLP 2000-11-01
|
|
procedure LoadFromFile(const FileName: string); override;
|
|
procedure SaveToFile(const FileName: string); override;
|
|
public
|
|
property DosFileFormat: boolean read fDosFileFormat write fDosFileFormat;
|
|
{begin} //mh 2000-10-19
|
|
property ExpandedStrings[Index: integer]: string read GetExpandedString;
|
|
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
|
|
{end} //mh 2000-10-19
|
|
property Ranges[Index: integer]: TSynEditRange read GetRange write PutRange;
|
|
property TabWidth: integer read fTabWidth write SetTabWidth; //mh 2000-10-19
|
|
property OnAdded: TStringListIndexEvent read fOnAdded write fOnAdded;
|
|
property OnChange: TNotifyEvent read fOnChange write fOnChange;
|
|
property OnChanging: TNotifyEvent read fOnChanging write fOnChanging;
|
|
property OnCleared: TNotifyEvent read fOnCleared write fOnCleared;
|
|
property OnDeleted: TStringListIndexEvent read fOnDeleted write fOnDeleted;
|
|
property OnInserted: TStringListIndexEvent read fOnInserted
|
|
write fOnInserted;
|
|
property OnPutted: TStringListIndexEvent read fOnPutted write fOnPutted;
|
|
end;
|
|
|
|
ESynEditStringList = class(Exception);
|
|
{end} //mh 2000-10-10
|
|
|
|
TSynChangeReason = (crInsert, crPaste, crDragDropInsert,
|
|
// Note: crSelDelete and crDragDropDelete have been deleted, because
|
|
// several undo entries can be chained together now via the ChangeNumber
|
|
// see also TCustomSynEdit.[Begin|End]UndoBlock methods
|
|
crDeleteAfterCursor, crDelete, {crSelDelete, crDragDropDelete, } //mh 2000-11-20
|
|
crLineBreak, crIndent, crUnindent,
|
|
crSilentDelete, crSilentDeleteAfterCursor, //mh 2000-10-30
|
|
crNothing);
|
|
|
|
TSynEditUndoItem = class(TObject)
|
|
public
|
|
fChangeReason: TSynChangeReason;
|
|
fChangeSelMode: TSynSelectionMode;
|
|
fChangeStartPos: TPoint;
|
|
fChangeEndPos: TPoint;
|
|
fChangeStr: string;
|
|
fChangeNumber: integer; //sbs 2000-11-19
|
|
end;
|
|
|
|
TSynEditUndoList = class(TObject)
|
|
private
|
|
fBlockChangeNumber: integer; //sbs 2000-11-19
|
|
fBlockCount: integer; //sbs 2000-11-19
|
|
fFullUndoImposible: boolean; //mh 2000-10-03
|
|
fItems: TList;
|
|
fLockCount: integer;
|
|
fMaxUndoActions: integer;
|
|
fNextChangeNumber: integer; //sbs 2000-11-19
|
|
fOnAdded: TNotifyEvent;
|
|
procedure EnsureMaxEntries;
|
|
function GetCanUndo: boolean;
|
|
function GetItemCount: integer;
|
|
procedure SetMaxUndoActions(Value: integer);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure AddChange(AReason: TSynChangeReason; AStart, AEnd: TPoint;
|
|
ChangeText: string; SelMode: TSynSelectionMode);
|
|
procedure BeginBlock; //sbs 2000-11-19
|
|
procedure Clear;
|
|
procedure EndBlock; //sbs 2000-11-19
|
|
procedure Lock;
|
|
function PeekItem: TSynEditUndoItem;
|
|
function PopItem: TSynEditUndoItem;
|
|
procedure PushItem(Item: TSynEditUndoItem);
|
|
procedure Unlock;
|
|
public
|
|
property BlockChangeNumber: integer read fBlockChangeNumber //sbs 2000-11-19
|
|
write fBlockChangeNumber;
|
|
property CanUndo: boolean read GetCanUndo;
|
|
property FullUndoImpossible: boolean read fFullUndoImposible; //mh 2000-10-03
|
|
property ItemCount: integer read GetItemCount;
|
|
property MaxUndoActions: integer read fMaxUndoActions
|
|
write SetMaxUndoActions;
|
|
property OnAddedUndo: TNotifyEvent read fOnAdded write fOnAdded;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{$IFNDEF FPC}
|
|
|
|
{$IFDEF SYN_COMPILER_3_UP} //mh 2000-10-18
|
|
resourcestring
|
|
{$ELSE}
|
|
const
|
|
{$ENDIF}
|
|
{$ELSE}
|
|
const
|
|
{$ENDIF}
|
|
SListIndexOutOfBounds = 'Invalid stringlist index %d';
|
|
|
|
{ TSynEditFiler }
|
|
|
|
type
|
|
TSynEditFiler = class(TObject)
|
|
protected
|
|
fBuffer: PChar;
|
|
fBufPtr: Cardinal;
|
|
fBufSize: Cardinal;
|
|
fDosFile: boolean;
|
|
fFiler: TFileStream;
|
|
procedure Flush; virtual;
|
|
procedure SetBufferSize(NewSize: Cardinal);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
public
|
|
property DosFile: boolean read fDosFile write fDosFile;
|
|
end;
|
|
|
|
constructor TSynEditFiler.Create;
|
|
const
|
|
kByte = 1024;
|
|
begin
|
|
inherited Create;
|
|
fDosFile := FALSE;
|
|
SetBufferSize(16 * kByte);
|
|
fBuffer[0] := #0;
|
|
end;
|
|
|
|
destructor TSynEditFiler.Destroy;
|
|
begin
|
|
Flush;
|
|
fFiler.Free;
|
|
SetBufferSize(0);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSynEditFiler.Flush;
|
|
begin
|
|
end;
|
|
|
|
procedure TSynEditFiler.SetBufferSize(NewSize: Cardinal);
|
|
begin
|
|
if NewSize <> fBufSize then begin
|
|
ReallocMem(fBuffer, NewSize);
|
|
fBufSize := NewSize;
|
|
end;
|
|
end;
|
|
|
|
{ TSynEditFileReader }
|
|
|
|
type
|
|
TSynEditFileReader = class(TSynEditFiler)
|
|
protected
|
|
fFilePos: Cardinal;
|
|
fFileSize: Cardinal;
|
|
procedure FillBuffer;
|
|
public
|
|
constructor Create(const FileName: string);
|
|
function EOF: boolean;
|
|
function ReadLine: string;
|
|
end;
|
|
|
|
constructor TSynEditFileReader.Create(const FileName: string);
|
|
begin
|
|
inherited Create;
|
|
fFiler := TFileStream.Create(FileName, fmOpenRead{ ToDo: or fmShareDenyWrite});
|
|
fFileSize := fFiler.Size;
|
|
fFiler.Seek(0, soFromBeginning);
|
|
end;
|
|
|
|
function TSynEditFileReader.EOF: boolean;
|
|
begin
|
|
Result := (fBuffer[fBufPtr] = #0) and (fFilePos >= fFileSize);
|
|
end;
|
|
|
|
procedure TSynEditFileReader.FillBuffer;
|
|
var
|
|
Count: Cardinal;
|
|
begin
|
|
if fBufPtr >= fBufSize - 1 then
|
|
fBufPtr := 0;
|
|
Count := fFileSize - fFilePos;
|
|
if Count >= fBufSize - fBufPtr then
|
|
Count := fBufSize - fBufPtr - 1;
|
|
fFiler.ReadBuffer(fBuffer[fBufPtr], Count);
|
|
fBuffer[fBufPtr + Count] := #0;
|
|
fFilePos := fFilePos + Count;
|
|
fBufPtr := 0;
|
|
end;
|
|
|
|
function TSynEditFileReader.ReadLine: string;
|
|
var
|
|
E, P, S: PChar;
|
|
begin
|
|
repeat
|
|
S := PChar(@fBuffer[fBufPtr]);
|
|
if S[0] = #0 then begin
|
|
FillBuffer;
|
|
S := PChar(@fBuffer[0]);
|
|
end;
|
|
E := PChar(@fBuffer[fBufSize]);
|
|
P := S;
|
|
while P + 2 < E do begin
|
|
case P[0] of
|
|
#10, #13:
|
|
begin
|
|
SetString(Result, S, P - S);
|
|
if P[0] = #13 then begin
|
|
fDosFile := TRUE;
|
|
Inc(P);
|
|
end;
|
|
Inc(P);
|
|
fBufPtr := P - fBuffer;
|
|
exit;
|
|
end;
|
|
#0:
|
|
if fFilePos >= fFileSize then begin
|
|
fBufPtr := P - fBuffer;
|
|
SetString(Result, S, P - S);
|
|
exit;
|
|
end;
|
|
end;
|
|
Inc(P);
|
|
end;
|
|
// put the partial string to the start of the buffer, and refill the buffer
|
|
Inc(P);
|
|
if S > fBuffer then
|
|
StrLCopy(fBuffer, S, P - S);
|
|
fBufPtr := P - S;
|
|
fBuffer[fBufPtr] := #0;
|
|
// if line is longer than half the buffer then grow it first
|
|
if 2 * Cardinal(P - S) > fBufSize then
|
|
SetBufferSize(fBufSize + fBufSize);
|
|
until FALSE;
|
|
end;
|
|
|
|
{ TSynEditFileWriter }
|
|
|
|
type
|
|
TSynEditFileWriter = class(TSynEditFiler)
|
|
protected
|
|
procedure Flush; override;
|
|
public
|
|
constructor Create(const FileName: string);
|
|
procedure WriteLine(const S: string);
|
|
end;
|
|
|
|
constructor TSynEditFileWriter.Create(const FileName: string);
|
|
begin
|
|
inherited Create;
|
|
fFiler := TFileStream.Create(FileName, fmCreate);
|
|
fFiler.Seek(0, soFromBeginning);
|
|
end;
|
|
|
|
procedure TSynEditFileWriter.Flush;
|
|
begin
|
|
if fBufPtr > 0 then begin
|
|
fFiler.WriteBuffer(fBuffer[0], fBufPtr);
|
|
fBufPtr := 0;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditFileWriter.WriteLine(const S: string);
|
|
var
|
|
L, NL: Cardinal;
|
|
begin
|
|
L := Length(S);
|
|
NL := 1 + Ord(fDosFile);
|
|
repeat
|
|
if fBufPtr + L + NL <= fBufSize then begin
|
|
if L > 0 then begin
|
|
Move(S[1], fBuffer[fBufPtr], L);
|
|
fBufPtr := fBufPtr + L;
|
|
end;
|
|
if fDosFile then begin
|
|
fBuffer[fBufPtr] := #13;
|
|
Inc(fBufPtr);
|
|
end;
|
|
fBuffer[fBufPtr] := #10;
|
|
Inc(fBufPtr);
|
|
exit;
|
|
end;
|
|
Flush;
|
|
if L + NL > fBufSize then
|
|
SetBufferSize(L + NL);
|
|
until FALSE;
|
|
end;
|
|
|
|
{ TSynEditStringList }
|
|
|
|
procedure ListIndexOutOfBounds(Index: integer);
|
|
begin
|
|
raise ESynEditStringList.CreateFmt(SListIndexOutOfBounds, [Index]);
|
|
end;
|
|
|
|
constructor TSynEditStringList.Create;
|
|
begin
|
|
inherited Create;
|
|
fDosFileFormat := TRUE;
|
|
{begin} //mh 2000-10-19
|
|
fIndexOfLongestLine := -1;
|
|
TabWidth := 8;
|
|
{end} //mh 2000-10-19
|
|
end;
|
|
|
|
destructor TSynEditStringList.Destroy;
|
|
{$IFDEF FPC}
|
|
var i: integer;
|
|
{$ENDIF}
|
|
begin
|
|
fOnChange := nil;
|
|
fOnChanging := nil;
|
|
inherited Destroy;
|
|
{$IFDEF FPC}
|
|
for i:=0 to fCount-1 do fList^[i].fString:='';
|
|
{$ENDIF}
|
|
fCount := 0;
|
|
SetCapacity(0);
|
|
end;
|
|
|
|
function TSynEditStringList.Add(const S: string): integer;
|
|
begin
|
|
BeginUpdate;
|
|
Result := fCount;
|
|
InsertItem(Result, S);
|
|
if Assigned(fOnAdded) then
|
|
fOnAdded(Result);
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TSynEditStringList.AddStrings(AStrings: TStrings);
|
|
var
|
|
i, FirstAdded: integer;
|
|
begin
|
|
{begin} //mh 2000-10-19
|
|
if AStrings.Count > 0 then begin
|
|
fIndexOfLongestLine := -1;
|
|
BeginUpdate;
|
|
try
|
|
i := fCount + AStrings.Count;
|
|
if i > fCapacity then
|
|
SetCapacity((i + 15) and (not 15));
|
|
FirstAdded := fCount;
|
|
for i := 0 to AStrings.Count - 1 do begin
|
|
with fList^[fCount] do begin
|
|
Pointer(fString) := nil;
|
|
fString := AStrings[i];
|
|
fObject := AStrings.Objects[i];
|
|
fRange := NullRange;
|
|
fExpandedLength := -1;
|
|
fFlags := [sfExpandedLengthUnknown];
|
|
end;
|
|
Inc(fCount);
|
|
end;
|
|
if Assigned(fOnAdded) then
|
|
fOnAdded(FirstAdded);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
{end} //mh 2000-10-19
|
|
end;
|
|
|
|
procedure TSynEditStringList.Clear;
|
|
{$IFDEF FPC}
|
|
var i: integer;
|
|
{$ENDIF}
|
|
begin
|
|
if fCount <> 0 then begin
|
|
BeginUpdate;
|
|
{$IFDEF FPC}
|
|
for i:=0 to fCount-1 do fList^[i].fString:='';
|
|
{$ENDIF}
|
|
fCount := 0;
|
|
SetCapacity(0);
|
|
if Assigned(fOnCleared) then
|
|
fOnCleared(Self);
|
|
EndUpdate;
|
|
end;
|
|
fIndexOfLongestLine := -1; //mh 2000-10-19
|
|
end;
|
|
|
|
procedure TSynEditStringList.Delete(Index: integer);
|
|
begin
|
|
if (Index < 0) or (Index > fCount) then
|
|
ListIndexOutOfBounds(Index);
|
|
BeginUpdate;
|
|
{$IFDEF FPC}
|
|
fList^[Index].fString:='';
|
|
{$ENDIF}
|
|
Dec(fCount);
|
|
if Index < fCount then begin
|
|
System.Move(fList^[Index + 1], fList^[Index],
|
|
(fCount - Index) * SynEditStringRecSize);
|
|
end;
|
|
fIndexOfLongestLine := -1; //mh 2000-10-19
|
|
if Assigned(fOnDeleted) then
|
|
fOnDeleted(Index);
|
|
EndUpdate;
|
|
end;
|
|
|
|
{begin} // DJLP 2000-11-01
|
|
procedure TSynEditStringList.DeleteLines(Index, NumLines: Integer);
|
|
var
|
|
LinesAfter: integer;
|
|
{$IFDEF FPC}
|
|
i: integer;
|
|
{$ENDIF}
|
|
begin
|
|
if NumLines > 0 then begin
|
|
if (Index < 0) or (Index > fCount) then
|
|
ListIndexOutOfBounds(Index);
|
|
LinesAfter := fCount - (Index + NumLines - 1);
|
|
if LinesAfter < 0 then
|
|
NumLines := fCount - Index - 1;
|
|
{$IFDEF FPC}
|
|
for i:=Index to Index+NumLines-1 do
|
|
fList^[i].fString:='';
|
|
{$ENDIF}
|
|
if LinesAfter > 0 then begin
|
|
BeginUpdate;
|
|
try
|
|
System.Move(fList^[Index + NumLines], fList^[Index],
|
|
LinesAfter * SynEditStringRecSize);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
Dec(fCount, NumLines);
|
|
if Assigned(fOnDeleted) then
|
|
fOnDeleted(Index);
|
|
end;
|
|
end;
|
|
{end} // DJLP 2000-11-01
|
|
|
|
procedure TSynEditStringList.Exchange(Index1, Index2: integer);
|
|
var
|
|
Temp: TSynEditStringRec;
|
|
begin
|
|
if (Index1 < 0) or (Index1 >= fCount) then
|
|
ListIndexOutOfBounds(Index1);
|
|
if (Index2 < 0) or (Index2 >= fCount) then
|
|
ListIndexOutOfBounds(Index2);
|
|
BeginUpdate;
|
|
Temp := fList^[Index1];
|
|
fList^[Index1] := fList^[Index2];
|
|
fList^[Index2] := Temp;
|
|
{begin} //mh 2000-10-19
|
|
if fIndexOfLongestLine = Index1 then
|
|
fIndexOfLongestLine := Index2
|
|
else if fIndexOfLongestLine = Index2 then
|
|
fIndexOfLongestLine := Index1;
|
|
{end} //mh 2000-10-19
|
|
EndUpdate;
|
|
end;
|
|
|
|
{begin} //mh 2000-10-19
|
|
function TSynEditStringList.ExpandedString(Index: integer): string;
|
|
var
|
|
HasTabs: boolean;
|
|
begin
|
|
with fList^[Index] do
|
|
if fString = '' then begin
|
|
Result := '';
|
|
Exclude(fFlags, sfExpandedLengthUnknown);
|
|
Exclude(fFlags, sfHasTabs);
|
|
Include(fFlags, sfHasNoTabs);
|
|
fExpandedLength := 0;
|
|
end else begin
|
|
Result := fConvertTabsProc(fString, fTabWidth, HasTabs);
|
|
fExpandedLength := Length(Result);
|
|
Exclude(fFlags, sfExpandedLengthUnknown);
|
|
Exclude(fFlags, sfHasTabs);
|
|
Exclude(fFlags, sfHasNoTabs);
|
|
if HasTabs then
|
|
Include(fFlags, sfHasTabs)
|
|
else
|
|
Include(fFlags, sfHasNoTabs);
|
|
end;
|
|
end;
|
|
{end} //mh 2000-10-19
|
|
|
|
{$IFDEF SYN_LAZARUS}
|
|
function TSynEditStringList.ExpandedStringLength(Index: integer): integer;
|
|
var
|
|
HasTabs: boolean;
|
|
begin
|
|
with fList^[Index] do
|
|
if length(fString) = 0 then begin
|
|
Result := 0;
|
|
Exclude(fFlags, sfExpandedLengthUnknown);
|
|
Exclude(fFlags, sfHasTabs);
|
|
Include(fFlags, sfHasNoTabs);
|
|
fExpandedLength := 0;
|
|
end else begin
|
|
Result := fSimulateConvertTabsProc(fString, fTabWidth, HasTabs);
|
|
fExpandedLength := Result;
|
|
Exclude(fFlags, sfExpandedLengthUnknown);
|
|
Exclude(fFlags, sfHasTabs);
|
|
Exclude(fFlags, sfHasNoTabs);
|
|
if HasTabs then
|
|
Include(fFlags, sfHasTabs)
|
|
else
|
|
Include(fFlags, sfHasNoTabs);
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function TSynEditStringList.Get(Index: integer): string;
|
|
begin
|
|
if (Index >= 0) and (Index < fCount) then
|
|
Result := fList^[Index].fString
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function TSynEditStringList.GetCapacity: integer;
|
|
begin
|
|
Result := fCapacity;
|
|
end;
|
|
|
|
function TSynEditStringList.GetCount: integer;
|
|
begin
|
|
Result := fCount;
|
|
end;
|
|
|
|
{begin} //mh 2000-10-19
|
|
function TSynEditStringList.GetExpandedString(Index: integer): string;
|
|
begin
|
|
if (Index >= 0) and (Index < fCount) then begin
|
|
if sfHasNoTabs in fList^[Index].fFlags then
|
|
Result := fList^[Index].fString
|
|
else
|
|
Result := ExpandedString(Index);
|
|
end else
|
|
Result := '';
|
|
end;
|
|
|
|
function TSynEditStringList.GetLengthOfLongestLine: integer; //mh 2000-10-19
|
|
var
|
|
i, MaxLen: integer;
|
|
PRec: PSynEditStringRec;
|
|
begin
|
|
if fIndexOfLongestLine < 0 then begin
|
|
MaxLen := 0;
|
|
if fCount > 0 then begin
|
|
PRec := @fList^[0];
|
|
for i := 0 to fCount - 1 do begin
|
|
if sfExpandedLengthUnknown in PRec^.fFlags then
|
|
{$IFDEF SYN_LAZARUS}
|
|
ExpandedStringLength(i);
|
|
{$ELSE}
|
|
ExpandedString(i);
|
|
{$ENDIF}
|
|
if PRec^.fExpandedLength > MaxLen then begin
|
|
MaxLen := PRec^.fExpandedLength;
|
|
fIndexOfLongestLine := i;
|
|
end;
|
|
Inc(PRec);
|
|
end;
|
|
end;
|
|
end;
|
|
if (fIndexOfLongestLine >= 0) and (fIndexOfLongestLine < fCount) then
|
|
Result := fList^[fIndexOfLongestLine].fExpandedLength
|
|
else
|
|
Result := 0;
|
|
end;
|
|
{end} //mh 2000-10-19
|
|
|
|
function TSynEditStringList.GetObject(Index: integer): TObject;
|
|
begin
|
|
if (Index >= 0) and (Index < fCount) then
|
|
Result := fList^[Index].fObject
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TSynEditStringList.GetRange(Index: integer): TSynEditRange;
|
|
begin
|
|
if (Index >= 0) and (Index < fCount) then
|
|
Result := fList^[Index].fRange
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TSynEditStringList.Grow;
|
|
var
|
|
Delta: Integer;
|
|
begin
|
|
if fCapacity > 64 then
|
|
Delta := fCapacity div 4
|
|
else
|
|
Delta := 16;
|
|
SetCapacity(fCapacity + Delta);
|
|
end;
|
|
|
|
procedure TSynEditStringList.Insert(Index: integer; const S: string);
|
|
begin
|
|
if (Index < 0) or (Index > fCount) then
|
|
ListIndexOutOfBounds(Index);
|
|
BeginUpdate;
|
|
InsertItem(Index, S);
|
|
if Assigned(fOnInserted) then
|
|
fOnInserted(Index);
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TSynEditStringList.InsertItem(Index: integer; const S: string);
|
|
begin
|
|
BeginUpdate;
|
|
if fCount = fCapacity then
|
|
Grow;
|
|
if Index < fCount then begin
|
|
System.Move(fList^[Index], fList^[Index + 1],
|
|
(fCount - Index) * SynEditStringRecSize);
|
|
end;
|
|
fIndexOfLongestLine := -1; //mh 2000-10-19
|
|
with fList^[Index] do begin
|
|
Pointer(fString) := nil;
|
|
fString := S;
|
|
fObject := nil;
|
|
fRange := NullRange;
|
|
{begin} //mh 2000-10-19
|
|
fExpandedLength := -1;
|
|
fFlags := [sfExpandedLengthUnknown];
|
|
{end} //mh 2000-10-19
|
|
end;
|
|
Inc(fCount);
|
|
EndUpdate;
|
|
end;
|
|
|
|
{begin} // DJLP 2000-11-01
|
|
procedure TSynEditStringList.InsertLines(Index, NumLines: integer);
|
|
begin
|
|
if NumLines > 0 then begin
|
|
BeginUpdate;
|
|
try
|
|
{$IFDEF FPC}
|
|
if FCapacity<fCount + NumLines then
|
|
{$ENDIF}
|
|
SetCapacity(fCount + NumLines);
|
|
if Index < fCount then begin
|
|
System.Move(fList^[Index], fList^[Index + NumLines],
|
|
(fCount - Index) * SynEditStringRecSize);
|
|
end;
|
|
FillChar(fList^[Index], NumLines * SynEditStringRecSize, 0);
|
|
Inc(fCount, NumLines);
|
|
if Assigned(fOnAdded) then
|
|
fOnAdded(Index);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditStringList.InsertStrings(Index: integer;
|
|
NewStrings: TStrings);
|
|
var
|
|
i, Cnt: integer;
|
|
begin
|
|
Cnt := NewStrings.Count;
|
|
if Cnt > 0 then begin
|
|
BeginUpdate;
|
|
try
|
|
InsertLines(Index, Cnt);
|
|
for i := 0 to Cnt - 1 do
|
|
Strings[Index + i] := NewStrings[i];
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
{end} // DJLP 2000-11-01
|
|
|
|
procedure TSynEditStringList.LoadFromFile(const FileName: string);
|
|
var
|
|
Reader: TSynEditFileReader;
|
|
begin
|
|
Reader := TSynEditFileReader.Create(FileName);
|
|
try
|
|
BeginUpdate;
|
|
try
|
|
Clear;
|
|
while not Reader.EOF do
|
|
Add(Reader.ReadLine);
|
|
fDosFileFormat := Reader.DosFile;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
finally
|
|
Reader.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditStringList.Put(Index: integer; const S: string);
|
|
begin
|
|
if (Index = 0) and (fCount = 0) then
|
|
Add(S)
|
|
else begin
|
|
if (Index < 0) or (Index >= fCount) then
|
|
ListIndexOutOfBounds(Index);
|
|
BeginUpdate;
|
|
{begin} //mh 2000-10-19
|
|
fIndexOfLongestLine := -1;
|
|
with fList^[Index] do begin
|
|
Include(fFlags, sfExpandedLengthUnknown);
|
|
Exclude(fFlags, sfHasTabs);
|
|
Exclude(fFlags, sfHasNoTabs);
|
|
fString := S;
|
|
end;
|
|
{end} //mh 2000-10-19
|
|
if Assigned(fOnPutted) then
|
|
fOnPutted(Index);
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditStringList.PutObject(Index: integer; AObject: TObject);
|
|
begin
|
|
if (Index < 0) or (Index >= fCount) then
|
|
ListIndexOutOfBounds(Index);
|
|
BeginUpdate;
|
|
fList^[Index].fObject := AObject;
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TSynEditStringList.PutRange(Index: integer; ARange: TSynEditRange);
|
|
begin
|
|
if (Index < 0) or (Index >= fCount) then
|
|
ListIndexOutOfBounds(Index);
|
|
BeginUpdate;
|
|
fList^[Index].fRange := ARange;
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TSynEditStringList.SaveToFile(const FileName: string);
|
|
var
|
|
Writer: TSynEditFileWriter;
|
|
i: integer;
|
|
begin
|
|
Writer := TSynEditFileWriter.Create(FileName);
|
|
try
|
|
Writer.DosFile := fDosFileFormat;
|
|
for i := 0 to fCount - 1 do
|
|
Writer.WriteLine(Get(i));
|
|
finally
|
|
Writer.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditStringList.SetCapacity(NewCapacity: integer);
|
|
begin
|
|
ReallocMem(fList, NewCapacity * SynEditStringRecSize);
|
|
fCapacity := NewCapacity;
|
|
end;
|
|
|
|
{begin} //mh 2000-10-19
|
|
procedure TSynEditStringList.SetTabWidth(Value: integer);
|
|
var
|
|
i: integer;
|
|
begin
|
|
if Value <> fTabWidth then begin
|
|
fTabWidth := Value;
|
|
fConvertTabsProc := GetBestConvertTabsProcEx(fTabWidth);
|
|
{$IFDEF SYN_LAZARUS}
|
|
fSimulateConvertTabsProc := GetBestSimulateConvertTabsProcEx(fTabWidth);
|
|
{$ENDIF}
|
|
fIndexOfLongestLine := -1;
|
|
{begin} //mh 2000-11-08
|
|
for i := 0 to fCount - 1 do
|
|
with fList^[i] do begin
|
|
fExpandedLength := -1;
|
|
Exclude(fFlags, sfHasNoTabs);
|
|
Include(fFlags, sfExpandedLengthUnknown);
|
|
end;
|
|
{end} //mh 2000-11-08
|
|
end;
|
|
end;
|
|
{end} //mh 2000-10-19
|
|
|
|
procedure TSynEditStringList.SetUpdateState(Updating: Boolean);
|
|
begin
|
|
if Updating then begin
|
|
if Assigned(fOnChanging) then
|
|
fOnChanging(Self);
|
|
end else begin
|
|
if Assigned(fOnChange) then
|
|
fOnChange(Self);
|
|
end;
|
|
end;
|
|
{end} //mh 2000-10-10
|
|
|
|
{ TSynEditUndoList }
|
|
|
|
constructor TSynEditUndoList.Create;
|
|
begin
|
|
inherited Create;
|
|
fItems := TList.Create;
|
|
fMaxUndoActions := 1024;
|
|
fNextChangeNumber := 1; //sbs 2000-11-19
|
|
end;
|
|
|
|
destructor TSynEditUndoList.Destroy;
|
|
begin
|
|
Clear;
|
|
fItems.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSynEditUndoList.AddChange(AReason: TSynChangeReason; AStart,
|
|
AEnd: TPoint; ChangeText: string; SelMode: TSynSelectionMode);
|
|
var
|
|
NewItem: TSynEditUndoItem;
|
|
begin
|
|
if fLockCount = 0 then begin
|
|
NewItem := TSynEditUndoItem.Create;
|
|
try
|
|
with NewItem do begin
|
|
fChangeReason := AReason;
|
|
fChangeSelMode := SelMode;
|
|
fChangeStartPos := AStart;
|
|
fChangeEndPos := AEnd;
|
|
fChangeStr := ChangeText;
|
|
{begin} //sbs 2000-11-19
|
|
if fBlockChangeNumber <> 0 then
|
|
fChangeNumber := fBlockChangeNumber
|
|
else begin
|
|
fChangeNumber := fNextChangeNumber;
|
|
if fBlockCount = 0 then begin
|
|
Inc(fNextChangeNumber);
|
|
if fNextChangeNumber = 0 then
|
|
Inc(fNextChangeNumber);
|
|
end;
|
|
end;
|
|
{end} //sbs 2000-11-19
|
|
end;
|
|
PushItem(NewItem);
|
|
except
|
|
NewItem.Free;
|
|
raise;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{begin} //sbs 2000-11-19
|
|
procedure TSynEditUndoList.BeginBlock;
|
|
begin
|
|
Inc(fBlockCount);
|
|
fBlockChangeNumber := fNextChangeNumber;
|
|
end;
|
|
{end} //sbs 2000-11-19
|
|
|
|
procedure TSynEditUndoList.Clear;
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := 0 to fItems.Count - 1 do
|
|
TSynEditUndoItem(fItems[i]).Free;
|
|
fItems.Clear;
|
|
fFullUndoImposible := FALSE; //mh 2000-10-03
|
|
end;
|
|
|
|
{begin} //sbs 2000-11-19
|
|
procedure TSynEditUndoList.EndBlock;
|
|
begin
|
|
if fBlockCount > 0 then begin
|
|
Dec(fBlockCount);
|
|
if fBlockCount = 0 then begin
|
|
fBlockChangeNumber := 0;
|
|
Inc(fNextChangeNumber);
|
|
if fNextChangeNumber = 0 then
|
|
Inc(fNextChangeNumber);
|
|
end;
|
|
end;
|
|
end;
|
|
{end} //sbs 2000-11-19
|
|
|
|
procedure TSynEditUndoList.EnsureMaxEntries;
|
|
var
|
|
Item: TSynEditUndoItem;
|
|
begin
|
|
if fItems.Count > fMaxUndoActions then begin //mh 2000-10-03
|
|
fFullUndoImposible := TRUE; //mh 2000-10-03
|
|
while fItems.Count > fMaxUndoActions do begin
|
|
Item := TSynEditUndoItem(fItems[0]);
|
|
Item.Free;
|
|
fItems.Delete(0);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TSynEditUndoList.GetCanUndo: boolean;
|
|
begin
|
|
Result := fItems.Count > 0;
|
|
end;
|
|
|
|
function TSynEditUndoList.GetItemCount: integer;
|
|
begin
|
|
Result := fItems.Count;
|
|
end;
|
|
|
|
procedure TSynEditUndoList.Lock;
|
|
begin
|
|
Inc(fLockCount);
|
|
end;
|
|
|
|
function TSynEditUndoList.PeekItem: TSynEditUndoItem;
|
|
var
|
|
iLast: integer;
|
|
begin
|
|
Result := nil;
|
|
iLast := fItems.Count - 1;
|
|
if iLast >= 0 then
|
|
Result := TSynEditUndoItem(fItems[iLast]);
|
|
end;
|
|
|
|
function TSynEditUndoList.PopItem: TSynEditUndoItem;
|
|
var
|
|
iLast: integer;
|
|
begin
|
|
Result := nil;
|
|
iLast := fItems.Count - 1;
|
|
if iLast >= 0 then begin
|
|
Result := TSynEditUndoItem(fItems[iLast]);
|
|
fItems.Delete(iLast);
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditUndoList.PushItem(Item: TSynEditUndoItem);
|
|
begin
|
|
if Assigned(Item) then begin
|
|
fItems.Add(Item);
|
|
EnsureMaxEntries;
|
|
if Assigned(fOnAdded) then
|
|
fOnAdded(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditUndoList.SetMaxUndoActions(Value: integer);
|
|
begin
|
|
if Value < 0 then
|
|
Value := 0;
|
|
if Value <> fMaxUndoActions then begin
|
|
fMaxUndoActions := Value;
|
|
EnsureMaxEntries;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditUndoList.Unlock;
|
|
begin
|
|
if fLockCount > 0 then
|
|
Dec(fLockCount);
|
|
end;
|
|
|
|
end.
|
|
|