{$ifdef nnn}begin end;{$endif} function TATSynEdit.DoCommand_TextTabulation: TATCommandResults; var N1, N2: integer; begin //multiline selection? //instead of tabulation, do indent if FOptKeyTabIndents then if Carets.Count=1 then begin Carets[0].GetSelLines(N1, N2); if (N1>=0) and (N2>N1) then begin Result:= DoCommand_TextIndentUnindent(true); Exit end; end; if FOptTabSpaces then Result:= DoCommand_TextInsertTabSpacesAtCarets(FOverwrite) else Result:= DoCommand_TextInsertAtCarets(#9, false, FOverwrite, false); end; function TATSynEdit.DoCommand_TextInsertAtCarets(const AText: atString; AKeepCaret, AOvrMode, ASelectThen: boolean): TATCommandResults; var List: TStringList; // function TextItem(i: integer): atString; begin if Assigned(List) and (Carets.Count=List.Count) and (i>=0) and (i1) or (Carets.IsSelection); if bNeedGroup then Strings.BeginUndoGroup; DoSelectionDeleteOrReset; //list allows to insert each clip-line into one caret if (AText<>sLineBreak) and (Length(AText)>1) and (Carets.Count>1) then begin List:= TStringList.Create; List.Text:= UTF8Encode(AText); end; try for i:= Carets.Count-1 downto 0 do begin Caret:= Carets[i]; Strings.TextInsert(Caret.PosX, Caret.PosY, TextItem(i), AOvrMode, Shift, PosAfter); DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter); if not AKeepCaret then begin Caret.EndX:= IfThen(ASelectThen, Caret.PosX, -1); Caret.EndY:= IfThen(ASelectThen, Caret.PosY, -1); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; end; end; finally if Assigned(List) then FreeAndNil(List); end; if bNeedGroup then Strings.EndUndoGroup; Result:= [cResultText, cResultCaretBottom]; end; function TATSynEdit.DoCommand_TextInsertTabSpacesAtCarets(AOvrMode: boolean): TATCommandResults; var Caret: TATCaretItem; Shift, PosAfter: TPoint; StrSpaces: atString; i: integer; begin Result:= []; if ModeReadOnly then Exit; Strings.BeginUndoGroup; try DoSelectionDeleteOrReset; for i:= Carets.Count-1 downto 0 do begin Caret:= Carets[i]; StrSpaces:= StringOfChar(' ', FTabSize - Caret.PosX mod FTabSize); Strings.TextInsert(Caret.PosX, Caret.PosY, StrSpaces, AOvrMode, Shift, PosAfter); DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; Caret.EndX:= -1; Caret.EndY:= -1; end; finally Strings.EndUndoGroup; end; Result:= [cResultText, cResultCaretBottom]; end; function TATSynEdit.DoCalcIndentCharsFromPrevLines(AX, AY: integer): integer; var Str: atString; NIndent, i: integer; begin Result:= -1; if not Strings.IsIndexValid(AY) then Exit; //allow smart unindent only if caret on 1st nonspace char //(else Bksp must delete 1 char) Str:= Strings.Lines[AY]; NIndent:= SGetIndentChars(Str); if not ((AX=NIndent) and (NIndent>0)) then Exit; //calc indent of N prev lines. //if indent1; if bNeedGroup then Strings.BeginUndoGroup; for i:= 0 to Carets.Count-1 do begin Caret:= Carets[i]; NDeleteLen:= ALen; if AAllowUnindent then begin NIndent:= DoCalcIndentCharsFromPrevLines(Caret.PosX, Caret.PosY); if NIndent>=0 then if Caret.PosX>NIndent then NDeleteLen:= Caret.PosX-NIndent else NDeleteLen:= Caret.PosX; end; Strings.TextDeleteLeft(Caret.PosX, Caret.PosY, NDeleteLen, Shift, PosAfter); DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; end; if bNeedGroup then Strings.EndUndoGroup; Result:= [cResultText, cResultCaretAny]; end; function TATSynEdit.DoCommand_TextDelete: TATCommandResults; var bColBlock: boolean; begin bColBlock:= not IsSelRectEmpty; if bColBlock then if FSelRect.Left=FSelRect.Right then begin DoSelect_None; bColBlock:= false; end; if bColBlock or Carets.IsSelection then Result:= DoCommand_TextDeleteSelection else Result:= DoCommand_TextDeleteRight(1); end; function TATSynEdit.DoCommand_TextDeleteSelection: TATCommandResults; var Caret: TATCaretItem; Shift, PosAfter: TPoint; i: integer; AX1, AY1, AX2, AY2: integer; bSel: boolean; begin Result:= []; if ModeReadOnly then Exit; if not IsSelRectEmpty and not OptCaretManyAllowed then begin DoSelectionDeleteColumnBlock; Result:= [cResultText, cResultCaretTop]; Exit end; if not Carets.IsSelection then Exit; Strings.BeginUndoGroup; try for i:= FCarets.Count-1 downto 0 do begin Caret:= Carets[i]; Caret.GetRange(AX1, AY1, AX2, AY2, bSel); if not bSel then Continue; Strings.TextDeleteRange(AX1, AY1, AX2, AY2, Shift, PosAfter); DoCaretsShift(AX1, AY1, Shift.X, Shift.Y, PosAfter); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; Caret.EndX:= -1; Caret.EndY:= -1; end; finally Strings.EndUndoGroup; end; Result:= [cResultText, cResultCaretTop]; end; function TATSynEdit.DoCommand_TextDeleteRight(ALen: integer): TATCommandResults; var Caret: TATCaretItem; i, Len, ShiftBelowX: integer; Shift, PosAfter: TPoint; bNeedGroup: boolean; begin Result:= []; if ModeReadOnly then Exit; //selection? delete it, exit. if Carets.IsSelection then begin Result:= DoCommand_TextDeleteSelection; exit end; bNeedGroup:= Carets.Count>1; if bNeedGroup then Strings.BeginUndoGroup; for i:= 0 to FCarets.Count-1 do begin Caret:= FCarets[i]; //offsetX for carets in line[PosY+1] ShiftBelowX:= 0; Len:= Length(Strings.Lines[Caret.PosY]); if Caret.PosX=Len then ShiftBelowX:= Len; Strings.TextDeleteRight(Caret.PosX, Caret.PosY, ALen, Shift, PosAfter); DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter, ShiftBelowX); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; end; if bNeedGroup then Strings.EndUndoGroup; Result:= [cResultText, cResultCaretAny]; end; function TATSynEdit.DoCommand_TextInsertEol(AKeepCaret: boolean): TATCommandResults; var Caret: TATCaretItem; Shift, PosAfter: TPoint; Str: atString; bNeedGroup: boolean; i: integer; begin Result:= []; if ModeReadOnly then Exit; if ModeOneLine then Exit; bNeedGroup:= Carets.Count>1; if bNeedGroup then Strings.BeginUndoGroup; DoSelectionDeleteOrReset; for i:= FCarets.Count-1 downto 0 do begin Caret:= FCarets[i]; Str:= GetAutoIndentString(Caret.PosX, Caret.PosY); Strings.TextInsertEol(Caret.PosX, Caret.PosY, AKeepCaret, Str, Shift, PosAfter); DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; end; if bNeedGroup then Strings.EndUndoGroup; Result:= [cResultText, cResultCaretBottom]; end; function TATSynEdit.DoCommand_TextDeleteLines: TATCommandResults; var Caret: TATCaretItem; Shift, PosAfter: TPoint; i: integer; begin Result:= []; if ModeReadOnly then Exit; Strings.BeginUndoGroup; try DoCaretsDeleteOnSameLines; for i:= FCarets.Count-1 downto 0 do begin Caret:= FCarets[i]; Strings.TextDeleteLine(Caret.PosX, Caret.PosY, Shift, PosAfter); DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; end; finally Strings.EndUndoGroup; end; Result:= [cResultText, cResultCaretTop]; end; function TATSynEdit.DoCommand_TextDuplicateLine: TATCommandResults; var Caret: TATCaretItem; Shift, PosAfter: TPoint; i: integer; begin Result:= []; if ModeReadOnly then Exit; Strings.BeginUndoGroup; try DoCaretsDeleteOnSameLines; for i:= FCarets.Count-1 downto 0 do begin Caret:= FCarets[i]; Strings.TextDuplicateLine(Caret.PosX, Caret.PosY, Shift, PosAfter); DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; end; finally Strings.EndUndoGroup; end; Result:= [cResultText, cResultCaretAny]; end; function TATSynEdit.DoCommand_TextDeleteToLineBegin: TATCommandResults; var Caret: TATCaretItem; Str: atString; i: integer; begin Result:= []; if ModeReadOnly then Exit; DoCaretsDeleteOnSameLines; for i:= FCarets.Count-1 downto 0 do begin Caret:= FCarets[i]; Str:= Strings.Lines[Caret.PosY]; Delete(Str, 1, Caret.PosX); Strings.Lines[Caret.PosY]:= Str; Caret.PosX:= 0; end; Result:= [cResultText, cResultCaretLeft]; end; function TATSynEdit.DoCommand_TextDeleteToLineEnd: TATCommandResults; var Caret: TATCaretItem; Str: atString; i: integer; begin Result:= []; if ModeReadOnly then Exit; DoCaretsDeleteOnSameLines; for i:= FCarets.Count-1 downto 0 do begin Caret:= FCarets[i]; Str:= Strings.Lines[Caret.PosY]; if Caret.PosX=Length(Str)) and ANext then begin Strings.TextDeleteRight(Caret.PosX, Caret.PosY, 1, Shift, PosAfter); end else //jump from beyond eol to eol? if (Caret.PosX>Length(Str)) and (not ANext) then begin Caret.PosX:= Length(Str); end else //delete inside line? if (Caret.PosX<=Length(Str)) then begin PosAfter.X:= SFindWordOffset(Str, Caret.PosX, ANext, false, FOptWordChars); if PosAfter.X<>Caret.PosX then begin System.Delete(Str, Min(Caret.PosX, PosAfter.X)+1, Abs(Caret.PosX-PosAfter.X)); Strings.Lines[Caret.PosY]:= Str; Shift.X:= -Abs(Caret.PosX-PosAfter.X); PosAfter.X:= Min(Caret.PosX, PosAfter.X); end; end; DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter); if ((Caret.PosX<>PosAfter.X) or (Caret.PosY<>PosAfter.Y)) and (FCarets.IndexOfPosXY(PosAfter.X, PosAfter.Y)>=0) then begin if FCarets.Count>1 then FCarets.Delete(i); end else begin Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; end; end; if ANext then Result:= [cResultText, cResultCaretBottom] else Result:= [cResultText, cResultCaretTop]; end; function TATSynEdit.DoCommand_TextIndentUnindent(ARight: boolean): TATCommandResults; var Y1, Y2: integer; NDecSpaces, NMinSpaces, i: integer; Str: atString; Caret: TATCaretItem; NShiftInit, NShift1, NShift2, NIndent1, NIndent2: integer; begin Result:= []; DoCaretSingleAsIs; Caret:= Carets[0]; Caret.GetSelLines(Y1, Y2, true{Allow no sel}); if Y1<0 then Exit; if Caret.EndY<0 then begin Caret.EndX:= Caret.PosX; Caret.EndY:= Caret.PosY; end; if FOptIndentSize>=0 then NDecSpaces:= FOptIndentSize else NDecSpaces:= Abs(FOptIndentSize)*FTabSize; //calc minimal indent of all NMinSpaces:= MaxInt; for i:= Y1 to Y2 do begin Str:= Strings.Lines[i]; if Trim(Str)='' then Continue; NMinSpaces:= Min(NMinSpaces, SGetIndentExpanded(Str, FTabSize)); end; if NMinSpaces=MaxInt then Exit; //consider "Unindent keeps align" if FOptIndentKeepsAlign then if (not ARight) and (NMinSpaces=NIndent1) and (Caret.PosX>0), NShiftInit, 0); NShift2:= IfThen((Caret.EndX>=NIndent2), NShiftInit, 0); //do indent Strings.BeginUndoGroup; try for i:= Y1 to Y2 do begin Str:= Strings.Lines[i]; if Trim(Str)='' then Continue; Str:= SIndentUnindent(Str, ARight, FOptIndentSize, FTabSize); if Strings.Lines[i]<>Str then Strings.Lines[i]:= Str; end; finally Strings.EndUndoGroup; end; //correct selection Caret.PosX:= Max(0, Caret.PosX+NShift1); Caret.EndX:= Max(0, Caret.EndX+NShift2); Result:= [cResultText, cResultCaretAny]; end; function TATSynEdit.DoCommand_Undo: TATCommandResults; begin Result:= []; if ModeReadOnly then Exit; Strings.Undo(FOptUndoGrouped); Result:= [cResultText, cResultCaretBottom]; end; function TATSynEdit.DoCommand_Redo: TATCommandResults; begin Result:= []; if ModeReadOnly then Exit; Strings.SetGroupMark; Strings.Redo(FOptUndoGrouped); Result:= [cResultText, cResultCaretBottom]; end; function TATSynEdit.DoCommand_TextInsertColumnBlockOnce(const AText: atString; AKeepCaret: boolean): TATCommandResults; var Caret: TATCaretItem; Block: TATStrings; begin Result:= []; if ModeReadOnly then Exit; //cannot handle carets/selections for colblock DoCaretSingleAsIs; DoSelect_None; Caret:= FCarets[0]; Block:= TATStrings.Create; try Block.LoadFromString(AText); Block.ActionDeleteFakeLine; if Block.Count=0 then Exit; Strings.TextInsertColumnBlock(Caret.PosX, Caret.PosY, Block, FOverwrite); if not AKeepCaret then Inc(Caret.PosY, Block.Count-1); finally FreeAndNil(Block); end; Result:= [cResultText, cResultCaretBottom]; end; function TATSynEdit.DoCommand_TextDeleteToFileEnd: TATCommandResults; var Caret: TATCaretItem; Str: atString; i: integer; begin Result:= []; if ModeReadOnly then Exit; DoCaretSingleAsIs; Caret:= FCarets[0]; Str:= Strings.Lines[Caret.PosY]; if Caret.PosX=Strings.Count-1 then Strings.LinesEnds[Caret.PosY]:= cEndNone; Result:= [cResultText, cResultCaretBottom]; end; function TATSynEdit.DoCommand_TextInsertEmptyAboveBelow(ADown: boolean): TATCommandResults; var Caret: TATCaretItem; PosAfter: TPoint; i: integer; begin Result:= []; if ModeReadOnly then Exit; DoCaretsDeleteOnSameLines; for i:= Carets.Count-1 downto 0 do begin Caret:= Carets[i]; Strings.LineInsert(Caret.PosY + IfThen(ADown, 1), ''); PosAfter.X:= 0; PosAfter.Y:= Caret.PosY+IfThen(ADown, 1); DoCaretsShift(0, Caret.PosY, 0, 1, PosAfter); Caret.PosX:= PosAfter.X; Caret.PosY:= PosAfter.Y; end; Result:= [cResultText, cResultCaretAny]; end; function TATSynEdit.DoCommand_TextChangeCase(AMode: TATCaseConvert): TATCommandResults; var Caret: TATCaretItem; Str1, Str2: atString; X1, Y1, X2, Y2: integer; Shift, PosAfter: TPoint; bSel: boolean; i: integer; begin Result:= []; if ModeReadOnly then Exit; Strings.BeginUndoGroup; for i:= Carets.Count-1 downto 0 do begin Caret:= Carets[i]; Caret.GetRange(X1, Y1, X2, Y2, bSel); if not bSel then begin SFindWordBounds(Strings.Lines[Caret.PosY], Caret.PosX, X1, X2, FOptWordChars); if X1<0 then Continue; Y1:= Caret.PosY; Y2:= Caret.PosY; end; Str1:= Strings.TextSubstring(X1, Y1, X2, Y2); case AMode of cCaseLower: Str2:= UnicodeLowerCase(Str1); cCaseUpper: Str2:= UnicodeUpperCase(Str1); cCaseTitle: Str2:= SCaseTitle(Str1, FOptWordChars); cCaseInvert: Str2:= SCaseInvert(Str1); cCaseSentence: Str2:= SCaseSentence(Str1, FOptWordChars); end; if Str1=Str2 then Continue; Strings.TextDeleteRange(X1, Y1, X2, Y2, Shift, PosAfter); Strings.TextInsert(X1, Y1, Str2, false, Shift, PosAfter); end; Strings.EndUndoGroup; Result:= [cResultText, cResultCaretAny]; end; procedure TATSynEdit.DoCommentSelectionLines(Act: TATCommentAction; const AComment: atString); var Caret: TATCaretItem; L: TStringList; NFrom, NTo, i: integer; Shift, PosAfter: TPoint; bChange: boolean; begin if Carets.Count=0 then exit; Caret:= Carets[0]; Caret.GetSelLines(NFrom, NTo, true); if NFrom<0 then exit; if NTo<0 then exit; L:= TStringList.Create; try for i:= NFrom to NTo do L.Add(Utf8Encode(Strings.Lines[i])); bChange:= SCommentLineAction(L, AComment, Act); if not bChange then exit; Assert(L.Count=(NTo-NFrom+1), 'DoCommentSel changed line count'); Strings.BeginUndoGroup; try for i:= NFrom to NTo do Strings.Lines[i]:= Utf8Decode(L[i-NFrom]); finally Strings.EndUndoGroup; end; finally L.Free; end; DoEventChange; Update(true); end; function TATSynEdit.DoCommand_TextTrimSpaces(AMode: TATTrimSpaces ): TATCommandResults; begin Result:= []; if ModeReadOnly then Exit; if Strings.ActionTrimSpaces(AMode) then Result:= [cResultCaretAny, cResultText]; end;