diff --git a/components/synedit/languages/syneditstrconst.ca.po b/components/synedit/languages/syneditstrconst.ca.po index 62ac861b61..65c02580d7 100644 --- a/components/synedit/languages/syneditstrconst.ca.po +++ b/components/synedit/languages/syneditstrconst.ca.po @@ -487,6 +487,10 @@ msgstr "" msgid "Quick Paste Selection" msgstr "" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "" diff --git a/components/synedit/languages/syneditstrconst.cs.po b/components/synedit/languages/syneditstrconst.cs.po index fd0e92c6d3..5f7fb57d4f 100644 --- a/components/synedit/languages/syneditstrconst.cs.po +++ b/components/synedit/languages/syneditstrconst.cs.po @@ -487,6 +487,10 @@ msgstr "Žádná akce" msgid "Quick Paste Selection" msgstr "Rychle vložit výběr" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Režim,Začít,Pokračovat" diff --git a/components/synedit/languages/syneditstrconst.de.po b/components/synedit/languages/syneditstrconst.de.po index 74063fa319..bbfbc1c684 100644 --- a/components/synedit/languages/syneditstrconst.de.po +++ b/components/synedit/languages/syneditstrconst.de.po @@ -490,6 +490,10 @@ msgstr "Keine Aktion" msgid "Quick Paste Selection" msgstr "Ausgewählten Text einfügen" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Modus,Beginn,Weiter" diff --git a/components/synedit/languages/syneditstrconst.es.po b/components/synedit/languages/syneditstrconst.es.po index ba035f4f5c..cd73d0cdcf 100644 --- a/components/synedit/languages/syneditstrconst.es.po +++ b/components/synedit/languages/syneditstrconst.es.po @@ -485,6 +485,10 @@ msgstr "Ninguna acción" msgid "Quick Paste Selection" msgstr "Pegado Rapido de Selección" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "" diff --git a/components/synedit/languages/syneditstrconst.fi.po b/components/synedit/languages/syneditstrconst.fi.po index 3d3f6e24ea..bf6727ef97 100644 --- a/components/synedit/languages/syneditstrconst.fi.po +++ b/components/synedit/languages/syneditstrconst.fi.po @@ -480,6 +480,10 @@ msgstr "Ei toimintoa" msgid "Quick Paste Selection" msgstr "Pika-liitä valinta" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Moodi,Ala,Jatka" diff --git a/components/synedit/languages/syneditstrconst.fr.po b/components/synedit/languages/syneditstrconst.fr.po index 67c7803ddb..b399d79bd7 100644 --- a/components/synedit/languages/syneditstrconst.fr.po +++ b/components/synedit/languages/syneditstrconst.fr.po @@ -486,6 +486,10 @@ msgstr "" msgid "Quick Paste Selection" msgstr "" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "" diff --git a/components/synedit/languages/syneditstrconst.he.po b/components/synedit/languages/syneditstrconst.he.po index fb99956147..1a056c4881 100644 --- a/components/synedit/languages/syneditstrconst.he.po +++ b/components/synedit/languages/syneditstrconst.he.po @@ -485,6 +485,10 @@ msgstr "ללא פעולה" msgid "Quick Paste Selection" msgstr "העתקה מהירה של הבחירה" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "מצב, התחלה, המשך" diff --git a/components/synedit/languages/syneditstrconst.hu.po b/components/synedit/languages/syneditstrconst.hu.po index 0fcad69abb..421ba8ac92 100644 --- a/components/synedit/languages/syneditstrconst.hu.po +++ b/components/synedit/languages/syneditstrconst.hu.po @@ -487,6 +487,10 @@ msgstr "Nincs művelet" msgid "Quick Paste Selection" msgstr "Kijelölés gyors beillesztése" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Mód,Kezdés,Folytatás" diff --git a/components/synedit/languages/syneditstrconst.id.po b/components/synedit/languages/syneditstrconst.id.po index 6777f8ca49..49bb92cdbd 100644 --- a/components/synedit/languages/syneditstrconst.id.po +++ b/components/synedit/languages/syneditstrconst.id.po @@ -490,6 +490,10 @@ msgstr "" msgid "Quick Paste Selection" msgstr "" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "" diff --git a/components/synedit/languages/syneditstrconst.it.po b/components/synedit/languages/syneditstrconst.it.po index ec74a85113..2c98591c6c 100644 --- a/components/synedit/languages/syneditstrconst.it.po +++ b/components/synedit/languages/syneditstrconst.it.po @@ -488,6 +488,10 @@ msgstr "Nessuna azione" msgid "Quick Paste Selection" msgstr "Selezione incolla veloce" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Modo,inizia,continua" diff --git a/components/synedit/languages/syneditstrconst.lt.po b/components/synedit/languages/syneditstrconst.lt.po index ac40ef1e40..5cb60b4be6 100644 --- a/components/synedit/languages/syneditstrconst.lt.po +++ b/components/synedit/languages/syneditstrconst.lt.po @@ -488,6 +488,10 @@ msgstr "Jokio veiksmo" msgid "Quick Paste Selection" msgstr "Spartusis atrankos įdėjimas" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Veiksena,Pradėti,Tęsti" diff --git a/components/synedit/languages/syneditstrconst.nl.po b/components/synedit/languages/syneditstrconst.nl.po index dd5d6f9997..86d10f81cf 100644 --- a/components/synedit/languages/syneditstrconst.nl.po +++ b/components/synedit/languages/syneditstrconst.nl.po @@ -487,6 +487,10 @@ msgstr "" msgid "Quick Paste Selection" msgstr "" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "" diff --git a/components/synedit/languages/syneditstrconst.pl.po b/components/synedit/languages/syneditstrconst.pl.po index 7ee5931759..8efb233a0b 100644 --- a/components/synedit/languages/syneditstrconst.pl.po +++ b/components/synedit/languages/syneditstrconst.pl.po @@ -491,6 +491,10 @@ msgstr "" msgid "Quick Paste Selection" msgstr "" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "" diff --git a/components/synedit/languages/syneditstrconst.po b/components/synedit/languages/syneditstrconst.po index 77df25c1ed..c2ccd0fefd 100644 --- a/components/synedit/languages/syneditstrconst.po +++ b/components/synedit/languages/syneditstrconst.po @@ -477,6 +477,10 @@ msgstr "" msgid "Quick Paste Selection" msgstr "" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "" diff --git a/components/synedit/languages/syneditstrconst.pt_BR.po b/components/synedit/languages/syneditstrconst.pt_BR.po index 5d4210aac4..5cfbe71169 100644 --- a/components/synedit/languages/syneditstrconst.pt_BR.po +++ b/components/synedit/languages/syneditstrconst.pt_BR.po @@ -486,6 +486,10 @@ msgstr "Sem Ação" msgid "Quick Paste Selection" msgstr "Colar Seleção Rapidamente" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Modo,Iniciar,Continuar" diff --git a/components/synedit/languages/syneditstrconst.ru.po b/components/synedit/languages/syneditstrconst.ru.po index b8772573ef..fbb907f3f0 100644 --- a/components/synedit/languages/syneditstrconst.ru.po +++ b/components/synedit/languages/syneditstrconst.ru.po @@ -486,6 +486,10 @@ msgstr "Нет действия" msgid "Quick Paste Selection" msgstr "Быстрая вставка выделенного" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Режим,Начать,Продолжить" diff --git a/components/synedit/languages/syneditstrconst.uk.po b/components/synedit/languages/syneditstrconst.uk.po index 93b8d47fa3..cc20eabec7 100644 --- a/components/synedit/languages/syneditstrconst.uk.po +++ b/components/synedit/languages/syneditstrconst.uk.po @@ -488,6 +488,10 @@ msgstr "Без Дії" msgid "Quick Paste Selection" msgstr "Швидкий Вибір Вставки" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "Режим,Почати,Продовжити" diff --git a/components/synedit/languages/syneditstrconst.zh_CN.po b/components/synedit/languages/syneditstrconst.zh_CN.po index 2467638bcf..574b47b31a 100644 --- a/components/synedit/languages/syneditstrconst.zh_CN.po +++ b/components/synedit/languages/syneditstrconst.zh_CN.po @@ -490,6 +490,10 @@ msgstr "" msgid "Quick Paste Selection" msgstr "" +#: syneditstrconst.syns_emcpluginmulticarettogglecaret +msgid "Toggle extra caret" +msgstr "" + #: syneditstrconst.syns_emcselection_opt msgid "Mode,Begin,Continue" msgstr "" diff --git a/components/synedit/syneditkeycmds.pp b/components/synedit/syneditkeycmds.pp index 2a6ae3fd1c..2276902e02 100644 --- a/components/synedit/syneditkeycmds.pp +++ b/components/synedit/syneditkeycmds.pp @@ -277,6 +277,7 @@ const ecPluginFirstCompletion = 19000; ecPluginFirstSyncro = 19010; ecPluginFirstTemplEdit = 19030; + ecPluginFirstMultiCaret = 19050; ecPluginFirst = 20000; @@ -1253,6 +1254,7 @@ initialization finalization ExtraIdentToIntFn := nil; ExtraIntToIdentFn := nil; + ExtraGetEditorCommandValues := nil; end. diff --git a/components/synedit/syneditmousecmds.pp b/components/synedit/syneditmousecmds.pp index 87dc02c4da..cdbd01916b 100644 --- a/components/synedit/syneditmousecmds.pp +++ b/components/synedit/syneditmousecmds.pp @@ -35,8 +35,8 @@ unit SynEditMouseCmds; interface uses - LazSynEditMouseCmdsTypes, Classes, Controls, SysUtils, SynEditStrConst, SynEditPointClasses, Dialogs, - LCLProc, Menus; + LazSynEditMouseCmdsTypes, Classes, Controls, SysUtils, SynEditStrConst, SynEditPointClasses, + SynEditKeyCmds, Dialogs, LCLProc, Menus; type @@ -306,6 +306,9 @@ const emcoWheelScrollPages = TSynEditorMouseCommandOpt(2); // Opt2 > 0 ==> percentage emcoWheelScrollPagesLessOne = TSynEditorMouseCommandOpt(3); // Opt2 > 0 ==> percentage +type + TMouseCmdNameAndOptProcs = function(emc: TSynEditorMouseCommand): String; + // Plugins don't know of other plugins, so they need to map the codes // Plugins all start at ecPluginFirst (overlapping) // If ask by SynEdit they add an offset @@ -316,8 +319,14 @@ function AllocatePluginMouseRange(Count: Integer; OffsetOnly: Boolean = False): function MouseCommandName(emc: TSynEditorMouseCommand): String; function MouseCommandConfigName(emc: TSynEditorMouseCommand): String; -function SynMouseCmdToIdent(SynMouseCmd: Longint; out Ident: String): Boolean; -function IdentToSynMouseCmd(const Ident: string; out SynMouseCmd: Longint): Boolean; +function SynMouseCmdToIdent(SynMouseCmd: Longint; var Ident: String): Boolean; +function IdentToSynMouseCmd(const Ident: string; var SynMouseCmd: Longint): Boolean; +procedure GetEditorMouseCommandValues(Proc: TGetStrProc); + +procedure RegisterMouseCmdIdentProcs(IdentToIntFn: TIdentToInt; IntToIdentFn: TIntToIdent); +procedure RegisterExtraGetEditorMouseCommandValues(AProc: TGetEditorCommandValuesProc); + +procedure RegisterMouseCmdNameAndOptProcs(ANamesProc: TMouseCmdNameAndOptProcs; AOptProc: TMouseCmdNameAndOptProcs = nil); const SYNEDIT_LINK_MODIFIER = {$IFDEF LCLcarbon}ssMeta{$ELSE}ssCtrl{$ENDIF}; @@ -326,28 +335,28 @@ implementation const SynMouseCommandNames: array [0..27] of TIdentMapEntry = ( - (Value: emcNone; Name: 'emcNone'), - (Value: emcStartSelections; Name: 'emcStartSelections'), + (Value: emcNone; Name: 'emcNone'), + (Value: emcStartSelections; Name: 'emcStartSelections'), (Value: emcStartColumnSelections; Name: 'emcStartColumnSelections'), - (Value: emcStartLineSelections; Name: 'emcStartLineSelections'), + (Value: emcStartLineSelections; Name: 'emcStartLineSelections'), - (Value: emcSelectWord; Name: 'emcSelectWord'), - (Value: emcSelectLine; Name: 'emcSelectLine'), - (Value: emcSelectPara; Name: 'emcSelectPara'), + (Value: emcSelectWord; Name: 'emcSelectWord'), + (Value: emcSelectLine; Name: 'emcSelectLine'), + (Value: emcSelectPara; Name: 'emcSelectPara'), - (Value: emcStartDragMove; Name: 'emcStartDragMove'), - (Value: emcPasteSelection; Name: 'emcPasteSelection'), - (Value: emcMouseLink; Name: 'emcMouseLink'), + (Value: emcStartDragMove; Name: 'emcStartDragMove'), + (Value: emcPasteSelection; Name: 'emcPasteSelection'), + (Value: emcMouseLink; Name: 'emcMouseLink'), - (Value: emcContextMenu; Name: 'emcContextMenu'), + (Value: emcContextMenu; Name: 'emcContextMenu'), - (Value: emcOnMainGutterClick; Name: 'emcOnMainGutterClick'), + (Value: emcOnMainGutterClick; Name: 'emcOnMainGutterClick'), - (Value: emcCodeFoldCollaps; Name: 'emcCodeFoldCollaps'), - (Value: emcCodeFoldExpand; Name: 'emcCodeFoldExpand'), - (Value: emcCodeFoldContextMenu; Name: 'emcCodeFoldContextMenu'), + (Value: emcCodeFoldCollaps; Name: 'emcCodeFoldCollaps'), + (Value: emcCodeFoldExpand; Name: 'emcCodeFoldExpand'), + (Value: emcCodeFoldContextMenu; Name: 'emcCodeFoldContextMenu'), - (Value: emcSynEditCommand; Name: 'emcSynEditCommand'), + (Value: emcSynEditCommand; Name: 'emcSynEditCommand'), (Value: emcWheelScrollDown; Name: 'emcWheelScrollDown'), (Value: emcWheelScrollUp; Name: 'emcWheelScrollUp'), @@ -356,16 +365,23 @@ const (Value: emcWheelHorizScrollDown; Name: 'emcWheelHorizScrollDown'), (Value: emcWheelHorizScrollUp; Name: 'emcWheelHorizScrollUp'), - (Value: emcWheelZoomOut; Name: 'emcWheelZoomOut'), - (Value: emcWheelZoomIn; Name: 'emcWheelZoomIn'), - (Value: emcWheelZoomNorm; Name: 'emcWheelZoomNorm'), + (Value: emcWheelZoomOut; Name: 'emcWheelZoomOut'), + (Value: emcWheelZoomIn; Name: 'emcWheelZoomIn'), + (Value: emcWheelZoomNorm; Name: 'emcWheelZoomNorm'), - (Value: emcStartSelectTokens; Name: 'emcStartSelectTokens'), - (Value: emcStartSelectWords; Name: 'emcStartSelectWords'), - (Value: emcStartSelectLines; Name: 'emcStartSelectLines') + (Value: emcStartSelectTokens; Name: 'emcStartSelectTokens'), + (Value: emcStartSelectWords; Name: 'emcStartSelectWords'), + (Value: emcStartSelectLines; Name: 'emcStartSelectLines') ); +var + ExtraIdentToIntFn: Array of TIdentToInt = nil; + ExtraIntToIdentFn: Array of TIntToIdent = nil; + ExtraGetEditorCommandValues: Array of TGetEditorCommandValuesProc = nil; + ExtraMouseCmdNameFn: Array of TMouseCmdNameAndOptProcs = nil; + ExtraMouseCmdOptFn: Array of TMouseCmdNameAndOptProcs = nil; + function AllocatePluginMouseRange(Count: Integer; OffsetOnly: Boolean = False): integer; const CurOffset : integer = 0; @@ -377,6 +393,8 @@ begin end; function MouseCommandName(emc: TSynEditorMouseCommand): String; +var + i: Integer; begin case emc of emcNone: Result := SYNS_emcNone; @@ -414,11 +432,20 @@ begin emcStartSelectWords: Result := SYNS_emcStartSelectWords; emcStartSelectLines: Result := SYNS_emcStartSelectLines; - else Result := '' + else begin + Result := ''; + i := 0; + while (i < length(ExtraMouseCmdNameFn)) and (Result = '') do begin + Result := ExtraMouseCmdNameFn[i](emc); + inc(i); + end; + end; end; end; function MouseCommandConfigName(emc: TSynEditorMouseCommand): String; +var + i: Integer; begin case emc of emcStartSelections, @@ -431,20 +458,90 @@ begin emcContextMenu: Result := SYNS_emcContextMenuCaretMove_opt; emcWheelScrollDown..emcWheelVertScrollUp: Result := SYNS_emcWheelScroll_opt; - else Result := '' + else begin + Result := ''; + i := 0; + while (i < length(ExtraMouseCmdOptFn)) and (Result = '') do begin + Result := ExtraMouseCmdOptFn[i](emc); + inc(i); + end; + end; end; end; -function SynMouseCmdToIdent(SynMouseCmd: Longint; out Ident: String): Boolean; +function SynMouseCmdToIdent(SynMouseCmd: Longint; var Ident: String): Boolean; +var + i: Integer; begin Ident := ''; Result := IntToIdent(SynMouseCmd, Ident, SynMouseCommandNames); + i := 0; + while (i < length(ExtraIntToIdentFn)) and (not Result) do begin + Result := ExtraIntToIdentFn[i](SynMouseCmd, Ident); + inc(i); + end; end; -function IdentToSynMouseCmd(const Ident: string; out SynMouseCmd: Longint): Boolean; +function IdentToSynMouseCmd(const Ident: string; var SynMouseCmd: Longint): Boolean; +var + i: Integer; begin SynMouseCmd := 0; Result := IdentToInt(Ident, SynMouseCmd, SynMouseCommandNames); + i := 0; + while (i < length(ExtraIdentToIntFn)) and (not Result) do begin + Result := ExtraIdentToIntFn[i](Ident, SynMouseCmd); + inc(i); + end; +end; + +procedure GetEditorMouseCommandValues(Proc: TGetStrProc); +var + i: Integer; +begin + for i := Low(SynMouseCommandNames) to High(SynMouseCommandNames) do + Proc(SynMouseCommandNames[I].Name); + i := 0; + while (i < length(ExtraGetEditorCommandValues)) do begin + ExtraGetEditorCommandValues[i](Proc); + inc(i); + end; +end; + +procedure RegisterMouseCmdIdentProcs(IdentToIntFn: TIdentToInt; IntToIdentFn: TIntToIdent); +var + i: Integer; +begin + i := length(ExtraIdentToIntFn); + SetLength(ExtraIdentToIntFn, i + 1); + ExtraIdentToIntFn[i] := IdentToIntFn; + i := length(ExtraIntToIdentFn); + SetLength(ExtraIntToIdentFn, i + 1); + ExtraIntToIdentFn[i] := IntToIdentFn; +end; + +procedure RegisterExtraGetEditorMouseCommandValues(AProc: TGetEditorCommandValuesProc); +var + i: Integer; +begin + i := length(ExtraGetEditorCommandValues); + SetLength(ExtraGetEditorCommandValues, i + 1); + ExtraGetEditorCommandValues[i] := AProc; +end; + +procedure RegisterMouseCmdNameAndOptProcs(ANamesProc: TMouseCmdNameAndOptProcs; + AOptProc: TMouseCmdNameAndOptProcs); +var + i: Integer; +begin + i := length(ExtraMouseCmdNameFn); + SetLength(ExtraMouseCmdNameFn, i + 1); + ExtraMouseCmdNameFn[i] := ANamesProc; + if AOptProc = nil then + exit; + i := length(ExtraMouseCmdOptFn); + SetLength(ExtraMouseCmdOptFn, i + 1); + ExtraMouseCmdOptFn[i] := AOptProc; end; { TSynEditMouseInternalActions } @@ -928,7 +1025,14 @@ begin end; initialization - RegisterIntegerConsts(TypeInfo(TSynEditorMouseCommand), TIdentToInt(@IdentToSynMouseCmd), TIntToIdent(@SynMouseCmdToIdent)); + RegisterIntegerConsts(TypeInfo(TSynEditorMouseCommand), @IdentToSynMouseCmd, @SynMouseCmdToIdent); + +finalization + ExtraIdentToIntFn := nil; + ExtraIntToIdentFn := nil; + ExtraGetEditorCommandValues := nil; + ExtraMouseCmdNameFn := nil; + ExtraMouseCmdOptFn := nil; end. diff --git a/components/synedit/syneditpointclasses.pas b/components/synedit/syneditpointclasses.pas index f974542137..3f00f63e7a 100644 --- a/components/synedit/syneditpointclasses.pas +++ b/components/synedit/syneditpointclasses.pas @@ -2836,6 +2836,10 @@ end; procedure TSynEditScreenCaretPainterInternal.BeginScroll(dx, dy: Integer; const rcScroll, rcClip: TRect); +{$IFDEF SynCaretNoHideInSroll} +var + rs: TIsInRectState; +{$ENDIF} begin assert(not((FInPaint or FInScroll)), 'TSynEditScreenCaretPainterInternal.BeginScroll: not((FInPaint or FInScroll))'); if (FState <> []) then @@ -2847,7 +2851,11 @@ begin inherited SetCaretPosEx(-1,-1); end; {$ELSE} - if ((IsInRect(rcClip) = irPartInside) or (IsInRect(rcScroll) = irPartInside)) and FIsDrawn then begin + rs := IsInRect(rcScroll); + if not( ((IsInRect(rcClip) = irOutside) and (rs = irOutside)) or + ((IsInRect(rcClip, Left+dx, Top+dy, Width, Height) = irInside) and (rs = irInside)) + ) + then begin HideCaret; inherited SetCaretPosEx(-1,-1); end; diff --git a/components/synedit/syneditstrconst.pp b/components/synedit/syneditstrconst.pp index 0898d8c95c..b51a5a6aec 100644 --- a/components/synedit/syneditstrconst.pp +++ b/components/synedit/syneditstrconst.pp @@ -434,6 +434,7 @@ resourcestring SYNS_emcContextMenuCaretMove_opt = '"Move caret, when selection exists", Never, "Click outside", Always'; SYNS_emcWheelScroll_opt = 'Speed,"System settings",Lines,Pages,"Pages (less one line)"'; + SYNS_emcPluginMultiCaretToggleCaret = 'Toggle extra caret'; implementation end. diff --git a/components/synedit/synpluginmulticaret.pp b/components/synedit/synpluginmulticaret.pp index 5814dec209..f46f863f2f 100644 --- a/components/synedit/synpluginmulticaret.pp +++ b/components/synedit/synpluginmulticaret.pp @@ -14,6 +14,7 @@ interface uses Classes, SysUtils, SynEdit, SynEditPointClasses, SynEditKeyCmds, SynEditTypes, LazSynTextArea, SynEditMiscProcs, LazSynEditText, SynEditMiscClasses, SynEditMouseCmds, + SynEditStrConst, {$IfDef SynMultiCaretDebug} LazLoggerBase, {$ELSE} LazLoggerDummy, {$ENDIF} LCLType, Controls, Graphics, Clipbrd; @@ -21,6 +22,11 @@ const emcPluginMultiCaretToggleCaret = emcPluginFirstMultiCaret; + //ecPluginMultiCaretSetCaret = ecPluginFirstMultiCaret + 0; + //ecPluginMultiCaretUnsetCaret = ecPluginFirstMultiCaret + 1; + //ecPluginMultiCaretToggleCaret = ecPluginFirstMultiCaret + 2; + //ecPluginMultiCaretClearAll = ecPluginFirstMultiCaret + 3; + type TSynPluginMultiCaretVisualList = class; @@ -177,18 +183,30 @@ type procedure ResetDefaults; override; end; - { TSynPluginMultiCaret } + { TSynPluginMultiCaretKeyStrokes } + + TSynPluginMultiCaretKeyStrokes = class(TSynEditKeyStrokes) + public + procedure ResetDefaults; override; + end; + + { TSynCustomPluginMultiCaret } TSynPluginMultiCaretStateFlag = ( sfProcessingCmd, sfProcessingMain, - sfExtendingColumnSel + sfExtendingColumnSel, sfSkipCaretsAtSelection ); TSynPluginMultiCaretStateFlags = set of TSynPluginMultiCaretStateFlag; - TSynPluginMultiCaret = class(TSynPluginMultiCaretBase) + TSynCustomPluginMultiCaret = class(TSynPluginMultiCaretBase) private + FEnableWithColumnSelection: Boolean; FStateFlags: TSynPluginMultiCaretStateFlags; FMouseActions: TSynPluginMultiCaretMouseActions; + FSelY1, FSElY2, FSelX: Integer; + + procedure RemoveCaretsInSelection; + procedure SetSkipCaretAtSel; protected procedure DoEditorRemoving(AValue: TCustomSynEdit); override; procedure DoEditorAdded(AValue: TCustomSynEdit); override; @@ -210,7 +228,15 @@ type public constructor Create(AOwner: TComponent); override; destructor Destroy; override; + function AddCaretAt(X, Y: Integer): Integer; property MouseActions: TSynPluginMultiCaretMouseActions read FMouseActions; + property EnableWithColumnSelection: Boolean read FEnableWithColumnSelection write FEnableWithColumnSelection default True; + end; + + TSynPluginMultiCaret = class(TSynCustomPluginMultiCaret) + published + property MouseActions; + property EnableWithColumnSelection; end; implementation @@ -223,6 +249,48 @@ var const EMPTY_LIST_LEN = 8; + SynMouseCommandNames: array [0..0] of TIdentMapEntry = ( + (Value: emcPluginMultiCaretToggleCaret; Name: 'emcPluginMultiCaretToggleCaret') + ); + +function SynMouseCmdToIdent(SynMouseCmd: Longint; var Ident: String): Boolean; +begin + Ident := ''; + Result := IntToIdent(SynMouseCmd, Ident, SynMouseCommandNames); +end; + +function IdentToSynMouseCmd(const Ident: string; var SynMouseCmd: Longint): Boolean; +begin + SynMouseCmd := 0; + Result := IdentToInt(Ident, SynMouseCmd, SynMouseCommandNames); +end; + +procedure GetEditorMouseCommandValues(Proc: TGetStrProc); +var + i: Integer; +begin + for i := Low(SynMouseCommandNames) to High(SynMouseCommandNames) do + Proc(SynMouseCommandNames[I].Name); +end; + +function MouseCommandName(emc: TSynEditorMouseCommand): String; +begin + case emc of + emcPluginMultiCaretToggleCaret: Result := SYNS_emcPluginMultiCaretToggleCaret; + else + Result := ''; + end; +end; + +function MouseCommandConfigName(emc: TSynEditorMouseCommand): String; +begin + case emc of + emcPluginMultiCaretToggleCaret: Result := ''; + else + Result := ''; + end; +end; + { TSynPluginMultiCaretVisual } constructor TSynPluginMultiCaretVisual.Create(AHandleOwner: TWinControl; @@ -1135,9 +1203,60 @@ begin AddCommand(emcPluginMultiCaretToggleCaret, False, mbXMiddle, ccAny, cdDown, [ssShift], [ssShift,ssCtrl,ssAlt]); end; -{ TSynPluginMultiCaret } +{ TSynPluginMultiCaretKeyStrokes } -procedure TSynPluginMultiCaret.DoEditorRemoving(AValue: TCustomSynEdit); +procedure TSynPluginMultiCaretKeyStrokes.ResetDefaults; +begin + inherited ResetDefaults; +end; + +{ TSynCustomPluginMultiCaret } + +procedure TSynCustomPluginMultiCaret.RemoveCaretsInSelection; +var + i, x, y: Integer; + bb, be: TPoint; + sm: TSynSelectionMode; +begin + bb := SelectionObj.FirstLineBytePos; + be := SelectionObj.LastLineBytePos; + sm := SelectionObj.ActiveSelectionMode; + if sm = smLine then begin + bb.x := 0; + be.x := MaxInt; + end; + if (sm = smColumn) and (bb.x > be.x) then begin + if bb.x = be.x then + exit; + i := bb.x; + bb.x := be.x; + be.x := i; + end; + + i := CaretsCount; + while i > 0 do begin + dec(i); + x := Carets.Caret[i].x; + y := Carets.Caret[i].y; + if (y < bb.y) or + (y > be.y) or + ( ((y = bb.y) or (sm = smColumn)) and (x <= bb.x) ) or + ( ((y = be.y) or (sm = smColumn)) and (x >= be.x) ) + then + Continue; + Carets.RemoveCaret(i); + end; +end; + +procedure TSynCustomPluginMultiCaret.SetSkipCaretAtSel; +begin + Include(FStateFlags, sfSkipCaretsAtSelection); + FSelY1 := SelectionObj.FirstLineBytePos.y; + FSElY2 := SelectionObj.LastLineBytePos.y; + FSelX := SelectionObj.FirstLineBytePos.x; +end; + +procedure TSynCustomPluginMultiCaret.DoEditorRemoving(AValue: TCustomSynEdit); begin if Editor <> nil then begin CaretObj.RemoveChangeHandler(@DoCaretChanged); @@ -1150,7 +1269,7 @@ begin inherited DoEditorRemoving(AValue); end; -procedure TSynPluginMultiCaret.DoEditorAdded(AValue: TCustomSynEdit); +procedure TSynCustomPluginMultiCaret.DoEditorAdded(AValue: TCustomSynEdit); begin inherited DoEditorAdded(AValue); if Editor <> nil then begin @@ -1163,7 +1282,7 @@ begin end; end; -procedure TSynPluginMultiCaret.DoAfterDecPaintLock(Sender: TObject); +procedure TSynCustomPluginMultiCaret.DoAfterDecPaintLock(Sender: TObject); begin if FPaintLock > 1 then begin inherited DoAfterDecPaintLock(Sender); @@ -1175,14 +1294,14 @@ begin FStateFlags := FStateFlags - [sfProcessingCmd, sfExtendingColumnSel]; end; -procedure TSynPluginMultiCaret.DoCaretChanged(Sender: TObject); +procedure TSynCustomPluginMultiCaret.DoCaretChanged(Sender: TObject); begin if (FStateFlags * [sfProcessingCmd, sfExtendingColumnSel] <> []) then exit; ClearCarets; end; -procedure TSynPluginMultiCaret.DoSelectionChanged(Sender: TObject); +procedure TSynCustomPluginMultiCaret.DoSelectionChanged(Sender: TObject); var i, x, y1, y2, y3: Integer; c: TPoint; @@ -1190,7 +1309,7 @@ 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 + If not ((y1 <> y2) and (Editor.SelectionMode = smColumn) and EnableWithColumnSelection) then begin ClearCarets; exit; end; @@ -1214,31 +1333,34 @@ begin end; -procedure TSynPluginMultiCaret.DoBeforeSetSelText(Sender: TObject; AMode: TSynSelectionMode; +procedure TSynCustomPluginMultiCaret.DoBeforeSetSelText(Sender: TObject; AMode: TSynSelectionMode; ANewText: PChar); begin SelectionObj.RemoveBeforeSetSelTextHandler(@DoBeforeSetSelText); + RemoveCaretsInSelection; SelectionObj.SelText := ''; if Carets.MainCaretIndex >= 0 then begin Editor.LogicalCaretXY := Carets.Caret[Carets.MainCaretIndex]; + FSelX := Carets.Caret[Carets.MainCaretIndex].x; end else - assert(False, 'TSynPluginMultiCaret.ProcessSynCommand: Maincaret index not found'); + assert(False, 'TSynCustomPluginMultiCaret.ProcessSynCommand: Maincaret index not found'); end; -procedure TSynPluginMultiCaret.ProcessSynCommand(Sender: TObject; AfterProcessing: boolean; +procedure TSynCustomPluginMultiCaret.ProcessSynCommand(Sender: TObject; AfterProcessing: boolean; var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; HandlerData: pointer); procedure ExecCommandRepeated; var - c, i: Integer; + c, i, y: Integer; + p: TPoint; begin Handled := True; Editor.BeginUpdate(True); try - c := AddCaret(Editor.LogicalCaretXY.x, Editor.CaretY, [cfMainCaret, cfNoneVisual, cfAddDuplicate]); + c := AddCaret(Editor.LogicalCaretXY.x, Editor.CaretY, [cfMainCaret, cfNoneVisual {, cfAddDuplicate}]); // Execute Command at current caret pos Include(FStateFlags, sfProcessingMain); @@ -1251,9 +1373,20 @@ procedure TSynPluginMultiCaret.ProcessSynCommand(Sender: TObject; AfterProcessin // Repeat command CaretObj.IncForcePastEOL; - for i := 0 to CaretsCount - 1 do begin + i := CaretsCount; + y := FSElY2; + while i > 0 do begin + dec(i); if i = c then continue; - Editor.LogicalCaretXY := Carets.Caret[i]; + p := Carets.Caret[i]; + if y > p.y then y := p.y; + if (sfSkipCaretsAtSelection in FStateFlags) and (y >= FSElY1) and + (y = p.y) and (FSelX = p.x) + then begin + dec(y); + continue; + end; + Editor.LogicalCaretXY := p; Editor.CommandProcessor(Command, AChar, nil, [hcfInit, hcfFinish]); end; CaretObj.DecForcePastEOL; @@ -1264,7 +1397,7 @@ procedure TSynPluginMultiCaret.ProcessSynCommand(Sender: TObject; AfterProcessin RemoveCaret(Carets.MainCaretIndex); end else - assert(False, 'TSynPluginMultiCaret.ProcessSynCommand: Maincaret index not found'); + assert(False, 'TSynCustomPluginMultiCaret.ProcessSynCommand: Maincaret index not found'); finally Editor.EndUpdate; end; @@ -1298,6 +1431,11 @@ begin ecLineBreak..ecChar: begin Include(FStateFlags, sfProcessingCmd); + if ((Command = ecDeleteChar) or (Command = ecDeleteLastChar)) and + Editor.SelAvail and (SelectionObj.ActiveSelectionMode = smColumn) and + not(eoPersistentBlock in Editor.Options2) + then + SetSkipCaretAtSel; if Editor.ReadOnly then exit; ExecCommandRepeated; end; @@ -1341,7 +1479,7 @@ begin RemoveCaret(Carets.MainCaretIndex); end else - assert(False, 'TSynPluginMultiCaret.ProcessSynCommand: Maincaret index not found'); + assert(False, 'TSynCustomPluginMultiCaret.ProcessSynCommand: Maincaret index not found'); ExecCommandRepeated; finally Editor.EndUpdate; @@ -1370,13 +1508,13 @@ begin end; -function TSynPluginMultiCaret.MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo; +function TSynCustomPluginMultiCaret.MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo; HandleActionProc: TSynEditMouseActionHandler): Boolean; begin Result := HandleActionProc(FMouseActions, AnInfo); end; -function TSynPluginMultiCaret.DoHandleMouseAction(AnAction: TSynEditMouseAction; +function TSynCustomPluginMultiCaret.DoHandleMouseAction(AnAction: TSynEditMouseAction; var AnInfo: TSynEditMouseActionInfo): Boolean; var i: Integer; @@ -1388,32 +1526,43 @@ begin if i >= 0 then RemoveCaret(i) else + if (AnInfo.NewCaret.BytePos <> CaretObj.BytePos) or (AnInfo.NewCaret.LinePos <> CaretObj.LinePos) then AddCaret(AnInfo.NewCaret.BytePos, AnInfo.NewCaret.LinePos); end; end; -function TSynPluginMultiCaret.CreateVisual: TSynPluginMultiCaretVisual; +function TSynCustomPluginMultiCaret.CreateVisual: TSynPluginMultiCaretVisual; begin Result := inherited CreateVisual; if FInPaint then Result.BeginPaint(FPaintClip); end; -constructor TSynPluginMultiCaret.Create(AOwner: TComponent); +constructor TSynCustomPluginMultiCaret.Create(AOwner: TComponent); begin FMouseActions := TSynPluginMultiCaretMouseActions.Create(Self); FMouseActions.ResetDefaults; + FEnableWithColumnSelection := True; inherited Create(AOwner); end; -destructor TSynPluginMultiCaret.Destroy; +destructor TSynCustomPluginMultiCaret.Destroy; begin inherited Destroy; FreeAndNil(FMouseActions); end; -{$IfDef SynMultiCaretDebug} +function TSynCustomPluginMultiCaret.AddCaretAt(X, Y: Integer): Integer; +begin + AddCaret(x, y); +end; + initialization + RegisterMouseCmdIdentProcs(@IdentToSynMouseCmd, @SynMouseCmdToIdent); + RegisterExtraGetEditorMouseCommandValues(@GetEditorMouseCommandValues); + RegisterMouseCmdNameAndOptProcs(@MouseCommandName, @MouseCommandConfigName); + +{$IfDef SynMultiCaretDebug} SynMCaretDebug := DebugLogger.FindOrRegisterLogGroup('SynMultiCaretDebug' {$IFDEF SynMultiCaretDebug} , True {$ENDIF} ); {$ENDIF} end. diff --git a/components/synedit/test/testmulticaret.pas b/components/synedit/test/testmulticaret.pas index f0ac7875e2..faf2d346e5 100644 --- a/components/synedit/test/testmulticaret.pas +++ b/components/synedit/test/testmulticaret.pas @@ -22,10 +22,13 @@ type FMultiCaret: TSynPluginMultiCaretTest; public procedure ReCreateEdit; reintroduce; + procedure ReCreateEdit(ALines: TStringArray; AOpt: TSynEditorOptions2 = []; + AOptRemove: TSynEditorOptions2 = []); procedure RunCmdSeq(cmds: Array of TSynEditorCommand; chars: array of String); published procedure CaretList; procedure Edit; + procedure Delete; procedure ReplaceColSel; procedure TabKey; procedure Paste; @@ -44,6 +47,14 @@ begin SynEdit.TabWidth := 4; end; +procedure TTestMultiCaret.ReCreateEdit(ALines: TStringArray; AOpt: TSynEditorOptions2; + AOptRemove: TSynEditorOptions2); +begin + ReCreateEdit; + SynEdit.Options2 := SynEdit.Options2 - AOptRemove + AOpt; + SetLines(ALines); +end; + procedure TTestMultiCaret.RunCmdSeq(cmds: array of TSynEditorCommand; chars: array of String); var i, j: Integer; @@ -231,6 +242,182 @@ begin end; +procedure TTestMultiCaret.Delete; + function TestText1: TStringArray; + begin + SetLength(Result, 8); + Result[0] := '1aA'; + Result[1] := '2bB'; + Result[2] := '3cC'; + Result[3] := '4dD'; + Result[4] := '5eE'; + Result[5] := '6fF'; + Result[6] := '7gG'; + Result[7] := ''; + end; + function TestText1Del: TStringArray; + begin + SetLength(Result, 8); + Result[0] := '1aA'; + Result[1] := '2B'; + Result[2] := '3C'; + Result[3] := '4D'; + Result[4] := '5E'; + Result[5] := '6F'; + Result[6] := '7gG'; + Result[7] := ''; + end; + function TestText1Del2: TStringArray; + begin + SetLength(Result, 8); + Result[0] := '1aA'; + Result[1] := 'B'; + Result[2] := 'C'; + Result[3] := 'D'; + Result[4] := 'E'; + Result[5] := 'F'; + Result[6] := '7gG'; + Result[7] := ''; + end; + function TestText1DelExtra: TStringArray; + begin + SetLength(Result, 8); + Result[0] := '1aA'; + Result[1] := '2B'; + Result[2] := '3'; + Result[3] := 'D'; + Result[4] := '5E'; + Result[5] := '6F'; + Result[6] := '7gG'; + Result[7] := ''; + end; +var + Opt, OptRemove: TSynEditorOptions2; +begin + PushBaseName('NO eoPersistentBlock, HAS eoOverwriteBlock'); + Opt := [eoOverwriteBlock]; + OptRemove := [eoPersistentBlock]; + + PushBaseName('ecDeleteLastChar'); + + PushBaseName('ecDeleteLastChar - zero width sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(3,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown], []); + RunCmdSeq([ecDeleteLastChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + + PopPushBaseName('ecDeleteLastChar - ONE width backward sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(3,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelLeft], []); + RunCmdSeq([ecDeleteLastChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + + PopPushBaseName('ecDeleteLastChar - ONE width sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(2,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelRight], []); + RunCmdSeq([ecDeleteLastChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + + PopPushBaseName('ecDeleteLastChar - Two width sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(1,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelRight, ecColSelRight], []); + RunCmdSeq([ecDeleteLastChar], []); + TestIsFullText('', TestText1Del2); + TestIsCaret('', 1, 6); + + PopPushBaseName('ecDeleteLastChar - ONE width sel / extra caret'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(2,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelRight], []); + FMultiCaret.AddCaretAt(4,3); + FMultiCaret.AddCaretAt(2,4); + RunCmdSeq([ecDeleteLastChar], []); + TestIsFullText('', TestText1DelExtra); + TestIsCaret('', 2, 6); + + PopPushBaseName('ecDeleteChar'); + + PushBaseName('ecDeleteChar - zero width sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(2,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown], []); + RunCmdSeq([ecDeleteChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + + PopPushBaseName('ecDeleteChar - ONE width backward sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(3,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelLeft], []); + RunCmdSeq([ecDeleteChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + + PopPushBaseName('ecDeleteChar - ONE width sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(2,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelRight], []); + RunCmdSeq([ecDeleteChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + + PopPushBaseName('ecDeleteChar - Two width sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(1,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelRight, ecColSelRight], []); + RunCmdSeq([ecDeleteChar], []); + TestIsFullText('', TestText1Del2); + TestIsCaret('', 1, 6); + + PopBaseName; + PopBaseName; + + PopPushBaseName('NO eoPersistentBlock, NO eoOverwriteBlock'); + Opt := []; + OptRemove := [eoOverwriteBlock, eoPersistentBlock]; + + PushBaseName('ecDeleteLastChar'); + PopPushBaseName('ecDeleteLastChar - Two width sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(1,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelRight, ecColSelRight], []); + RunCmdSeq([ecDeleteLastChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + + PopPushBaseName('ecDeleteChar'); + PopPushBaseName('ecDeleteChar - Two width backward sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(4,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelLeft, ecColSelLeft], []); + RunCmdSeq([ecDeleteChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + PopBaseName; + PopBaseName; + + PopPushBaseName('NO eoPersistentBlock, NO eoOverwriteBlock'); + Opt := [eoPersistentBlock]; + OptRemove := [eoOverwriteBlock]; + + PopPushBaseName('ecDeleteLastChar - Two width sel'); + ReCreateEdit(TestText1, Opt, OptRemove); + SetCaret(1,2); + RunCmdSeq([ecColSelDown, ecColSelDown, ecColSelDown, ecColSelDown, ecColSelRight, ecColSelRight], []); + RunCmdSeq([ecDeleteLastChar], []); + TestIsFullText('', TestText1Del); + TestIsCaret('', 2, 6); + + PopBaseName; +end; + procedure TTestMultiCaret.ReplaceColSel; function TestText1: TStringArray; begin diff --git a/ide/editoroptions.pp b/ide/editoroptions.pp index ebb0ef535c..a62a20e1ed 100644 --- a/ide/editoroptions.pp +++ b/ide/editoroptions.pp @@ -59,7 +59,7 @@ uses SynHighlighterPas, SynHighlighterPerl, SynHighlighterPHP, SynHighlighterSQL, SynHighlighterPython, SynHighlighterUNIXShellScript, SynHighlighterXML, SynHighlighterJScript, SynHighlighterDiff, SynHighlighterBat, SynHighlighterIni, - SynHighlighterPo, + SynHighlighterPo, SynPluginMultiCaret, // codetools LinkScanner, CodeToolManager, // IDEIntf @@ -736,6 +736,8 @@ type mbaContextMenuDebug, mbaContextMenuTab, + mbaMultiCaretToggle, + // Old values, needed to load old config moTCLNone, moTMIgnore, moTMPaste, @@ -743,7 +745,7 @@ type moTCLJumpOrBlock ); - TMouseOptButtonAction = mbaNone..mbaContextMenuTab; + TMouseOptButtonAction = mbaNone..mbaMultiCaretToggle; const MouseOptButtonActionOld: Array [moTCLNone..moTCLJumpOrBlock] of TMouseOptButtonActionOld = ( @@ -913,7 +915,7 @@ type property TextAltLeftClick: TMouseOptButtonAction read FTextAltLeftClick write FTextAltLeftClick default mbaSelectColumn; property TextShiftCtrlLeftClick: TMouseOptButtonAction read FTextShiftCtrlLeftClick write FTextShiftCtrlLeftClick - default mbaNone; // continue selection + default mbaMultiCaretToggle; // continue selection property TextShiftAltLeftClick: TMouseOptButtonAction read FTextShiftAltLeftClick write FTextShiftAltLeftClick default mbaNone; // continue selection property TextAltCtrlLeftClick: TMouseOptButtonAction read FTextAltCtrlLeftClick write FTextAltCtrlLeftClick @@ -1312,6 +1314,7 @@ type fUndoLimit: Integer; fTabWidth: Integer; FBracketHighlightStyle: TSynEditBracketHighlightStyle; + FMultiCaretOnColumnSelect: Boolean; // Display options fVisibleRightMargin: Boolean; @@ -1586,6 +1589,8 @@ type property ReverseFoldPopUpOrder: Boolean read FReverseFoldPopUpOrder write FReverseFoldPopUpOrder default True; property UseTabHistory: Boolean read fUseTabHistory write fUseTabHistory; + property MultiCaretOnColumnSelect: Boolean + read FMultiCaretOnColumnSelect write FMultiCaretOnColumnSelect default True; // Highlighter Pas property PasExtendedKeywordsMode: Boolean @@ -3260,7 +3265,7 @@ begin FTextAltCtrlLeftClick := mbaNone; FTextShiftLeftClick := mbaNone; FTextShiftAltLeftClick := mbaNone; - FTextShiftCtrlLeftClick := mbaNone; + FTextShiftCtrlLeftClick := mbaMultiCaretToggle; FTextShiftAltCtrlLeftClick := mbaNone; // middle FTextMiddleClick := mbaPaste; @@ -3431,6 +3436,8 @@ procedure TEditorMouseOptions.ResetTextToDefault; AddCommand(emcContextMenu, True, AButton, AClickCount, ADir, AShift, AShiftMask, emcoSelectionCaretMoveOutside, 0, 1); mbaContextMenuTab: AddCommand(emcContextMenu, True, AButton, AClickCount, ADir, AShift, AShiftMask, emcoSelectionCaretMoveOutside, 0, 2); + mbaMultiCaretToggle: + AddCommand(emcPluginMultiCaretToggleCaret, True, AButton, AClickCount, ADir, AShift, AShiftMask, 0, 0, 0); end; end; end; @@ -3505,8 +3512,8 @@ begin if FTextShiftCtrlLeftClick = mbaNone then SelKey := [ssShift] else SelKey := []; - AddBtnClick(FTextCtrlLeftClick, mbXLeft, [SYNEDIT_LINK_MODIFIER], ModKeys, False, SelKey); - AddBtnClick(FTextShiftCtrlLeftClick, mbXLeft, [ssShift, SYNEDIT_LINK_MODIFIER], ModKeys, False, SelKey); + AddBtnClick(FTextCtrlLeftClick, mbXLeft, [SYNEDIT_LINK_MODIFIER], ModKeys, False, SelKey); + AddBtnClick(FTextShiftCtrlLeftClick, mbXLeft, [ssShift, SYNEDIT_LINK_MODIFIER], ModKeys, False, SelKey); if FTextShiftAltLeftClick = mbaNone then SelKey := [ssShift] @@ -4387,6 +4394,7 @@ begin FGutterSeparatorIndex := 3; fSynEditOptions := SynEditDefaultOptions; fSynEditOptions2 := SynEditDefaultOptions2; + FMultiCaretOnColumnSelect := True; // Display options fEditorFont := SynDefaultFontName; @@ -5539,6 +5547,10 @@ begin end; end; + {$IFnDEF WithoutSynMultiCaret} + if ASynEdit is TIDESynEditor then + TIDESynEditor(ASynEdit).MultiCaret.EnableWithColumnSelection := MultiCaretOnColumnSelect; + {$ENDIF} // Display options ASynEdit.Gutter.Visible := fVisibleGutter; diff --git a/ide/frames/editor_general_options.lfm b/ide/frames/editor_general_options.lfm index a36689f3f2..be034ce91d 100644 --- a/ide/frames/editor_general_options.lfm +++ b/ide/frames/editor_general_options.lfm @@ -200,7 +200,7 @@ object EditorGeneralOptionsFrame: TEditorGeneralOptionsFrame AnchorSideTop.Side = asrBottom Left = 6 Height = 19 - Top = 254 + Top = 273 Width = 152 BorderSpacing.Left = 6 BorderSpacing.Top = 6 @@ -214,7 +214,7 @@ object EditorGeneralOptionsFrame: TEditorGeneralOptionsFrame AnchorSideTop.Side = asrBottom Left = 218 Height = 19 - Top = 254 + Top = 273 Width = 152 BorderSpacing.Top = 6 Caption = 'OverwriteBlockCheckBox' @@ -251,7 +251,6 @@ object EditorGeneralOptionsFrame: TEditorGeneralOptionsFrame Top = 0 Width = 434 Caption = 'UndoGroupDivider' - Autosize = True Anchors = [akTop, akLeft, akRight] Font.Style = [fsBold] ParentFont = False @@ -267,7 +266,6 @@ object EditorGeneralOptionsFrame: TEditorGeneralOptionsFrame Top = 65 Width = 434 Caption = 'ScrollGroupDivider' - Autosize = True Anchors = [akTop, akLeft, akRight] BorderSpacing.Top = 6 Font.Style = [fsBold] @@ -284,7 +282,6 @@ object EditorGeneralOptionsFrame: TEditorGeneralOptionsFrame Top = 130 Width = 434 Caption = 'CaretGroupDivider' - Autosize = True Anchors = [akTop, akLeft, akRight] BorderSpacing.Top = 6 Font.Style = [fsBold] @@ -292,16 +289,15 @@ object EditorGeneralOptionsFrame: TEditorGeneralOptionsFrame end object BlockGroupDivider: TDividerBevel AnchorSideLeft.Control = Owner - AnchorSideTop.Control = ScrollPastEndLineCheckBox + AnchorSideTop.Control = MultiCaretOnColumnSelection AnchorSideTop.Side = asrBottom AnchorSideRight.Control = Owner AnchorSideRight.Side = asrBottom Left = 0 Height = 15 - Top = 233 + Top = 252 Width = 434 Caption = 'BlockGroupDivider' - Autosize = True Anchors = [akTop, akLeft, akRight] BorderSpacing.Top = 6 Font.Style = [fsBold] @@ -318,4 +314,16 @@ object EditorGeneralOptionsFrame: TEditorGeneralOptionsFrame Caption = 'Scroll Hint' TabOrder = 16 end + object MultiCaretOnColumnSelection: TCheckBox + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = ScrollPastEndLineCheckBox + AnchorSideTop.Side = asrBottom + Left = 6 + Height = 19 + Top = 227 + Width = 183 + BorderSpacing.Left = 6 + Caption = 'MultiCaretOnColumnSelection' + TabOrder = 17 + end end diff --git a/ide/frames/editor_general_options.pas b/ide/frames/editor_general_options.pas index 885d9fb782..20fd408ec7 100644 --- a/ide/frames/editor_general_options.pas +++ b/ide/frames/editor_general_options.pas @@ -34,6 +34,7 @@ type { TEditorGeneralOptionsFrame } TEditorGeneralOptionsFrame = class(TAbstractIDEOptionsEditor) + MultiCaretOnColumnSelection: TCheckBox; CursorSkipsTabCheckBox: TCheckBox; CaretGroupDivider: TDividerBevel; BlockGroupDivider: TDividerBevel; @@ -138,6 +139,7 @@ begin CursorSkipsTabCheckBox.Caption := dlgCursorSkipsTab; HomeKeyJumpsToNearestStartCheckBox.Caption := dlgHomeKeyJumpsToNearestStart; EndKeyJumpsToNearestStartCheckBox.Caption := dlgEndKeyJumpsToNearestStart; + MultiCaretOnColumnSelection.Caption := dlgMultiCaretOnColumnSelection; // Block BlockGroupDivider.Caption := dlgBlockGroupOptions; @@ -171,6 +173,7 @@ begin CursorSkipsTabCheckBox.Checked := eoCaretSkipTab in SynEditOptions2; HomeKeyJumpsToNearestStartCheckBox.Checked := eoEnhanceHomeKey in SynEditOptions; EndKeyJumpsToNearestStartCheckBox.Checked := eoEnhanceEndKey in SynEditOptions2; + MultiCaretOnColumnSelection.Checked := MultiCaretOnColumnSelect; // block PersistentBlockCheckBox.Checked := eoPersistentBlock in SynEditOptions2; @@ -230,6 +233,7 @@ begin UpdateOptionFromBool(CursorSkipsTabCheckBox.Checked, eoCaretSkipTab); UpdateOptionFromBool(HomeKeyJumpsToNearestStartCheckBox.Checked, eoEnhanceHomeKey); UpdateOptionFromBool(EndKeyJumpsToNearestStartCheckBox.Checked, eoEnhanceEndKey); + MultiCaretOnColumnSelect := MultiCaretOnColumnSelection.Checked; // block UpdateOptionFromBool(PersistentBlockCheckBox.Checked, eoPersistentBlock); diff --git a/ide/frames/editor_mouseaction_options.pas b/ide/frames/editor_mouseaction_options.pas index 7f72249a4c..9fb3b36495 100644 --- a/ide/frames/editor_mouseaction_options.pas +++ b/ide/frames/editor_mouseaction_options.pas @@ -577,6 +577,7 @@ procedure TEditorMouseOptionsFrame.Setup(ADialog: TAbstractOptionsEditorDialog); ACombo.Items.Add(dlfMouseSimpleButtonContextMenu); // mbaContextMenu ACombo.Items.Add(dlfMouseSimpleButtonContextMenuDbg); // mbaContextMenuDebug; ACombo.Items.Add(dlfMouseSimpleButtonContextMenuTab); // mbaContextMenuTab; + ACombo.Items.Add(dlfMouseSimpleButtonMultiCaretToggle); // mbaMultiCaretToggle; end; procedure SetupWheelCombo(ACombo: TComboBox); diff --git a/ide/lazarusidestrconsts.pas b/ide/lazarusidestrconsts.pas index 88892075fd..395f24be84 100644 --- a/ide/lazarusidestrconsts.pas +++ b/ide/lazarusidestrconsts.pas @@ -1604,6 +1604,7 @@ resourcestring dlfMouseSimpleButtonContextMenu = 'Context Menu'; dlfMouseSimpleButtonContextMenuDbg = 'Context Menu (debug)'; dlfMouseSimpleButtonContextMenuTab = 'Context Menu (tab)'; + dlfMouseSimpleButtonMultiCaretToggle = 'Toggle extra Caret'; dlfMouseSimpleWheelNothing = 'Nothing/Default'; dlfMouseSimpleWheelSrollDef = 'Scroll (System speed)'; @@ -1687,6 +1688,7 @@ resourcestring dlgCopyWordAtCursorOnCopyNone = 'Copy word on copy none'; dlgHomeKeyJumpsToNearestStart = 'Home key jumps to nearest start'; dlgEndKeyJumpsToNearestStart = 'End key jumps to nearest end'; + dlgMultiCaretOnColumnSelection = 'Enable multi caret for column selection'; dlgColorLink = '(Edit Color)'; dlgKeyLink = '(Edit Key)'; dlgBracketHighlight = 'Bracket highlight'; diff --git a/ide/mouseactiondialog.pas b/ide/mouseactiondialog.pas index 161ed738b1..89f1d7132c 100644 --- a/ide/mouseactiondialog.pas +++ b/ide/mouseactiondialog.pas @@ -48,6 +48,7 @@ type MousePos: TPoint; var Handled: Boolean); private FKeyMap: TKeyCommandRelationList; + procedure AddMouseCmd(const S: string); public { public declarations } Procedure ResetInputs; @@ -80,10 +81,20 @@ end; { MouseaActionDialog } -procedure TMouseaActionDialog.FormCreate(Sender: TObject); +procedure TMouseaActionDialog.AddMouseCmd(const S: string); var i: Integer; - CName: String; + s2: String; +begin + if IdentToSynMouseCmd(S, i) then begin + s2 := MouseCommandName(i); + if s2 = '' then s2 := s; + ActionBox.Items.AddObject(s2, TObject(ptrint(i))); + end; +end; + +procedure TMouseaActionDialog.FormCreate(Sender: TObject); +var mb: TSynMouseButton; cc: TSynMAClickCount; begin @@ -109,11 +120,7 @@ begin CapturePanel.ControlStyle := ControlStyle + [csTripleClicks, csQuadClicks]; CaretCheck.Caption := dlgMouseOptCaretMove; ActionBox.Clear; - for i:= 0 to emcMax do begin - CName := MouseCommandName(i); - if CName <> '' then - ActionBox.Items.AddObject(CName, TObject(ptrint(i))); - end; + GetEditorMouseCommandValues(@AddMouseCmd); ButtonBox.Clear; for mb := low(TSynMouseButton) to high(TSynMouseButton) do ButtonBox.Items.add(ButtonName[mb]); diff --git a/ide/sourcesyneditor.pas b/ide/sourcesyneditor.pas index 9a0e32a8db..8b45fadd10 100644 --- a/ide/sourcesyneditor.pas +++ b/ide/sourcesyneditor.pas @@ -56,7 +56,7 @@ uses SynEditTextBuffer, SynEditFoldedView, SynTextDrawer, SynEditTextBase, LazSynEditText, SynPluginTemplateEdit, SynPluginSyncroEdit, LazSynTextArea, SynEditHighlighter, SynEditHighlighterFoldBase, SynHighlighterPas, SynEditMarkupHighAll, SynEditKeyCmds, - SynEditMarkupIfDef, SynEditMiscProcs, SynPluginMultiCaret, + SynEditMarkupIfDef, SynEditMiscProcs, SynPluginMultiCaret, SynEditPointClasses, etSrcEditMarks, LazarusIDEStrConsts; type @@ -1610,8 +1610,9 @@ begin FUserWordsList := TFPList.Create; FTemplateEdit:=TSynPluginTemplateEdit.Create(Self); FSyncroEdit := TSynPluginSyncroEdit.Create(Self); - {$IFDEF WithSynMultiCaret} + {$IFnDEF WithoutSynMultiCaret} FMultiCaret := TSynPluginMultiCaret.Create(Self); + FMultiCaret.MouseActions.Clear; // will be added to SynEdit FMultiCaret.SetCaretTypeSize(ctVerticalLine, 2, 1024, -1, 0, [ccsRelativeHeight]); FMultiCaret.SetCaretTypeSize(ctBlock, 1024, 1024, 0, 0, [ccsRelativeWidth, ccsRelativeHeight]); FMultiCaret.Color := $606060;