unit primeMarkup;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, SynEditMarkupHighAll, LazSynEditText, SynEditMiscClasses,
Graphics;
type
{ TprimeMarkupBase }
TprimeMarkupBase = class(TSynEditMarkupHighlightMatches)
private
procedure DoLineChange(Sender: TSynEditStrings; p_LinePos, p_BytePos, p_Count,
p_LineBrkCnt: Integer; p_Text: String);
protected
procedure SetLines(const p_new: TSynEditStrings); override;
public
constructor Create(p_SynEdit : TSynEditBase);
destructor Destroy; override;
procedure AddMark(p_Start, p_Finish: TPoint);
procedure ClearMarks;
property Matches;
end;
implementation
uses
LCLProc;
{ TprimeMarkupBase }
procedure TprimeMarkupBase.DoLineChange(Sender: TSynEditStrings; p_LinePos,
p_BytePos, p_Count, p_LineBrkCnt: Integer; p_Text: String);
var
i, end_BytePos: Integer;
m: TSynMarkupHighAllMatch;
abs_Count, abs_LineBrkCnt: Integer;
min, max: Integer;
changed, StartMoved, EndMoved: Boolean;
begin
if Matches.Count = 0 then exit;
min := MaxInt;
max := Integer($ffffffff);
{$IFDEF primeMarkupDebug}
DebugLn('*** %s.DoLineChange l=%d x=%d c=%d crlf=%d t=%s', [ClassName, p_LinePos, p_BytePos, p_Count, p_LineBrkCnt, p_Text]);
{$ENDIF}
if (p_Count < 0)
or (p_LineBrkCnt < 0) then begin
// ggf. Marks löschen
abs_Count := Abs(p_Count);
abs_LineBrkCnt := Abs(p_LineBrkCnt);
end_BytePos := p_BytePos + abs_Count;
i := Matches.Count - 1;
while i >= 0 do begin
m := Matches[i];
changed := false;
StartMoved := false;
EndMoved := false;
if p_Count < 0 then begin
// Zeichen entfernt
if (m.StartPoint.y = p_LinePos)
and (m.StartPoint.x >= p_BytePos) then begin
// Änderung beginnt in der Zeile vor der Markierung
// -> Startposition anpassen
if (m.StartPoint.x >= end_BytePos) then begin
// komplett vor der Markierung
dec(m.StartPoint.x, abs_Count);
end else begin
// Änderung reicht in die Markierung
m.StartPoint.x := p_BytePos;
end;
Matches.StartPoint[i] := m.StartPoint;
if (m.EndPoint.y = p_LinePos) then begin
// Ende der Markierung ist in der gleichen Zeile
if (m.EndPoint.x > end_BytePos) then begin
// und komplett vor dem Ende der Markierung
// -> also auch Endposition anpassen
dec(m.EndPoint.x, abs_Count);
Matches.EndPoint[i] := m.EndPoint;
end else begin
// und komplett nach dem Ende der Markierung
// -> Markierung entfernen
Matches.Delete(i);
end;
end;
changed := true;
end else if (m.EndPoint.y = p_LinePos)
and (m.EndPoint.x > p_BytePos) then begin
// Änderung ist in der Zeile vor dem Ende der Markierung
if (m.EndPoint.x > end_BytePos) then begin
// und komplett vor dem Ende der Markierung
// -> also auch Endposition anpassen
dec(m.EndPoint.x, abs_Count);
end else begin
// und reicht über das Ende der Markierung hinaus
// -> Endposition kürzen
m.EndPoint.x := p_BytePos;
end;
Matches.EndPoint[i] := m.EndPoint;
changed := true;
end;
end;
if p_LineBrkCnt < 0 then begin
// Zeilen entfernt
if p_Count <> 0 then DebugLn('*****'#13'***** %s.DoLineChange l=%d x=%d c=%d crlf=%d t=%s !!! c != 0'#13'*****', [ClassName, p_LinePos, p_BytePos, p_Count, p_LineBrkCnt, p_Text]);
if (m.StartPoint.y = p_LinePos + abs_LineBrkCnt) then begin
// Zeilenzusammenführung:
// Markup aus der nächsten Zeile wird in diese Zeile geholt
// Start
inc(m.StartPoint.x, p_BytePos - 1);
dec(m.StartPoint.y, abs_LineBrkCnt);
changed := true;
StartMoved := true;
// End
if (m.EndPoint.y = p_LinePos + abs_LineBrkCnt) then
inc(m.EndPoint.x, p_BytePos - 1);
dec(m.EndPoint.y, abs_LineBrkCnt);
EndMoved := true;
end else if (m.EndPoint.y = p_LinePos + abs_LineBrkCnt) then begin
// Zeilenzusammenführung:
// Markup aus der nächsten Zeile wird in diese Zeile geholt
inc(m.EndPoint.x, p_BytePos - 1);
dec(m.EndPoint.y, abs_LineBrkCnt);
changed := true;
EndMoved := true;
end;
// y anpassen
if (m.StartPoint.y > p_LinePos)
and not StartMoved then begin
dec(m.StartPoint.y, abs_LineBrkCnt);
changed := true;
end;
if (m.EndPoint.y > p_LinePos)
and not EndMoved then begin
dec(m.EndPoint.y, abs_LineBrkCnt);
changed := true;
end;
Matches.StartPoint[i] := m.StartPoint;
Matches.EndPoint[i] := m.EndPoint;
end;
if changed then begin
if m.StartPoint.y < min then min := m.StartPoint.y;
if m.StartPoint.y > max then max := m.StartPoint.y;
if m.EndPoint.y < min then min := m.EndPoint.y;
if m.EndPoint.y > max then max := m.EndPoint.y;
end;
dec(i);
end;
end else begin
// ggf. Marks verschieben
for i := 0 to Matches.Count - 1 do begin
m := Matches[i];
changed := false;
StartMoved := false;
EndMoved := false;
if (m.StartPoint.y = p_LinePos)
and (m.StartPoint.x >= p_BytePos) then begin
// Änderung ist in der Zeile vor der Markierung
// -> Startposition anpassen
inc(m.StartPoint.x, p_Count);
if p_LineBrkCnt > 0 then begin
inc(m.StartPoint.y, p_LineBrkCnt);
StartMoved := true;
dec(m.StartPoint.x, p_BytePos - 1);
end;
if m.EndPoint.y = p_LinePos then begin
// Ende der Markierung ist in der gleichen Zeile
// -> also auch Endposition anpassen
inc(m.EndPoint.x, p_Count);
if p_LineBrkCnt > 0 then begin
inc(m.EndPoint.y, p_LineBrkCnt);
EndMoved := true;
dec(m.EndPoint.x, p_BytePos - 1);
end;
end;
changed := true;
end;
if (m.EndPoint.y = p_LinePos)
and (m.EndPoint.x > p_BytePos)
and (
(m.StartPoint.y <> p_LinePos)
or (m.StartPoint.x < p_BytePos)
)then begin
// Änderung ist in der Markierung
// -> also Endposition anpassen
inc(m.EndPoint.x, p_Count);
if p_LineBrkCnt > 0 then begin
if not EndMoved then begin
inc(m.EndPoint.y, p_LineBrkCnt);
EndMoved := true;
end;
dec(m.EndPoint.x, p_BytePos - 1);
end;
changed := true;
end;
if p_LineBrkCnt > 0 then begin
// y anpassen
if not StartMoved
and (m.StartPoint.y > p_LinePos) then begin
inc(m.StartPoint.y, p_LineBrkCnt);
changed := true;
end;
if not EndMoved
and (m.EndPoint.y > p_LinePos) then begin
inc(m.EndPoint.y, p_LineBrkCnt);
changed := true;
end;
end;
Matches.StartPoint[i] := m.StartPoint;
Matches.EndPoint[i] := m.EndPoint;
if changed then begin
if m.StartPoint.y < min then min := m.StartPoint.y;
if m.StartPoint.y > max then max := m.StartPoint.y;
if m.EndPoint.y < min then min := m.EndPoint.y;
if m.EndPoint.y > max then max := m.EndPoint.y;
end;
end;
end;
if min <= max then begin
{$IFDEF primeMarkupDebug}
DebugLn('### InvalidateSynLines(%d, %d)', [min, max]);
{$ENDIF}
InvalidateSynLines(min, max);
end;
end;
procedure TprimeMarkupBase.SetLines(const p_new: TSynEditStrings);
var
l_old: TSynEditStrings;
begin
l_old := Lines;
if (l_old <> p_new)
and Assigned(l_old) then begin
// alten ChangeHandler entfernen
l_old.RemoveEditHandler(@DoLineChange);
end;
inherited SetLines(p_new);
if (l_old <> p_new)
and Assigned(p_new) then begin
// neuen ChangeHandler eintragen
p_new.AddEditHandler(@DoLineChange);
end;
end;
constructor TprimeMarkupBase.Create(p_SynEdit: TSynEditBase);
begin
inherited Create(p_SynEdit);
end;
destructor TprimeMarkupBase.Destroy;
begin
Lines := nil;
inherited Destroy;
end;
procedure TprimeMarkupBase.AddMark(p_Start, p_Finish: TPoint);
var
r: TSynMarkupHighAllMatch;
begin
r.StartPoint := p_Start;
r.EndPoint := p_Finish;
Matches[Matches.Count] := r;
end;
procedure TprimeMarkupBase.ClearMarks;
begin
Matches.Count := 0;
end;
end.