lazarus/components/synedit/synpluginmulticaret.pp
martin 4f3dd33568 SynEdit: rewrite internal caret
git-svn-id: trunk@47714 -
2015-02-12 03:49:23 +00:00

1411 lines
44 KiB
ObjectPascal

unit SynPluginMultiCaret;
{$mode objfpc}{$H+}
{$DEFINE SynMultiCaretAssert}
{off $DEFINE SynMultiCaretDebug}
{$IfDef SynMultiCaretAssert}
{$ASSERTIONS on}
{$ENDIF}
interface
uses
Classes, SysUtils, SynEdit, SynEditPointClasses, SynEditKeyCmds, SynEditTypes,
LazSynTextArea, SynEditMiscProcs, LazSynEditText, SynEditMiscClasses, SynEditMouseCmds,
{$IfDef SynMultiCaretDebug} LazLoggerBase, {$ELSE} LazLoggerDummy, {$ENDIF}
LCLType, Controls, Graphics, Clipbrd;
const
emcPluginMultiCaretToggleCaret = emcPluginFirstMultiCaret;
type
TSynPluginMultiCaretVisualList = class;
{ TSynPluginMultiCaretVisual }
TSynPluginMultiCaretVisual = class(TSynEditScreenCaret)
private
FListIndex: Integer;
FUsedList: TSynPluginMultiCaretVisualList;
FUnUsedList: TSynPluginMultiCaretVisualList;
{$IfDef SynMultiCaretAssert}
FIsUsed: Boolean;
{$ENDIF}
public
constructor Create(AHandleOwner: TWinControl;
APainterClass: TSynEditScreenCaretPainterClass;
AnUsedList, AnUnUsedList: TSynPluginMultiCaretVisualList);
procedure MoveToUsed;
procedure MoveToUnUsed;
property ListIndex: Integer read FListIndex;
property UsedList: TSynPluginMultiCaretVisualList read FUsedList;
property UnUsedList: TSynPluginMultiCaretVisualList read FUnUsedList;
end;
{ TSynPluginMultiCaretVisualList }
TSynPluginMultiCaretVisualList = class
private
FList: Array of TSynPluginMultiCaretVisual;
FCount: Integer;
function GetScreenCaret(Index: Integer): TSynPluginMultiCaretVisual;
public
destructor Destroy; override;
procedure Add(AScreenCaret: TSynPluginMultiCaretVisual);
procedure Remove(AScreenCaret: TSynPluginMultiCaretVisual);
procedure Clear; // free visuals
function Count: Integer;
property ScreenCaret[Index: Integer]: TSynPluginMultiCaretVisual read GetScreenCaret; default;
end;
TCaretFlag = (cfMainCaret, cfNoneVisual, cfAddDuplicate);
TCaretFlags = set of TCaretFlag;
{ TSynPluginMultiCaretList }
TSynPluginMultiCaretList = class
private type
//TCaretFlag = (cfMainCaret, cfNoneVisual);
//TCaretFlags = set of TCaretFlag;
TCaretData = record
x, y: Integer; // logical
Flags: TCaretFlags;
Visual: TSynPluginMultiCaretVisual;
end;
private
FLowIndex, FHighIndex: Integer;
FMainCaretIndex: Integer;
FCarets: Array of TCaretData;
function FindEqOrNextCaretRawIdx(X, Y: Integer; LowIdx: integer = -1; HighIdx: integer = -1): Integer;
function GetCaret(Index: Integer): TPoint; inline;
function GetCaretX(Index: Integer): Integer; inline;
function GetCaretY(Index: Integer): Integer; inline;
function GetFlags(Index: Integer): TCaretFlags;
function GetMainCaretIndex: Integer;
function GetVisual(Index: Integer): TSynPluginMultiCaretVisual; inline;
procedure SetCaret(Index: Integer; AValue: TPoint); inline;
procedure SetCaretX(Index: Integer; AValue: Integer); inline;
procedure SetCaretY(Index: Integer; AValue: Integer); inline;
procedure SetVisual(Index: Integer; AValue: TSynPluginMultiCaretVisual); inline;
function InternalRemoveCaretEx(RawIndex: Integer; AlternativeRawIndex: Integer = -1): Integer;
function InternalRemoveCaret(RawIndex: Integer): integer;
procedure AdjustAfterChange(RawIndex: Integer);
public
constructor Create;
function AddCaret(X, Y: Integer; flags: TCaretFlags = []): Integer;
procedure RemoveCaret(Index: Integer);
procedure Clear(AFreeVisual: Boolean = False);
function Count: Integer;
function FindCaretIdx(X, Y: Integer): Integer;
procedure AdjustAllAfterEdit(aLinePos, aBytePos, aCount, aLineBrkCnt: Integer);
procedure FindAndRemoveMergedCarets;
property Caret[Index: Integer]: TPoint read GetCaret write SetCaret;
property CaretX[Index: Integer]: Integer read GetCaretX write SetCaretX;
property CaretY[Index: Integer]: Integer read GetCaretY write SetCaretY;
property Visual[Index: Integer]: TSynPluginMultiCaretVisual read GetVisual write SetVisual;
property Flags[Index: Integer]: TCaretFlags read GetFlags;
property MainCaretIndex: Integer read GetMainCaretIndex;
end;
{ TSynPluginMultiCaretBase }
TSynPluginMultiCaretBase = class(TLazSynEditPlugin)
private
FCarets: TSynPluginMultiCaretList;
FColor: TColor;
FUsedList: TSynPluginMultiCaretVisualList;
FUnUsedList: TSynPluginMultiCaretVisualList;
FInPaint: Boolean;
FPaintClip: TRect;
FCustomPixelWidth, FCustomPixelHeight: Array [TSynCaretType] of Integer;
FCustomOffsetX, FCustomOffsetY: Array [TSynCaretType] of Integer;
FCustomFlags: Array [TSynCaretType] of TSynCustomCaretSizeFlags;
FPaintLock: Integer;
FPaintLockFlags: set of
(plfUpdateCaretsPos, plfDeferUpdateCaretsPos,
plfBoundsChanged, plfTextSizeChanged);
function GetTextArea: TLazSynTextArea;
procedure DoTextSizeChanged(Sender: TObject);
procedure DoBoundsChanged(Sender: TObject);
procedure DoEditorPaintEvent(Sender: TObject; EventType: TSynPaintEvent;
const prcClip: TRect);
procedure DoEditorScrollEvent(Sender: TObject; EventType: TSynScrollEvent; dx,
dy: Integer; const prcScroll, prcClip: TRect);
procedure DoEditorStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
procedure DoAfterDecPaintLock(Sender: TObject); virtual;
procedure DoBeforeIncPaintLock(Sender: TObject); virtual;
procedure DoBufferChanged(Sender: TObject);
procedure DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
aLineBrkCnt: Integer; aText: String);
procedure SetColor(AValue: TColor);
property TextArea: TLazSynTextArea read GetTextArea;
function CreateVisual: TSynPluginMultiCaretVisual; virtual;
function GetVisual: TSynPluginMultiCaretVisual;
protected
function AddCaret(X, Y: Integer; flags: TCaretFlags = []): Integer;
procedure RemoveCaret(Index: Integer);
procedure UpdateCaretsPos;
procedure ClearCarets;
function CaretsCount: Integer;
procedure DoEditorRemoving(AValue: TCustomSynEdit); override;
procedure DoEditorAdded(AValue: TCustomSynEdit); override;
property Carets: TSynPluginMultiCaretList read FCarets;
property PaintLock: Integer read FPaintLock;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetCaretTypeSize(AType: TSynCaretType; AWidth, AHeight, AXOffs, AYOffs: Integer; AFlags: TSynCustomCaretSizeFlags);
property Color: TColor read FColor write SetColor;
end;
{ TSynPluginMultiCaretMouseActions }
TSynPluginMultiCaretMouseActions = class(TSynEditMouseActions)
public
procedure ResetDefaults; override;
end;
{ TSynPluginMultiCaret }
TSynPluginMultiCaretStateFlag = (
sfProcessingCmd, sfProcessingMain,
sfExtendingColumnSel
);
TSynPluginMultiCaretStateFlags = set of TSynPluginMultiCaretStateFlag;
TSynPluginMultiCaret = class(TSynPluginMultiCaretBase)
private
FStateFlags: TSynPluginMultiCaretStateFlags;
FMouseActions: TSynPluginMultiCaretMouseActions;
protected
procedure DoEditorRemoving(AValue: TCustomSynEdit); override;
procedure DoEditorAdded(AValue: TCustomSynEdit); override;
procedure DoAfterDecPaintLock(Sender: TObject); override;
procedure DoCaretChanged(Sender: TObject);
procedure DoSelectionChanged(Sender: TObject);
procedure DoBeforeSetSelText(Sender: TObject; AMode: TSynSelectionMode; ANewText: PChar);
procedure ProcessSynCommand(Sender: TObject; AfterProcessing: boolean;
var Handled: boolean; var Command: TSynEditorCommand;
var AChar: TUTF8Char; Data: pointer; HandlerData: pointer);
function MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo;
HandleActionProc: TSynEditMouseActionHandler): Boolean;
function DoHandleMouseAction(AnAction: TSynEditMouseAction;
var AnInfo: TSynEditMouseActionInfo): Boolean;
function CreateVisual: TSynPluginMultiCaretVisual; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property MouseActions: TSynPluginMultiCaretMouseActions read FMouseActions;
end;
implementation
var
SynMCaretDebug: PLazLoggerLogGroup;
const
EMPTY_LIST_LEN = 8;
{ TSynPluginMultiCaretVisual }
constructor TSynPluginMultiCaretVisual.Create(AHandleOwner: TWinControl;
APainterClass: TSynEditScreenCaretPainterClass; AnUsedList,
AnUnUsedList: TSynPluginMultiCaretVisualList);
begin
FListIndex := -1;
FUsedList := AnUsedList;
FUnUsedList := AnUnUsedList;
inherited Create(AHandleOwner, APainterClass);
end;
procedure TSynPluginMultiCaretVisual.MoveToUsed;
begin
{$IfDef SynMultiCaretAssert}
assert((FListIndex < 0) or (not FIsUsed), 'TSynPluginMultiCaretVisual.MoveToUsed: not yet on list');
FIsUsed := True;
{$ENDIF}
if FListIndex >= 0 then
FUnUsedList.Remove(Self);
FUsedList.Add(Self);
end;
procedure TSynPluginMultiCaretVisual.MoveToUnUsed;
begin
{$IfDef SynMultiCaretAssert}
assert((FListIndex < 0) or FIsUsed, 'TSynPluginMultiCaretVisual.MoveToUnUsed: not yet on list');
FIsUsed := False;
{$ENDIF}
if FListIndex >= 0 then
FUsedList.Remove(Self);
FUnUsedList.Add(Self);
Visible := False;
end;
{ TSynPluginMultiCaretVisualList }
function TSynPluginMultiCaretVisualList.GetScreenCaret(Index: Integer): TSynPluginMultiCaretVisual;
begin
Result := FList[Index];
end;
destructor TSynPluginMultiCaretVisualList.Destroy;
begin
inherited Destroy;
Clear;
end;
procedure TSynPluginMultiCaretVisualList.Add(AScreenCaret: TSynPluginMultiCaretVisual);
begin
if (AScreenCaret.ListIndex >= 0) and (AScreenCaret.ListIndex < FCount) and
(FList[AScreenCaret.ListIndex] = AScreenCaret)
then begin
assert(False, 'TSynPluginMultiCaretVisualList.Add: not on list');
exit;
end;
if FCount = Length(FList) then debugln(SynMCaretDebug, ['TSynPluginMultiCaretVisualList.Add ', FCount + max(16, FCount div 16)]);
if FCount = Length(FList) then
SetLength(FList, FCount + max(16, FCount div 16));
FList[FCount] := AScreenCaret;
AScreenCaret.FListIndex := FCount;
inc(FCount);
end;
procedure TSynPluginMultiCaretVisualList.Remove(AScreenCaret: TSynPluginMultiCaretVisual);
var
t: TSynPluginMultiCaretVisual;
begin
if (AScreenCaret.ListIndex < 0) or (AScreenCaret.ListIndex >= FCount) or
(FList[AScreenCaret.ListIndex] <> AScreenCaret)
then begin
assert(False, 'TSynPluginMultiCaretVisualList.Remove: not on list');
exit;
end;
if AScreenCaret.ListIndex < FCount then begin
t := FList[FCount - 1];
FList[AScreenCaret.ListIndex] := t;
t.FListIndex := AScreenCaret.ListIndex;
end;
AScreenCaret.FListIndex := -1;
dec(FCount);
end;
procedure TSynPluginMultiCaretVisualList.Clear;
var
i: Integer;
begin
for i := 0 to FCount - 1 do
FList[i].Free;
FCount := 0;
SetLength(FList, EMPTY_LIST_LEN);
end;
function TSynPluginMultiCaretVisualList.Count: Integer;
begin
Result := FCount;
end;
{ TSynPluginMultiCaretList }
function TSynPluginMultiCaretList.FindEqOrNextCaretRawIdx(X, Y: Integer; LowIdx: integer;
HighIdx: integer): Integer;
var
l, h: Integer;
begin
if LowIdx < 0
then l := FLowIndex
else l := LowIdx;
if HighIdx < 0
then h := FHighIndex
else h := HighIdx;
if h < l then begin
Result := h;
exit;
end;
Result := (l + h) div 2;
while (h > l) do begin
if (FCarets[Result].y > y) or ((FCarets[Result].y = y) and (FCarets[Result].x >= x)) then
h := Result
else
l := Result + 1;
Result := (l + h) div 2;
end;
if (FCarets[Result].y < y) or ((FCarets[Result].y = y) and (FCarets[Result].x < x)) then
inc(Result);
end;
function TSynPluginMultiCaretList.GetCaret(Index: Integer): TPoint;
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.GetCaret: (Index>=FLowIndex) and (Index <= FHighIndex)');
Result.x := FCarets[Index].x;
Result.y := FCarets[Index].y;
end;
function TSynPluginMultiCaretList.GetCaretX(Index: Integer): Integer;
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.GetCaretX: (Index>=FLowIndex) and (Index <= FHighIndex)');
Result := FCarets[Index].x;
end;
function TSynPluginMultiCaretList.GetCaretY(Index: Integer): Integer;
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.GetCaretY: (Index>=FLowIndex) and (Index <= FHighIndex)');
Result := FCarets[Index].y;
end;
function TSynPluginMultiCaretList.GetFlags(Index: Integer): TCaretFlags;
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.GetFlags: (Index>=FLowIndex) and (Index <= FHighIndex)');
Result := FCarets[Index].Flags;
end;
function TSynPluginMultiCaretList.GetMainCaretIndex: Integer;
begin
if FMainCaretIndex >= FLowIndex then
Result := FMainCaretIndex - FLowIndex
else
Result := -1;
end;
function TSynPluginMultiCaretList.GetVisual(Index: Integer): TSynPluginMultiCaretVisual;
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.GetVisual: (Index>=FLowIndex) and (Index <= FHighIndex)');
Result := FCarets[Index].Visual;
end;
procedure TSynPluginMultiCaretList.SetCaret(Index: Integer; AValue: TPoint);
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.SetCaret: (Index>=FLowIndex) and (Index <= FHighIndex)');
if (FCarets[Index].x = AValue.x) and (FCarets[Index].y = AValue.y) then exit;
FCarets[Index].x := AValue.x;
FCarets[Index].y := AValue.y;
AdjustAfterChange(Index);
end;
procedure TSynPluginMultiCaretList.SetCaretX(Index: Integer; AValue: Integer);
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.SetCaretX: (Index>=FLowIndex) and (Index <= FHighIndex)');
if FCarets[Index].x = AValue then exit;
FCarets[Index].x := AValue;
AdjustAfterChange(Index);
end;
procedure TSynPluginMultiCaretList.SetCaretY(Index: Integer; AValue: Integer);
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.SetCaretY: (Index>=FLowIndex) and (Index <= FHighIndex)');
if FCarets[Index].y = AValue then exit;
FCarets[Index].y := AValue;
AdjustAfterChange(Index);
end;
procedure TSynPluginMultiCaretList.SetVisual(Index: Integer; AValue: TSynPluginMultiCaretVisual);
begin
Index := Index + FLowIndex;
assert((Index>=FLowIndex) and (Index <= FHighIndex), 'TSynPluginMultiCaretList.SetVisual: (Index>=FLowIndex) and (Index <= FHighIndex)');
if FCarets[Index].Visual <> nil then
FCarets[Index].Visual.MoveToUnUsed;
FCarets[Index].Visual := AValue;
if AValue <> nil then
AValue.MoveToUsed;
end;
function TSynPluginMultiCaretList.InternalRemoveCaretEx(RawIndex: Integer;
AlternativeRawIndex: Integer): Integer;
begin
assert((RawIndex>=FLowIndex) and (RawIndex <= FHighIndex), 'TSynPluginMultiCaretList.InternalRemoveCaretEx: (Index>=FLowIndex) and (Index <= FHighIndex)');
if (RawIndex = FMainCaretIndex) and (AlternativeRawIndex >= FLowIndex) then
Result := InternalRemoveCaret(AlternativeRawIndex)
else
Result := InternalRemoveCaret(RawIndex);
end;
function TSynPluginMultiCaretList.InternalRemoveCaret(RawIndex: Integer): integer;
begin
assert((RawIndex>=FLowIndex) and (RawIndex <= FHighIndex), 'TSynPluginMultiCaretList.InternalRemoveCaret: (RawIndex>=FLowIndex) and (RawIndex <= FHighIndex)');
Result := 0; // change to LowCaret .. RawIndex
if FCarets[RawIndex].Visual <> nil then
FCarets[RawIndex].Visual.MoveToUnUsed;
if RawIndex = FMainCaretIndex then
FMainCaretIndex := -1;
if RawIndex > (FHighIndex + FLowIndex) div 2 then begin
if (RawIndex < FHighIndex) then
Move(FCarets[RawIndex+1], FCarets[RawIndex], (FHighIndex - RawIndex) * SizeOf(FCarets[0]));
dec(FHighIndex);
if RawIndex < FMainCaretIndex then
dec(FMainCaretIndex);
end
else begin
if (RawIndex > FLowIndex) then
Move(FCarets[FLowIndex], FCarets[FLowIndex+1], (RawIndex - FLowIndex) * SizeOf(FCarets[0]));
inc(FLowIndex);
if RawIndex > FMainCaretIndex then
inc(FMainCaretIndex);
Result := 1;
end;
//debugln(SynMCaretDebug, ['TSynPluginMultiCaretList.InternalRemoveCaret ', RawIndex, ' , ', count]);
end;
procedure TSynPluginMultiCaretList.AdjustAfterChange(RawIndex: Integer);
var
NewIdx, y, x: Integer;
v: TCaretData;
begin
assert((RawIndex>=FLowIndex) and (RawIndex <= FHighIndex), 'TSynPluginMultiCaretList.AdjustAfterChange: (Index>=FLowIndex) and (Index <= FHighIndex)');
NewIdx := RawIndex;
y := FCarets[RawIndex].y;
x := FCarets[RawIndex].x;
if (RawIndex > FLowIndex) and
((y < FCarets[RawIndex-1].y) or ((y = FCarets[RawIndex-1].y) and (x <= FCarets[RawIndex-1].x)))
then begin
if (RawIndex-1 > FLowIndex) and
((y < FCarets[RawIndex-2].y) or ((y = FCarets[RawIndex-2].y) and (x < FCarets[RawIndex-2].x)))
then
NewIdx := FindEqOrNextCaretRawIdx(x,y, FLowIndex, RawIndex - 2)
else
NewIdx := RawIndex-1;
if (y = FCarets[NewIdx].y) and (x = FCarets[NewIdx].x) then begin
InternalRemoveCaretEx(RawIndex, NewIdx);
exit;
end;
v := FCarets[RawIndex];
debugln(SynMCaretDebug, ['TSynPluginMultiCaretList.AdjustAfterChange ', NewIdx, ' ',RawIndex]);
Move(FCarets[NewIdx], FCarets[NewIdx+1], (RawIndex-NewIdx) * SizeOf(FCarets[0]));
FCarets[NewIdx] := v;
end
else
if (RawIndex < FHighIndex) and
((y > FCarets[RawIndex+1].y) or ((y = FCarets[RawIndex+1].y) and (x >= FCarets[RawIndex+1].x)))
then begin
if (RawIndex+1 < FHighIndex) and
((y > FCarets[RawIndex+2].y) or ((y = FCarets[RawIndex+2].y) and (x > FCarets[RawIndex+2].x)))
then
NewIdx := FindEqOrNextCaretRawIdx(x,y, RawIndex + 2, FHighIndex)
else
NewIdx := RawIndex+1;
if (y = FCarets[NewIdx].y) and (x = FCarets[NewIdx].x) then begin
InternalRemoveCaretEx(RawIndex, NewIdx);
exit;
end;
v := FCarets[RawIndex];
debugln(SynMCaretDebug, ['TSynPluginMultiCaretList.AdjustAfterChange ', NewIdx, ' ',RawIndex]);
Move(FCarets[RawIndex+1], FCarets[RawIndex], (NewIdx-RawIndex) * SizeOf(FCarets[0]));
FCarets[NewIdx] := v;
end;
end;
constructor TSynPluginMultiCaretList.Create;
begin
FLowIndex := 0;
FHighIndex := -1;
FMainCaretIndex := -1;
end;
function TSynPluginMultiCaretList.AddCaret(X, Y: Integer; flags: TCaretFlags): Integer;
var
NewCarets: Array of TCaretData;
Len, AddLen, i, Middle: Integer;
begin
Result := FindEqOrNextCaretRawIdx(x, y);
if Result < FLowIndex then
Result := FLowIndex;
if ((Result <= FHighIndex) and (FCarets[Result].x = x) and (FCarets[Result].y = y)) and
not(cfAddDuplicate in flags)
then begin
FCarets[Result].Flags := flags - [cfMainCaret];
if cfMainCaret in flags then
FMainCaretIndex := Result;
Result := Result - FLowIndex;
exit;
end;
Len := length(FCarets) - 1;
Middle := (FLowIndex + FHighIndex) div 2;
if (FLowIndex > 0) and ((Result < Middle) or (FHighIndex = len))
then begin
// use space in front of list
if (Result > FHighIndex) and (FHighIndex = High(FCarets)) // moving all entries
then i := max(FLowIndex div 2 - 1, 0) // Make some room at the end of the list
else i := 0;
if Result > FLowIndex then
Move(FCarets[FLowIndex], FCarets[FLowIndex-1-i], (Result-FLowIndex) * SizeOf(FCarets[0]));
FLowIndex := FLowIndex - 1 - i;
FHighIndex := FHighIndex - i;
Result := Result - 1 - i;
if Result > FMainCaretIndex
then FMainCaretIndex := FMainCaretIndex - 1 - i
else FMainCaretIndex := FMainCaretIndex - i;
end
else
if FHighIndex < Len then begin
// use space at end of list
if (Result = FLowIndex) and (FLowIndex = 0) // moving all entries
then i := max((High(FCarets)-FHighIndex) div 2 - 1, 0) // Make some room at the start of the list
else i := 0;
if Result <= FHighIndex then
Move(FCarets[Result], FCarets[Result+1+i], (FHighIndex-Result+1) * SizeOf(FCarets[0]));
FHighIndex := FHighIndex + 1 + i;
FLowIndex := FLowIndex + i;
Result := Result + i;
if Result <= FMainCaretIndex
then FMainCaretIndex := FMainCaretIndex + 1 + i
else FMainCaretIndex := FMainCaretIndex + i;
end
else begin
// realloc all
AddLen := Max(32, Len div 8);
SetLength(NewCarets, Len + 2 * AddLen);
i := Result-FLowIndex;
if i > 0 then
Move(FCarets[FLowIndex], NewCarets[AddLen], (i) * SizeOf(FCarets[0]));
if Result <= FHighIndex then
Move(FCarets[Result], NewCarets[AddLen+i+1], (FHighIndex-Result+1) * SizeOf(FCarets[0]));
if Result <= FMainCaretIndex
then FMainCaretIndex := FMainCaretIndex - FLowIndex + AddLen + 1
else FMainCaretIndex := FMainCaretIndex - FLowIndex + AddLen;
FLowIndex := AddLen;
FHighIndex := AddLen + Len + 1;
Result := i + AddLen;
FCarets := NewCarets;
end;
FCarets[Result].x := x;
FCarets[Result].y := y;
FCarets[Result].Visual := nil;
FCarets[Result].Flags := flags - [cfMainCaret, cfAddDuplicate];
if cfMainCaret in flags then
FMainCaretIndex := Result;
Result := Result - FLowIndex;
end;
procedure TSynPluginMultiCaretList.RemoveCaret(Index: Integer);
begin
InternalRemoveCaret(Index+FLowIndex);
end;
procedure TSynPluginMultiCaretList.Clear(AFreeVisual: Boolean);
var
i: Integer;
begin
if AFreeVisual then
begin
for i := FLowIndex to FHighIndex do
if FCarets[i].Visual <> nil then begin
FCarets[i].Visual.UsedList.Remove(FCarets[i].Visual);
FCarets[i].Visual.Free;
end
end
else
for i := FLowIndex to FHighIndex do
if FCarets[i].Visual <> nil then
FCarets[i].Visual.MoveToUnUsed;
SetLength(FCarets, EMPTY_LIST_LEN);
FLowIndex := 4;
FHighIndex := 3;
FMainCaretIndex := -1;
end;
function TSynPluginMultiCaretList.Count: Integer;
begin
Result := FHighIndex - FLowIndex + 1;
end;
function TSynPluginMultiCaretList.FindCaretIdx(X, Y: Integer): Integer;
begin
Result := FindEqOrNextCaretRawIdx(x, y);
if (Result > FHighIndex) or (FCarets[Result].x <> x) or (FCarets[Result].y <> y)
then
Result := -1
else
Result := Result - FLowIndex;
end;
procedure TSynPluginMultiCaretList.AdjustAllAfterEdit(aLinePos, aBytePos, aCount,
aLineBrkCnt: Integer);
var
i, j, lowest: Integer;
begin
if Count = 0 then exit;
lowest := FindEqOrNextCaretRawIdx(aBytePos, aLinePos);
if aLineBrkCnt = 0 then begin
if aCount < 0 then begin
i := lowest;
while i <= FHighIndex do begin
if (FCarets[i].y = aLinePos) and (FCarets[i].x >= aBytePos) then
FCarets[i].x := Max(aBytePos, FCarets[i].x + aCount)
else
break;
inc(i);
end;
end
else begin // aCount >= 0
for i := lowest to FHighIndex do begin
if (FCarets[i].y = aLinePos) and (FCarets[i].x >= aBytePos) then
FCarets[i].x := FCarets[i].x + aCount
else
break;
end;
end;
end
else // aLineBrkCnt = 0
begin // aCount is always 0 (aBytePos:=max(1,aBytePos+aCount)) // aBytePos is the end of line
if aLineBrkCnt < 0 then begin
j := aLinePos+(-aLineBrkCnt);
i := lowest;
while i <= FHighIndex do begin
if (FCarets[i].y < j) then
FCarets[i].x := aBytePos;
if (FCarets[i].y = j) then
FCarets[i].x := FCarets[i].x - 1 + aBytePos
else
break;
FCarets[i].y := aLinePos;
inc(i);
end;
while i <= FHighIndex do begin
FCarets[i].y := FCarets[i].y + aLineBrkCnt;
inc(i);
end;
end
else begin // aLineBrkCnt >= 0
i := lowest;
while i <= FHighIndex do begin
if (FCarets[i].y = aLinePos) then
FCarets[i].x := FCarets[i].x + 1 - aBytePos
else
break;
FCarets[i].y := FCarets[i].y + aLineBrkCnt;
inc(i);
end;
while i <= FHighIndex do begin
FCarets[i].y := FCarets[i].y + aLineBrkCnt;
inc(i);
end;
end;
end;
end;
procedure TSynPluginMultiCaretList.FindAndRemoveMergedCarets;
var
i, i2: Integer;
c: TCaretData;
begin
i := FLowIndex + 1;
while i <= FHighIndex do begin
if (FCarets[i].y = FCarets[i-1].y) and (FCarets[i].x = FCarets[i-1].x) then begin
i := i + InternalRemoveCaretEx(i, i-1);
continue;
end;
if (FCarets[i].y < FCarets[i-1].y) or
((FCarets[i].y = FCarets[i-1].y) and (FCarets[i].x < FCarets[i-1].x))
then begin
// should not happen
debugln(SynMCaretDebug, ['TSynPluginMultiCaretList.FindAndRemoveMergedCarets BUBBLE SORTING']);
i2 := i;
c := FCarets[i2];
repeat
FCarets[i2] := FCarets[i2-1];
dec(i2);
until (i2 = FLowIndex) or (FCarets[i2].y > FCarets[i2-1].y) or
((FCarets[i2].y = FCarets[i2-1].y) and (FCarets[i2].x > FCarets[i2-1].x));
FCarets[i2] := c;
if FMainCaretIndex = i then
FMainCaretIndex := i2;
if (FMainCaretIndex < i) and (FMainCaretIndex >= i2) then
inc(FMainCaretIndex);
end;
inc(i);
end;
end;
{ TSynPluginMultiCaretBase }
procedure TSynPluginMultiCaretBase.DoBoundsChanged(Sender: TObject);
var
i: Integer;
ta: TLazSynTextArea;
begin
if FPaintLock > 0 then begin
include(FPaintLockFlags, plfBoundsChanged);
exit;
end;
ta := TextArea;
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].ClipRect := ta.Bounds;
UpdateCaretsPos;
end;
procedure TSynPluginMultiCaretBase.DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos,
aCount, aLineBrkCnt: Integer; aText: String);
begin
Carets.AdjustAllAfterEdit(aLinePos, aBytePos, aCount, aLineBrkCnt);
end;
procedure TSynPluginMultiCaretBase.SetColor(AValue: TColor);
var
i: Integer;
begin
if FColor = AValue then Exit;
FColor := AValue;
for i := 0 to FUsedList.Count - 1 do
TSynEditScreenCaretPainterInternal(FUsedList[i].Painter).Color := FColor;
end;
function TSynPluginMultiCaretBase.CreateVisual: TSynPluginMultiCaretVisual;
begin
Result := TSynPluginMultiCaretVisual.Create(Editor,
TSynEditScreenCaretPainterInternal,
FUsedList, FUnUsedList);
Result.PaintTimer:= ScreenCaret.PaintTimer;
end;
function TSynPluginMultiCaretBase.GetVisual: TSynPluginMultiCaretVisual;
var
ta: TLazSynTextArea;
i: TSynCaretType;
begin
if FUnUsedList.Count > 0 then
Result := FUnUsedList[FUnUsedList.Count-1]
else
Result := CreateVisual;
ta := TextArea;
Result.ClipRect := ta.Bounds;
Result.CharHeight := ta.LineHeight - Max(0, ta.ExtraLineSpacing);
Result.CharWidth := ta.CharWidth;
if Editor.InsertMode then
Result.DisplayType := Editor.InsertCaret
else
Result.DisplayType := Editor.OverwriteCaret;
for i := low(TSynCaretType) to high(TSynCaretType) do
Result.SetCaretTypeSize(i, FCustomPixelWidth[i], FCustomPixelHeight[i], FCustomOffsetX[i], FCustomOffsetY[i], FCustomFlags[i]);
TSynEditScreenCaretPainterInternal(Result.Painter).Color := FColor;
end;
procedure TSynPluginMultiCaretBase.DoTextSizeChanged(Sender: TObject);
var
i: Integer;
ta: TLazSynTextArea;
begin
if FPaintLock > 0 then begin
include(FPaintLockFlags, plfTextSizeChanged);
exit;
end;
ta := TextArea;
for i := 0 to FUsedList.Count - 1 do begin
FUsedList[i].CharHeight := ta.LineHeight - Max(0, ta.ExtraLineSpacing);
FUsedList[i].CharWidth := ta.CharWidth;
end;
UpdateCaretsPos;
end;
procedure TSynPluginMultiCaretBase.DoEditorPaintEvent(Sender: TObject;
EventType: TSynPaintEvent; const prcClip: TRect);
var
i: Integer;
begin
if EventType = peAfterPaint then
UpdateCaretsPos;
case EventType of
peBeforePaint:
begin
FInPaint := True;
FPaintClip := prcClip;
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].BeginPaint(prcClip);
for i := 0 to FUnUsedList.Count - 1 do
FUnUsedList[i].BeginPaint(prcClip);
end;
peAfterPaint:
begin
FInPaint := False;
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].FinishPaint(prcClip);
for i := 0 to FUnUsedList.Count - 1 do
FUnUsedList[i].FinishPaint(prcClip);
end;
end;
end;
procedure TSynPluginMultiCaretBase.DoEditorScrollEvent(Sender: TObject;
EventType: TSynScrollEvent; dx, dy: Integer; const prcScroll, prcClip: TRect);
var
i: Integer;
begin
case EventType of
peBeforeScroll:
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].BeginScroll(dx, dy, prcScroll, prcClip);
peAfterScroll:
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].FinishScroll(dx, dy, prcScroll, prcClip, True);
peAfterScrollFailed:
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].FinishScroll(dx, dy, prcScroll, prcClip, False);
end;
if EventType = peAfterScroll then
UpdateCaretsPos;
end;
procedure TSynPluginMultiCaretBase.DoEditorStatusChanged(Sender: TObject;
Changes: TSynStatusChanges);
var
i: Integer;
v: Boolean;
begin
if scFocus in Changes then begin
v := (Editor.Focused or (eoPersistentCaret in Editor.Options)) and not (eoNoCaret in Editor.Options);
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].Visible := v;
end;
if scInsertMode in Changes then
for i := 0 to FUsedList.Count - 1 do
if Editor.InsertMode
then FUsedList[i].DisplayType := Editor.InsertCaret
else FUsedList[i].DisplayType := Editor.OverwriteCaret;
if scOptions in Changes then begin
for i := 0 to FUsedList.Count - 1 do begin
if Editor.InsertMode
then FUsedList[i].DisplayType := Editor.InsertCaret
else FUsedList[i].DisplayType := Editor.OverwriteCaret;
UpdateCaretsPos;
end;
end;
end;
procedure TSynPluginMultiCaretBase.DoAfterDecPaintLock(Sender: TObject);
begin
if FPaintLock > 0 then
Dec(FPaintLock);
if FPaintLock > 0 then
exit;
Include(FPaintLockFlags, plfDeferUpdateCaretsPos);
if plfBoundsChanged in FPaintLockFlags then
DoBoundsChanged(nil);
if plfTextSizeChanged in FPaintLockFlags then
DoTextSizeChanged(nil);
Exclude(FPaintLockFlags, plfDeferUpdateCaretsPos);
if plfUpdateCaretsPos in FPaintLockFlags then
UpdateCaretsPos;
FPaintLockFlags := [];
ScreenCaret.UnLock; // unlock timer
end;
procedure TSynPluginMultiCaretBase.DoBeforeIncPaintLock(Sender: TObject);
begin
inc(FPaintLock);
if FPaintLock = 1 then
ScreenCaret.Lock; // lock timer
end;
function TSynPluginMultiCaretBase.GetTextArea: TLazSynTextArea;
begin
Result := TLazSynSurfaceManager(PaintArea).TextArea;
end;
function TSynPluginMultiCaretBase.AddCaret(X, Y: Integer; flags: TCaretFlags): Integer;
var
y1, y2: Integer;
begin
Result := Carets.AddCaret(x,y, flags);
if cfNoneVisual in flags then
exit;
if (eoNoCaret in Editor.Options) or
not((eoPersistentCaret in Editor.Options) or Editor.Focused)
then begin
Carets.Visual[Result] := nil;
exit;
end;
y1 := Editor.RowToScreenRow(y);
if (y1 < 0) or (y1 > Editor.LinesInWindow + 1) then
y := -1; // not visible
if y > 1 then
y2 := Editor.RowToScreenRow(y-1);
if (y > 0) and (y1 <> y2) or (y=1) then begin
if Carets.Visual[Result] = nil then
Carets.Visual[Result] := GetVisual;
x := Editor.LogicalToPhysicalPos(Point(x, y)).x;
Carets.Visual[Result].DisplayPos := TextArea.RowColumnToPixels(Point(x, y1));
Carets.Visual[Result].Visible := True;
end
else
Carets.Visual[Result] := nil;
end;
procedure TSynPluginMultiCaretBase.RemoveCaret(Index: Integer);
begin
Carets.RemoveCaret(Index);
end;
procedure TSynPluginMultiCaretBase.UpdateCaretsPos;
var
i, x, y, w: Integer;
y1, y2: Integer;
begin
if plfDeferUpdateCaretsPos in FPaintLockFlags then exit;
if FPaintLock > 0 then begin
include(FPaintLockFlags, plfUpdateCaretsPos);
exit;
end;
if (eoNoCaret in Editor.Options) or
not((eoPersistentCaret in Editor.Options) or Editor.Focused)
then begin
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].Visible := False;
exit;
end;
w := Editor.LinesInWindow + 1;
for i := 0 to CaretsCount - 1 do begin
if cfNoneVisual in Carets.Flags[i] then continue;
x := Carets.CaretX[i];
y := Carets.CaretY[i];
y1 := Editor.RowToScreenRow(y);
if (y1 < 0) or (y1 > w) then begin
Carets.Visual[i] := nil;
continue;
end;
if y > 1 then
y2 := Editor.RowToScreenRow(y-1);
if (y1 <> y2) or (y=1) then begin
if Carets.Visual[i] = nil then
Carets.Visual[i] := GetVisual;
x := Editor.LogicalToPhysicalPos(Point(x, y)).x;
Carets.Visual[i].DisplayPos := TextArea.RowColumnToPixels(Point(x, y1));
Carets.Visual[i].Visible := True;
end
else
Carets.Visual[i] := nil;
end;
end;
procedure TSynPluginMultiCaretBase.ClearCarets;
begin
Carets.Clear(True);
FUsedList.Clear;
FUnUsedList.Clear;
end;
function TSynPluginMultiCaretBase.CaretsCount: Integer;
begin
Result := Carets.Count;
end;
procedure TSynPluginMultiCaretBase.DoBufferChanged(Sender: TObject);
begin
TSynEditStrings(Sender).RemoveNotifyHandler(senrAfterDecPaintLock, @DoAfterDecPaintLock);
TSynEditStrings(Sender).RemoveNotifyHandler(senrBeforeIncPaintLock, @DoBeforeIncPaintLock);
TSynEditStrings(Sender).RemoveEditHandler(@DoLinesEdited);
ViewedTextBuffer.AddEditHandler(@DoLinesEdited);
ViewedTextBuffer.AddNotifyHandler(senrBeforeIncPaintLock, @DoBeforeIncPaintLock);
ViewedTextBuffer.AddNotifyHandler(senrAfterDecPaintLock, @DoAfterDecPaintLock);
end;
procedure TSynPluginMultiCaretBase.DoEditorRemoving(AValue: TCustomSynEdit);
begin
inherited DoEditorRemoving(AValue);
if Editor <> nil then begin
ClearCarets;
TextArea.RemoveBoundsChangeHandler(@DoBoundsChanged);
TextArea.RemoveTextSizeChangeHandler(@DoTextSizeChanged);
Editor.UnRegisterStatusChangedHandler(@DoEditorStatusChanged);
Editor.UnRegisterScrollEventHandler(@DoEditorScrollEvent);
Editor.UnRegisterPaintEventHandler(@DoEditorPaintEvent);
ViewedTextBuffer.RemoveNotifyHandler(senrAfterDecPaintLock, @DoAfterDecPaintLock);
ViewedTextBuffer.RemoveNotifyHandler(senrBeforeIncPaintLock, @DoBeforeIncPaintLock);
ViewedTextBuffer.RemoveEditHandler(@DoLinesEdited);
ViewedTextBuffer.RemoveGenericHandler(senrTextBufferChanged, TMethod(@DoBufferChanged));
end;
end;
procedure TSynPluginMultiCaretBase.DoEditorAdded(AValue: TCustomSynEdit);
begin
if Editor <> nil then begin
ViewedTextBuffer.AddGenericHandler(senrTextBufferChanged, TMethod(@DoBufferChanged));
ViewedTextBuffer.AddEditHandler(@DoLinesEdited);
ViewedTextBuffer.AddNotifyHandler(senrBeforeIncPaintLock, @DoBeforeIncPaintLock);
ViewedTextBuffer.AddNotifyHandler(senrAfterDecPaintLock, @DoAfterDecPaintLock);
Editor.RegisterPaintEventHandler(@DoEditorPaintEvent, [peBeforePaint, peAfterPaint]);
Editor.RegisterScrollEventHandler(@DoEditorScrollEvent, [peBeforeScroll, peAfterScroll, peAfterScrollFailed]);
Editor.RegisterStatusChangedHandler(@DoEditorStatusChanged, [scInsertMode, scFocus, scOptions]);
TextArea.AddTextSizeChangeHandler(@DoTextSizeChanged);
TextArea.AddBoundsChangeHandler(@DoBoundsChanged);
if ScreenCaret.Painter.ClassType = TSynEditScreenCaretPainterSystem then
ScreenCaret.ChangePainter(TSynEditScreenCaretPainterInternal);
end;
inherited DoEditorAdded(AValue);
end;
constructor TSynPluginMultiCaretBase.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FColor := clBlack;
FCarets := TSynPluginMultiCaretList.Create;
FUsedList := TSynPluginMultiCaretVisualList.Create;
FUnUsedList := TSynPluginMultiCaretVisualList.Create;
end;
destructor TSynPluginMultiCaretBase.Destroy;
begin
inherited Destroy;
FreeAndNil(FCarets);
FreeAndNil(FUsedList);
FreeAndNil(FUnUsedList);
end;
procedure TSynPluginMultiCaretBase.SetCaretTypeSize(AType: TSynCaretType; AWidth, AHeight,
AXOffs, AYOffs: Integer; AFlags: TSynCustomCaretSizeFlags);
var
i: Integer;
begin
FCustomPixelWidth[AType] := AWidth;
FCustomPixelHeight[AType] := AHeight;
FCustomOffsetX[AType] := AXOffs;
FCustomOffsetY[AType] := AYOffs;
FCustomFlags[AType] := AFlags;
for i := 0 to FUsedList.Count - 1 do
FUsedList[i].SetCaretTypeSize(AType, AWidth, AHeight, AXOffs, AYOffs, AFlags)
end;
{ TSynPluginMultiCaretMouseActions }
procedure TSynPluginMultiCaretMouseActions.ResetDefaults;
begin
Clear; // todo left button
AddCommand(emcPluginMultiCaretToggleCaret, False, mbXMiddle, ccAny, cdDown, [ssShift], [ssShift,ssCtrl,ssAlt]);
end;
{ TSynPluginMultiCaret }
procedure TSynPluginMultiCaret.DoEditorRemoving(AValue: TCustomSynEdit);
begin
if Editor <> nil then begin
CaretObj.RemoveChangeHandler(@DoCaretChanged);
SelectionObj.RemoveChangeHandler(@DoSelectionChanged);
Editor.UnregisterCommandHandler(@ProcessSynCommand);
//Editor.UnRegisterKeyTranslationHandler(@TranslateKey);
Editor.UnregisterMouseActionSearchHandler(@MaybeHandleMouseAction);
Editor.UnregisterMouseActionExecHandler(@DoHandleMouseAction);
end;
inherited DoEditorRemoving(AValue);
end;
procedure TSynPluginMultiCaret.DoEditorAdded(AValue: TCustomSynEdit);
begin
inherited DoEditorAdded(AValue);
if Editor <> nil then begin
Editor.RegisterMouseActionSearchHandler(@MaybeHandleMouseAction);
Editor.RegisterMouseActionExecHandler(@DoHandleMouseAction);
Editor.RegisterCommandHandler(@ProcessSynCommand, nil, [hcfInit, hcfFinish]);
//Editor.RegisterKeyTranslationHandler(@TranslateKey);
SelectionObj.AddChangeHandler(@DoSelectionChanged);
CaretObj.AddChangeHandler(@DoCaretChanged);
end;
end;
procedure TSynPluginMultiCaret.DoAfterDecPaintLock(Sender: TObject);
begin
if FPaintLock > 1 then begin
inherited DoAfterDecPaintLock(Sender);
exit;
end;
UpdateCaretsPos;
inherited DoAfterDecPaintLock(Sender);
FStateFlags := FStateFlags - [sfProcessingCmd, sfExtendingColumnSel];
end;
procedure TSynPluginMultiCaret.DoCaretChanged(Sender: TObject);
begin
if (FStateFlags * [sfProcessingCmd, sfExtendingColumnSel] <> []) then
exit;
ClearCarets;
end;
procedure TSynPluginMultiCaret.DoSelectionChanged(Sender: TObject);
var
i, x, y1, y2, y3: Integer;
c: TPoint;
begin
if (sfProcessingCmd in FStateFlags) then exit;
y1 := Editor.BlockBegin.y;
y2 := Editor.BlockEnd.y;
If not ((y1 <> y2) and (Editor.SelectionMode = smColumn)) then begin
ClearCarets;
exit;
end;
x := Editor.LogicalCaretXY.x;
y3 := Editor.CaretY;
i := CaretsCount;
while i > 0 do begin
dec(i);
c := Carets.Caret[i];
if (c.x <> x) or
(c.y < y1) or (c.y > y2) or (c.y = y3)
then
RemoveCaret(i);
end;
for i := y1 to y2 do begin
if i= y3 then continue;
AddCaret(x, i);
end;
end;
procedure TSynPluginMultiCaret.DoBeforeSetSelText(Sender: TObject; AMode: TSynSelectionMode;
ANewText: PChar);
begin
SelectionObj.RemoveBeforeSetSelTextHandler(@DoBeforeSetSelText);
SelectionObj.SelText := '';
if Carets.MainCaretIndex >= 0 then begin
Editor.LogicalCaretXY := Carets.Caret[Carets.MainCaretIndex];
end
else
assert(False, 'TSynPluginMultiCaret.ProcessSynCommand: Maincaret index not found');
end;
procedure TSynPluginMultiCaret.ProcessSynCommand(Sender: TObject; AfterProcessing: boolean;
var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer;
HandlerData: pointer);
procedure ExecCommandRepeated;
var
c, i: Integer;
begin
Handled := True;
Editor.BeginUpdate(True);
try
c := AddCaret(Editor.LogicalCaretXY.x, Editor.CaretY, [cfMainCaret, cfNoneVisual, cfAddDuplicate]);
// Execute Command at current caret pos
Include(FStateFlags, sfProcessingMain);
if Editor.SelAvail and (SelectionObj.ActiveSelectionMode = smColumn) then
SelectionObj.AddBeforeSetSelTextHandler(@DoBeforeSetSelText);
Editor.CommandProcessor(Command, AChar, data, [hcfInit, hcfFinish]);
SelectionObj.RemoveBeforeSetSelTextHandler(@DoBeforeSetSelText);
Exclude(FStateFlags, sfProcessingMain);
// if there was no change, then do not re-exec ?
// Repeat command
CaretObj.IncForcePastEOL;
for i := 0 to CaretsCount - 1 do begin
if i = c then continue;
Editor.LogicalCaretXY := Carets.Caret[i];
Editor.CommandProcessor(Command, AChar, nil, [hcfInit, hcfFinish]);
end;
CaretObj.DecForcePastEOL;
Carets.FindAndRemoveMergedCarets;
if Carets.MainCaretIndex >= 0 then begin
Editor.LogicalCaretXY := Carets.Caret[Carets.MainCaretIndex];
RemoveCaret(Carets.MainCaretIndex);
end
else
assert(False, 'TSynPluginMultiCaret.ProcessSynCommand: Maincaret index not found');
finally
Editor.EndUpdate;
end;
end;
procedure HandleNewColSelection;
begin
end;
var
ClipHelper: TSynClipboardStream;
begin
if (sfProcessingCmd in FStateFlags) or (CaretsCount = 0) then
exit;
if AfterProcessing then begin
if sfExtendingColumnSel in FStateFlags then
HandleNewColSelection;
UpdateCaretsPos;
exit;
end;
if Handled then exit;
(* use Editor.CommandProcessor(... SkipInit=[hcfInit, hcfFinish])
command is already initialized / prevent macro recorder from recording again.
*)
case Command of
// TODO: delete and smColumn -- only delete once
ecDeleteLastChar..ecDeleteLine,
ecLineBreak..ecChar:
begin
Include(FStateFlags, sfProcessingCmd);
if Editor.ReadOnly then exit;
ExecCommandRepeated;
end;
ecPaste:
begin
Include(FStateFlags, sfProcessingCmd);
if Editor.ReadOnly then exit;
if (SelectionObj.ActiveSelectionMode = smColumn) and
(SelectionObj.StartLinePos <> SelectionObj.EndLinePos)
then begin
ClipHelper := TSynClipboardStream.Create;
try
ClipHelper.ReadFromClipboard(Clipboard);
if ClipHelper.SelectionMode = smColumn then begin
Exclude(FStateFlags, sfProcessingCmd);
exit;
end;
finally
ClipHelper.Free;
end;
end;
ExecCommandRepeated;
end;
ecTab..ecShiftTab:
begin
Include(FStateFlags, sfProcessingCmd);
if Editor.ReadOnly then exit;
if (eoTabIndent in Editor.Options) and Editor.SelAvail and
(SelectionObj.ActiveSelectionMode = smColumn)
then begin
// no indent for column mode, when multicaret
Editor.BeginUpdate(True);
try
AddCaret(Editor.LogicalCaretXY.x, Editor.CaretY, [cfMainCaret, cfNoneVisual, cfAddDuplicate]);
Editor.SelText := '';
if Carets.MainCaretIndex >= 0 then begin
Editor.LogicalCaretXY := Carets.Caret[Carets.MainCaretIndex];
RemoveCaret(Carets.MainCaretIndex);
end
else
assert(False, 'TSynPluginMultiCaret.ProcessSynCommand: Maincaret index not found');
ExecCommandRepeated;
finally
Editor.EndUpdate;
end;
end
else
ExecCommandRepeated;
end;
ecSelColCmdRangeStart..ecSelColCmdRangeEnd:
begin
Include(FStateFlags, sfExtendingColumnSel);
end;
ecCopy,
ecScrollUp..ecScrollRight,
ecInsertMode..ecToggleMode,
ecNormalSelect, ecLineSelect,
ecSetMarker0..ecSetMarker9,
ecToggleMarker0..ecToggleMarker9,
EcFoldLevel1..EcFoldLevel9, EcFoldLevel0, EcFoldCurrent,
ecGotFocus, ecLostFocus
:
; // Ignore, if no changes occur
else
ClearCarets;
end;
end;
function TSynPluginMultiCaret.MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo;
HandleActionProc: TSynEditMouseActionHandler): Boolean;
begin
Result := HandleActionProc(FMouseActions, AnInfo);
end;
function TSynPluginMultiCaret.DoHandleMouseAction(AnAction: TSynEditMouseAction;
var AnInfo: TSynEditMouseActionInfo): Boolean;
var
i: Integer;
begin
Result := False;
if AnAction.Command = emcPluginMultiCaretToggleCaret then begin
i := Carets.FindCaretIdx(AnInfo.NewCaret.BytePos, AnInfo.NewCaret.LinePos);
if i >= 0 then
RemoveCaret(i)
else
AddCaret(AnInfo.NewCaret.BytePos, AnInfo.NewCaret.LinePos);
end;
end;
function TSynPluginMultiCaret.CreateVisual: TSynPluginMultiCaretVisual;
begin
Result := inherited CreateVisual;
if FInPaint then
Result.BeginPaint(FPaintClip);
end;
constructor TSynPluginMultiCaret.Create(AOwner: TComponent);
begin
FMouseActions := TSynPluginMultiCaretMouseActions.Create(Self);
FMouseActions.ResetDefaults;
inherited Create(AOwner);
end;
destructor TSynPluginMultiCaret.Destroy;
begin
inherited Destroy;
FreeAndNil(FMouseActions);
end;
{$IfDef SynMultiCaretDebug}
initialization
SynMCaretDebug := DebugLogger.FindOrRegisterLogGroup('SynMultiCaretDebug' {$IFDEF SynMultiCaretDebug} , True {$ENDIF} );
{$ENDIF}
end.