Преглед изворни кода

Now that Scintilla does proper multiple selection copy thanks to SCI_SETCOPYSEPARATOR, tie up the loose end by implementing multiple selection paste as well. Huge improvement 👍

Todo: the rectangular paste check.
Martijn Laan пре 11 месеци
родитељ
комит
a959c5a0fd
3 измењених фајлова са 56 додато и 5 уклоњено
  1. 17 2
      Components/ScintEdit.pas
  2. 38 3
      Projects/Src/IDE.MainForm.pas
  3. 1 0
      whatsnew.htm

+ 17 - 2
Components/ScintEdit.pas

@@ -166,8 +166,10 @@ type
     function GetSelectionAnchorVirtualSpace(Selection: Integer): Integer;
     function GetSelectionCaretPosition(Selection: Integer): Integer;
     function GetSelectionCaretVirtualSpace(Selection: Integer): Integer;
+    function GetSelectionEndPosition(Selection: Integer): Integer;
     function GetSelectionCount: Integer;
     function GetSelectionMode: TScintSelectionMode;
+    function GetSelectionStartPosition(Selection: Integer): Integer;
     function GetSelText: String;
     function GetTopLine: Integer;
     function GetZoom: Integer;
@@ -403,9 +405,12 @@ type
     property SelectionCaretPosition[Selection: Integer]: Integer read GetSelectionCaretPosition write SetSelectionCaretPosition;
     property SelectionCaretVirtualSpace[Selection: Integer]: Integer read GetSelectionCaretVirtualSpace write SetSelectionCaretVirtualSpace;
     property SelectionCount: Integer read GetSelectionCount;
+    property SelectionEndPosition[Selection: Integer]: Integer read GetSelectionEndPosition;
     property SelectionMode: TScintSelectionMode read GetSelectionMode write SetSelectionMode;
+    property SelectionStartPosition[Selection: Integer]: Integer read GetSelectionStartPosition;
     property SelText: String read GetSelText write SetSelText;
     property Styler: TScintCustomStyler read FStyler write SetStyler;
+    property Target: TScintRange read GetTarget;
     property TopLine: Integer read GetTopLine write SetTopLine;
     property WordChars: AnsiString read FWordChars;
     property WordCharsAsSet: TSysCharSet read FWordCharsAsSet;
@@ -1295,8 +1300,8 @@ procedure TScintEdit.GetSelections(const RangeList: TScintRangeList);
 begin
   RangeList.Clear;
   for var I := 0 to SelectionCount-1 do begin
-    var StartPos := Call(SCI_GETSELECTIONNSTART, I, 0);
-    var EndPos := Call(SCI_GETSELECTIONNEND, I, 0);
+    var StartPos := GetSelectionStartPosition(I);
+    var EndPos := GetSelectionEndPosition(I);
     RangeList.Add(TScintRange.Create(StartPos, EndPos));
   end;
 end;
@@ -1349,6 +1354,11 @@ begin
   Result := Call(SCI_GETSELECTIONS, 0, 0);
 end;
 
+function TScintEdit.GetSelectionEndPosition(Selection: Integer): Integer;
+begin
+  Result := Call(SCI_GETSELECTIONNEND, Selection, 0)
+end;
+
 function TScintEdit.GetSelectionMode: TScintSelectionMode;
 begin
   case Call(SCI_GETSELECTIONMODE, 0, 0) of
@@ -1361,6 +1371,11 @@ begin
   end;
 end;
 
+function TScintEdit.GetSelectionStartPosition(Selection: Integer): Integer;
+begin
+  Result := Call(SCI_GETSELECTIONNSTART, Selection, 0);
+end;
+
 function TScintEdit.GetSelText: String;
 begin
   Result := ConvertRawStringToString(GetRawSelText);

+ 38 - 3
Projects/Src/IDE.MainForm.pas

@@ -552,6 +552,7 @@ type
     function MemoToTabIndex(const AMemo: TIDEScintEdit): Integer;
     procedure MemoUpdateUI(Sender: TObject; Updated: TScintEditUpdates);
     procedure MemoZoom(Sender: TObject);
+    function MultipleSelectionPaste(const AMemo: TIDESCintEdit): Boolean;
     procedure UpdateReopenTabMenu(const Menu: TMenuItem);
     procedure ModifyMRUMainFilesList(const AFilename: String; const AddNewItem: Boolean);
     procedure ModifyMRUParametersList(const AParameter: String; const AddNewItem: Boolean);
@@ -1427,6 +1428,10 @@ begin
         HtmlHelp(GetDesktopWindow, PChar(HelpFile), HH_KEYWORD_LOOKUP, DWORD(@KLink));
       end;
     end;
+  end else if ((Key = Ord('V')) or (Key = VK_INSERT)) and (Shift * [ssShift, ssAlt, ssCtrl] = [ssCtrl]) then begin
+    if not FActiveMemo.ReadOnly and Clipboard.HasFormat(CF_TEXT) then { Also see EMenuClick }
+      if MultipleSelectionPaste(FActiveMemo) then
+        Key := 0;
   end else if (Key = VK_SPACE) and (Shift * [ssShift, ssAlt, ssCtrl] = [ssShift, ssCtrl]) then begin
     Key := 0;
     { Based on SciTE 5.50's SciTEBase::MenuCommand IDM_SHOWCALLTIP }
@@ -2859,7 +2864,7 @@ begin
   ERedo.Enabled := MemoHasFocus and FActiveMemo.CanRedo;
   ECut.Enabled := MemoHasFocus and not MemoIsReadOnly and not FActiveMemo.SelEmpty;
   ECopy.Enabled := MemoHasFocus and not FActiveMemo.SelEmpty;
-  EPaste.Enabled := MemoHasFocus and not MemoIsReadOnly and Clipboard.HasFormat(CF_TEXT);
+  EPaste.Enabled := MemoHasFocus and not MemoIsReadOnly and Clipboard.HasFormat(CF_TEXT); { Also see MemoKeyDown }
   EDelete.Enabled := MemoHasFocus and not FActiveMemo.SelEmpty;
   ESelectAll.Enabled := MemoHasFocus;
   ESelectNextOccurrence.Enabled := MemoHasFocus;
@@ -2901,11 +2906,41 @@ begin
   FActiveMemo.CopyToClipboard;
 end;
 
-procedure TMainForm.EPasteClick(Sender: TObject);
+function TMainForm.MultipleSelectionPaste(const AMemo: TIDEScintEdit): Boolean;
 begin
-  FActiveMemo.PasteFromClipboard;
+  { Scintilla doesn't yet properly support multiple selection paste. Handle it
+    here, VSCode style: if there's multiple selections and the paste text has the
+    same amount of lines then paste 1 line per selection. Otherwise do nothing
+    to allow Scintilla's default behaviour (which is to paste all lines into
+    each selection if SC_MULTIPASTE_EACH is on). }
+  Result := False;
+  var SelectionCount := AMemo.SelectionCount;
+  if SelectionCount > 1 then begin
+    var RectangularPaste := False; {todo} { First check for the case Scintilla *does* support }
+    if not RectangularPaste then begin
+      var PasteText := Clipboard.AsText.Replace(#13#10, #13).Split([#13, #10]);
+      if SelectionCount = Length(PasteText) then begin
+        for var I := 0 to SelectionCount-1 do begin
+          var StartPos := AMemo.SelectionStartPosition[I]; { Can't use AMemo.GetSelections because each paste can update other selections }
+          var EndPos := AMemo.SelectionEndPosition[I];
+          AMemo.ReplaceTextRange(StartPos, EndPos, PasteText[I], srmMinimal);
+          { Update the selection to an empty selection at the end of the inserted
+            text, just like SCI_REPLACESEL }
+          var Pos := AMemo.Target.EndPos; { SCI_REPLACETARGET* updates the target }
+          AMemo.SelectionCaretPosition[I] := Pos;
+          AMemo.SelectionAnchorPosition[I] := Pos;
+        end;
+        Result := True;
+      end;
+    end;
+  end;
 end;
 
+procedure TMainForm.EPasteClick(Sender: TObject);
+begin
+  if not MultipleSelectionPaste(FActiveMemo) then
+    FActiveMemo.PasteFromClipboard;
+end;
 
 procedure TMainForm.EDeleteClick(Sender: TObject);
 begin

+ 1 - 0
whatsnew.htm

@@ -43,6 +43,7 @@ For conditions of distribution and use, see <a href="files/is/license.txt">LICEN
   <li>Added shortcut to remove a selection by clicking it (Ctrl+Click or Alt+Click).</li>
   <li>Multiple selection now works over Left, Right, Up, Down, Home and End navigation and selection commands.</li>
   <li>Multiple selection now works over word and line deletion commands, and line end insertion.</li>
+  <li>Multiple selection now works better with Copy and Paste commands.</li>
   <li>Left, Right, etc. navigation with rectangular selection is now allowed.</li>
   <li>The Find and Replace dialogs and the tools from the <i>Tools</i> menu which generate script text now all work better with multiple selections present.</li>
 </ul>