PageRenderTime 70ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/components/synedit/syneditpointclasses.pas

http://github.com/graemeg/lazarus
Pascal | 3487 lines | 2900 code | 399 blank | 188 comment | 379 complexity | c58c656596b082f0b7a7d16f58d7d30c MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception
  1. {-------------------------------------------------------------------------------
  2. The contents of this file are subject to the Mozilla Public License
  3. Version 1.1 (the "License"); you may not use this file except in compliance
  4. with the License. You may obtain a copy of the License at
  5. http://www.mozilla.org/MPL/
  6. Software distributed under the License is distributed on an "AS IS" basis,
  7. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  8. the specific language governing rights and limitations under the License.
  9. Alternatively, the contents of this file may be used under the terms of the
  10. GNU General Public License Version 2 or later (the "GPL"), in which case
  11. the provisions of the GPL are applicable instead of those above.
  12. If you wish to allow use of your version of this file only under the terms
  13. of the GPL and not to allow others to use your version of this file
  14. under the MPL, indicate your decision by deleting the provisions above and
  15. replace them with the notice and other provisions required by the GPL.
  16. If you do not delete the provisions above, a recipient may use your version
  17. of this file under either the MPL or the GPL.
  18. -------------------------------------------------------------------------------}
  19. (* Naming Conventions:
  20. Byte = Logical: Refers to the location any TextToken has in the String.
  21. In Utf8String some TextToken can have more than one byte
  22. Char = Physical: Refers to the (x-)location on the screen matrix.
  23. Some TextToken (like tab) can spawn multiply char locations
  24. *)
  25. unit SynEditPointClasses;
  26. {$I synedit.inc}
  27. {off $DEFINE SynCaretDebug}
  28. interface
  29. uses
  30. Classes, SysUtils, Controls, LCLProc, LCLType, LCLIntf, ExtCtrls, Graphics, Forms,
  31. {$IFDEF SYN_MBCSSUPPORT}
  32. Imm,
  33. {$ENDIF}
  34. LazSynEditText, SynEditTypes, SynEditMiscProcs;//, SynEditTextBuffer;
  35. type
  36. TInvalidateLines = procedure(FirstLine, LastLine: integer) of Object;
  37. TLinesCountChanged = procedure(FirstLine, Count: integer) of Object;
  38. TMaxLeftCharFunc = function: Integer of object;
  39. { TSynEditPointBase }
  40. TSynEditPointBase = class
  41. private
  42. function GetLocked: Boolean;
  43. protected
  44. FLines: TSynEditStrings;
  45. FOnChangeList: TMethodList;
  46. FLockCount: Integer;
  47. procedure SetLines(const AValue: TSynEditStrings); virtual;
  48. procedure DoLock; virtual;
  49. Procedure DoUnlock; virtual;
  50. public
  51. constructor Create;
  52. constructor Create(Lines: TSynEditStrings);
  53. destructor Destroy; override;
  54. procedure AddChangeHandler(AHandler: TNotifyEvent);
  55. procedure RemoveChangeHandler(AHandler: TNotifyEvent);
  56. procedure Lock;
  57. Procedure Unlock;
  58. property Lines: TSynEditStrings read FLines write SetLines;
  59. property Locked: Boolean read GetLocked;
  60. end;
  61. TSynEditBaseCaret = class;
  62. TSynEditCaret = class;
  63. TSynBlockPersistMode = (
  64. sbpDefault,
  65. sbpWeak, // selstart/end are treated as outside the block
  66. sbpStrong // selstart/end are treated as inside the block
  67. );
  68. TSynBeforeSetSelTextEvent = procedure(Sender: TObject; AMode: TSynSelectionMode; ANewText: PChar) of object;
  69. { TSynBeforeSetSelTextList }
  70. TSynBeforeSetSelTextList = Class(TMethodList)
  71. public
  72. procedure CallBeforeSetSelTextHandlers(Sender: TObject; AMode: TSynSelectionMode; ANewText: PChar);
  73. end;
  74. { TSynEditSelection }
  75. TSynEditSelection = class(TSynEditPointBase)
  76. private
  77. FOnBeforeSetSelText: TSynBeforeSetSelTextList;
  78. FAutoExtend: Boolean;
  79. FCaret: TSynEditCaret;
  80. FHide: Boolean;
  81. FInternalCaret: TSynEditBaseCaret;
  82. FInvalidateLinesMethod: TInvalidateLines;
  83. FEnabled: Boolean;
  84. FHookedLines: Boolean;
  85. FIsSettingText: Boolean;
  86. FForceSingleLineSelected: Boolean;
  87. FActiveSelectionMode: TSynSelectionMode;
  88. FSelectionMode: TSynSelectionMode;
  89. FStartLinePos: Integer; // 1 based
  90. FStartBytePos: Integer; // 1 based
  91. FAltStartLinePos, FAltStartBytePos: Integer; // 1 based // Alternate, for min selection
  92. FEndLinePos: Integer; // 1 based
  93. FEndBytePos: Integer; // 1 based
  94. FPersistent: Boolean;
  95. FPersistentLock, FWeakPersistentIdx, FStrongPersistentIdx: Integer;
  96. FIgnoreNextCaretMove: Boolean;
  97. (* On any modification, remember the position of the caret.
  98. If it gets moved from there to either end of the block, this should be ignored
  99. This happens, if Block and caret are adjusted directly
  100. *)
  101. FLastCarePos: TPoint;
  102. FStickyAutoExtend: Boolean;
  103. function AdjustBytePosToCharacterStart(Line: integer; BytePos: integer): integer;
  104. function GetFirstLineBytePos: TPoint;
  105. function GetLastLineBytePos: TPoint;
  106. function GetLastLineHasSelection: Boolean;
  107. procedure SetAutoExtend(AValue: Boolean);
  108. procedure SetCaret(const AValue: TSynEditCaret);
  109. procedure SetEnabled(const Value : Boolean);
  110. procedure SetActiveSelectionMode(const Value: TSynSelectionMode);
  111. procedure SetForceSingleLineSelected(AValue: Boolean);
  112. procedure SetHide(const AValue: Boolean);
  113. procedure SetPersistent(const AValue: Boolean);
  114. procedure SetSelectionMode (const AValue: TSynSelectionMode);
  115. function GetStartLineBytePos: TPoint;
  116. procedure ConstrainStartLineBytePos(var Value: TPoint);
  117. procedure SetStartLineBytePos(Value: TPoint);
  118. procedure AdjustStartLineBytePos(Value: TPoint);
  119. function GetEndLineBytePos: TPoint;
  120. procedure SetEndLineBytePos(Value: TPoint);
  121. function GetSelText: string;
  122. procedure SetSelText(const Value: string);
  123. procedure DoCaretChanged(Sender: TObject);
  124. procedure AdjustAfterTrimming; // TODO: Move into TrimView?
  125. protected
  126. procedure DoLock; override;
  127. procedure DoUnlock; override;
  128. Procedure LineChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
  129. procedure DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
  130. aLineBrkCnt: Integer; aText: String);
  131. public
  132. constructor Create(ALines: TSynEditStrings; aActOnLineChanges: Boolean);
  133. destructor Destroy; override;
  134. procedure AssignFrom(Src: TSynEditSelection);
  135. procedure SetSelTextPrimitive(PasteMode: TSynSelectionMode; Value: PChar; AReplace: Boolean = False);
  136. function SelAvail: Boolean;
  137. function SelCanContinue(ACaret: TSynEditCaret): Boolean;
  138. function IsBackwardSel: Boolean; // SelStart < SelEnd ?
  139. procedure BeginMinimumSelection; // current selection will be minimum while follow caret (autoExtend) // until next setSelStart or end of follow
  140. procedure SortSelectionPoints;
  141. procedure IgnoreNextCaretMove;
  142. // Mode can NOT be changed in nested calls
  143. procedure IncPersistentLock(AMode: TSynBlockPersistMode = sbpDefault); // Weak: Do not extend (but rather move) block, if at start/end
  144. procedure DecPersistentLock;
  145. procedure Clear;
  146. procedure AddBeforeSetSelTextHandler(AHandler: TSynBeforeSetSelTextEvent);
  147. procedure RemoveBeforeSetSelTextHandler(AHandler: TSynBeforeSetSelTextEvent);
  148. property Enabled: Boolean read FEnabled write SetEnabled;
  149. property ForceSingleLineSelected: Boolean read FForceSingleLineSelected write SetForceSingleLineSelected;
  150. property ActiveSelectionMode: TSynSelectionMode
  151. read FActiveSelectionMode write SetActiveSelectionMode;
  152. property SelectionMode: TSynSelectionMode
  153. read FSelectionMode write SetSelectionMode;
  154. property SelText: String read GetSelText write SetSelText;
  155. // Start and End positions are in the order they where defined
  156. // This may mean Startpos is behind EndPos in the text
  157. property StartLineBytePos: TPoint
  158. read GetStartLineBytePos write SetStartLineBytePos;
  159. property StartLineBytePosAdjusted: TPoint
  160. write AdjustStartLineBytePos;
  161. property EndLineBytePos: TPoint
  162. read GetEndLineBytePos write SetEndLineBytePos;
  163. property StartLinePos: Integer read FStartLinePos;
  164. property EndLinePos: Integer read FEndLinePos;
  165. property StartBytePos: Integer read FStartBytePos;
  166. property EndBytePos: Integer read FEndBytePos;
  167. // First and Last Pos are ordered according to the text flow (LTR)
  168. property FirstLineBytePos: TPoint read GetFirstLineBytePos;
  169. property LastLineBytePos: TPoint read GetLastLineBytePos;
  170. property LastLineHasSelection: Boolean read GetLastLineHasSelection;
  171. property InvalidateLinesMethod : TInvalidateLines write FInvalidateLinesMethod;
  172. property Caret: TSynEditCaret read FCaret write SetCaret;
  173. property Persistent: Boolean read FPersistent write SetPersistent;
  174. // automatically Start/Extend selection if caret moves
  175. // (depends if caret was at block border or not)
  176. property AutoExtend: Boolean read FAutoExtend write SetAutoExtend;
  177. property StickyAutoExtend: Boolean read FStickyAutoExtend write FStickyAutoExtend;
  178. property Hide: Boolean read FHide write SetHide;
  179. end;
  180. { TSynEditCaret }
  181. TSynEditCaretFlag = (
  182. scCharPosValid, scBytePosValid
  183. );
  184. TSynEditCaretFlags = set of TSynEditCaretFlag;
  185. TSynEditCaretUpdateFlag = (
  186. scuForceSet, // Change even if equal to old
  187. scuChangedX, scuChangedY, //
  188. scuNoInvalidate // Keep the Char/Byte ValidFlags
  189. );
  190. TSynEditCaretUpdateFlags = set of TSynEditCaretUpdateFlag;
  191. { TSynEditBaseCaret
  192. No Checks at all.
  193. Caller MUST ensure at least not to set x to invalid pos (middle of char) (incl update x, after SetLine)
  194. }
  195. TSynEditBaseCaret = class(TSynEditPointBase)
  196. private
  197. FFlags: TSynEditCaretFlags;
  198. FLinePos: Integer; // 1 based
  199. FCharPos: Integer; // 1 based
  200. FBytePos, FBytePosOffset: Integer; // 1 based
  201. function GetBytePos: Integer;
  202. function GetBytePosOffset: Integer;
  203. function GetCharPos: Integer;
  204. function GetFullLogicalPos: TLogCaretPoint;
  205. function GetLineBytePos: TPoint;
  206. function GetLineCharPos: TPoint;
  207. procedure SetBytePos(AValue: Integer);
  208. procedure SetBytePosOffset(AValue: Integer);
  209. procedure SetCharPos(AValue: Integer);
  210. procedure SetFullLogicalPos(AValue: TLogCaretPoint);
  211. procedure SetLineBytePos(AValue: TPoint);
  212. procedure SetLineCharPos(AValue: TPoint);
  213. procedure SetLinePos(AValue: Integer);
  214. function GetLineText: string;
  215. procedure SetLineText(AValue: string);
  216. protected
  217. procedure ValidateBytePos;
  218. procedure ValidateCharPos;
  219. procedure InternalSetLineCharPos(NewLine, NewCharPos: Integer;
  220. UpdFlags: TSynEditCaretUpdateFlags); virtual;
  221. procedure InternalSetLineByterPos(NewLine, NewBytePos, NewByteOffs: Integer;
  222. UpdFlags: TSynEditCaretUpdateFlags); virtual;
  223. public
  224. constructor Create;
  225. procedure AssignFrom(Src: TSynEditBaseCaret);
  226. procedure Invalidate; // force to 1,1
  227. procedure InvalidateBytePos; // 1,1 IF no validCharPos
  228. procedure InvalidateCharPos;
  229. function IsAtLineChar(aPoint: TPoint): Boolean;
  230. function IsAtLineByte(aPoint: TPoint; aByteOffset: Integer = -1): Boolean;
  231. function IsAtPos(aCaret: TSynEditCaret): Boolean;
  232. property LinePos: Integer read FLinePos write SetLinePos;
  233. property CharPos: Integer read GetCharPos write SetCharPos;
  234. property LineCharPos: TPoint read GetLineCharPos write SetLineCharPos;
  235. property BytePos: Integer read GetBytePos write SetBytePos;
  236. property BytePosOffset: Integer read GetBytePosOffset write SetBytePosOffset;
  237. property LineBytePos: TPoint read GetLineBytePos write SetLineBytePos;
  238. property FullLogicalPos: TLogCaretPoint read GetFullLogicalPos write SetFullLogicalPos;
  239. property LineText: string read GetLineText write SetLineText;
  240. end;
  241. { TSynEditCaret }
  242. TSynEditCaret = class(TSynEditBaseCaret)
  243. private
  244. FLinesEditedRegistered: Boolean;
  245. FAllowPastEOL: Boolean;
  246. FAutoMoveOnEdit: Integer;
  247. FForcePastEOL: Integer;
  248. FForceAdjustToNextChar: Integer;
  249. FKeepCaretX: Boolean;
  250. FLastCharPos: Integer; // used by KeepCaretX
  251. FOldLinePos: Integer; // 1 based
  252. FOldCharPos: Integer; // 1 based
  253. FAdjustToNextChar: Boolean;
  254. FMaxLeftChar: TMaxLeftCharFunc;
  255. FChangeOnTouch: Boolean;
  256. FSkipTabs: Boolean;
  257. FTouched: Boolean;
  258. procedure AdjustToChar;
  259. function GetMaxLeftPastEOL: Integer;
  260. function GetOldLineCharPos: TPoint;
  261. function GetOldLineBytePos: TPoint;
  262. function GetOldFullLogicalPos: TLogCaretPoint;
  263. procedure SetAllowPastEOL(const AValue: Boolean);
  264. procedure SetSkipTabs(const AValue: Boolean);
  265. procedure SetKeepCaretX(const AValue: Boolean);
  266. procedure RegisterLinesEditedHandler;
  267. protected
  268. procedure InternalSetLineCharPos(NewLine, NewCharPos: Integer;
  269. UpdFlags: TSynEditCaretUpdateFlags); override;
  270. procedure InternalSetLineByterPos(NewLine, NewBytePos, NewByteOffs: Integer;
  271. UpdFlags: TSynEditCaretUpdateFlags); override;
  272. procedure DoLock; override;
  273. Procedure DoUnlock; override;
  274. procedure SetLines(const AValue: TSynEditStrings); override;
  275. procedure DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
  276. aLineBrkCnt: Integer; aText: String);
  277. public
  278. constructor Create;
  279. destructor Destroy; override;
  280. procedure AssignFrom(Src: TSynEditBaseCaret);
  281. procedure IncForcePastEOL;
  282. procedure DecForcePastEOL;
  283. procedure IncForceAdjustToNextChar;
  284. procedure DecForceAdjustToNextChar;
  285. procedure IncAutoMoveOnEdit;
  286. procedure DecAutoMoveOnEdit;
  287. procedure ChangeOnTouch;
  288. procedure Touch(aChangeOnTouch: Boolean = False);
  289. function WasAtLineChar(aPoint: TPoint): Boolean;
  290. function WasAtLineByte(aPoint: TPoint): Boolean;
  291. function MoveHoriz(ACount: Integer): Boolean; // Logical // False, if past EOL (not mowed)/BOl
  292. property OldLinePos: Integer read FOldLinePos;
  293. property OldCharPos: Integer read FOldCharPos;
  294. property OldLineCharPos: TPoint read GetOldLineCharPos;
  295. property OldLineBytePos: TPoint read GetOldLineBytePos;
  296. property OldFullLogicalPos: TLogCaretPoint read GetOldFullLogicalPos;
  297. property AdjustToNextChar: Boolean read FAdjustToNextChar write FAdjustToNextChar; deprecated;
  298. property SkipTabs: Boolean read FSkipTabs write SetSkipTabs;
  299. property AllowPastEOL: Boolean read FAllowPastEOL write SetAllowPastEOL;
  300. property KeepCaretX: Boolean read FKeepCaretX write SetKeepCaretX;
  301. property KeepCaretXPos: Integer read FLastCharPos write FLastCharPos;
  302. property MaxLeftChar: TMaxLeftCharFunc read FMaxLeftChar write FMaxLeftChar;
  303. end;
  304. TSynCaretType = (ctVerticalLine, ctHorizontalLine, ctHalfBlock, ctBlock, ctCostum);
  305. TSynCaretLockFlags = set of (sclfUpdateDisplay, sclfUpdateDisplayType);
  306. { TSynEditScreenCaretTimer
  307. Allow sync between carets which use an internal painter
  308. }
  309. TSynEditScreenCaretTimer = class
  310. private
  311. FDisplayCycle: Boolean;
  312. FTimer: TTimer;
  313. FTimerList: TMethodList;
  314. FAfterPaintList: TMethodList;
  315. FLocCount: Integer;
  316. FLocFlags: set of (lfTimer, lfRestart);
  317. procedure DoTimer(Sender: TObject);
  318. procedure DoAfterPaint(Data: PtrInt);
  319. public
  320. constructor Create;
  321. destructor Destroy; override;
  322. procedure AddAfterPaintHandler(AHandler: TNotifyEvent); // called once
  323. procedure AddHandler(AHandler: TNotifyEvent);
  324. procedure RemoveHandler(AHandler: TNotifyEvent);
  325. procedure RemoveHandler(AHandlerOwner: TObject);
  326. procedure IncLock;
  327. procedure DecLock;
  328. procedure AfterPaintEvent;
  329. procedure RestartCycle;
  330. property DisplayCycle: Boolean read FDisplayCycle;
  331. end;
  332. TSynEditScreenCaret = class;
  333. { TSynEditScreenCaretPainter }
  334. TSynEditScreenCaretPainter = class
  335. private
  336. FLeft, FTop, FHeight, FWidth: Integer;
  337. FCreated, FShowing: Boolean;
  338. FInPaint, FInScroll: Boolean;
  339. FPaintClip: TRect;
  340. FScrollX, FScrollY: Integer;
  341. FScrollRect, FScrollClip: TRect;
  342. function GetHandle: HWND;
  343. function GetHandleAllocated: Boolean;
  344. protected
  345. FHandleOwner: TWinControl;
  346. FOwner: TSynEditScreenCaret;
  347. FNeedPositionConfirmed: boolean;
  348. procedure Init; virtual;
  349. property Handle: HWND read GetHandle;
  350. property HandleAllocated: Boolean read GetHandleAllocated;
  351. procedure BeginScroll(dx, dy: Integer; const rcScroll, rcClip: TRect); virtual;
  352. procedure FinishScroll(dx, dy: Integer; const rcScroll, rcClip: TRect; Success: Boolean); virtual;
  353. procedure BeginPaint(rcClip: TRect); virtual;
  354. procedure FinishPaint(rcClip: TRect); virtual;
  355. public
  356. constructor Create(AHandleOwner: TWinControl; AOwner: TSynEditScreenCaret);
  357. function CreateCaret(w, h: Integer): Boolean; virtual;
  358. function DestroyCaret: Boolean; virtual;
  359. function HideCaret: Boolean; virtual;
  360. function ShowCaret: Boolean; virtual;
  361. function SetCaretPosEx(x, y: Integer): Boolean; virtual;
  362. property Left: Integer read FLeft;
  363. property Top: Integer read FTop;
  364. property Width: Integer read FWidth;
  365. property Height: Integer read FHeight;
  366. property Created: Boolean read FCreated;
  367. property Showing: Boolean read FShowing;
  368. property InPaint: Boolean read FInPaint;
  369. property InScroll: Boolean read FInScroll;
  370. property NeedPositionConfirmed: boolean read FNeedPositionConfirmed;
  371. end;
  372. TSynEditScreenCaretPainterClass = class of TSynEditScreenCaretPainter;
  373. { TSynEditScreenCaretPainterSystem }
  374. TSynEditScreenCaretPainterSystem = class(TSynEditScreenCaretPainter)
  375. protected
  376. //procedure BeginScroll(dx, dy: Integer; const rcScroll, rcClip: TRect); override;
  377. procedure FinishScroll(dx, dy: Integer; const rcScroll, rcClip: TRect; Success: Boolean); override;
  378. procedure BeginPaint(rcClip: TRect); override;
  379. //procedure FinishPaint(rcClip: TRect); override; // unhide, currently done by editor
  380. public
  381. function CreateCaret(w, h: Integer): Boolean; override;
  382. function DestroyCaret: Boolean; override;
  383. function HideCaret: Boolean; override;
  384. function ShowCaret: Boolean; override;
  385. function SetCaretPosEx(x, y: Integer): Boolean; override;
  386. end;
  387. { TSynEditScreenCaretPainterInternal }
  388. TSynEditScreenCaretPainterInternal = class(TSynEditScreenCaretPainter)
  389. private type
  390. TIsInRectState = (irInside, irPartInside, irOutside);
  391. TPainterState = (psAfterPaintAdded, psCleanOld, psRemoveTimer);
  392. TPainterStates = set of TPainterState;
  393. private
  394. FColor: TColor;
  395. FForcePaintEvents: Boolean;
  396. FIsDrawn: Boolean;
  397. FSavePen: TPen;
  398. FOldX, FOldY, FOldW, FOldH: Integer;
  399. FState: TPainterStates;
  400. FCanPaint: Boolean;
  401. procedure DoTimer(Sender: TObject);
  402. procedure DoPaint(ACanvas: TCanvas; X, Y, H, W: Integer);
  403. procedure Paint;
  404. procedure Invalidate;
  405. procedure AddAfterPaint(AStates: TPainterStates = []);
  406. procedure DoAfterPaint(Sender: TObject);
  407. procedure ExecAfterPaint;
  408. function CurrentCanvas: TCanvas;
  409. procedure SetColor(AValue: TColor);
  410. function IsInRect(ARect: TRect): TIsInRectState;
  411. function IsInRect(ARect: TRect; X, Y, W, H: Integer): TIsInRectState;
  412. protected
  413. procedure Init; override;
  414. procedure BeginScroll(dx, dy: Integer; const rcScroll, rcClip: TRect); override;
  415. procedure FinishScroll(dx, dy: Integer; const rcScroll, rcClip: TRect; Success: Boolean); override;
  416. procedure BeginPaint(rcClip: TRect); override;
  417. procedure FinishPaint(rcClip: TRect); override;
  418. public
  419. destructor Destroy; override;
  420. function CreateCaret(w, h: Integer): Boolean; override;
  421. function DestroyCaret: Boolean; override;
  422. function HideCaret: Boolean; override;
  423. function ShowCaret: Boolean; override;
  424. function SetCaretPosEx(x, y: Integer): Boolean; override;
  425. property Color: TColor read FColor write SetColor;
  426. property ForcePaintEvents: Boolean read FForcePaintEvents write FForcePaintEvents;
  427. end;
  428. // relative dimensions in percent from 0 to 1024 (=100%)
  429. TSynCustomCaretSizeFlag = (ccsRelativeLeft, ccsRelativeTop, ccsRelativeWidth, ccsRelativeHeight);
  430. TSynCustomCaretSizeFlags = set of TSynCustomCaretSizeFlag;
  431. { TSynEditScreenCaret }
  432. TSynEditScreenCaret = class
  433. private
  434. FCharHeight: Integer;
  435. FCharWidth: Integer;
  436. FClipRight: Integer;
  437. FClipBottom: Integer;
  438. FClipLeft: Integer;
  439. FClipTop: Integer;
  440. FDisplayPos: TPoint;
  441. FDisplayType: TSynCaretType;
  442. FExtraLinePixel, FExtraLineChars: Integer;
  443. FOnExtraLineCharsChanged: TNotifyEvent;
  444. FVisible: Boolean;
  445. FHandleOwner: TWinControl;
  446. FCaretPainter: TSynEditScreenCaretPainter;
  447. FPaintTimer: TSynEditScreenCaretTimer;
  448. FPaintTimerOwned: Boolean;
  449. function GetHandle: HWND;
  450. function GetHandleAllocated: Boolean;
  451. procedure SetCharHeight(const AValue: Integer);
  452. procedure SetCharWidth(const AValue: Integer);
  453. procedure SetClipRight(const AValue: Integer);
  454. procedure SetDisplayPos(const AValue: TPoint);
  455. procedure SetDisplayType(const AType: TSynCaretType);
  456. procedure SetVisible(const AValue: Boolean);
  457. private
  458. FClipExtraPixel: Integer;
  459. {$IFDeF SynCaretDebug}
  460. FDebugShowCount: Integer;
  461. {$ENDIF}
  462. FPixelWidth, FPixelHeight: Integer;
  463. FOffsetX, FOffsetY: Integer;
  464. FCustomPixelWidth, FCustomPixelHeight: Array [TSynCaretType] of Integer;
  465. FCustomOffsetX, FCustomOffsetY: Array [TSynCaretType] of Integer;
  466. FCustomFlags: Array [TSynCaretType] of TSynCustomCaretSizeFlags;
  467. FLockCount: Integer;
  468. FLockFlags: TSynCaretLockFlags;
  469. function GetHasPaintTimer: Boolean;
  470. function GetPaintTimer: TSynEditScreenCaretTimer;
  471. procedure SetClipBottom(const AValue: Integer);
  472. procedure SetClipExtraPixel(AValue: Integer);
  473. procedure SetClipLeft(const AValue: Integer);
  474. procedure SetClipRect(const AValue: TRect);
  475. procedure SetClipTop(const AValue: Integer);
  476. procedure CalcExtraLineChars;
  477. procedure SetPaintTimer(AValue: TSynEditScreenCaretTimer);
  478. procedure UpdateDisplayType;
  479. procedure UpdateDisplay;
  480. procedure ShowCaret;
  481. procedure HideCaret;
  482. property HandleAllocated: Boolean read GetHandleAllocated;
  483. protected
  484. property Handle: HWND read GetHandle;
  485. public
  486. constructor Create(AHandleOwner: TWinControl);
  487. constructor Create(AHandleOwner: TWinControl; APainterClass: TSynEditScreenCaretPainterClass);
  488. procedure ChangePainter(APainterClass: TSynEditScreenCaretPainterClass);
  489. destructor Destroy; override;
  490. procedure BeginScroll(dx, dy: Integer; const rcScroll, rcClip: TRect);
  491. procedure FinishScroll(dx, dy: Integer; const rcScroll, rcClip: TRect; Success: Boolean);
  492. procedure BeginPaint(rcClip: TRect);
  493. procedure FinishPaint(rcClip: TRect);
  494. procedure Lock;
  495. procedure UnLock;
  496. procedure AfterPaintEvent; // next async
  497. procedure Hide; // Keep visible = true
  498. procedure DestroyCaret(SkipHide: boolean = False);
  499. procedure ResetCaretTypeSizes;
  500. procedure SetCaretTypeSize(AType: TSynCaretType; AWidth, AHeight, AXOffs, AYOffs: Integer;
  501. AFlags: TSynCustomCaretSizeFlags = []);
  502. property HandleOwner: TWinControl read FHandleOwner;
  503. property PaintTimer: TSynEditScreenCaretTimer read GetPaintTimer write SetPaintTimer;
  504. property HasPaintTimer: Boolean read GetHasPaintTimer;
  505. property Painter: TSynEditScreenCaretPainter read FCaretPainter;
  506. property CharWidth: Integer read FCharWidth write SetCharWidth;
  507. property CharHeight: Integer read FCharHeight write SetCharHeight;
  508. property ClipLeft: Integer read FClipLeft write SetClipLeft;
  509. property ClipRight: Integer read FClipRight write SetClipRight; // First pixel outside the allowed area
  510. property ClipTop: Integer read FClipTop write SetClipTop;
  511. property ClipRect: TRect write SetClipRect;
  512. property ClipBottom: Integer read FClipBottom write SetClipBottom;
  513. property ClipExtraPixel: Integer read FClipExtraPixel write SetClipExtraPixel; // Amount of pixels, after the last full char (half visible char width)
  514. property Visible: Boolean read FVisible write SetVisible;
  515. property DisplayType: TSynCaretType read FDisplayType write SetDisplayType;
  516. property DisplayPos: TPoint read FDisplayPos write SetDisplayPos;
  517. property ExtraLineChars: Integer read FExtraLineChars; // Extend the longest line by x chars
  518. property OnExtraLineCharsChanged: TNotifyEvent
  519. read FOnExtraLineCharsChanged write FOnExtraLineCharsChanged;
  520. end;
  521. implementation
  522. { TSynBeforeSetSelTextList }
  523. procedure TSynBeforeSetSelTextList.CallBeforeSetSelTextHandlers(Sender: TObject;
  524. AMode: TSynSelectionMode; ANewText: PChar);
  525. var
  526. i: Integer;
  527. begin
  528. i:=Count;
  529. while NextDownIndex(i) do
  530. TSynBeforeSetSelTextEvent(Items[i])(Sender, AMode, ANewText);
  531. end;
  532. { TSynEditBaseCaret }
  533. function TSynEditBaseCaret.GetBytePos: Integer;
  534. begin
  535. ValidateBytePos;
  536. Result := FBytePos;
  537. end;
  538. function TSynEditBaseCaret.GetBytePosOffset: Integer;
  539. begin
  540. ValidateBytePos;
  541. Result := FBytePosOffset;
  542. end;
  543. function TSynEditBaseCaret.GetCharPos: Integer;
  544. begin
  545. ValidateCharPos;
  546. Result := FCharPos;
  547. end;
  548. function TSynEditBaseCaret.GetFullLogicalPos: TLogCaretPoint;
  549. begin
  550. ValidateBytePos;
  551. Result.Y := FLinePos;
  552. Result.X := FBytePos;
  553. Result.Offs := FBytePosOffset;
  554. end;
  555. function TSynEditBaseCaret.GetLineBytePos: TPoint;
  556. begin
  557. ValidateBytePos;
  558. Result := Point(FBytePos, FLinePos);
  559. end;
  560. function TSynEditBaseCaret.GetLineCharPos: TPoint;
  561. begin
  562. ValidateCharPos;
  563. Result := Point(FCharPos, FLinePos);
  564. end;
  565. procedure TSynEditBaseCaret.SetBytePos(AValue: Integer);
  566. begin
  567. InternalSetLineByterPos(FLinePos, AValue, 0, [scuChangedX]);
  568. end;
  569. procedure TSynEditBaseCaret.SetBytePosOffset(AValue: Integer);
  570. begin
  571. ValidateBytePos;
  572. InternalSetLineByterPos(FLinePos, FBytePos, AValue, [scuChangedX]);
  573. end;
  574. procedure TSynEditBaseCaret.SetCharPos(AValue: Integer);
  575. begin
  576. InternalSetLineCharPos(FLinePos, AValue, [scuChangedX]);
  577. end;
  578. procedure TSynEditBaseCaret.SetFullLogicalPos(AValue: TLogCaretPoint);
  579. begin
  580. InternalSetLineByterPos(AValue.y, AValue.x, AValue.Offs, [scuChangedX, scuChangedY]);
  581. end;
  582. procedure TSynEditBaseCaret.SetLineBytePos(AValue: TPoint);
  583. begin
  584. InternalSetLineByterPos(AValue.y, AValue.x, 0, [scuChangedX, scuChangedY]);
  585. end;
  586. procedure TSynEditBaseCaret.SetLineCharPos(AValue: TPoint);
  587. begin
  588. InternalSetLineCharPos(AValue.y, AValue.X, [scuChangedX, scuChangedY]);
  589. end;
  590. procedure TSynEditBaseCaret.SetLinePos(AValue: Integer);
  591. begin
  592. // TODO: may temporary lead to invalid x bytepos. Must be adjusted *before* calculating char
  593. //if scBytePosValid in FFlags then
  594. // InternalSetLineByterPos(AValue, FBytePos, FBytePosOffset, [scuChangedY])
  595. //else
  596. ValidateCharPos;
  597. InternalSetLineCharPos(AValue, FCharPos, [scuChangedY]);
  598. end;
  599. function TSynEditBaseCaret.GetLineText: string;
  600. begin
  601. if (LinePos >= 1) and (LinePos <= FLines.Count) then
  602. Result := FLines[LinePos - 1]
  603. else
  604. Result := '';
  605. end;
  606. procedure TSynEditBaseCaret.SetLineText(AValue: string);
  607. begin
  608. if (LinePos >= 1) and (LinePos <= Max(1, FLines.Count)) then
  609. FLines[LinePos - 1] := AValue;
  610. end;
  611. procedure TSynEditBaseCaret.ValidateBytePos;
  612. begin
  613. if scBytePosValid in FFlags then
  614. exit;
  615. assert(scCharPosValid in FFlags, 'ValidateBytePos: no charpos set');
  616. Include(FFlags, scBytePosValid);
  617. FBytePos := FLines.LogPhysConvertor.PhysicalToLogical(FLinePos-1, FCharPos, FBytePosOffset);
  618. end;
  619. procedure TSynEditBaseCaret.ValidateCharPos;
  620. begin
  621. if scCharPosValid in FFlags then
  622. exit;
  623. assert(scBytePosValid in FFlags, 'ValidateCharPos: no bytepos set');
  624. Include(FFlags, scCharPosValid);
  625. FCharPos := FLines.LogPhysConvertor.LogicalToPhysical(FLinePos-1, FBytePos, FBytePosOffset);
  626. end;
  627. procedure TSynEditBaseCaret.InternalSetLineCharPos(NewLine, NewCharPos: Integer;
  628. UpdFlags: TSynEditCaretUpdateFlags);
  629. begin
  630. if (fCharPos = NewCharPos) and (fLinePos = NewLine) and
  631. (scCharPosValid in FFlags) and not (scuForceSet in UpdFlags)
  632. then
  633. exit;
  634. if not (scuNoInvalidate in UpdFlags) then
  635. Exclude(FFlags, scBytePosValid);
  636. Include(FFlags, scCharPosValid);
  637. if NewLine < 1 then begin
  638. NewLine := 1;
  639. Exclude(FFlags, scBytePosValid);
  640. end;
  641. if NewCharPos < 1 then begin
  642. NewCharPos := 1;
  643. Exclude(FFlags, scBytePosValid);
  644. end;
  645. FCharPos := NewCharPos;
  646. FLinePos := NewLine;
  647. end;
  648. procedure TSynEditBaseCaret.InternalSetLineByterPos(NewLine, NewBytePos, NewByteOffs: Integer;
  649. UpdFlags: TSynEditCaretUpdateFlags);
  650. begin
  651. if (FBytePos = NewBytePos) and (FBytePosOffset = NewByteOffs) and
  652. (FLinePos = NewLine) and (scBytePosValid in FFlags) and not (scuForceSet in UpdFlags)
  653. then
  654. exit;
  655. if not (scuNoInvalidate in UpdFlags) then
  656. Exclude(FFlags, scCharPosValid);
  657. Include(FFlags, scBytePosValid);
  658. if NewLine < 1 then begin
  659. NewLine := 1;
  660. Exclude(FFlags, scCharPosValid);
  661. end;
  662. if NewBytePos < 1 then begin
  663. NewBytePos := 1;
  664. Exclude(FFlags, scCharPosValid);
  665. end;
  666. FBytePos := NewBytePos;
  667. FBytePosOffset := NewByteOffs;
  668. FLinePos := NewLine;
  669. end;
  670. constructor TSynEditBaseCaret.Create;
  671. begin
  672. inherited Create;
  673. fLinePos := 1;
  674. fCharPos := 1;
  675. FBytePos := 1;
  676. FBytePosOffset := 0;
  677. FFlags := [scCharPosValid, scBytePosValid];
  678. end;
  679. procedure TSynEditBaseCaret.AssignFrom(Src: TSynEditBaseCaret);
  680. begin
  681. FLinePos := Src.FLinePos;
  682. FCharPos := Src.FCharPos;
  683. FBytePos := Src.FBytePos;
  684. FBytePosOffset := Src.FBytePosOffset;
  685. FFlags := Src.FFlags;
  686. SetLines(Src.FLines);
  687. end;
  688. procedure TSynEditBaseCaret.Invalidate;
  689. begin
  690. FLinePos := 1;
  691. FCharPos := 1;
  692. FBytePos := 1;
  693. FFlags := [];
  694. end;
  695. procedure TSynEditBaseCaret.InvalidateBytePos;
  696. begin
  697. if not (scCharPosValid in FFlags) then
  698. Invalidate
  699. else
  700. Exclude(FFlags, scBytePosValid);
  701. end;
  702. procedure TSynEditBaseCaret.InvalidateCharPos;
  703. begin
  704. if not (scBytePosValid in FFlags) then
  705. Invalidate
  706. else
  707. Exclude(FFlags, scCharPosValid);
  708. end;
  709. function TSynEditBaseCaret.IsAtLineChar(aPoint: TPoint): Boolean;
  710. begin
  711. ValidateCharPos;
  712. Result := (FLinePos = aPoint.y) and (FCharPos = aPoint.x);
  713. end;
  714. function TSynEditBaseCaret.IsAtLineByte(aPoint: TPoint; aByteOffset: Integer): Boolean;
  715. begin
  716. ValidateBytePos;
  717. Result := (FLinePos = aPoint.y) and (BytePos = aPoint.x) and
  718. ( (aByteOffset < 0) or (FBytePosOffset = aByteOffset) );
  719. end;
  720. function TSynEditBaseCaret.IsAtPos(aCaret: TSynEditCaret): Boolean;
  721. begin
  722. if (scBytePosValid in FFlags) or (scBytePosValid in aCaret.FFlags) then
  723. Result := IsAtLineByte(aCaret.LineBytePos, aCaret.BytePosOffset)
  724. else
  725. Result := IsAtLineChar(aCaret.LineCharPos);
  726. end;
  727. { TSynEditPointBase }
  728. function TSynEditPointBase.GetLocked: Boolean;
  729. begin
  730. Result := FLockCount > 0;
  731. end;
  732. procedure TSynEditPointBase.SetLines(const AValue: TSynEditStrings);
  733. begin
  734. FLines := AValue;
  735. end;
  736. procedure TSynEditPointBase.DoLock;
  737. begin
  738. end;
  739. procedure TSynEditPointBase.DoUnlock;
  740. begin
  741. end;
  742. constructor TSynEditPointBase.Create;
  743. begin
  744. FOnChangeList := TMethodList.Create;
  745. end;
  746. constructor TSynEditPointBase.Create(Lines : TSynEditStrings);
  747. begin
  748. Create;
  749. FLines := Lines;
  750. end;
  751. destructor TSynEditPointBase.Destroy;
  752. begin
  753. FreeAndNil(FOnChangeList);
  754. inherited Destroy;
  755. end;
  756. procedure TSynEditPointBase.AddChangeHandler(AHandler : TNotifyEvent);
  757. begin
  758. FOnChangeList.Add(TMethod(AHandler));
  759. end;
  760. procedure TSynEditPointBase.RemoveChangeHandler(AHandler : TNotifyEvent);
  761. begin
  762. FOnChangeList.Remove(TMethod(AHandler));
  763. end;
  764. procedure TSynEditPointBase.Lock;
  765. begin
  766. if FLockCount = 0 then
  767. DoLock;
  768. inc(FLockCount);
  769. end;
  770. procedure TSynEditPointBase.Unlock;
  771. begin
  772. dec(FLockCount);
  773. if FLockCount = 0 then
  774. DoUnLock;
  775. end;
  776. { TSynEditCaret }
  777. constructor TSynEditCaret.Create;
  778. begin
  779. inherited Create;
  780. FMaxLeftChar := nil;
  781. FAllowPastEOL := True;
  782. FForcePastEOL := 0;
  783. FAutoMoveOnEdit := 0;
  784. if FLines <> nil then
  785. FLines.AddEditHandler(@DoLinesEdited);
  786. end;
  787. destructor TSynEditCaret.Destroy;
  788. begin
  789. if FLines <> nil then
  790. FLines.RemoveEditHandler(@DoLinesEdited);
  791. inherited Destroy;
  792. end;
  793. procedure TSynEditCaret.AssignFrom(Src: TSynEditBaseCaret);
  794. begin
  795. FOldCharPos := FCharPos;
  796. FOldLinePos := FLinePos;
  797. inherited AssignFrom(Src);
  798. if Src is TSynEditCaret then begin
  799. FMaxLeftChar := TSynEditCaret(Src).FMaxLeftChar;
  800. FAllowPastEOL := TSynEditCaret(Src).FAllowPastEOL;
  801. FKeepCaretX := TSynEditCaret(Src).FKeepCaretX;
  802. FLastCharPos := TSynEditCaret(Src).FLastCharPos;
  803. end
  804. else begin
  805. AdjustToChar;
  806. FLastCharPos := FCharPos;
  807. end;
  808. end;
  809. procedure TSynEditCaret.DoLock;
  810. begin
  811. FTouched := False;
  812. ValidateCharPos;
  813. //ValidateBytePos;
  814. FOldCharPos := FCharPos;
  815. FOldLinePos := FLinePos;
  816. end;
  817. procedure TSynEditCaret.DoUnlock;
  818. begin
  819. if not FChangeOnTouch then
  820. FTouched := False;
  821. FChangeOnTouch := False;
  822. ValidateCharPos;
  823. //ValidateBytePos;
  824. if (FOldCharPos <> FCharPos) or (FOldLinePos <> FLinePos) or FTouched then
  825. fOnChangeList.CallNotifyEvents(self);
  826. // All notifications called, reset oldpos
  827. FTouched := False;
  828. FOldCharPos := FCharPos;
  829. FOldLinePos := FLinePos;
  830. end;
  831. procedure TSynEditCaret.SetLines(const AValue: TSynEditStrings);
  832. begin
  833. if FLines = AValue then exit;
  834. // Do not check flag. It will be cleared in Assign
  835. if (FLines <> nil) then
  836. FLines.RemoveEditHandler(@DoLinesEdited);
  837. FLinesEditedRegistered := False;
  838. inherited SetLines(AValue);
  839. if FAutoMoveOnEdit > 0 then
  840. RegisterLinesEditedHandler;
  841. end;
  842. procedure TSynEditCaret.RegisterLinesEditedHandler;
  843. begin
  844. if FLinesEditedRegistered or (FLines = nil) then
  845. exit;
  846. FLinesEditedRegistered := True;
  847. FLines.AddEditHandler(@DoLinesEdited);
  848. end;
  849. procedure TSynEditCaret.DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
  850. aLineBrkCnt: Integer; aText: String);
  851. // Todo: refactor / this is a copy from selection
  852. function AdjustPoint(aPoint: Tpoint): TPoint; inline;
  853. begin
  854. Result := aPoint;
  855. if aLineBrkCnt < 0 then begin
  856. (* Lines Deleted *)
  857. if aPoint.y > aLinePos then begin
  858. Result.y := Max(aLinePos, Result.y + aLineBrkCnt);
  859. if Result.y = aLinePos then
  860. Result.x := Result.x + aBytePos - 1;
  861. end;
  862. end
  863. else
  864. if aLineBrkCnt > 0 then begin
  865. (* Lines Inserted *)
  866. if (aPoint.y = aLinePos) and (aPoint.x >= aBytePos) then begin
  867. Result.x := Result.x - aBytePos + 1;
  868. Result.y := Result.y + aLineBrkCnt;
  869. end;
  870. if aPoint.y > aLinePos then begin
  871. Result.y := Result.y + aLineBrkCnt;
  872. end;
  873. end
  874. else
  875. if aCount <> 0 then begin
  876. (* Chars Insert/Deleted *)
  877. if (aPoint.y = aLinePos) and (aPoint.x >= aBytePos) then
  878. Result.x := Max(aBytePos, Result.x + aCount);
  879. end;
  880. end;
  881. var
  882. p: TPoint;
  883. begin
  884. if (FAutoMoveOnEdit > 0) and
  885. ( (aLineBrkCnt <> 0) or (aLinePos = FLinePos) )
  886. then begin
  887. IncForcePastEOL;
  888. ValidateBytePos;
  889. p := AdjustPoint(Point(FBytePos, FLinePos));
  890. InternalSetLineByterPos(p.y, p.x, FBytePosOffset, [scuChangedX, scuChangedY, scuForceSet]);
  891. DecForcePastEOL;
  892. end;
  893. end;
  894. procedure TSynEditCaret.AdjustToChar;
  895. var
  896. CharWidthsArr: TPhysicalCharWidths;
  897. CharWidths: PPhysicalCharWidth;
  898. i, LogLen: Integer;
  899. ScreenPos: Integer;
  900. LogPos: Integer;
  901. L: String;
  902. begin
  903. ValidateCharPos;
  904. L := LineText;
  905. if FLines.LogPhysConvertor.CurrentLine = FLinePos then begin
  906. CharWidths := FLines.LogPhysConvertor.CurrentWidths;
  907. LogLen := FLines.LogPhysConvertor.CurrentWidthsCount;
  908. end
  909. else begin
  910. CharWidthsArr := FLines.GetPhysicalCharWidths(Pchar(L), length(L), FLinePos-1);
  911. LogLen := Length(CharWidthsArr);
  912. if LogLen > 0 then
  913. CharWidths := @CharWidthsArr[0];
  914. end;
  915. ScreenPos := 1;
  916. LogPos := 0;
  917. while LogPos < LogLen do begin
  918. if ScreenPos = FCharPos then exit;
  919. if ScreenPos + (CharWidths[LogPos] and PCWMask) > FCharPos then begin
  920. if (L[LogPos+1] = #9) and (not FSkipTabs) then exit;
  921. i := FCharPos;
  922. if FAdjustToNextChar or (FForceAdjustToNextChar > 0) then
  923. FCharPos := ScreenPos + (CharWidths[LogPos] and PCWMask)
  924. else
  925. FCharPos := ScreenPos;
  926. if FCharPos <> i then
  927. Exclude(FFlags, scBytePosValid);
  928. exit;
  929. end;
  930. ScreenPos := ScreenPos + (CharWidths[LogPos] and PCWMask);
  931. inc(LogPos);
  932. end;
  933. end;
  934. function TSynEditCaret.GetMaxLeftPastEOL: Integer;
  935. begin
  936. if FMaxLeftChar <> nil then
  937. Result := FMaxLeftChar()
  938. else
  939. Result := MaxInt;
  940. end;
  941. procedure TSynEditCaret.InternalSetLineCharPos(NewLine, NewCharPos: Integer;
  942. UpdFlags: TSynEditCaretUpdateFlags);
  943. var
  944. LogEolPos, MaxPhysX, NewLogCharPos, Offs: Integer;
  945. L: String;
  946. begin
  947. if not (scuChangedX in UpdFlags) and FKeepCaretX then
  948. NewCharPos := FLastCharPos;
  949. Lock;
  950. FTouched := True;
  951. try
  952. if (fCharPos = NewCharPos) and (fLinePos = NewLine) and
  953. (scCharPosValid in FFlags) and not (scuForceSet in UpdFlags)
  954. then begin
  955. // Lines may have changed, so the other pos can be invalid
  956. if not (scuNoInvalidate in UpdFlags) then
  957. Exclude(FFlags, scBytePosValid);
  958. exit;
  959. end;
  960. if NewLine > FLines.Count then begin
  961. NewLine := FLines.Count;
  962. Exclude(UpdFlags, scuNoInvalidate);
  963. end;
  964. if NewLine < 1 then begin // Only allowed, if Lines.Count = 0
  965. NewLine := 1;
  966. if (NewCharPos > 1) and (FAllowPastEOL or (FForcePastEOL > 0))
  967. then MaxPhysX := GetMaxLeftPastEOL
  968. else MaxPhysX := 1;
  969. if NewCharPos > MaxPhysX then
  970. NewCharPos := MaxPhysX;
  971. NewLogCharPos := NewCharPos;
  972. Offs := 0;
  973. Exclude(UpdFlags, scuNoInvalidate);
  974. end else begin
  975. if FAdjustToNextChar or (FForceAdjustToNextChar > 0) then
  976. NewLogCharPos := Lines.LogPhysConvertor.PhysicalToLogical(NewLine-1, NewCharPos, Offs, cspDefault, [lpfAdjustToNextChar])
  977. else
  978. NewLogCharPos := Lines.LogPhysConvertor.PhysicalToLogical(NewLine-1, NewCharPos, Offs, cspDefault, [lpfAdjustToCharBegin]);
  979. Offs := Lines.LogPhysConvertor.UnAdjustedPhysToLogColOffs;
  980. L := Lines[NewLine - 1];
  981. if (Offs > 0) and (not FSkipTabs) and (L[NewLogCharPos] = #9) then begin
  982. // get the unadjusted result
  983. NewLogCharPos := Lines.LogPhysConvertor.UnAdjustedPhysToLogResult
  984. end
  985. else begin
  986. // get adjusted Result
  987. NewCharPos := Lines.LogPhysConvertor.AdjustedPhysToLogOrigin;
  988. Offs := 0;
  989. end;
  990. LogEolPos := length(L)+1;
  991. if NewLogCharPos > LogEolPos then begin
  992. if FAllowPastEOL or (FForcePastEOL > 0) then begin
  993. MaxPhysX := GetMaxLeftPastEOL;
  994. if NewCharPos > MaxPhysX then begin
  995. NewLogCharPos := NewLogCharPos - (NewCharPos - MaxPhysX);
  996. NewCharPos := MaxPhysX;
  997. Exclude(UpdFlags, scuNoInvalidate);
  998. end;
  999. end
  1000. else begin
  1001. NewCharPos := NewCharPos - (NewLogCharPos - LogEolPos);
  1002. NewLogCharPos := LogEolPos;
  1003. Exclude(UpdFlags, scuNoInvalidate);
  1004. end;
  1005. end;
  1006. end;
  1007. if NewCharPos < 1 then begin
  1008. NewCharPos := 1;
  1009. Exclude(UpdFlags, scuNoInvalidate);
  1010. end;
  1011. inherited InternalSetLineCharPos(NewLine, NewCharPos, UpdFlags);
  1012. inherited InternalSetLineByterPos(NewLine, NewLogCharPos, Offs, [scuNoInvalidate, scuChangedX]);
  1013. if (scuChangedX in UpdFlags) or (not FKeepCaretX) then
  1014. FLastCharPos := FCharPos;
  1015. finally
  1016. Unlock;
  1017. end;
  1018. end;
  1019. procedure TSynEditCaret.InternalSetLineByterPos(NewLine, NewBytePos, NewByteOffs: Integer;
  1020. UpdFlags: TSynEditCaretUpdateFlags);
  1021. var
  1022. MaxPhysX, NewCharPos, LogEolPos: Integer;
  1023. L: String;
  1024. begin
  1025. if not (scuChangedX in UpdFlags) and FKeepCaretX then begin
  1026. Exclude(UpdFlags, scuNoInvalidate);
  1027. InternalSetLineCharPos(NewLine, FLastCharPos, UpdFlags);
  1028. exit;
  1029. end;
  1030. Lock;
  1031. FTouched := True;
  1032. try
  1033. if (FBytePos = NewBytePos) and (FBytePosOffset = NewByteOffs) and
  1034. (FLinePos = NewLine) and (scBytePosValid in FFlags) and not (scuForceSet in UpdFlags)
  1035. then begin
  1036. // Lines may have changed, so the other pos can be invalid
  1037. if not (scuNoInvalidate in UpdFlags) then
  1038. Exclude(FFlags, scCharPosValid);
  1039. exit;
  1040. end;
  1041. if NewLine > FLines.Count then begin
  1042. NewLine := FLines.Count;
  1043. Exclude(UpdFlags, scuNoInvalidate);
  1044. end;
  1045. if NewLine < 1 then begin // Only allowed, if Lines.Count = 0
  1046. L := '';
  1047. NewLine := 1;
  1048. LogEolPos := 1;
  1049. if (NewBytePos > 1) and (FAllowPastEOL or (FForcePastEOL > 0))
  1050. then MaxPhysX := GetMaxLeftPastEOL
  1051. else MaxPhysX := 1;
  1052. if NewBytePos > MaxPhysX then
  1053. NewBytePos := MaxPhysX;
  1054. NewByteOffs := 0;
  1055. NewCharPos := NewBytePos;
  1056. Exclude(UpdFlags, scuNoInvalidate);
  1057. end else begin
  1058. L := Lines[NewLine - 1];
  1059. LogEolPos := length(L)+1;
  1060. if (NewBytePos > LogEolPos) then begin
  1061. if not(FAllowPastEOL or (FForcePastEOL > 0)) then
  1062. NewBytePos := LogEolPos;
  1063. NewByteOffs := 0;
  1064. end
  1065. else
  1066. if (NewByteOffs > 0) and ( (FSkipTabs) or (L[NewBytePos] <> #9) ) then
  1067. NewByteOffs := 0;
  1068. if FAdjustToNextChar or (FForceAdjustToNextChar > 0) then
  1069. NewCharPos := Lines.LogPhysConvertor.LogicalToPhysical(NewLine-1, NewBytePos, NewByteOffs, cslDefault, [lpfAdjustToNextChar])
  1070. else
  1071. NewCharPos := Lines.LogPhysConvertor.LogicalToPhysical(NewLine-1, NewBytePos, NewByteOffs, cslDefault, [lpfAdjustToCharBegin]);
  1072. NewBytePos := Lines.LogPhysConvertor.AdjustedLogToPhysOrigin;
  1073. if (NewBytePos > LogEolPos) then begin
  1074. MaxPhysX := GetMaxLeftPastEOL;
  1075. if NewCharPos > MaxPhysX then begin
  1076. NewBytePos := NewBytePos - (NewCharPos - MaxPhysX);
  1077. NewCharPos := MaxPhysX;
  1078. Exclude(UpdFlags, scuNoInvalidate);
  1079. end;
  1080. end;
  1081. end;
  1082. if NewBytePos < 1 then begin
  1083. NewBytePos := 1;
  1084. Exclude(UpdFlags, scuNoInvalidate);
  1085. end;
  1086. inherited InternalSetLineByterPos(NewLine, NewBytePos, NewByteOffs, UpdFlags);
  1087. inherited InternalSetLineCharPos(NewLine, NewCharPos, [scuNoInvalidate, scuChangedX]);
  1088. if (scuChangedX in UpdFlags) and FKeepCaretX then
  1089. FLastCharPos := FCharPos;
  1090. finally
  1091. Unlock;
  1092. end;
  1093. end;
  1094. function TSynEditCaret.GetOldLineCharPos: TPoint;
  1095. begin
  1096. Result := Point(FOldCharPos, FOldLinePos);
  1097. end;
  1098. function TSynEditCaret.GetOldLineBytePos: TPoint;
  1099. begin
  1100. Result := FLines.PhysicalToLogicalPos(OldLineCharPos);
  1101. end;
  1102. function TSynEditCaret.GetOldFullLogicalPos: TLogCaretPoint;
  1103. begin
  1104. Result.Y := FOldLinePos;
  1105. Result.X := FLines.LogPhysConvertor.PhysicalToLogical(ToIdx(FOldLinePos), FOldCharPos, Result.Offs);
  1106. end;
  1107. procedure TSynEditCaret.SetAllowPastEOL(const AValue: Boolean);
  1108. begin
  1109. if FAllowPastEOL = AValue then exit;
  1110. FAllowPastEOL := AValue;
  1111. if not FAllowPastEOL then begin
  1112. // TODO: this would set x=LastX
  1113. //if scBytePosValid in FFlags then
  1114. // InternalSetLineByterPos(FLinePos, FBytePos, FBytePosOffset, [scuForceSet]); // NO scuChangedX => FLastCharPos is kept
  1115. //else
  1116. ValidateCharPos;
  1117. InternalSetLineCharPos(FLinePos, FCharPos, [scuForceSet]); // NO scuChangedX => FLastCharPos is kept
  1118. end;
  1119. end;
  1120. procedure TSynEditCaret.SetKeepCaretX(const AValue: Boolean);
  1121. begin
  1122. if FKeepCaretX = AValue then exit;
  1123. FKeepCaretX := AValue;
  1124. if FKeepCaretX then begin
  1125. ValidateCharPos;
  1126. FLastCharPos := FCharPos;
  1127. end;
  1128. end;
  1129. procedure TSynEditCaret.SetSkipTabs(const AValue: Boolean);
  1130. begin
  1131. if FSkipTabs = AValue then exit;
  1132. FSkipTabs := AValue;
  1133. if FSkipTabs then begin
  1134. Lock;
  1135. AdjustToChar;
  1136. Unlock;
  1137. end;
  1138. end;
  1139. procedure TSynEditCaret.IncForcePastEOL;
  1140. begin
  1141. inc(FForcePastEOL);
  1142. end;
  1143. procedure TSynEditCaret.DecForcePastEOL;
  1144. begin
  1145. dec(FForcePastEOL);
  1146. end;
  1147. procedure TSynEditCaret.IncForceAdjustToNextChar;
  1148. begin
  1149. Inc(FForceAdjustToNextChar);
  1150. end;
  1151. procedure TSynEditCaret.DecForceAdjustToNextChar;
  1152. begin
  1153. Dec(FForceAdjustToNextChar);
  1154. end;
  1155. procedure TSynEditCaret.IncAutoMoveOnEdit;
  1156. begin
  1157. if FAutoMoveOnEdit = 0 then begin
  1158. RegisterLinesEditedHandler;
  1159. ValidateBytePos;
  1160. end;
  1161. inc(FAutoMoveOnEdit);
  1162. end;
  1163. procedure TSynEditCaret.DecAutoMoveOnEdit;
  1164. begin
  1165. dec(FAutoMoveOnEdit);
  1166. end;
  1167. procedure TSynEditCaret.ChangeOnTouch;
  1168. begin
  1169. FChangeOnTouch := True;
  1170. if not Locked then
  1171. FTouched := False;
  1172. end;
  1173. procedure TSynEditCaret.Touch(aChangeOnTouch: Boolean);
  1174. begin
  1175. if aChangeOnTouch then
  1176. ChangeOnTouch;
  1177. FTouched := True;
  1178. end;
  1179. function TSynEditCaret.WasAtLineChar(aPoint: TPoint): Boolean;
  1180. begin
  1181. Result := (FOldLinePos = aPoint.y) and (FOldCharPos = aPoint.x);
  1182. end;
  1183. function TSynEditCaret.WasAtLineByte(aPoint: TPoint): Boolean;
  1184. begin
  1185. Result := (FOldLinePos = aPoint.y) and
  1186. (FLines.PhysicalToLogicalPos(Point(FOldCharPos, FOldLinePos)).X = aPoint.x);
  1187. end;
  1188. function TSynEditCaret.MoveHoriz(ACount: Integer): Boolean;
  1189. var
  1190. L: String;
  1191. CharWidths: TPhysicalCharWidths;
  1192. GotCharWidths: Boolean;
  1193. MaxOffs: Integer;
  1194. p: Integer;
  1195. NC: Boolean;
  1196. NF: Integer;
  1197. function GetMaxOffs(AlogPos: Integer): Integer;
  1198. begin
  1199. if not GotCharWidths then
  1200. CharWidths := FLines.GetPhysicalCharWidths(Pchar(L), length(L), FLinePos-1);
  1201. GotCharWidths := True;
  1202. Result := CharWidths[AlogPos-1];
  1203. end;
  1204. begin
  1205. GotCharWidths := False;
  1206. L := LineText;
  1207. ValidateBytePos;
  1208. If ACount > 0 then begin
  1209. if (FBytePos <= length(L)) and (L[FBytePos] = #9) and (not FSkipTabs) then
  1210. MaxOffs := GetMaxOffs(FBytePos) - 1
  1211. else
  1212. MaxOffs := 0;
  1213. while ACount > 0 do begin
  1214. if FBytePosOffset < MaxOffs then
  1215. inc(FBytePosOffset)
  1216. else begin
  1217. if (FBytePos > length(L)) and not (FAllowPastEOL or (FForcePastEOL > 0)) then
  1218. break;
  1219. FBytePos := FLines.LogicPosAddChars(L, FBytePos, 1, True);
  1220. FBytePosOffset := 0;
  1221. if (FBytePos <= length(L)) and (L[FBytePos] = #9) and (not FSkipTabs) then
  1222. MaxOffs := GetMaxOffs(FBytePos) - 1
  1223. else
  1224. MaxOffs := 0;
  1225. end;
  1226. dec(ACount);
  1227. end;
  1228. Result := ACount = 0;
  1229. p := FBytePos;
  1230. IncForceAdjustToNextChar;
  1231. InternalSetLineByterPos(FLinePos, FBytePos, FBytePosOffset, [scuChangedX, scuForceSet]);
  1232. DecForceAdjustToNextChar;
  1233. if p > FBytePos then
  1234. Result := False; // MaxLeftChar
  1235. end
  1236. else begin
  1237. while ACount < 0 do begin
  1238. if FBytePosOffset > 0 then
  1239. dec(FBytePosOffset)
  1240. else begin
  1241. if FBytePos = 1 then
  1242. break;
  1243. FBytePos := FLines.LogicPosAddChars(L, FBytePos, -1, True);
  1244. if (FBytePos <= length(L)) and (L[FBytePos] = #9) and (not FSkipTabs) then
  1245. FBytePosOffset := GetMaxOffs(FBytePos) - 1
  1246. else
  1247. FBytePosOffset := 0;
  1248. end;
  1249. inc(ACount);
  1250. end;
  1251. Result := ACount = 0;
  1252. NC := FAdjustToNextChar;
  1253. NF := FForceAdjustToNextChar;
  1254. FAdjustToNextChar := False;
  1255. FForceAdjustToNextChar := 0;
  1256. InternalSetLineByterPos(FLinePos, FBytePos, FBytePosOffset, [scuChangedX, scuForceSet]);
  1257. FAdjustToNextChar := NC;
  1258. FForceAdjustToNextChar := NF;
  1259. end;
  1260. end;
  1261. { TSynEditSelection }
  1262. constructor TSynEditSelection.Create(ALines : TSynEditStrings; aActOnLineChanges: Boolean);
  1263. begin
  1264. Inherited Create(ALines);
  1265. FOnBeforeSetSelText := TSynBeforeSetSelTextList.Create;
  1266. FInternalCaret := TSynEditBaseCaret.Create;
  1267. FInternalCaret.Lines := FLines;
  1268. FActiveSelectionMode := smNormal;
  1269. FStartLinePos := 1;
  1270. FStartBytePos := 1;
  1271. FAltStartLinePos := -1;
  1272. FAltStartBytePos := -1;
  1273. FEndLinePos := 1;
  1274. FEndBytePos := 1;
  1275. FEnabled := True;
  1276. FHookedLines := aActOnLineChanges;
  1277. FIsSettingText := False;
  1278. if FHookedLines then begin
  1279. FLines.AddEditHandler(@DoLinesEdited);
  1280. FLines.AddChangeHandler(senrLineChange, @LineChanged);
  1281. end;
  1282. end;
  1283. destructor TSynEditSelection.Destroy;
  1284. begin
  1285. FreeAndNil(FOnBeforeSetSelText);
  1286. FreeAndNil(FInternalCaret);
  1287. if FHookedLines then begin
  1288. FLines.RemoveEditHandler(@DoLinesEdited);
  1289. FLines.RemoveChangeHandler(senrLineChange, @LineChanged);
  1290. end;
  1291. inherited Destroy;
  1292. end;
  1293. procedure TSynEditSelection.AssignFrom(Src: TSynEditSelection);
  1294. begin
  1295. //FEnabled := src.FEnabled;
  1296. FHide := src.FHide;
  1297. FActiveSelectionMode := src.FActiveSelectionMode;
  1298. FSelectionMode := src.FSelectionMode;
  1299. FStartLinePos := src.FStartLinePos; // 1 based
  1300. FStartBytePos := src.FStartBytePos; // 1 based
  1301. FEndLinePos := src.FEndLinePos; // 1 based
  1302. FEndBytePos := src.FEndBytePos; // 1 based
  1303. FPersistent := src.FPersistent;
  1304. end;
  1305. procedure TSynEditSelection.AdjustAfterTrimming;
  1306. begin
  1307. if FStartBytePos > Length(FLines[FStartLinePos-1]) + 1 then
  1308. FStartBytePos := Length(FLines[FStartLinePos-1]) + 1;
  1309. if FEndBytePos > Length(FLines[FEndLinePos-1]) + 1 then
  1310. FEndBytePos := Length(FLines[FEndLinePos-1]) + 1;
  1311. // Todo: Call ChangeNotification
  1312. end;
  1313. procedure TSynEditSelection.DoLock;
  1314. begin
  1315. inherited DoLock;
  1316. FLastCarePos := Point(-1, -1);
  1317. end;
  1318. procedure TSynEditSelection.DoUnlock;
  1319. begin
  1320. inherited DoUnlock;
  1321. FLastCarePos := Point(-1, -1);
  1322. end;
  1323. function TSynEditSelection.GetSelText : string;
  1324. function CopyPadded(const S: string; Index, Count: integer): string;
  1325. var
  1326. SrcLen: Integer;
  1327. DstLen: integer;
  1328. P: PChar;
  1329. begin
  1330. SrcLen := Length(S);
  1331. DstLen := Index + Count;
  1332. if SrcLen >= DstLen then
  1333. Result := Copy(S, Index, Count)
  1334. else begin
  1335. SetLength(Result, DstLen);
  1336. P := PChar(Pointer(Result));
  1337. StrPCopy(P, Copy(S, Index, Count));
  1338. Inc(P, SrcLen);
  1339. FillChar(P^, DstLen - Srclen, $20);
  1340. end;
  1341. end;
  1342. procedure CopyAndForward(const S: string; Index, Count: Integer; var P:
  1343. PChar);
  1344. var
  1345. pSrc: PChar;
  1346. SrcLen: Integer;
  1347. DstLen: Integer;
  1348. begin
  1349. SrcLen := Length(S);
  1350. if (Index <= SrcLen) and (Count > 0) then begin
  1351. Dec(Index);
  1352. pSrc := PChar(Pointer(S)) + Index;
  1353. DstLen := Min(SrcLen - Index, Count);
  1354. Move(pSrc^, P^, DstLen);
  1355. Inc(P, DstLen);
  1356. P^ := #0;
  1357. end;
  1358. end;
  1359. procedure CopyPaddedAndForward(const S: string; Index, Count: Integer;
  1360. var P: PChar);
  1361. var
  1362. OldP: PChar;
  1363. Len: Integer;
  1364. begin
  1365. OldP := P;
  1366. CopyAndForward(S, Index, Count, P);
  1367. Len := Count - (P - OldP);
  1368. FillChar(P^, Len, #$20);
  1369. Inc(P, Len);
  1370. end;
  1371. var
  1372. First, Last, TotalLen: Integer;
  1373. ColFrom, ColTo: Integer;
  1374. I: Integer;
  1375. P: PChar;
  1376. C1, C2: Integer;
  1377. Col, Len: array of Integer;
  1378. begin
  1379. if not SelAvail then
  1380. Result := ''
  1381. else begin
  1382. if IsBackwardSel then begin
  1383. ColFrom := FEndBytePos;
  1384. First := FEndLinePos - 1;
  1385. ColTo := FStartBytePos;
  1386. Last := FStartLinePos - 1;
  1387. end else begin
  1388. ColFrom := FStartBytePos;
  1389. First := FStartLinePos - 1;
  1390. ColTo := FEndBytePos;
  1391. Last := FEndLinePos - 1;
  1392. end;
  1393. TotalLen := 0;
  1394. case ActiveSelectionMode of
  1395. smNormal:
  1396. if (First = Last) then begin
  1397. Result := Copy(FLines[First], ColFrom, ColTo - ColFrom);
  1398. I := (ColTo - ColFrom) - length(Result);
  1399. if I > 0 then
  1400. Result := Result + StringOfChar(' ', I);
  1401. end else begin
  1402. // step1: calculate total length of result string
  1403. TotalLen := Max(0, Length(FLines[First]) - ColFrom + 1);
  1404. for i := First + 1 to Last - 1 do
  1405. Inc(TotalLen, Length(FLines[i]));
  1406. Inc(TotalLen, ColTo - 1);
  1407. Inc(TotalLen, Length(sLineBreak) * (Last - First));
  1408. // step2: build up result string
  1409. SetLength(Result, TotalLen);
  1410. P := PChar(Pointer(Result));
  1411. CopyAndForward(FLines[First], ColFrom, MaxInt, P);
  1412. CopyAndForward(sLineBreak, 1, MaxInt, P);
  1413. for i := First + 1 to Last - 1 do begin
  1414. CopyAndForward(FLines[i], 1, MaxInt, P);
  1415. CopyAndForward(sLineBreak, 1, MaxInt, P);
  1416. end;
  1417. CopyPaddedAndForward(FLines[Last], 1, ColTo - 1, P);
  1418. end;
  1419. smColumn:
  1420. begin
  1421. // Calculate the byte positions for each line
  1422. SetLength(Col, Last - First + 1);
  1423. SetLength(Len, Last - First + 1);
  1424. FInternalCaret.Invalidate;
  1425. FInternalCaret.LineBytePos := FirstLineBytePos;
  1426. C1 := FInternalCaret.CharPos;
  1427. FInternalCaret.LineBytePos := LastLineBytePos;
  1428. C2 := FInternalCaret.CharPos;
  1429. if C1 > C2 then
  1430. SwapInt(C1, C2);
  1431. TotalLen := 0;
  1432. for i := First to Last do begin
  1433. FInternalCaret.LineCharPos := Point(C1, i + 1);
  1434. Col[i - First] := FInternalCaret.BytePos;
  1435. FInternalCaret.LineCharPos := Point(C2, i + 1);
  1436. Len[i - First] := Max(0, FInternalCaret.BytePos - Col[i - First]);
  1437. Inc(TotalLen, Len[i - First]);
  1438. end;
  1439. Inc(TotalLen, Length(LineEnding) * (Last - First));
  1440. // build up result string
  1441. SetLength(Result, TotalLen);
  1442. P := PChar(Pointer(Result));
  1443. for i := First to Last do begin
  1444. CopyPaddedAndForward(FLines[i], Col[i-First], Len[i-First], P);
  1445. if i < Last then
  1446. CopyAndForward(LineEnding, 1, MaxInt, P);
  1447. end;
  1448. end;
  1449. smLine:
  1450. begin
  1451. // If block selection includes LastLine,
  1452. // line break code(s) of the last line will not be added.
  1453. // step1: calclate total length of result string
  1454. for i := First to Last do
  1455. Inc(TotalLen, Length(FLines[i]) + Length(LineEnding));
  1456. if Last = FLines.Count - 1 then
  1457. Dec(TotalLen, Length(LineEnding));
  1458. // step2: build up result string
  1459. SetLength(Result, TotalLen);
  1460. P := PChar(Pointer(Result));
  1461. for i := First to Last - 1 do begin
  1462. CopyAndForward(FLines[i], 1, MaxInt, P);
  1463. CopyAndForward(LineEnding, 1, MaxInt, P);
  1464. end;
  1465. CopyAndForward(FLines[Last], 1, MaxInt, P);
  1466. if Last < FLines.Count - 1 then
  1467. CopyAndForward(LineEnding, 1, MaxInt, P);
  1468. end;
  1469. end;
  1470. end;
  1471. end;
  1472. procedure TSynEditSelection.SetSelText(const Value : string);
  1473. begin
  1474. SetSelTextPrimitive(FActiveSelectionMode, PChar(Value));
  1475. end;
  1476. procedure TSynEditSelection.DoCaretChanged(Sender: TObject);
  1477. procedure SwapAltStart;
  1478. var
  1479. x, y: Integer;
  1480. begin
  1481. if FAltStartLinePos < FStartLinePos then
  1482. FInvalidateLinesMethod(FAltStartLinePos, FStartLinePos)
  1483. else
  1484. FInvalidateLinesMethod(FStartLinePos, FAltStartLinePos);
  1485. y := FAltStartLinePos;
  1486. x := FAltStartBytePos;
  1487. FAltStartLinePos := FStartLinePos;
  1488. FAltStartBytePos := FStartBytePos;
  1489. FStartLinePos := y;
  1490. FStartBytePos := x;
  1491. end;
  1492. procedure FixMinimumSelection;
  1493. begin
  1494. if FAltStartLinePos < 0 then exit;
  1495. case ComparePoints(Point(FAltStartBytePos, FAltStartLinePos), StartLineBytePos) of
  1496. -1: begin // alt is before start
  1497. if ComparePoints(StartLineBytePos, EndLineBytePos) <= 0 then
  1498. SwapAltStart;
  1499. end;
  1500. 1: begin // start is before alt
  1501. if ComparePoints(StartLineBytePos, EndLineBytePos) >= 0 then
  1502. SwapAltStart;
  1503. end;
  1504. end;
  1505. end;
  1506. var
  1507. f: Boolean;
  1508. begin
  1509. // FIgnoreNextCaretMove => caret skip selection
  1510. if FIgnoreNextCaretMove then begin
  1511. FIgnoreNextCaretMove := False;
  1512. FLastCarePos := Point(-1, -1);
  1513. exit;
  1514. end;
  1515. if (FCaret.IsAtLineByte(StartLineBytePos) or
  1516. FCaret.IsAtLineByte(EndLineBytePos)) and
  1517. FCaret.WasAtLineChar(FLastCarePos)
  1518. then
  1519. exit;
  1520. FLastCarePos := Point(-1, -1);
  1521. if FAutoExtend or FStickyAutoExtend then begin
  1522. f := FStickyAutoExtend;
  1523. if (not FHide) and (FCaret.WasAtLineByte(EndLineBytePos)) then begin
  1524. SetEndLineBytePos(FCaret.LineBytePos);
  1525. FixMinimumSelection;
  1526. end
  1527. else
  1528. if (not FHide) and (FCaret.WasAtLineByte(StartLineBytePos)) then begin
  1529. AdjustStartLineBytePos(FCaret.LineBytePos);
  1530. FAltStartLinePos := -1;
  1531. FAltStartBytePos := -1;
  1532. end
  1533. else begin
  1534. StartLineBytePos := Point(FCaret.OldCharPos, FCaret.OldLinePos);
  1535. EndLineBytePos := FCaret.LineBytePos;
  1536. if Persistent and IsBackwardSel then
  1537. SortSelectionPoints;
  1538. end;
  1539. FStickyAutoExtend := f;
  1540. exit;
  1541. end;
  1542. if FPersistent or (FPersistentLock > 0) then
  1543. exit;
  1544. StartLineBytePos := FCaret.LineBytePos;
  1545. end;
  1546. procedure TSynEditSelection.LineChanged(Sender: TSynEditStrings; AIndex, ACount: Integer);
  1547. var
  1548. i, i2: Integer;
  1549. begin
  1550. if (FCaret <> nil) and (not FCaret.AllowPastEOL) and (not FIsSettingText) then begin
  1551. i := ToPos(AIndex);
  1552. i2 := i + ACount - 1;
  1553. //AdjustAfterTrimming;
  1554. if (FStartLinePos >= i) and (FStartLinePos <= i2) then
  1555. if FStartBytePos > Length(FLines[FStartLinePos-1]) + 1 then
  1556. FStartBytePos := Length(FLines[FStartLinePos-1]) + 1;
  1557. if (FEndLinePos >= i) and (FEndLinePos <= i2) then
  1558. if FEndBytePos > Length(FLines[FEndLinePos-1]) + 1 then
  1559. FEndBytePos := Length(FLines[FEndLinePos-1]) + 1;
  1560. end;
  1561. end;
  1562. procedure TSynEditSelection.DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
  1563. aLineBrkCnt: Integer; aText: String);
  1564. function AdjustPoint(aPoint: Tpoint; AIsStart: Boolean): TPoint; inline;
  1565. begin
  1566. Result := aPoint;
  1567. if aLineBrkCnt < 0 then begin
  1568. (* Lines Deleted *)
  1569. if aPoint.y > aLinePos then begin
  1570. Result.y := Max(aLinePos, Result.y + aLineBrkCnt);
  1571. if Result.y = aLinePos then
  1572. Result.x := Result.x + aBytePos - 1;
  1573. end;
  1574. end
  1575. else
  1576. if aLineBrkCnt > 0 then begin
  1577. (* Lines Inserted *)
  1578. if (aPoint.y = aLinePos) and (aPoint.x >= aBytePos) then begin
  1579. Result.x := Result.x - aBytePos + 1;
  1580. Result.y := Result.y + aLineBrkCnt;
  1581. end;
  1582. if aPoint.y > aLinePos then begin
  1583. Result.y := Result.y + aLineBrkCnt;
  1584. end;
  1585. end
  1586. else
  1587. if aCount <> 0 then begin
  1588. (* Chars Insert/Deleted *)
  1589. if (aPoint.y = aLinePos) then begin
  1590. if (FWeakPersistentIdx > 0) and (FWeakPersistentIdx > FStrongPersistentIdx) then begin
  1591. if (AIsStart and (aPoint.x >= aBytePos)) or
  1592. (not AIsStart and (aPoint.x > aBytePos))
  1593. then
  1594. Result.x := Max(aBytePos, Result.x + aCount);
  1595. end
  1596. else
  1597. if (FStrongPersistentIdx > 0) then begin
  1598. if (AIsStart and (aPoint.x > aBytePos)) or
  1599. (not AIsStart and (aPoint.x >= aBytePos))
  1600. then
  1601. Result.x := Max(aBytePos, Result.x + aCount);
  1602. end
  1603. else begin
  1604. if (aPoint.x >= aBytePos) then
  1605. Result.x := Max(aBytePos, Result.x + aCount);
  1606. end;
  1607. end;
  1608. end;
  1609. end;
  1610. begin
  1611. if FIsSettingText then exit;
  1612. if FPersistent or (FPersistentLock > 0) or
  1613. ((FCaret <> nil) and (not FCaret.Locked))
  1614. then begin
  1615. if FActiveSelectionMode <> smColumn then begin // TODO: adjust ypos, height in smColumn mode
  1616. AdjustStartLineBytePos(AdjustPoint(StartLineBytePos, True));
  1617. EndLineBytePos := AdjustPoint(EndLineBytePos, False);
  1618. end;
  1619. // Todo: Change Lines in smColumn
  1620. end
  1621. else begin
  1622. // Change the Selection, if change was made by owning SynEdit (Caret.Locked)
  1623. // (InternalSelection has no Caret)
  1624. if (FCaret <> nil) and (FCaret.Locked) then
  1625. StartLineBytePos := FCaret.LineBytePos;
  1626. end;
  1627. end;
  1628. procedure TSynEditSelection.SetSelTextPrimitive(PasteMode : TSynSelectionMode;
  1629. Value : PChar; AReplace: Boolean = False);
  1630. var
  1631. BB, BE: TPoint;
  1632. procedure DeleteSelection;
  1633. var
  1634. y, l, r, xb, xe: Integer;
  1635. Str: string;
  1636. Start, P: PChar;
  1637. //LogCaretXY: TPoint;
  1638. begin
  1639. case ActiveSelectionMode of
  1640. smNormal, smLine:
  1641. begin
  1642. if FLines.Count > 0 then begin
  1643. if AReplace and (Value <> nil) then begin
  1644. // AReplace = True
  1645. while Value^ <> #0 do begin
  1646. Start := PChar(Value);
  1647. P := GetEOL(Start);
  1648. Value := P;
  1649. if Value^ = #13 then Inc(Value);
  1650. if Value^ = #10 then Inc(Value);
  1651. SetString(Str, Start, P - Start);
  1652. if BE.y > BB.y then begin
  1653. // FLines.EditDelete(BB.x, BB.Y, 1+Length(FLines[BB.y-1]) - BB.x);
  1654. //// if Str <> '' then
  1655. // FLines.EditInsert(BB.x, BB.Y, Str);
  1656. FLines.EditReplace(BB.x, BB.Y, 1+Length(FLines[BB.y-1]) - BB.x, Str);
  1657. if (PasteMode = smLine) or (Value > P) then begin
  1658. inc(BB.y);
  1659. BB.x := 1;
  1660. end
  1661. else
  1662. BB.X := BB.X + length(Str);
  1663. end
  1664. else begin
  1665. // BE will be block-.nd, also used by SynEdit to set caret
  1666. if (ActiveSelectionMode = smLine) or (Value > P) then begin
  1667. FLines.EditReplace(BB.x, BB.Y, BE.x - BB.x, Str);
  1668. FLines.EditLineBreak(BB.x+length(Str), BB.Y);
  1669. //FLines.EditDelete(BB.x, BB.Y, BE.x - BB.x);
  1670. //FLines.EditLineBreak(BB.x, BB.Y);
  1671. //FLines.EditInsert(BB.x, BB.Y, Str);
  1672. inc(BE.y);
  1673. BE.x := 1;
  1674. end
  1675. else begin
  1676. //FLines.EditDelete(BB.x, BB.Y, BE.x - BB.x);
  1677. // if Str <> '' then
  1678. //FLines.EditInsert(BB.x, BB.Y, Str);
  1679. FLines.EditReplace(BB.x, BB.Y, BE.x - BB.x, Str);
  1680. BE.X := BB.X + length(Str);
  1681. end;
  1682. BB := BE; // end of selection
  1683. end;
  1684. if (BB.Y = BE.Y) and (BB.X = BE.X) then begin
  1685. FInternalCaret.LineBytePos := BB;
  1686. exit;
  1687. end;
  1688. end;
  1689. end;
  1690. // AReplace = False
  1691. if BE.Y > BB.Y + 1 then begin
  1692. FLines.EditLinesDelete(BB.Y + 1, BE.Y - BB.Y - 1);
  1693. BE.Y := BB.Y + 1;
  1694. end;
  1695. if BE.Y > BB.Y then begin
  1696. l := length(FLines[BB.Y - 1]);
  1697. BE.X := BE.X + Max(l, BB.X - 1);
  1698. FLines.EditLineJoin(BB.Y, StringOfChar(' ', Max(0, BB.X - (l+1))));
  1699. BE.Y := BB.Y;
  1700. end;
  1701. if BE.X <> BB.X then
  1702. FLines.EditDelete(BB.X, BB.Y, BE.X - BB.X);
  1703. end;
  1704. FInternalCaret.LineBytePos := BB;
  1705. end;
  1706. smColumn:
  1707. begin
  1708. // AReplace has no effect
  1709. FInternalCaret.LineBytePos := BB;
  1710. l := FInternalCaret.CharPos;
  1711. FInternalCaret.LineBytePos := BE;
  1712. r := FInternalCaret.CharPos;
  1713. // swap l, r if needed
  1714. if l > r then
  1715. SwapInt(l, r);
  1716. for y := BB.Y to BE.Y do begin
  1717. FInternalCaret.LineCharPos := Point(l, y);
  1718. xb := FInternalCaret.BytePos;
  1719. FInternalCaret.LineCharPos := Point(r, y);
  1720. xe := Min(FInternalCaret.BytePos, 1 + length(FInternalCaret.LineText));
  1721. if xe > xb then
  1722. FLines.EditDelete(xb, y, xe - xb);
  1723. end;
  1724. FInternalCaret.LineCharPos := Point(l, BB.Y);
  1725. BB := FInternalCaret.LineBytePos;
  1726. // Column deletion never removes a line entirely,
  1727. // so no (vertical) mark updating is needed here.
  1728. end;
  1729. end;
  1730. end;
  1731. procedure InsertText;
  1732. function CountLines(p: PChar): integer;
  1733. begin
  1734. Result := 0;
  1735. while p^ <> #0 do begin
  1736. if p^ = #13 then
  1737. Inc(p);
  1738. if p^ = #10 then
  1739. Inc(p);
  1740. Inc(Result);
  1741. p := GetEOL(p);
  1742. end;
  1743. end;
  1744. function InsertNormal: Integer;
  1745. var
  1746. Str: string;
  1747. Start: PChar;
  1748. P: PChar;
  1749. LogCaretXY: TPoint;
  1750. begin
  1751. Result := 0;
  1752. LogCaretXY := FInternalCaret.LineBytePos;
  1753. Start := PChar(Value);
  1754. P := GetEOL(Start);
  1755. if P^ = #0 then begin
  1756. FLines.EditInsert(LogCaretXY.X, LogCaretXY.Y, Value);
  1757. FInternalCaret.BytePos := FInternalCaret.BytePos + Length(Value);
  1758. end else begin
  1759. FLines.EditLineBreak(LogCaretXY.X, LogCaretXY.Y);
  1760. if (P <> Start) or (LogCaretXY.X > 1 + length(FLines[ToIdx(LogCaretXY.Y)])) then begin
  1761. SetString(Str, Value, P - Start);
  1762. FLines.EditInsert(LogCaretXY.X, LogCaretXY.Y, Str);
  1763. end
  1764. else
  1765. Str := '';
  1766. Result := CountLines(P);
  1767. if Result > 1 then
  1768. FLines.EditLinesInsert(LogCaretXY.Y + 1, Result - 1);
  1769. while P^ <> #0 do begin
  1770. if P^ = #13 then
  1771. Inc(P);
  1772. if P^ = #10 then
  1773. Inc(P);
  1774. LogCaretXY.Y := LogCaretXY.Y + 1;
  1775. Start := P;
  1776. P := GetEOL(Start);
  1777. if P <> Start then begin
  1778. SetString(Str, Start, P - Start);
  1779. FLines.EditInsert(1, LogCaretXY.Y, Str);
  1780. end
  1781. else
  1782. Str := '';
  1783. end;
  1784. FInternalCaret.LinePos := LogCaretXY.Y;
  1785. FInternalCaret.BytePos := 1 + Length(Str);
  1786. end;
  1787. end;
  1788. function InsertColumn: Integer;
  1789. var
  1790. Str: string;
  1791. Start: PChar;
  1792. P: PChar;
  1793. begin
  1794. // Insert string at current position
  1795. Result := 0;
  1796. Start := PChar(Value);
  1797. repeat
  1798. P := GetEOL(Start);
  1799. if P <> Start then begin
  1800. SetLength(Str, P - Start);
  1801. Move(Start^, Str[1], P - Start);
  1802. FLines.EditInsert(FInternalCaret.BytePos, FInternalCaret.LinePos, Str);
  1803. end;
  1804. if p^ in [#10,#13] then begin
  1805. if (p[1] in [#10,#13]) and (p[1]<>p^) then
  1806. inc(p,2)
  1807. else
  1808. Inc(P);
  1809. if FInternalCaret.LinePos = FLines.Count then
  1810. FLines.EditLinesInsert(FInternalCaret.LinePos + 1, 1);
  1811. // No need to inc result => adding at EOF
  1812. FInternalCaret.LinePos := FInternalCaret.LinePos + 1;
  1813. end;
  1814. Start := P;
  1815. until P^ = #0;
  1816. FInternalCaret.BytePos:= FInternalCaret.BytePos + Length(Str);
  1817. end;
  1818. function InsertLine: Integer;
  1819. var
  1820. Start: PChar;
  1821. P: PChar;
  1822. Str: string;
  1823. begin
  1824. Result := 0;
  1825. FInternalCaret.CharPos := 1;
  1826. // Insert string before current line
  1827. Start := PChar(Value);
  1828. repeat
  1829. P := GetEOL(Start);
  1830. if P <> Start then begin
  1831. SetLength(Str, P - Start);
  1832. Move(Start^, Str[1], P - Start);
  1833. end else
  1834. Str := '';
  1835. if (P^ = #0) then begin // Not a full line?
  1836. FLines.EditInsert(1, FInternalCaret.LinePos, Str);
  1837. FInternalCaret.BytePos := 1 + Length(Str);
  1838. end else begin
  1839. FLines.EditLinesInsert(FInternalCaret.LinePos, 1, Str);
  1840. FInternalCaret.LinePos := FInternalCaret.LinePos + 1;
  1841. Inc(Result);
  1842. if P^ = #13 then
  1843. Inc(P);
  1844. if P^ = #10 then
  1845. Inc(P);
  1846. Start := P;
  1847. end;
  1848. until P^ = #0;
  1849. end;
  1850. begin
  1851. if Value = '' then
  1852. Exit;
  1853. if FLines.Count = 0 then
  1854. FLines.Add('');
  1855. // Using a TStringList to do this would be easier, but if we're dealing
  1856. // with a large block of text, it would be very inefficient. Consider:
  1857. // Assign Value parameter to TStringList.Text: that parses through it and
  1858. // creates a copy of the string for each line it finds. That copy is passed
  1859. // to the Add method, which in turn creates a copy. Then, when you actually
  1860. // use an item in the list, that creates a copy to return to you. That's
  1861. // 3 copies of every string vs. our one copy below. I'd prefer no copies,
  1862. // but we aren't set up to work with PChars that well.
  1863. case PasteMode of
  1864. smNormal:
  1865. InsertNormal;
  1866. smColumn:
  1867. InsertColumn;
  1868. smLine:
  1869. InsertLine;
  1870. end;
  1871. end;
  1872. begin
  1873. FOnBeforeSetSelText.CallBeforeSetSelTextHandlers(Self, PasteMode, Value);
  1874. FIsSettingText := True;
  1875. FStickyAutoExtend := False;
  1876. FLines.BeginUpdate; // Todo: can we get here, without paintlock?
  1877. try
  1878. // BB is lower than BE
  1879. BB := FirstLineBytePos;
  1880. BE := LastLineBytePos;
  1881. FInternalCaret.Invalidate;
  1882. if SelAvail then begin
  1883. if FActiveSelectionMode = smLine then begin
  1884. BB.X := 1;
  1885. if BE.Y = FLines.Count then begin
  1886. // Keep the (CrLf of) last line, since no Line exists to replace it
  1887. BE.x := 1 + length(FLines[BE.Y - 1]);
  1888. end else begin
  1889. inc(BE.Y);
  1890. BE.x := 1;
  1891. end;
  1892. end;
  1893. DeleteSelection;
  1894. StartLineBytePos := BB; // deletes selection // calls selection changed
  1895. // Need to update caret (syncro edit follows on every edit)
  1896. if FCaret <> nil then
  1897. FCaret.LineCharPos := FInternalCaret.LineCharPos; // must equal BB
  1898. end
  1899. else
  1900. if FCaret <> nil then
  1901. StartLineBytePos := FCaret.LineBytePos;
  1902. FInternalCaret.LineBytePos := StartLineBytePos;
  1903. if (Value <> nil) and (Value[0] <> #0) then begin
  1904. InsertText;
  1905. StartLineBytePos := FInternalCaret.LineBytePos; // reset selection
  1906. end;
  1907. if FCaret <> nil then
  1908. FCaret.LineCharPos := FInternalCaret.LineCharPos;
  1909. finally
  1910. FLines.EndUpdate;
  1911. FIsSettingText := False;
  1912. end;
  1913. end;
  1914. function TSynEditSelection.GetStartLineBytePos : TPoint;
  1915. begin
  1916. Result.y := FStartLinePos;
  1917. Result.x := FStartBytePos;
  1918. end;
  1919. procedure TSynEditSelection.SetEnabled(const Value : Boolean);
  1920. begin
  1921. if FEnabled = Value then exit;
  1922. FEnabled := Value;
  1923. if not Enabled then SetStartLineBytePos(StartLineBytePos);
  1924. end;
  1925. procedure TSynEditSelection.ConstrainStartLineBytePos(var Value: TPoint);
  1926. begin
  1927. Value.y := MinMax(Value.y, 1, fLines.Count);
  1928. if (FCaret = nil) or FCaret.AllowPastEOL then
  1929. Value.x := Max(Value.x, 1)
  1930. else
  1931. Value.x := MinMax(Value.x, 1, length(Lines[Value.y - 1])+1);
  1932. if (ActiveSelectionMode = smNormal) then begin
  1933. if (Value.y >= 1) and (Value.y <= FLines.Count) then
  1934. Value.x := AdjustBytePosToCharacterStart(Value.y,Value.x)
  1935. else
  1936. Value.x := 1;
  1937. end;
  1938. end;
  1939. procedure TSynEditSelection.SetStartLineBytePos(Value : TPoint);
  1940. // logical position (byte)
  1941. var
  1942. nInval1, nInval2: integer;
  1943. WasAvail: boolean;
  1944. begin
  1945. FStickyAutoExtend := False;
  1946. FAltStartLinePos := -1;
  1947. FAltStartBytePos := -1;
  1948. WasAvail := SelAvail;
  1949. ConstrainStartLineBytePos(Value);
  1950. if WasAvail then begin
  1951. if FStartLinePos < FEndLinePos then begin
  1952. nInval1 := Min(Value.Y, FStartLinePos);
  1953. nInval2 := Max(Value.Y, FEndLinePos);
  1954. end else begin
  1955. nInval1 := Min(Value.Y, FEndLinePos);
  1956. nInval2 := Max(Value.Y, FStartLinePos);
  1957. end;
  1958. FInvalidateLinesMethod(nInval1, nInval2);
  1959. end;
  1960. FActiveSelectionMode := FSelectionMode;
  1961. FForceSingleLineSelected := False;
  1962. FHide := False;
  1963. FStartLinePos := Value.Y;
  1964. FStartBytePos := Value.X;
  1965. FEndLinePos := Value.Y;
  1966. FEndBytePos := Value.X;
  1967. if FCaret <> nil then
  1968. FLastCarePos := Point(FCaret.OldCharPos, FCaret.OldLinePos);
  1969. if WasAvail then
  1970. fOnChangeList.CallNotifyEvents(self);
  1971. end;
  1972. procedure TSynEditSelection.AdjustStartLineBytePos(Value: TPoint);
  1973. begin
  1974. if FEnabled then begin
  1975. ConstrainStartLineBytePos(Value);
  1976. if (Value.X <> FStartBytePos) or (Value.Y <> FStartLinePos) then begin
  1977. if (ActiveSelectionMode = smColumn) and (Value.X <> FStartBytePos) then
  1978. FInvalidateLinesMethod(Min(FStartLinePos, Min(FEndLinePos, Value.Y)),
  1979. Max(FStartLinePos, Max(FEndLinePos, Value.Y)))
  1980. else
  1981. if (ActiveSelectionMode <> smColumn) or (FStartBytePos <> FEndBytePos) then
  1982. FInvalidateLinesMethod(FStartLinePos, Value.Y);
  1983. FStartLinePos := Value.Y;
  1984. FStartBytePos := Value.X;
  1985. if FCaret <> nil then
  1986. FLastCarePos := Point(FCaret.OldCharPos, FCaret.OldLinePos);
  1987. FOnChangeList.CallNotifyEvents(self);
  1988. end;
  1989. end;
  1990. end;
  1991. function TSynEditSelection.GetEndLineBytePos : TPoint;
  1992. begin
  1993. Result.y := FEndLinePos;
  1994. Result.x := FEndBytePos;
  1995. end;
  1996. procedure TSynEditSelection.SetEndLineBytePos(Value : TPoint);
  1997. {$IFDEF SYN_MBCSSUPPORT}
  1998. var
  1999. s: string;
  2000. {$ENDIF}
  2001. begin
  2002. if FEnabled then begin
  2003. FStickyAutoExtend := False;
  2004. Value.y := MinMax(Value.y, 1, fLines.Count);
  2005. if (FCaret = nil) or FCaret.AllowPastEOL then
  2006. Value.x := Max(Value.x, 1)
  2007. else
  2008. Value.x := MinMax(Value.x, 1, length(Lines[Value.y - 1])+1);
  2009. if (ActiveSelectionMode = smNormal) then
  2010. if (Value.y >= 1) and (Value.y <= fLines.Count) then
  2011. Value.x := AdjustBytePosToCharacterStart(Value.y,Value.x)
  2012. else
  2013. Value.x := 1;
  2014. if (Value.X <> FEndBytePos) or (Value.Y <> FEndLinePos) then begin
  2015. {$IFDEF SYN_MBCSSUPPORT}
  2016. if Value.Y <= fLines.Count then begin
  2017. s := fLines[Value.Y - 1];
  2018. if (Length(s) >= Value.X) and (mbTrailByte = ByteType(s, Value.X)) then
  2019. Dec(Value.X);
  2020. end;
  2021. {$ENDIF}
  2022. if (Value.X <> FEndBytePos) or (Value.Y <> FEndLinePos) then begin
  2023. if (ActiveSelectionMode = smColumn) and (Value.X <> FEndBytePos) then
  2024. FInvalidateLinesMethod(Min(FStartLinePos, Min(FEndLinePos, Value.Y)),
  2025. Max(FStartLinePos, Max(FEndLinePos, Value.Y)))
  2026. else
  2027. if (ActiveSelectionMode <> smColumn) or (FStartBytePos <> FEndBytePos) then
  2028. FInvalidateLinesMethod(FEndLinePos, Value.Y);
  2029. FEndLinePos := Value.Y;
  2030. FEndBytePos := Value.X;
  2031. if FCaret <> nil then
  2032. FLastCarePos := Point(FCaret.OldCharPos, FCaret.OldLinePos);
  2033. FOnChangeList.CallNotifyEvents(self);
  2034. end;
  2035. end;
  2036. end;
  2037. end;
  2038. procedure TSynEditSelection.SetSelectionMode(const AValue: TSynSelectionMode);
  2039. begin
  2040. FSelectionMode := AValue;
  2041. SetActiveSelectionMode(AValue);
  2042. fOnChangeList.CallNotifyEvents(self);
  2043. end;
  2044. procedure TSynEditSelection.SetActiveSelectionMode(const Value: TSynSelectionMode);
  2045. begin
  2046. FStickyAutoExtend := False;
  2047. if FActiveSelectionMode <> Value then begin
  2048. FActiveSelectionMode := Value;
  2049. if SelAvail then
  2050. FInvalidateLinesMethod(-1, -1);
  2051. FOnChangeList.CallNotifyEvents(self);
  2052. end;
  2053. end;
  2054. procedure TSynEditSelection.SetForceSingleLineSelected(AValue: Boolean);
  2055. var
  2056. WasAvail: Boolean;
  2057. begin
  2058. if FForceSingleLineSelected = AValue then Exit;
  2059. WasAvail := SelAvail;
  2060. FForceSingleLineSelected := AValue;
  2061. if WasAvail <> SelAvail then begin
  2062. FInvalidateLinesMethod(Min(FStartLinePos, FEndLinePos),
  2063. Max(FStartLinePos, FEndLinePos) );
  2064. fOnChangeList.CallNotifyEvents(self);
  2065. end;
  2066. end;
  2067. procedure TSynEditSelection.SetHide(const AValue: Boolean);
  2068. begin
  2069. if FHide = AValue then exit;
  2070. FHide := AValue;
  2071. FInvalidateLinesMethod(Min(FStartLinePos, FEndLinePos),
  2072. Max(FStartLinePos, FEndLinePos) );
  2073. FOnChangeList.CallNotifyEvents(self);
  2074. end;
  2075. procedure TSynEditSelection.SetPersistent(const AValue: Boolean);
  2076. begin
  2077. if FPersistent = AValue then exit;
  2078. FPersistent := AValue;
  2079. if (not FPersistent) and (FCaret <> nil) and
  2080. not ( FCaret.IsAtLineByte(StartLineBytePos) or
  2081. FCaret.IsAtLineByte(EndLineBytePos) )
  2082. then
  2083. Clear;
  2084. end;
  2085. // Only needed if the Selection is set from External
  2086. function TSynEditSelection.AdjustBytePosToCharacterStart(Line : integer; BytePos : integer) : integer;
  2087. begin
  2088. Result := BytePos;
  2089. if Result < 1 then
  2090. Result := 1
  2091. else if (Line >= 1) and (Line <= FLines.Count) then begin
  2092. Result := FLines.LogicPosAdjustToChar(FLines[Line-1], Result, False);
  2093. end;
  2094. if Result <> BytePos then debugln(['Selection needed byte adjustment Line=', Line, ' BytePos=', BytePos, ' Result=', Result]);
  2095. end;
  2096. function TSynEditSelection.GetFirstLineBytePos: TPoint;
  2097. begin
  2098. if IsBackwardSel then
  2099. Result := EndLineBytePos
  2100. else
  2101. Result := StartLineBytePos;
  2102. end;
  2103. function TSynEditSelection.GetLastLineBytePos: TPoint;
  2104. begin
  2105. if IsBackwardSel then
  2106. Result := StartLineBytePos
  2107. else
  2108. Result := EndLineBytePos;
  2109. end;
  2110. function TSynEditSelection.GetLastLineHasSelection: Boolean;
  2111. begin
  2112. Result := (LastLineBytePos.x > 1) or ((FActiveSelectionMode = smLine) and FForceSingleLineSelected);
  2113. end;
  2114. procedure TSynEditSelection.SetAutoExtend(AValue: Boolean);
  2115. begin
  2116. if FAutoExtend = AValue then Exit;
  2117. FAutoExtend := AValue;
  2118. end;
  2119. procedure TSynEditSelection.SetCaret(const AValue: TSynEditCaret);
  2120. begin
  2121. if FCaret = AValue then exit;
  2122. if FCaret <> nil then
  2123. Caret.RemoveChangeHandler(@DoCaretChanged);
  2124. FCaret := AValue;
  2125. if FCaret <> nil then
  2126. Caret.AddChangeHandler(@DoCaretChanged);
  2127. end;
  2128. function TSynEditSelection.SelAvail : Boolean;
  2129. begin
  2130. if FHide then exit(False);
  2131. if (FActiveSelectionMode = smColumn) then begin
  2132. Result := (FStartBytePos <> FEndBytePos) and (FStartLinePos = FEndLinePos);
  2133. if (not Result) and (FStartLinePos <> FEndLinePos) then begin
  2134. // Todo: Cache values, but we need notification, if ines are modified (even only by change of tabwidth...)
  2135. Result := Lines.LogicalToPhysicalPos(StartLineBytePos).X <>
  2136. Lines.LogicalToPhysicalPos(EndLineBytePos).X;
  2137. end;
  2138. end
  2139. else
  2140. Result := (FStartBytePos <> FEndBytePos) or (FStartLinePos <> FEndLinePos)
  2141. or ( (FActiveSelectionMode = smLine) and FForceSingleLineSelected);
  2142. end;
  2143. function TSynEditSelection.SelCanContinue(ACaret: TSynEditCaret): Boolean;
  2144. begin
  2145. if SelAvail then exit(True);
  2146. Result := (not FHide) and
  2147. (FActiveSelectionMode = smColumn) and (FEndLinePos = ACaret.LinePos) and
  2148. (FEndBytePos = ACaret.BytePos);
  2149. end;
  2150. function TSynEditSelection.IsBackwardSel: Boolean;
  2151. begin
  2152. Result := (FStartLinePos > FEndLinePos)
  2153. or ((FStartLinePos = FEndLinePos) and (FStartBytePos > FEndBytePos));
  2154. end;
  2155. procedure TSynEditSelection.BeginMinimumSelection;
  2156. begin
  2157. if SelAvail then begin
  2158. FAltStartLinePos := FEndLinePos;
  2159. FAltStartBytePos := FEndBytePos;
  2160. end
  2161. else begin
  2162. FAltStartLinePos := -1;
  2163. FAltStartBytePos := -1;
  2164. end;
  2165. end;
  2166. procedure TSynEditSelection.SortSelectionPoints;
  2167. begin
  2168. if IsBackwardSel then begin
  2169. SwapInt(FStartLinePos, FEndLinePos);
  2170. SwapInt(FStartBytePos, FEndBytePos);
  2171. end;
  2172. end;
  2173. procedure TSynEditSelection.IgnoreNextCaretMove;
  2174. begin
  2175. FIgnoreNextCaretMove := True;
  2176. end;
  2177. procedure TSynEditSelection.IncPersistentLock(AMode: TSynBlockPersistMode);
  2178. begin
  2179. inc(FPersistentLock);
  2180. if (sbpWeak = AMode) and (FWeakPersistentIdx = 0) then
  2181. FWeakPersistentIdx := FPersistentLock;
  2182. if (sbpStrong = AMode) and (FStrongPersistentIdx = 0) then
  2183. FStrongPersistentIdx := FPersistentLock;
  2184. end;
  2185. procedure TSynEditSelection.DecPersistentLock;
  2186. begin
  2187. dec(FPersistentLock);
  2188. if FWeakPersistentIdx > FPersistentLock then
  2189. FWeakPersistentIdx := 0;
  2190. if FStrongPersistentIdx > FPersistentLock then
  2191. FStrongPersistentIdx := 0;
  2192. if (FPersistentLock = 0) and (FCaret <> nil) and FCaret.Locked then
  2193. FLastCarePos := Point(FCaret.OldCharPos, FCaret.OldLinePos);
  2194. end;
  2195. procedure TSynEditSelection.Clear;
  2196. begin
  2197. if Caret <> nil then
  2198. StartLineBytePos := Caret.LineBytePos
  2199. else
  2200. StartLineBytePos := StartLineBytePos;
  2201. end;
  2202. procedure TSynEditSelection.AddBeforeSetSelTextHandler(AHandler: TSynBeforeSetSelTextEvent);
  2203. begin
  2204. FOnBeforeSetSelText.Add(TMethod(AHandler));
  2205. end;
  2206. procedure TSynEditSelection.RemoveBeforeSetSelTextHandler(AHandler: TSynBeforeSetSelTextEvent);
  2207. begin
  2208. FOnBeforeSetSelText.Remove(TMethod(AHandler));
  2209. end;
  2210. { TSynEditScreenCaretTimer }
  2211. procedure TSynEditScreenCaretTimer.DoAfterPaint(Data: PtrInt);
  2212. begin
  2213. FAfterPaintList.CallNotifyEvents(Self);
  2214. while FAfterPaintList.Count > 0 do
  2215. FAfterPaintList.Delete(FAfterPaintList.Count - 1);
  2216. end;
  2217. procedure TSynEditScreenCaretTimer.DoTimer(Sender: TObject);
  2218. begin
  2219. if FLocCount > 0 then begin
  2220. include(FLocFlags, lfTimer);
  2221. exit;
  2222. end;
  2223. FDisplayCycle := not FDisplayCycle;
  2224. FTimerList.CallNotifyEvents(Self);
  2225. end;
  2226. constructor TSynEditScreenCaretTimer.Create;
  2227. begin
  2228. FTimerList := TMethodList.Create;
  2229. FAfterPaintList := TMethodList.Create;
  2230. FTimer := TTimer.Create(nil);
  2231. FTimer.Enabled := False;
  2232. FTimer.Interval := 500;
  2233. FTimer.OnTimer := @DoTimer;
  2234. end;
  2235. destructor TSynEditScreenCaretTimer.Destroy;
  2236. begin
  2237. Application.RemoveAsyncCalls(Self);
  2238. FreeAndNil(FTimer);
  2239. FreeAndNil(FTimerList);
  2240. FreeAndNil(FAfterPaintList);
  2241. inherited Destroy;
  2242. end;
  2243. procedure TSynEditScreenCaretTimer.AddAfterPaintHandler(AHandler: TNotifyEvent);
  2244. begin
  2245. if FAfterPaintList.Count = 0 then
  2246. Application.QueueAsyncCall(@DoAfterPaint, 0);
  2247. FAfterPaintList.Add(TMethod(AHandler));
  2248. end;
  2249. procedure TSynEditScreenCaretTimer.AddHandler(AHandler: TNotifyEvent);
  2250. begin
  2251. FTimerList.Add(TMethod(AHandler));
  2252. if not FTimer.Enabled then
  2253. RestartCycle;
  2254. end;
  2255. procedure TSynEditScreenCaretTimer.RemoveHandler(AHandler: TNotifyEvent);
  2256. begin
  2257. FTimerList.Remove(TMethod(AHandler));
  2258. if FTimerList.Count = 0 then
  2259. FTimer.Enabled := False;
  2260. end;
  2261. procedure TSynEditScreenCaretTimer.RemoveHandler(AHandlerOwner: TObject);
  2262. begin
  2263. FTimerList.RemoveAllMethodsOfObject(AHandlerOwner);
  2264. FAfterPaintList.RemoveAllMethodsOfObject(AHandlerOwner);
  2265. if FTimerList.Count = 0 then FTimer.Enabled := False;
  2266. end;
  2267. procedure TSynEditScreenCaretTimer.IncLock;
  2268. begin
  2269. inc(FLocCount);
  2270. end;
  2271. procedure TSynEditScreenCaretTimer.DecLock;
  2272. begin
  2273. if FLocCount > 0 then
  2274. dec(FLocCount);
  2275. if FLocCount > 0 then
  2276. exit;
  2277. if lfRestart in FLocFlags then
  2278. RestartCycle
  2279. else;
  2280. if lfTimer in FLocFlags then
  2281. DoTimer(nil);
  2282. FLocFlags := [];
  2283. end;
  2284. procedure TSynEditScreenCaretTimer.AfterPaintEvent;
  2285. begin
  2286. Application.RemoveAsyncCalls(Self);
  2287. DoAfterPaint(0);
  2288. end;
  2289. procedure TSynEditScreenCaretTimer.RestartCycle;
  2290. begin
  2291. if FLocCount > 0 then begin
  2292. include(FLocFlags, lfRestart);
  2293. exit;
  2294. end;
  2295. if FTimerList.Count = 0 then exit;
  2296. FTimer.Enabled := False;
  2297. FDisplayCycle := False;
  2298. DoTimer(nil);
  2299. FTimer.Enabled := True;
  2300. end;
  2301. { TSynEditScreenCaretPainter }
  2302. function TSynEditScreenCaretPainter.GetHandle: HWND;
  2303. begin
  2304. Result := FHandleOwner.Handle;
  2305. end;
  2306. function TSynEditScreenCaretPainter.GetHandleAllocated: Boolean;
  2307. begin
  2308. Result := FHandleOwner.HandleAllocated;
  2309. end;
  2310. procedure TSynEditScreenCaretPainter.Init;
  2311. begin
  2312. //
  2313. end;
  2314. constructor TSynEditScreenCaretPainter.Create(AHandleOwner: TWinControl;
  2315. AOwner: TSynEditScreenCaret);
  2316. begin
  2317. FLeft := -1;
  2318. FTop := -1;
  2319. inherited Create;
  2320. FHandleOwner := AHandleOwner;
  2321. FOwner := AOwner;
  2322. Init;
  2323. end;
  2324. function TSynEditScreenCaretPainter.CreateCaret(w, h: Integer): Boolean;
  2325. begin
  2326. FLeft := -1;
  2327. FTop := -1;
  2328. FWidth := w;
  2329. FHeight := h;
  2330. FCreated := True;
  2331. FShowing := False;
  2332. Result := True;
  2333. end;
  2334. function TSynEditScreenCaretPainter.DestroyCaret: Boolean;
  2335. begin
  2336. FCreated := False;
  2337. FShowing := False;
  2338. Result := True;
  2339. end;
  2340. function TSynEditScreenCaretPainter.HideCaret: Boolean;
  2341. begin
  2342. FShowing := False;
  2343. Result := True;
  2344. end;
  2345. function TSynEditScreenCaretPainter.ShowCaret: Boolean;
  2346. begin
  2347. FShowing := True;
  2348. Result := True;
  2349. end;
  2350. function TSynEditScreenCaretPainter.SetCaretPosEx(x, y: Integer): Boolean;
  2351. begin
  2352. FLeft := x;
  2353. FTop := y;
  2354. FNeedPositionConfirmed := False;
  2355. Result := True;
  2356. end;
  2357. procedure TSynEditScreenCaretPainter.BeginScroll(dx, dy: Integer; const rcScroll,
  2358. rcClip: TRect);
  2359. begin
  2360. FInScroll := True;
  2361. FScrollX := dx;
  2362. FScrollY := dy;
  2363. FScrollRect := rcScroll;
  2364. FScrollClip := rcClip;
  2365. end;
  2366. procedure TSynEditScreenCaretPainter.FinishScroll(dx, dy: Integer; const rcScroll,
  2367. rcClip: TRect; Success: Boolean);
  2368. begin
  2369. FInScroll := False;
  2370. end;
  2371. procedure TSynEditScreenCaretPainter.BeginPaint(rcClip: TRect);
  2372. begin
  2373. FInPaint := True;
  2374. FPaintClip := rcClip;
  2375. end;
  2376. procedure TSynEditScreenCaretPainter.FinishPaint(rcClip: TRect);
  2377. begin
  2378. FInPaint := False;
  2379. end;
  2380. { TSynEditScreenCaretPainterSystem }
  2381. procedure TSynEditScreenCaretPainterSystem.FinishScroll(dx, dy: Integer; const rcScroll,
  2382. rcClip: TRect; Success: Boolean);
  2383. begin
  2384. inherited FinishScroll(dx, dy, rcScroll, rcClip, Success);
  2385. if Success then
  2386. inherited SetCaretPosEx(-1, -1);
  2387. FNeedPositionConfirmed := True;
  2388. end;
  2389. procedure TSynEditScreenCaretPainterSystem.BeginPaint(rcClip: TRect);
  2390. begin
  2391. inherited BeginPaint(rcClip);
  2392. if Showing then
  2393. if not HideCaret then
  2394. DestroyCaret; // only if was Showing
  2395. end;
  2396. function TSynEditScreenCaretPainterSystem.CreateCaret(w, h: Integer): Boolean;
  2397. begin
  2398. // do not create caret during paint / Issue 0021924
  2399. Result := HandleAllocated and not InPaint;
  2400. if not Result then
  2401. exit;
  2402. inherited CreateCaret(w, h);
  2403. inherited SetCaretPosEx(-1, -1);
  2404. Result := LCLIntf.CreateCaret(Handle, 0, w, h);
  2405. SetCaretRespondToFocus(Handle, False); // Only for GTK
  2406. if not Result then inherited DestroyCaret;
  2407. end;
  2408. function TSynEditScreenCaretPainterSystem.DestroyCaret: Boolean;
  2409. begin
  2410. Result := inherited DestroyCaret;
  2411. if HandleAllocated then
  2412. Result := LCLIntf.DestroyCaret(Handle);
  2413. end;
  2414. function TSynEditScreenCaretPainterSystem.HideCaret: Boolean;
  2415. begin
  2416. inherited HideCaret;
  2417. if HandleAllocated then
  2418. Result := LCLIntf.HideCaret(Handle)
  2419. else
  2420. Result := False;
  2421. end;
  2422. function TSynEditScreenCaretPainterSystem.ShowCaret: Boolean;
  2423. begin
  2424. Result := HandleAllocated;
  2425. if not Result then
  2426. exit;
  2427. inherited ShowCaret;
  2428. Result := LCLIntf.ShowCaret(Handle);
  2429. end;
  2430. function TSynEditScreenCaretPainterSystem.SetCaretPosEx(x, y: Integer): Boolean;
  2431. begin
  2432. Result := HandleAllocated;
  2433. if not Result then
  2434. exit;
  2435. inherited SetCaretPosEx(x, y);
  2436. Result := LCLIntf.SetCaretPosEx(Handle, x, y);
  2437. end;
  2438. { TSynEditScreenCaretPainterInternal }
  2439. procedure TSynEditScreenCaretPainterInternal.DoTimer(Sender: TObject);
  2440. begin
  2441. assert(not((not Showing) and FIsDrawn), 'TSynEditScreenCaretPainterInternal.DoTimer: not((not Showing) and FIsDrawn)');
  2442. if (FState <> []) then
  2443. ExecAfterPaint;
  2444. if (not Showing) or NeedPositionConfirmed then exit;
  2445. if FIsDrawn <> FOwner.PaintTimer.DisplayCycle then
  2446. Paint;
  2447. end;
  2448. procedure TSynEditScreenCaretPainterInternal.DoPaint(ACanvas: TCanvas; X, Y, H, W: Integer);
  2449. var
  2450. l: Integer;
  2451. am: TAntialiasingMode;
  2452. begin
  2453. if ForcePaintEvents and (not FInPaint) then begin
  2454. Invalidate;
  2455. exit;
  2456. end;
  2457. am := ACanvas.AntialiasingMode;
  2458. FSavePen.Assign(ACanvas.Pen);
  2459. l := X + W div 2;
  2460. ACanvas.MoveTo(l, Y);
  2461. ACanvas.Pen.Mode := pmNotXOR;
  2462. ACanvas.Pen.Style := psSolid;
  2463. ACanvas.Pen.Color := FColor;
  2464. ACanvas.AntialiasingMode := amOff;
  2465. ACanvas.pen.EndCap := pecFlat;
  2466. ACanvas.pen.Width := Width;
  2467. ACanvas.LineTo(l, Y+H);
  2468. ACanvas.Pen.Assign(FSavePen);
  2469. ACanvas.AntialiasingMode := am;
  2470. end;
  2471. procedure TSynEditScreenCaretPainterInternal.Paint;
  2472. begin
  2473. if not HandleAllocated then begin
  2474. FIsDrawn := False;
  2475. exit;
  2476. end;
  2477. if FInPaint or FInScroll then begin
  2478. if FCanPaint then
  2479. FIsDrawn := not FIsDrawn; //change the state, that is applied at the end of paint
  2480. exit;
  2481. end;
  2482. if (FState <> []) then
  2483. ExecAfterPaint;
  2484. FIsDrawn := not FIsDrawn;
  2485. DoPaint(CurrentCanvas, FLeft, FTop, FHeight, FWidth);
  2486. end;
  2487. procedure TSynEditScreenCaretPainterInternal.Invalidate;
  2488. var
  2489. r: TRect;
  2490. begin
  2491. r.Left := Left;
  2492. r.Top := Top;
  2493. r.Right := Left+Width+1;
  2494. r.Bottom := Top+Height+1;
  2495. InvalidateRect(Handle, @r, False);
  2496. end;
  2497. procedure TSynEditScreenCaretPainterInternal.AddAfterPaint(AStates: TPainterStates);
  2498. begin
  2499. if not(psAfterPaintAdded in FState) then
  2500. FOwner.PaintTimer.AddAfterPaintHandler(@DoAfterPaint);
  2501. FState := FState + [psAfterPaintAdded] + AStates;
  2502. end;
  2503. procedure TSynEditScreenCaretPainterInternal.DoAfterPaint(Sender: TObject);
  2504. begin
  2505. Exclude(FState, psAfterPaintAdded);
  2506. DoTimer(nil);
  2507. end;
  2508. procedure TSynEditScreenCaretPainterInternal.ExecAfterPaint;
  2509. begin
  2510. if FInPaint or FInScroll then
  2511. exit;
  2512. if (psCleanOld in FState) then begin
  2513. DoPaint(CurrentCanvas, FOldX, FOldY, FOldH, FOldW);
  2514. Exclude(FState, psCleanOld);
  2515. end;
  2516. if (psRemoveTimer in FState) and not(FInPaint or FInScroll) then begin
  2517. FOwner.PaintTimer.RemoveHandler(@DoTimer);
  2518. Exclude(FState, psRemoveTimer);
  2519. end;
  2520. end;
  2521. function TSynEditScreenCaretPainterInternal.CurrentCanvas: TCanvas;
  2522. begin
  2523. Result := TCustomControl(FHandleOwner).Canvas;
  2524. end;
  2525. procedure TSynEditScreenCaretPainterInternal.SetColor(AValue: TColor);
  2526. var
  2527. d: Boolean;
  2528. begin
  2529. if FColor = AValue then Exit;
  2530. d := FIsDrawn;
  2531. if FIsDrawn then Paint;
  2532. FColor := AValue;
  2533. if d then Paint;
  2534. end;
  2535. function TSynEditScreenCaretPainterInternal.IsInRect(ARect: TRect): TIsInRectState;
  2536. begin
  2537. Result := IsInRect(ARect, Left, Top, Width, Height);
  2538. end;
  2539. function TSynEditScreenCaretPainterInternal.IsInRect(ARect: TRect; X, Y, W,
  2540. H: Integer): TIsInRectState;
  2541. begin
  2542. if (Y >= ARect.Bottom) or (X >= ARect.Right) or (Y+H < ARect.Top) or (X+W < ARect.Left)
  2543. then
  2544. Result := irOutside
  2545. else
  2546. if (Y >= ARect.Top) and (X >= ARect.Left) and (Y+H < ARect.Bottom) and (X+W < ARect.Right)
  2547. then
  2548. Result := irInside
  2549. else
  2550. Result := irPartInside;
  2551. end;
  2552. procedure TSynEditScreenCaretPainterInternal.Init;
  2553. begin
  2554. {$IFDEF LCLCarbon}
  2555. FForcePaintEvents := True;
  2556. {$ELSE}
  2557. {$IFDEF LCLQt}
  2558. FForcePaintEvents := True;
  2559. {$ELSE}
  2560. FForcePaintEvents := False;
  2561. {$ENDIF}
  2562. {$ENDIF}
  2563. FSavePen := TPen.Create;
  2564. FColor := clBlack;
  2565. FOldY := -1;
  2566. FCanPaint := True;
  2567. inherited Init;
  2568. end;
  2569. procedure TSynEditScreenCaretPainterInternal.BeginScroll(dx, dy: Integer; const rcScroll,
  2570. rcClip: TRect);
  2571. {$IFDEF SynCaretNoHideInSroll}
  2572. var
  2573. rs: TIsInRectState;
  2574. {$ENDIF}
  2575. begin
  2576. assert(not((FInPaint or FInScroll)), 'TSynEditScreenCaretPainterInternal.BeginScroll: not((FInPaint or FInScroll))');
  2577. if (FState <> []) then
  2578. ExecAfterPaint;
  2579. {$IFnDEF SynCaretNoHideInSroll}
  2580. if not ((IsInRect(rcClip) = irOutside) and (IsInRect(rcScroll) = irOutside)) then begin
  2581. HideCaret;
  2582. inherited SetCaretPosEx(-1,-1);
  2583. end;
  2584. {$ELSE}
  2585. rs := IsInRect(rcScroll);
  2586. if not( ((IsInRect(rcClip) = irOutside) and (rs = irOutside)) or
  2587. ((IsInRect(rcClip, Left+dx, Top+dy, Width, Height) = irInside) and (rs = irInside))
  2588. )
  2589. then begin
  2590. HideCaret;
  2591. inherited SetCaretPosEx(-1,-1);
  2592. end;
  2593. {$ENDIF}
  2594. FCanPaint := False;
  2595. inherited BeginScroll(dx, dy, rcScroll, rcClip);
  2596. end;
  2597. procedure TSynEditScreenCaretPainterInternal.FinishScroll(dx, dy: Integer; const rcScroll,
  2598. rcClip: TRect; Success: Boolean);
  2599. begin
  2600. assert(FInScroll, 'TSynEditScreenCaretPainterInternal.FinishScroll: FInScroll');
  2601. assert((FState-[psAfterPaintAdded]) = [], 'TSynEditScreenCaretPainterInternal.FinishScroll: FState = []');
  2602. inherited FinishScroll(dx, dy, rcScroll, rcClip, Success);
  2603. FCanPaint := True;
  2604. {$IFDEF SynCaretNoHideInSroll}
  2605. if Success and ((IsInRect(rcClip) = irInside) or (IsInRect(rcScroll) = irInside)) then begin
  2606. inherited SetCaretPosEx(Left+dx, Top+dy);
  2607. FNeedPositionConfirmed := True;
  2608. end;
  2609. {$ENDIF}
  2610. end;
  2611. procedure TSynEditScreenCaretPainterInternal.BeginPaint(rcClip: TRect);
  2612. begin
  2613. assert(not (FInPaint or FInScroll), 'TSynEditScreenCaretPainterInternal.BeginPaint: not (FInPaint or FInScroll)');
  2614. FCanPaint := IsInRect(rcClip)= irInside;
  2615. if (psCleanOld in FState) and not FCanPaint then begin
  2616. if IsInRect(rcClip, FOldX, FOldY, FOldW, FOldH) <> irInside then begin
  2617. debugln(['TSynEditScreenCaretPainterInternal.BeginPaint Invalidate for psCleanOld']);
  2618. Invalidate;
  2619. end;
  2620. Exclude(FState, psCleanOld);
  2621. end;
  2622. if not(psCleanOld in FState) then begin
  2623. FOldX := Left;
  2624. FOldY := Top;
  2625. FOldW := Width;
  2626. FOldH := Height;
  2627. end;
  2628. inherited BeginPaint(rcClip);
  2629. end;
  2630. procedure TSynEditScreenCaretPainterInternal.FinishPaint(rcClip: TRect);
  2631. begin
  2632. assert(FInPaint, 'TSynEditScreenCaretPainterInternal.FinishPaint: FInPaint');
  2633. assert(FCanPaint = (IsInRect(rcClip)= irInside), 'TSynEditScreenCaretPainterInternal.FinishPaint: FCanPaint = (IsInRect(rcClip)= irInside)');
  2634. assert(FCanPaint = (IsInRect(FPaintClip)= irInside), 'TSynEditScreenCaretPainterInternal.FinishPaint: FCanPaint = (IsInRect(rcClip)= irInside)');
  2635. // partly restore IF irPartInside;
  2636. // Better recalc size to remainder outside cliprect
  2637. if (psCleanOld in FState) and (not ForcePaintEvents) then
  2638. DoPaint(CurrentCanvas, FOldX, FOldY, FOldH, FOldW);
  2639. // if changes where made, then FIsDrawn is alvays false
  2640. if FIsDrawn then
  2641. DoPaint(CurrentCanvas, FLeft, FTop, FHeight, FWidth); // restore any part that is in the cliprect
  2642. inherited FinishPaint(rcClip);
  2643. FCanPaint := True;
  2644. end;
  2645. destructor TSynEditScreenCaretPainterInternal.Destroy;
  2646. begin
  2647. assert(not(FInPaint or FInScroll), 'TSynEditScreenCaretPainterInternal.Destroy: not(FInPaint or FInScroll)');
  2648. if FOwner.HasPaintTimer then
  2649. FOwner.PaintTimer.RemoveHandler(Self);
  2650. HideCaret;
  2651. FreeAndNil(FSavePen);
  2652. inherited Destroy;
  2653. end;
  2654. function TSynEditScreenCaretPainterInternal.CreateCaret(w, h: Integer): Boolean;
  2655. begin
  2656. DestroyCaret;
  2657. Result := inherited CreateCaret(w, h);
  2658. if InPaint then // InScroll ??
  2659. FCanPaint := IsInRect(FPaintClip) = irInside;
  2660. Result := True;
  2661. end;
  2662. function TSynEditScreenCaretPainterInternal.DestroyCaret: Boolean;
  2663. begin
  2664. HideCaret;
  2665. inherited DestroyCaret;
  2666. Result := True;
  2667. end;
  2668. function TSynEditScreenCaretPainterInternal.HideCaret: Boolean;
  2669. begin
  2670. inherited HideCaret;
  2671. if (not FCanPaint) and FIsDrawn then begin
  2672. AddAfterPaint([psCleanOld, psRemoveTimer]);
  2673. FIsDrawn := False;
  2674. exit(True);
  2675. end;
  2676. FOwner.PaintTimer.RemoveHandler(@DoTimer);
  2677. if FIsDrawn then Paint;
  2678. assert(not FIsDrawn, 'TSynEditScreenCaretPainterInternal.HideCaret: not FIsDrawn');
  2679. Result := True;
  2680. end;
  2681. function TSynEditScreenCaretPainterInternal.ShowCaret: Boolean;
  2682. begin
  2683. if Showing then exit(True);
  2684. inherited ShowCaret;
  2685. Exclude(FState, psRemoveTimer);
  2686. // Exclude(FState, psCleanOld); // only if not moved
  2687. FOwner.PaintTimer.RemoveHandler(@DoTimer);
  2688. FOwner.PaintTimer.AddHandler(@DoTimer);
  2689. FOwner.PaintTimer.RestartCycle;
  2690. Result := True;
  2691. end;
  2692. function TSynEditScreenCaretPainterInternal.SetCaretPosEx(x, y: Integer): Boolean;
  2693. var
  2694. d: Boolean;
  2695. begin
  2696. if (not FCanPaint) and FIsDrawn then begin
  2697. AddAfterPaint([psCleanOld]);
  2698. FIsDrawn := False;
  2699. end;
  2700. d := FIsDrawn;
  2701. if d then Paint;
  2702. inherited SetCaretPosEx(x, y);
  2703. if InPaint then // InScroll ??
  2704. FCanPaint := IsInRect(FPaintClip) = irInside;
  2705. if d then Paint;
  2706. // else aftecpaint needs show
  2707. FOwner.PaintTimer.RestartCycle; // if not d ??
  2708. Result := True;
  2709. end;
  2710. { TSynEditScreenCaret }
  2711. constructor TSynEditScreenCaret.Create(AHandleOwner: TWinControl);
  2712. begin
  2713. Create(AHandleOwner, TSynEditScreenCaretPainterSystem);
  2714. //Create(AHandleOwner, TSynEditScreenCaretPainterInternal);
  2715. end;
  2716. constructor TSynEditScreenCaret.Create(AHandleOwner: TWinControl;
  2717. APainterClass: TSynEditScreenCaretPainterClass);
  2718. begin
  2719. inherited Create;
  2720. FCaretPainter := APainterClass.Create(AHandleOwner, Self);
  2721. FLockCount := -1;
  2722. ResetCaretTypeSizes;
  2723. FHandleOwner := AHandleOwner;
  2724. FVisible := False;
  2725. FClipExtraPixel := 0;
  2726. FLockCount := 0;
  2727. end;
  2728. procedure TSynEditScreenCaret.ChangePainter(APainterClass: TSynEditScreenCaretPainterClass);
  2729. begin
  2730. DestroyCaret;
  2731. FreeAndNil(FCaretPainter);
  2732. FCaretPainter := APainterClass.Create(FHandleOwner, Self);
  2733. UpdateDisplay;
  2734. end;
  2735. destructor TSynEditScreenCaret.Destroy;
  2736. begin
  2737. DestroyCaret;
  2738. FreeAndNil(FCaretPainter);
  2739. if FPaintTimerOwned then
  2740. FreeAndNil(FPaintTimer);
  2741. inherited Destroy;
  2742. end;
  2743. procedure TSynEditScreenCaret.BeginScroll(dx, dy: Integer; const rcScroll, rcClip: TRect);
  2744. begin
  2745. Painter.BeginScroll(dx, dy, rcScroll, rcClip);
  2746. end;
  2747. procedure TSynEditScreenCaret.FinishScroll(dx, dy: Integer; const rcScroll, rcClip: TRect;
  2748. Success: Boolean);
  2749. begin
  2750. Painter.FinishScroll(dx, dy, rcScroll, rcClip, Success);
  2751. end;
  2752. procedure TSynEditScreenCaret.BeginPaint(rcClip: TRect);
  2753. begin
  2754. Painter.BeginPaint(rcClip);
  2755. end;
  2756. procedure TSynEditScreenCaret.FinishPaint(rcClip: TRect);
  2757. begin
  2758. Painter.FinishPaint(rcClip);
  2759. end;
  2760. procedure TSynEditScreenCaret.Hide;
  2761. begin
  2762. HideCaret;
  2763. end;
  2764. procedure TSynEditScreenCaret.DestroyCaret(SkipHide: boolean = False);
  2765. begin
  2766. if Painter.Created then begin
  2767. {$IFDeF SynCaretDebug}
  2768. debugln(['SynEditCaret DestroyCaret for HandleOwner=',FHandleOwner, ' DebugShowCount=', FDebugShowCount, ' FVisible=', FVisible, ' FCurrentVisible=', Painter.Showing]);
  2769. {$ENDIF}
  2770. FCaretPainter.DestroyCaret;
  2771. end;
  2772. if not SkipHide then
  2773. FVisible := False;
  2774. end;
  2775. procedure TSynEditScreenCaret.Lock;
  2776. begin
  2777. inc(FLockCount);
  2778. if FPaintTimer <> nil then
  2779. FPaintTimer.IncLock;
  2780. end;
  2781. procedure TSynEditScreenCaret.UnLock;
  2782. begin
  2783. dec(FLockCount);
  2784. if (FLockCount=0) then begin
  2785. if (sclfUpdateDisplayType in FLockFlags) then UpdateDisplayType;
  2786. if (sclfUpdateDisplay in FLockFlags) then UpdateDisplay;
  2787. end;
  2788. if FPaintTimer <> nil then
  2789. FPaintTimer.DecLock;
  2790. end;
  2791. procedure TSynEditScreenCaret.AfterPaintEvent;
  2792. begin
  2793. if FPaintTimer <> nil then
  2794. FPaintTimer.AfterPaintEvent;
  2795. end;
  2796. procedure TSynEditScreenCaret.ResetCaretTypeSizes;
  2797. var
  2798. i: TSynCaretType;
  2799. begin
  2800. for i := low(TSynCaretType) to high(TSynCaretType) do begin
  2801. FCustomPixelWidth[i] := 0;
  2802. end;
  2803. if FLockCount >= 0 then UpdateDisplayType;
  2804. end;
  2805. procedure TSynEditScreenCaret.SetCaretTypeSize(AType: TSynCaretType; AWidth, AHeight, AXOffs,
  2806. AYOffs: Integer; AFlags: TSynCustomCaretSizeFlags);
  2807. begin
  2808. FCustomPixelWidth[AType] := AWidth;
  2809. FCustomPixelHeight[AType] := AHeight;
  2810. FCustomOffsetX[AType] := AXOffs;
  2811. FCustomOffsetY[AType] := AYOffs;
  2812. FCustomFlags[AType] := AFlags;
  2813. if FDisplayType = AType then UpdateDisplayType;
  2814. end;
  2815. procedure TSynEditScreenCaret.SetClipRight(const AValue: Integer);
  2816. begin
  2817. if FClipRight = AValue then exit;
  2818. FClipRight := AValue;
  2819. UpdateDisplay;
  2820. end;
  2821. procedure TSynEditScreenCaret.SetCharHeight(const AValue: Integer);
  2822. begin
  2823. if FCharHeight = AValue then exit;
  2824. FCharHeight := AValue;
  2825. UpdateDisplayType;
  2826. end;
  2827. function TSynEditScreenCaret.GetHandle: HWND;
  2828. begin
  2829. Result :=FHandleOwner.Handle;
  2830. end;
  2831. function TSynEditScreenCaret.GetHandleAllocated: Boolean;
  2832. begin
  2833. Result :=FHandleOwner.HandleAllocated;
  2834. end;
  2835. procedure TSynEditScreenCaret.SetCharWidth(const AValue: Integer);
  2836. begin
  2837. if FCharWidth = AValue then exit;
  2838. FCharWidth := AValue;
  2839. UpdateDisplayType;
  2840. end;
  2841. procedure TSynEditScreenCaret.SetDisplayPos(const AValue: TPoint);
  2842. begin
  2843. if (FDisplayPos.x = AValue.x) and (FDisplayPos.y = AValue.y) and
  2844. (FVisible = Painter.Showing) and (not Painter.NeedPositionConfirmed)
  2845. then
  2846. exit;
  2847. FDisplayPos := AValue;
  2848. UpdateDisplay;
  2849. end;
  2850. procedure TSynEditScreenCaret.SetDisplayType(const AType: TSynCaretType);
  2851. begin
  2852. if FDisplayType = AType then exit;
  2853. FDisplayType := AType;
  2854. UpdateDisplayType;
  2855. end;
  2856. procedure TSynEditScreenCaret.SetVisible(const AValue: Boolean);
  2857. begin
  2858. if FVisible = AValue then exit;
  2859. FVisible := AValue;
  2860. UpdateDisplay;
  2861. end;
  2862. procedure TSynEditScreenCaret.UpdateDisplayType;
  2863. begin
  2864. if FLockCount > 0 then begin
  2865. Include(FLockFlags, sclfUpdateDisplayType);
  2866. exit;
  2867. end;
  2868. Exclude(FLockFlags, sclfUpdateDisplayType);
  2869. case FDisplayType of
  2870. ctVerticalLine, ctCostum:
  2871. begin
  2872. FPixelWidth := 2;
  2873. FPixelHeight := FCharHeight - 2;
  2874. FOffsetX := -1;
  2875. FOffsetY := 1;
  2876. FExtraLinePixel := 1;
  2877. end;
  2878. ctBlock:
  2879. begin
  2880. FPixelWidth := FCharWidth;
  2881. FPixelHeight := FCharHeight - 2;
  2882. FOffsetX := 0;
  2883. FOffsetY := 1;
  2884. FExtraLinePixel := FCharWidth;
  2885. end;
  2886. ctHalfBlock:
  2887. begin
  2888. FPixelWidth := FCharWidth;
  2889. FPixelHeight := (FCharHeight - 2) div 2;
  2890. FOffsetX := 0;
  2891. FOffsetY := FPixelHeight + 1;
  2892. FExtraLinePixel := FCharWidth;
  2893. end;
  2894. ctHorizontalLine:
  2895. begin
  2896. FPixelWidth := FCharWidth;
  2897. FPixelHeight := 2;
  2898. FOffsetX := 0;
  2899. FOffsetY := FCharHeight - 1;
  2900. FExtraLinePixel := FCharWidth;
  2901. end;
  2902. end;
  2903. if (FCustomPixelWidth[FDisplayType] <> 0) then begin
  2904. if ccsRelativeWidth in FCustomFlags[FDisplayType]
  2905. then FPixelWidth := FCharWidth * FCustomPixelWidth[FDisplayType] div 1024
  2906. else FPixelWidth := FCustomPixelWidth[FDisplayType];
  2907. if ccsRelativeLeft in FCustomFlags[FDisplayType]
  2908. then FOffsetX := FCharWidth * FCustomOffsetX[FDisplayType] div 1024
  2909. else FOffsetX := FCustomOffsetX[FDisplayType];
  2910. FExtraLinePixel := Max(0, FPixelWidth + FOffsetX);
  2911. end;
  2912. if (FCustomPixelHeight[FDisplayType] <> 0) then begin
  2913. if ccsRelativeHeight in FCustomFlags[FDisplayType]
  2914. then FPixelHeight := FCharHeight * FCustomPixelHeight[FDisplayType] div 1024
  2915. else FPixelHeight := FCustomPixelHeight[FDisplayType];
  2916. if ccsRelativeTop in FCustomFlags[FDisplayType]
  2917. then FOffsetY := FCharHeight * FCustomOffsetY[FDisplayType] div 1024
  2918. else FOffsetY := FCustomOffsetY[FDisplayType];
  2919. end;
  2920. CalcExtraLineChars;
  2921. DestroyCaret(True);
  2922. UpdateDisplay;
  2923. end;
  2924. procedure TSynEditScreenCaret.SetClipBottom(const AValue: Integer);
  2925. begin
  2926. if FClipBottom = AValue then exit;
  2927. FClipBottom := AValue;
  2928. UpdateDisplay;
  2929. end;
  2930. function TSynEditScreenCaret.GetPaintTimer: TSynEditScreenCaretTimer;
  2931. begin
  2932. if FPaintTimer = nil then begin
  2933. FPaintTimer := TSynEditScreenCaretTimer.Create;
  2934. FPaintTimerOwned := True;
  2935. FPaintTimer.FLocCount := FLockCount;
  2936. end;
  2937. Result := FPaintTimer;
  2938. end;
  2939. function TSynEditScreenCaret.GetHasPaintTimer: Boolean;
  2940. begin
  2941. Result := FPaintTimer <> nil;
  2942. end;
  2943. procedure TSynEditScreenCaret.SetClipExtraPixel(AValue: Integer);
  2944. begin
  2945. if FClipExtraPixel = AValue then Exit;
  2946. {$IFDeF SynCaretDebug}
  2947. debugln(['SynEditCaret ClipRect for HandleOwner=',FHandleOwner, ' ExtraPixel=', dbgs(AValue)]);
  2948. debugln(['TSynEditScreenCaret.SetClipExtraPixel ',FHandleOwner,' Focus=',FindControl(GetFocus)]);
  2949. {$ENDIF}
  2950. FClipExtraPixel := AValue;
  2951. CalcExtraLineChars;
  2952. UpdateDisplay;
  2953. end;
  2954. procedure TSynEditScreenCaret.SetClipLeft(const AValue: Integer);
  2955. begin
  2956. if FClipLeft = AValue then exit;
  2957. FClipLeft := AValue;
  2958. UpdateDisplay;
  2959. end;
  2960. procedure TSynEditScreenCaret.SetClipRect(const AValue: TRect);
  2961. begin
  2962. if (FClipLeft = AValue.Left) and (FClipRight = AValue.Right) and
  2963. (FClipTop = AValue.Top) and (FClipBottom = AValue.Bottom)
  2964. then
  2965. exit;
  2966. {$IFDeF SynCaretDebug}
  2967. debugln(['SynEditCaret ClipRect for HandleOwner=',FHandleOwner, ' Rect=', dbgs(AValue)]);
  2968. {$ENDIF}
  2969. FClipLeft := AValue.Left;
  2970. FClipRight := AValue.Right;
  2971. FClipTop := AValue.Top;
  2972. FClipBottom := AValue.Bottom;
  2973. UpdateDisplay;
  2974. end;
  2975. procedure TSynEditScreenCaret.SetClipTop(const AValue: Integer);
  2976. begin
  2977. if FClipTop = AValue then exit;
  2978. FClipTop := AValue;
  2979. UpdateDisplay;
  2980. end;
  2981. procedure TSynEditScreenCaret.CalcExtraLineChars;
  2982. var
  2983. OldExtraChars: Integer;
  2984. begin
  2985. if FCharWidth = 0 then exit;
  2986. OldExtraChars := FExtraLineChars;
  2987. FExtraLineChars := Max(0, FExtraLinePixel - FClipExtraPixel + FCharWidth - 1)
  2988. div FCharWidth;
  2989. if (FExtraLineChars <> OldExtraChars) and assigned(FOnExtraLineCharsChanged) then
  2990. FOnExtraLineCharsChanged(Self);
  2991. end;
  2992. procedure TSynEditScreenCaret.SetPaintTimer(AValue: TSynEditScreenCaretTimer);
  2993. begin
  2994. assert(FPaintTimer = nil, 'TSynEditScreenCaret.SetPaintTimer: FPaintTimer = nil');
  2995. if FPaintTimer = nil then
  2996. FPaintTimer := AValue;
  2997. end;
  2998. procedure TSynEditScreenCaret.UpdateDisplay;
  2999. begin
  3000. if FLockCount > 0 then begin
  3001. Include(FLockFlags, sclfUpdateDisplay);
  3002. exit;
  3003. end;
  3004. Exclude(FLockFlags, sclfUpdateDisplay);
  3005. if FVisible then
  3006. ShowCaret
  3007. else
  3008. HideCaret;
  3009. end;
  3010. procedure TSynEditScreenCaret.ShowCaret;
  3011. var
  3012. x, y, w, h: Integer;
  3013. begin
  3014. if not HandleAllocated then
  3015. exit;
  3016. x := FDisplayPos.x + FOffsetX;
  3017. y := FDisplayPos.y + FOffsetY;
  3018. w := FPixelWidth;
  3019. h := FPixelHeight;
  3020. if x + w >= FClipRight then
  3021. w := FClipRight - x - 1;
  3022. if x < FClipLeft then begin
  3023. w := w - (FClipLeft - w);
  3024. x := FClipLeft;
  3025. end;
  3026. if y + h >= FClipBottom then
  3027. h := FClipBottom - y - 1;
  3028. if y < FClipTop then begin
  3029. h := h - (FClipTop - y);
  3030. y := FClipTop;
  3031. end;
  3032. if (w <= 0) or (h < 0) or
  3033. (x < FClipLeft) or (x >= FClipRight) or
  3034. (y < FClipTop) or (y >= FClipBottom)
  3035. then begin
  3036. HideCaret;
  3037. exit;
  3038. end;
  3039. if (not Painter.Created) or (FCaretPainter.Width <> w) or (FCaretPainter.Height <> h) then begin
  3040. {$IFDeF SynCaretDebug}
  3041. debugln(['SynEditCaret CreateCaret for HandleOwner=',FHandleOwner, ' DebugShowCount=', FDebugShowCount, ' Width=', w, ' pref-width=', FPixelWidth, ' Height=', FPixelHeight, ' FCurrentCreated=',Painter.Created, ' FCurrentVisible=',Painter.Showing]);
  3042. FDebugShowCount := 0;
  3043. {$ENDIF}
  3044. // // Create caret includes destroy
  3045. FCaretPainter.CreateCaret(w, h);
  3046. end;
  3047. if (x <> Painter.Left) or (y <> Painter.Top) or (Painter.NeedPositionConfirmed) then begin
  3048. {$IFDeF SynCaretDebug}
  3049. debugln(['SynEditCaret SetPos for HandleOwner=',FHandleOwner, ' x=', x, ' y=',y]);
  3050. {$ENDIF}
  3051. FCaretPainter.SetCaretPosEx(x, y);
  3052. end;
  3053. if (not Painter.Showing) then begin
  3054. {$IFDeF SynCaretDebug}
  3055. debugln(['SynEditCaret ShowCaret for HandleOwner=',FHandleOwner, ' FDebugShowCount=',FDebugShowCount, ' FVisible=', FVisible, ' FCurrentVisible=', Painter.Showing]);
  3056. inc(FDebugShowCount);
  3057. {$ENDIF}
  3058. if not FCaretPainter.ShowCaret then begin
  3059. {$IFDeF SynCaretDebug}
  3060. debugln(['SynEditCaret ShowCaret FAILED for HandleOwner=',FHandleOwner, ' FDebugShowCount=',FDebugShowCount]);
  3061. {$ENDIF}
  3062. DestroyCaret(True);
  3063. end;
  3064. end;
  3065. end;
  3066. procedure TSynEditScreenCaret.HideCaret;
  3067. begin
  3068. if not HandleAllocated then
  3069. exit;
  3070. if not Painter.Created then exit;
  3071. if Painter.Showing then begin
  3072. {$IFDeF SynCaretDebug}
  3073. debugln(['SynEditCaret HideCaret for HandleOwner=',FHandleOwner, ' FDebugShowCount=',FDebugShowCount, ' FVisible=', FVisible, ' FCurrentVisible=', Painter.Showing]);
  3074. dec(FDebugShowCount);
  3075. {$ENDIF}
  3076. if FCaretPainter.HideCaret then
  3077. else begin
  3078. {$IFDeF SynCaretDebug}
  3079. debugln(['SynEditCaret HideCaret FAILED for HandleOwner=',FHandleOwner, ' FDebugShowCount=',FDebugShowCount]);
  3080. {$ENDIF}
  3081. DestroyCaret(True);
  3082. end;
  3083. end;
  3084. end;
  3085. end.