mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 07:02:55 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			8929 lines
		
	
	
		
			294 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			8929 lines
		
	
	
		
			294 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {-------------------------------------------------------------------------------
 | |
| The contents of this file are subject to the Mozilla Public License
 | |
| Version 1.1 (the "License"); you may not use this file except in compliance
 | |
| with the License. You may obtain a copy of the License at
 | |
| http://www.mozilla.org/MPL/
 | |
| 
 | |
| Software distributed under the License is distributed on an "AS IS" basis,
 | |
| WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 | |
| the specific language governing rights and limitations under the License.
 | |
| 
 | |
| The Original Code is: SynEdit.pas, released 2000-04-07.
 | |
| The Original Code is based on mwCustomEdit.pas by Martin Waldenburg, part of
 | |
| the mwEdit component suite.
 | |
| Portions created by Martin Waldenburg are Copyright (C) 1998 Martin Waldenburg.
 | |
| All Rights Reserved.
 | |
| 
 | |
| Contributors to the SynEdit and mwEdit projects are listed in the
 | |
| Contributors.txt file.
 | |
| 
 | |
| Alternatively, the contents of this file may be used under the terms of the
 | |
| GNU General Public License Version 2 or later (the "GPL"), in which case
 | |
| the provisions of the GPL are applicable instead of those above.
 | |
| If you wish to allow use of your version of this file only under the terms
 | |
| of the GPL and not to allow others to use your version of this file
 | |
| under the MPL, indicate your decision by deleting the provisions above and
 | |
| replace them with the notice and other provisions required by the GPL.
 | |
| If you do not delete the provisions above, a recipient may use your version
 | |
| of this file under either the MPL or the GPL.
 | |
| 
 | |
| $Id$
 | |
| 
 | |
| You may retrieve the latest version of this file at the SynEdit home page,
 | |
| located at http://SynEdit.SourceForge.net
 | |
| 
 | |
| Known Issues:
 | |
| 
 | |
|   -DoubleBuffered
 | |
|   -Font.CharSet
 | |
|   -THintWindow
 | |
|   -DragAcceptFiles
 | |
| 
 | |
| -------------------------------------------------------------------------------}
 | |
| 
 | |
| unit SynEdit;
 | |
| {$IFDEF Windows}
 | |
|   {$IFnDEF WithoutWinIME}
 | |
|     {$DEFINE WinIME}
 | |
|     {$DEFINE WinIMEFull}
 | |
|   {$ENDIF}
 | |
| {$ENDIF}
 | |
| 
 | |
| {$I synedit.inc}
 | |
| 
 | |
| 
 | |
| {$IFDEF LCLGTK1}
 | |
| {$DEFINE EnableDoubleBuf} // gtk1 does not have double buffering
 | |
| {$ENDIF}
 | |
| {$IFDEF LCLGTK2}
 | |
| { $DEFINE EnableDoubleBuf} // gtk2.10 paints faster to memory
 | |
|                            // gtk2.12 paints faster directly
 | |
| {$ENDIF}
 | |
| 
 | |
| interface
 | |
| 
 | |
| { $DEFINE SYNSCROLLDEBUG}
 | |
| { $DEFINE VerboseKeys}
 | |
| { $DEFINE VerboseSynEditInvalidate}
 | |
| { $DEFINE SYNDEBUGPRINT}
 | |
| {$IFDEF SynUndoDebug}
 | |
|   {$Define SynUndoDebugItems}
 | |
|   {$Define SynUndoDebugCalls}
 | |
| {$ENDIF}
 | |
| 
 | |
| uses
 | |
|   {$IFDEF WinIME}
 | |
|   LazSynIMM,
 | |
|   {$ENDIF}
 | |
|   {$IFDEF USE_UTF8BIDI_LCL}
 | |
|   FreeBIDI, utf8bidi,
 | |
|   {$ENDIF}
 | |
|   Types, LCLIntf, LCLType, LMessages, LazUTF8, LCLProc,
 | |
|   SysUtils, Classes, Messages, Controls, Graphics, Forms, StdCtrls, ExtCtrls, Menus,
 | |
|   SynEditTypes, SynEditSearch, SynEditKeyCmds, SynEditMouseCmds, SynEditMiscProcs,
 | |
|   SynEditPointClasses, SynBeautifier, SynEditMarks,
 | |
|   // Markup
 | |
|   SynEditMarkup, SynEditMarkupHighAll, SynEditMarkupBracket, SynEditMarkupWordGroup,
 | |
|   SynEditMarkupCtrlMouseLink, SynEditMarkupSpecialLine, SynEditMarkupSelection,
 | |
|   SynEditMarkupSpecialChar,
 | |
|   // Lines
 | |
|   SynEditTextBase, LazSynEditText, SynEditTextBuffer, SynEditLines,
 | |
|   SynEditTextTrimmer, SynEditTextTabExpander, SynEditTextDoubleWidthChars,
 | |
|   SynEditFoldedView,
 | |
|   // Gutter
 | |
|   SynGutterBase, SynGutter, SynGutterCodeFolding, SynGutterChanges,
 | |
|   SynGutterLineNumber, SynGutterMarks, SynGutterLineOverview,
 | |
|   SynEditMiscClasses, SynEditHighlighter, LazSynTextArea, SynTextDrawer,
 | |
|   LResources, Clipbrd
 | |
|   {$IFDEF SYN_COMPILER_4_UP}
 | |
|   , StdActns
 | |
|   {$ENDIF}
 | |
|   ;
 | |
| 
 | |
| const
 | |
|   ScrollBarWidth=0;
 | |
| 
 | |
|   // SynDefaultFont is determined in InitSynDefaultFont()
 | |
|   SynDefaultFontName:    String       = '';
 | |
|   SynDefaultFontHeight:  Integer      = 13;
 | |
|   SynDefaultFontSize:    Integer      = 10;
 | |
|   SynDefaultFontPitch:   TFontPitch   = fpFixed;
 | |
|   SynDefaultFontQuality: TFontQuality = fqNonAntialiased;
 | |
| 
 | |
|   {$IFNDEF SYN_COMPILER_3_UP}
 | |
|    // not defined in all Delphi versions
 | |
|   WM_MOUSEWHEEL = $020A;
 | |
|   {$ENDIF}
 | |
| 
 | |
|   // maximum scroll range
 | |
|   MAX_SCROLL = 32767;
 | |
| 
 | |
| type
 | |
|   TSynEditMarkupClass = SynEditMarkup.TSynEditMarkupClass;
 | |
|   TSynReplaceAction = (raCancel, raSkip, raReplace, raReplaceAll);
 | |
| 
 | |
|   TSynDropFilesEvent = procedure(Sender: TObject; X, Y: integer; AFiles: TStrings)
 | |
|     of object;
 | |
| 
 | |
|   THookedCommandEvent = procedure(Sender: TObject; AfterProcessing: boolean;
 | |
|     var Handled: boolean; var Command: TSynEditorCommand;
 | |
|     var AChar: TUTF8Char;
 | |
|     Data: pointer; HandlerData: pointer) of object;
 | |
|   THookedCommandFlag = (
 | |
|     hcfInit,     // run before On[User]CommandProcess (outside UndoBlock / should not do execution)
 | |
|     hcfPreExec,  // Run before CommandProcessor (unless handled by On[User]CommandProcess)
 | |
|     hcfPostExec, // Run after CommandProcessor (unless handled by On[User]CommandProcess)
 | |
|     hcfFinish    // Run at the very end
 | |
|   );
 | |
|   THookedCommandFlags = set of THookedCommandFlag;
 | |
| 
 | |
|   THookedKeyTranslationEvent = procedure(Sender: TObject;
 | |
|     Code: word; SState: TShiftState; var Data: pointer; var IsStartOfCombo: boolean;
 | |
|     var Handled: boolean; var Command: TSynEditorCommand;
 | |
|     FinishComboOnly: Boolean; var ComboKeyStrokes: TSynEditKeyStrokes) of object;
 | |
| 
 | |
|   TPaintEvent = procedure(Sender: TObject; ACanvas: TCanvas) of object;
 | |
| 
 | |
|   TChangeUpdatingEvent = procedure(ASender: TObject; AnUpdating: Boolean) of object;
 | |
| 
 | |
|   TProcessCommandEvent = procedure(Sender: TObject;
 | |
|     var Command: TSynEditorCommand;
 | |
|     var AChar: TUTF8Char;
 | |
|     Data: pointer) of object;
 | |
| 
 | |
|   TReplaceTextEvent = procedure(Sender: TObject; const ASearch, AReplace:
 | |
|     string; Line, Column: integer; var ReplaceAction: TSynReplaceAction) of object;
 | |
| 
 | |
|   TSynCopyPasteAction = (scaContinue, scaPlainText, scaAbort);
 | |
|   TSynCopyPasteEvent = procedure(Sender: TObject; var AText: String;
 | |
|     var AMode: TSynSelectionMode; ALogStartPos: TPoint;
 | |
|     var AnAction: TSynCopyPasteAction) of object;
 | |
| 
 | |
|   TSynEditCaretType = SynEditPointClasses.TSynCaretType;
 | |
| 
 | |
|   TSynCaretAdjustMode = ( // used in TextBetweenPointsEx
 | |
|     scamIgnore, // Caret stays at the same numeric values, if text is inserted before caret, the text moves, but the caret stays
 | |
|     scamAdjust, // Caret moves with text, if text is inserted
 | |
|     scamEnd,
 | |
|     scamBegin
 | |
|   );
 | |
| 
 | |
|   (* This is used, if text is *replaced*.
 | |
|      What to do with marks in text that is deleted/replaced
 | |
|   *)
 | |
|   TSynMarksAdjustMode = ( // used in SetTextBetweenPoints
 | |
|     smaMoveUp, //
 | |
|     smaKeep
 | |
|     // smaDrop
 | |
|   );
 | |
| 
 | |
|   TSynEditTextFlag = (
 | |
|     setSelect // select the new text
 | |
|   );
 | |
|   TSynEditTextFlags = set of TSynEditTextFlag;
 | |
| 
 | |
|   TSynStateFlag = (sfCaretChanged, sfHideCursor,
 | |
|     sfEnsureCursorPos, sfEnsureCursorPosAtResize,
 | |
|     sfIgnoreNextChar, sfPainting, sfHasScrolled,
 | |
|     sfScrollbarChanged, sfHorizScrollbarVisible, sfVertScrollbarVisible,
 | |
|     sfAfterLoadFromFileNeeded,
 | |
|     // Mouse-states
 | |
|     sfLeftGutterClick, sfRightGutterClick,
 | |
|     sfDblClicked, sfTripleClicked, sfQuadClicked,
 | |
|     sfWaitForDragging, sfIsDragging, sfWaitForMouseSelecting, sfMouseSelecting, sfMouseDoneSelecting,
 | |
|     sfIgnoreUpClick,
 | |
|     sfSelChanged
 | |
|     );                                           //mh 2000-10-30
 | |
|   TSynStateFlags = set of TSynStateFlag;
 | |
| 
 | |
|   TSynEditorOption = (
 | |
|     eoAutoIndent,              // Will indent the caret on new lines with the same amount of leading white space as the preceding line
 | |
|     eoBracketHighlight,        // Highlight matching bracket
 | |
|     eoEnhanceHomeKey,          // home key jumps to line start if nearer, similar to visual studio
 | |
|     eoGroupUndo,               // When undoing/redoing actions, handle all continous changes of the same kind in one call instead undoing/redoing each command separately
 | |
|     eoHalfPageScroll,          // When scrolling with page-up and page-down commands, only scroll a half page at a time
 | |
|     eoHideRightMargin,         // Hides the right margin line
 | |
|     eoKeepCaretX,              // When moving through lines w/o Cursor Past EOL, keeps the X position of the cursor
 | |
|     eoNoCaret,                 // Makes it so the caret is never visible
 | |
|     eoNoSelection,             // Disables selecting text
 | |
|     eoPersistentCaret,         // Do not hide caret when focus lost // TODO: Windows may hide it, if another component sets up a caret
 | |
|     eoScrollByOneLess,         // Forces scrolling to be one less
 | |
|     eoScrollPastEof,           // Allows the cursor to go past the end of file marker
 | |
|     eoScrollPastEol,           // Allows the cursor to go past the last character into the white space at the end of a line
 | |
|     eoScrollHintFollows,       // The scroll hint follows the mouse when scrolling vertically
 | |
|     eoShowScrollHint,          // Shows a hint of the visible line numbers when scrolling vertically
 | |
|     eoShowSpecialChars,        // Shows the special Characters
 | |
|     eoSmartTabs,               // When tabbing, the cursor will go to the next non-white space character of the previous line
 | |
|     eoTabIndent,               // When active <Tab> and <Shift><Tab> act as block indent, unindent when text is selected
 | |
|     eoTabsToSpaces,            // Converts a tab character to a specified number of space characters
 | |
|     eoTrimTrailingSpaces,      // Spaces at the end of lines will be trimmed and not saved
 | |
| 
 | |
|     // Not implemented
 | |
|     eoAutoSizeMaxScrollWidth,  //TODO Automatically resizes the MaxScrollWidth property when inserting text
 | |
|     eoDisableScrollArrows,     //TODO Disables the scroll bar arrow buttons when you can't scroll in that direction any more
 | |
|     eoHideShowScrollbars,      //TODO if enabled, then the scrollbars will only show when necessary.  If you have ScrollPastEOL, then it the horizontal bar will always be there (it uses MaxLength instead)
 | |
|     eoDropFiles,               //TODO Allows the editor accept file drops
 | |
|     eoSmartTabDelete,          //TODO similar to Smart Tabs, but when you delete characters
 | |
|     eoSpacesToTabs,            // Converts space characters to tabs and spaces
 | |
|     eoAutoIndentOnPaste,       // Indent text inserted from clipboard
 | |
|     //eoSpecialLineDefaultFg,    //TODO disables the foreground text color override when using the OnSpecialLineColor event
 | |
| 
 | |
|     // Only for compatibility, moved to TSynEditorMouseOptions
 | |
|     // keep in one block
 | |
|     eoAltSetsColumnMode,       //
 | |
|     eoDragDropEditing,         // Allows you to select a block of text and drag it within the document to another location
 | |
|     eoRightMouseMovesCursor,   // When clicking with the right mouse for a popup menu, move the cursor to that location
 | |
|     eoDoubleClickSelectsLine,  // Select line on double click
 | |
|     eoShowCtrlMouseLinks       // Pressing Ctrl (SYNEDIT_LINK_MODIFIER) will highlight the word under the mouse cursor
 | |
|     );
 | |
|   TSynEditorOptions = set of TSynEditorOption;
 | |
| 
 | |
|   TSynEditorOption2 = (
 | |
|     eoCaretSkipsSelection,     // Caret skips selection on VK_LEFT/VK_RIGHT
 | |
|     eoCaretSkipTab,            // Caret can not enter tabs
 | |
|     eoAlwaysVisibleCaret,      // Move caret to be always visible when scrolling
 | |
|     eoEnhanceEndKey,           // end key jumps to visual/hard line end whichever is nearer
 | |
|     eoFoldedCopyPaste,         // Remember folds in copy/paste operations
 | |
|     eoPersistentBlock,         // Keep block if caret moves away or text is edited
 | |
|     eoOverwriteBlock,          // Non persitent block, gets overwritten on insert/del
 | |
|     eoAutoHideCursor           // Hide the mouse cursor, on keyboard action
 | |
|   );
 | |
|   TSynEditorOptions2 = set of TSynEditorOption2;
 | |
| 
 | |
|   TSynEditorMouseOption = SynEditMouseCmds.TSynEditorMouseOption;
 | |
|     //emUseMouseActions,
 | |
|     //emAltSetsColumnMode,       // Alt modifier, triggers column mode selection
 | |
|     //emDragDropEditing,         // Allows you to select a block of text and drag it within the document to another location
 | |
|     //emRightMouseMovesCursor,   // When clicking with the right mouse for a popup menu, move the cursor to that location
 | |
|     //emDoubleClickSelectsLine,  // Select line on double click
 | |
|     //emShowCtrlMouseLinks       // Pressing Ctrl (SYNEDIT_LINK_MODIFIER) will highlight the word under the mouse cursor
 | |
|   TSynEditorMouseOptions = SynEditMouseCmds.TSynEditorMouseOptions;
 | |
| 
 | |
|   // options for textbuffersharing
 | |
|   TSynEditorShareOption = (
 | |
|     eosShareMarks              // Shared Editors use the same list of marks
 | |
|   );
 | |
|   TSynEditorShareOptions = set of TSynEditorShareOption;
 | |
| 
 | |
|   TSynVisibleSpecialChars = SynEditTypes.TSynVisibleSpecialChars;
 | |
| 
 | |
| const
 | |
|   // MouseAction related options MUST NOT be included here
 | |
|   SYNEDIT_DEFAULT_OPTIONS = [
 | |
|     eoAutoIndent,
 | |
|     eoScrollPastEol,
 | |
|     eoSmartTabs,
 | |
|     eoTabsToSpaces,
 | |
|     eoTrimTrailingSpaces,
 | |
|     eoGroupUndo,
 | |
|     eoBracketHighlight
 | |
|   ];
 | |
| 
 | |
|   // Those will be prevented from being set => so evtl they may be removed
 | |
|   SYNEDIT_UNIMPLEMENTED_OPTIONS = [
 | |
|     eoAutoSizeMaxScrollWidth,  //TODO Automatically resizes the MaxScrollWidth property when inserting text
 | |
|     eoDisableScrollArrows,     //TODO Disables the scroll bar arrow buttons when you can't scroll in that direction any more
 | |
|     eoDropFiles,               //TODO Allows the editor accept file drops
 | |
|     eoHideShowScrollbars,      //TODO if enabled, then the scrollbars will only show when necessary.  If you have ScrollPastEOL, then it the horizontal bar will always be there (it uses MaxLength instead)
 | |
|     eoSmartTabDelete,          //TODO similar to Smart Tabs, but when you delete characters
 | |
|     ////eoSpecialLineDefaultFg,    //TODO disables the foreground text color override when using the OnSpecialLineColor event
 | |
|     eoAutoIndentOnPaste,       // Indent text inserted from clipboard
 | |
|     eoSpacesToTabs             // Converts space characters to tabs and spaces
 | |
|   ];
 | |
| 
 | |
|   SYNEDIT_OLD_MOUSE_OPTIONS = [
 | |
|     eoAltSetsColumnMode,       //
 | |
|     eoDragDropEditing,         // Allows you to select a block of text and drag it within the document to another location
 | |
|     eoRightMouseMovesCursor,   // When clicking with the right mouse for a popup menu, move the cursor to that location
 | |
|     eoDoubleClickSelectsLine,  // Select line on double click
 | |
|     eoShowCtrlMouseLinks       // Pressing Ctrl (SYNEDIT_LINK_MODIFIER) will highlight the word under the mouse cursor
 | |
|   ];
 | |
| 
 | |
|   SYNEDIT_OLD_MOUSE_OPTIONS_MAP: array [eoAltSetsColumnMode..eoShowCtrlMouseLinks] of TSynEditorMouseOption = (
 | |
|     emAltSetsColumnMode,       // eoAltSetsColumnMode
 | |
|     emDragDropEditing,         // eoDragDropEditing
 | |
|     emRightMouseMovesCursor,   // eoRightMouseMovesCursor
 | |
|     emDoubleClickSelectsLine,  // eoDoubleClickSelectsLine
 | |
|     emShowCtrlMouseLinks       // eoShowCtrlMouseLinks
 | |
|   );
 | |
| 
 | |
|   SYNEDIT_DEFAULT_SHARE_OPTIONS = [
 | |
|     eosShareMarks
 | |
|   ];
 | |
| 
 | |
|   SYNEDIT_DEFAULT_OPTIONS2 = [
 | |
|     eoFoldedCopyPaste,
 | |
|     eoOverwriteBlock
 | |
|   ];
 | |
| 
 | |
|   SYNEDIT_DEFAULT_MOUSE_OPTIONS = [];
 | |
| 
 | |
|   SYNEDIT_DEFAULT_VISIBLESPECIALCHARS = [
 | |
|     vscSpace,
 | |
|     vscTabAtLast
 | |
|   ];
 | |
| 
 | |
| type
 | |
| // use scAll to update a statusbar when another TCustomSynEdit got the focus
 | |
|   TSynStatusChange = SynEditTypes.TSynStatusChange;
 | |
|   TSynStatusChanges = SynEditTypes.TSynStatusChanges;
 | |
| 
 | |
|   TStatusChangeEvent = procedure(Sender: TObject; Changes: TSynStatusChanges)
 | |
|     of object;
 | |
| 
 | |
|   TCustomSynEdit = class;
 | |
| 
 | |
|   TSynLineState = (slsNone, slsSaved, slsUnsaved);
 | |
| 
 | |
| 
 | |
|   { TLazSynEditPlugin }
 | |
| 
 | |
|   TLazSynEditPlugin = class(TSynEditFriend)
 | |
|   protected
 | |
|     procedure BeforeEditorChange; virtual;
 | |
|     procedure AfterEditorChange; virtual;
 | |
|     procedure RegisterToEditor(AValue: TCustomSynEdit);
 | |
|     procedure UnRegisterFromEditor(AValue: TCustomSynEdit);
 | |
|     procedure SetEditor(const AValue: TCustomSynEdit); virtual;
 | |
|     function  GetEditor: TCustomSynEdit;
 | |
|     function  OwnedByEditor: Boolean; virtual; // if true, this will be destroyed by synedit
 | |
|     procedure DoEditorDestroyed(const AValue: TCustomSynEdit); virtual;
 | |
| 
 | |
|     procedure DoEditorAdded(AValue: TCustomSynEdit); virtual;
 | |
|     procedure DoEditorRemoving(AValue: TCustomSynEdit); virtual;
 | |
|   public
 | |
|     constructor Create(AOwner: TComponent); override;
 | |
|     destructor Destroy; override;
 | |
|     property Editor: TCustomSynEdit read GetEditor write SetEditor;
 | |
|   end;
 | |
| 
 | |
|   { TSynHookedKeyTranslationList }
 | |
| 
 | |
|   TSynHookedKeyTranslationList = Class(TMethodList)
 | |
|   public
 | |
|     procedure CallHookedKeyTranslationHandlers(Sender: TObject;
 | |
|       Code: word; SState: TShiftState; var Data: pointer;
 | |
|       var IsStartOfCombo: boolean; var Handled: boolean;
 | |
|       var Command: TSynEditorCommand;
 | |
|       // ComboKeyStrokes decides, either FinishComboOnly, or new stroke
 | |
|       var ComboKeyStrokes: TSynEditKeyStrokes
 | |
|     );
 | |
|   end;
 | |
| 
 | |
|   { TLazSynKeyDownEventList }
 | |
| 
 | |
|   { TLazSynMouseDownEventList }
 | |
| 
 | |
|   TLazSynMouseDownEventList = Class(TMethodList)
 | |
|   public
 | |
|     procedure CallMouseDownHandlers(Sender: TObject; Button: TMouseButton;
 | |
|                                     Shift: TShiftState; X, Y: Integer);
 | |
|   end;
 | |
| 
 | |
|   { TLazSynKeyDownEventList }
 | |
| 
 | |
|   TLazSynKeyDownEventList = Class(TMethodList)
 | |
|   public
 | |
|     procedure CallKeyDownHandlers(Sender: TObject; var Key: Word; Shift: TShiftState);
 | |
|   end;
 | |
| 
 | |
|   { TLazSynKeyPressEventList }
 | |
| 
 | |
|   TLazSynKeyPressEventList = Class(TMethodList)
 | |
|   public
 | |
|     procedure CallKeyPressHandlers(Sender: TObject; var Key: char);
 | |
|   end;
 | |
| 
 | |
|   { TLazSynUtf8KeyPressEventList }
 | |
| 
 | |
|   TLazSynUtf8KeyPressEventList = Class(TMethodList)
 | |
|   public
 | |
|     procedure CallUtf8KeyPressHandlers(Sender: TObject; var UTF8Key: TUTF8Char);
 | |
|   end;
 | |
| 
 | |
| 
 | |
|   TSynMouseLinkEvent = procedure (
 | |
|     Sender: TObject; X, Y: Integer; var AllowMouseLink: Boolean) of object;
 | |
| 
 | |
|   TSynHomeMode = (synhmDefault, synhmFirstWord);
 | |
| 
 | |
|   TSynCoordinateMappingFlag = SynEditTypes.TSynCoordinateMappingFlag;
 | |
|   TSynCoordinateMappingFlags = SynEditTypes.TSynCoordinateMappingFlags;
 | |
| 
 | |
|   TLazSynWordBoundary = (
 | |
|     swbWordBegin,
 | |
|     swbWordEnd,
 | |
|     swbTokenBegin,
 | |
|     swbTokenEnd,
 | |
|     swbCaseChange
 | |
|   );
 | |
| 
 | |
|   { TCustomSynEdit }
 | |
| 
 | |
|   TCustomSynEdit = class(TSynEditBase)
 | |
|     procedure SelAvailChange(Sender: TObject);
 | |
|   {$IFDEF WinIME}
 | |
|   private
 | |
|     FImeHandler: LazSynIme;
 | |
|     procedure SetImeHandler(AValue: LazSynIme);
 | |
|     procedure WMImeRequest(var Msg: TMessage); message WM_IME_REQUEST;
 | |
|     procedure WMImeNotify(var Msg: TMessage); message WM_IME_NOTIFY;
 | |
|     procedure WMImeStartComposition(var Msg: TMessage); message WM_IME_STARTCOMPOSITION;
 | |
|     procedure WMImeComposition(var Msg: TMessage); message WM_IME_COMPOSITION;
 | |
|     procedure WMImeEndComposition(var Msg: TMessage); message WM_IME_ENDCOMPOSITION;
 | |
|   protected
 | |
|     // SynEdit takes ownership
 | |
|     property ImeHandler: LazSynIme read FImeHandler write SetImeHandler;
 | |
|   {$ENDIF}
 | |
|   private
 | |
|     procedure WMDropFiles(var Msg: TMessage); message WM_DROPFILES;
 | |
|     procedure WMEraseBkgnd(var Msg: TMessage); message WM_ERASEBKGND;
 | |
|     procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE;
 | |
|     procedure WMHScroll(var Msg: {$IFDEF SYN_LAZARUS}TLMScroll{$ELSE}TWMScroll{$ENDIF}); message WM_HSCROLL;
 | |
|     procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
 | |
|     procedure WMExit(var Message: TLMExit); message LM_EXIT;
 | |
|     procedure WMMouseWheel(var Message: TLMMouseEvent); message LM_MOUSEWHEEL;
 | |
|     //procedure WMMouseWheel(var Msg: TMessage); message WM_MOUSEWHEEL;
 | |
|     procedure WMSetFocus(var Msg: TLMSetFocus); message WM_SETFOCUS;
 | |
|     procedure WMVScroll(var Msg: {$IFDEF SYN_LAZARUS}TLMScroll{$ELSE}TWMScroll{$ENDIF}); message WM_VSCROLL;
 | |
|   private
 | |
|     FBlockIndent: integer;
 | |
|     FBlockTabIndent: integer;
 | |
|     FCaret: TSynEditCaret;
 | |
|     FInternalCaret: TSynEditCaret;
 | |
|     FScreenCaret: TSynEditScreenCaret;
 | |
|     FInternalBlockSelection: TSynEditSelection;
 | |
|     FOnChangeUpdating: TChangeUpdatingEvent;
 | |
|     FMouseSelectionMode: TSynSelectionMode;
 | |
|     fMarkupManager : TSynEditMarkupManager;
 | |
|     fMarkupHighAll : TSynEditMarkupHighlightAll;
 | |
|     fMarkupHighCaret : TSynEditMarkupHighlightAllCaret;
 | |
|     fMarkupBracket : TSynEditMarkupBracket;
 | |
|     fMarkupWordGroup : TSynEditMarkupWordGroup;
 | |
|     fMarkupCtrlMouse : TSynEditMarkupCtrlMouseLink;
 | |
|     fMarkupSpecialLine : TSynEditMarkupSpecialLine;
 | |
|     fMarkupSelection : TSynEditMarkupSelection;
 | |
|     fMarkupSpecialChar : TSynEditMarkupSpecialChar;
 | |
|     fFontDummy: TFont;
 | |
|     FLastSetFontSize: Integer;
 | |
|     fInserting: Boolean;
 | |
|     fLastMouseCaret: TPoint;  // Char; physical (screen)
 | |
|     FLastMousePoint: TPoint;  // Pixel
 | |
|     FChangedLinesStart: integer; // 1 based, 0 means invalid
 | |
|     FChangedLinesEnd: integer; // 1 based, 0 means invalid, -1 means rest of screen
 | |
|     FBeautifier, FDefaultBeautifier: TSynCustomBeautifier;
 | |
|     FBeautifyStartLineIdx, FBeautifyEndLineIdx: Integer;
 | |
| 
 | |
|     FFoldedLinesView:  TSynEditFoldedView;
 | |
|     FShareOptions: TSynEditorShareOptions;
 | |
|     FVisibleSpecialChars: TSynVisibleSpecialChars;
 | |
|     FTrimmedLinesView: TSynEditStringTrimmingList;
 | |
|     FDoubleWidthChrLinesView: SynEditStringDoubleWidthChars;
 | |
|     FTabbedLinesView:  TSynEditStringTabExpander;
 | |
|     FTheLinesView: TSynEditStrings;
 | |
|     FLines: TSynEditStrings;          // The real (un-mapped) line-buffer
 | |
|     FStrings: TStrings;               // External TStrings based interface to the Textbuffer
 | |
|     FTopLinesView: TSynEditStrings;   // The linesview that holds the real line-buffer/FLines
 | |
|     FDisplayView: TLazSynDisplayView;
 | |
| 
 | |
|     fExtraCharSpacing: integer;
 | |
|     fMaxLeftChar: Integer; // 1024
 | |
|     FOldWidth, FOldHeight: Integer;
 | |
| 
 | |
|     FPaintLock: Integer;
 | |
|     FPaintLockOwnerCnt: Integer;
 | |
|     FUndoBlockAtPaintLock: Integer;
 | |
|     FScrollBarUpdateLock: Integer;
 | |
|     FInvalidateRect: TRect;
 | |
|     FIsInDecPaintLock: Boolean;
 | |
|     fReadOnly: Boolean;
 | |
|     FScrollBars: TScrollStyle;
 | |
|     FOldTopView: Integer;
 | |
|     FLastTextChangeStamp: Int64;
 | |
|     fHighlighter: TSynCustomHighlighter;
 | |
|     fUndoList: TSynEditUndoList;
 | |
|     fRedoList: TSynEditUndoList;
 | |
|     FBookMarks: array[0..9] of TSynEditMark;
 | |
|     fMouseDownX: integer;
 | |
|     fMouseDownY: integer;
 | |
|     fBookMarkOpt: TSynBookMarkOpt;
 | |
|     FMouseWheelAccumulator, FMouseWheelLinesAccumulator: integer;
 | |
|     fHideSelection: boolean;
 | |
|     fOverwriteCaret: TSynEditCaretType;
 | |
|     fInsertCaret: TSynEditCaretType;
 | |
|     FKeyStrokes: TSynEditKeyStrokes;
 | |
|     FCurrentComboKeyStrokes: TSynEditKeyStrokes; // Holding info about the keystroke(s) already received for a mult-stroke-combo
 | |
|     FMouseActions, FMouseSelActions, FMouseTextActions: TSynEditMouseInternalActions;
 | |
|     FMouseActionSearchHandlerList: TSynEditMouseActionSearchList;
 | |
|     FMouseActionExecHandlerList: TSynEditMouseActionExecList;
 | |
|     FMarkList: TSynEditMarkList;
 | |
|     fExtraLineSpacing: integer;
 | |
|     FUseUTF8: boolean;
 | |
|     fWantTabs: boolean;
 | |
|     FLeftGutter, FRightGutter: TSynGutter;
 | |
|     fTabWidth: integer;
 | |
|     fTextDrawer: TheTextDrawer;
 | |
|     FPaintLineColor, FPaintLineColor2: TSynSelectedColor;
 | |
|     fStateFlags: TSynStateFlags;
 | |
|     FOptions: TSynEditorOptions;
 | |
|     FOptions2: TSynEditorOptions2;
 | |
|     FMouseOptions: TSynEditorMouseOptions;
 | |
|     fStatusChanges: TSynStatusChanges;
 | |
|     fTSearch: TSynEditSearch;
 | |
|     fHookedCommandHandlers: TList;
 | |
|     FHookedKeyTranslationList: TSynHookedKeyTranslationList;
 | |
|     FMouseDownEventList: TLazSynMouseDownEventList;
 | |
|     FKeyDownEventList: TLazSynKeyDownEventList;
 | |
|     FKeyPressEventList: TLazSynKeyPressEventList;
 | |
|     FUtf8KeyPressEventList: TLazSynUtf8KeyPressEventList;
 | |
|     FStatusChangedList: TObject;
 | |
|     FPlugins: TList;
 | |
|     fScrollTimer: TTimer;
 | |
|     FScrollDeltaX, FScrollDeltaY: Integer;
 | |
|     FInMouseClickEvent: Boolean;
 | |
|     FMouseClickDoPopUp: Boolean;
 | |
|     // event handlers
 | |
|     FOnCutCopy: TSynCopyPasteEvent;
 | |
|     FOnPaste: TSynCopyPasteEvent;
 | |
|     fOnChange: TNotifyEvent;
 | |
|     FOnClearMark: TPlaceMarkEvent;                                              // djlp 2000-08-29
 | |
|     fOnCommandProcessed: TProcessCommandEvent;
 | |
|     fOnDropFiles: TSynDropFilesEvent;
 | |
|     fOnPaint: TPaintEvent;
 | |
|     FOnPlaceMark: TPlaceMarkEvent;
 | |
|     fOnProcessCommand: TProcessCommandEvent;
 | |
|     fOnProcessUserCommand: TProcessCommandEvent;
 | |
|     fOnReplaceText: TReplaceTextEvent;
 | |
|     fOnSpecialLineColors: TSpecialLineColorsEvent;// needed, because bug fpc 11926
 | |
|     fOnStatusChange: TStatusChangeEvent;
 | |
|     FOnSpecialLineMarkup: TSpecialLineMarkupEvent;// needed, because bug fpc 11926
 | |
|     FOnClickLink: TMouseEvent;
 | |
|     FOnMouseLink: TSynMouseLinkEvent;
 | |
|     FPendingFoldState: String;
 | |
| 
 | |
|     procedure AquirePrimarySelection;
 | |
|     function GetChangeStamp: int64;
 | |
|     function GetCharsInWindow: Integer;
 | |
|     function GetCharWidth: integer;
 | |
|     function GetDefSelectionMode: TSynSelectionMode;
 | |
|     function GetFoldState: String;
 | |
|     function GetLeftChar: Integer;
 | |
|     function GetLineHeight: integer;
 | |
|     function GetLinesInWindow: Integer;
 | |
|     function GetModified: Boolean;
 | |
|     function GetMouseActions: TSynEditMouseActions;
 | |
|     function GetMouseSelActions: TSynEditMouseActions;
 | |
|     function GetMouseTextActions: TSynEditMouseActions;
 | |
|     function GetPaintLockOwner: TSynEditBase;
 | |
|     function GetPlugin(Index: Integer): TLazSynEditPlugin;
 | |
|     function GetRightEdge: Integer;
 | |
|     function GetRightEdgeColor: TColor;
 | |
|     function GetTextBetweenPoints(aStartPoint, aEndPoint: TPoint): String;
 | |
|     function GetTopLine: Integer;
 | |
|     procedure SetBlockTabIndent(AValue: integer);
 | |
|     procedure SetBracketMatchColor(AValue: TSynSelectedColor);
 | |
|     procedure SetDefSelectionMode(const AValue: TSynSelectionMode);
 | |
|     procedure SetFoldedCodeColor(AValue: TSynSelectedColor);
 | |
|     procedure SetFoldState(const AValue: String);
 | |
|     procedure SetHighlightAllColor(AValue: TSynSelectedColor);
 | |
|     procedure SetIncrementColor(AValue: TSynSelectedColor);
 | |
|     procedure SetLineHighlightColor(AValue: TSynSelectedColor);
 | |
|     procedure SetMouseActions(const AValue: TSynEditMouseActions);
 | |
|     procedure SetMouseLinkColor(AValue: TSynSelectedColor);
 | |
|     procedure SetMouseSelActions(const AValue: TSynEditMouseActions);
 | |
|     procedure SetMouseTextActions(AValue: TSynEditMouseActions);
 | |
|     procedure SetPaintLockOwner(const AValue: TSynEditBase);
 | |
|     procedure SetShareOptions(const AValue: TSynEditorShareOptions);
 | |
|     procedure SetTextBetweenPointsSimple(aStartPoint, aEndPoint: TPoint; const AValue: String);
 | |
|     procedure SetTextBetweenPointsEx(aStartPoint, aEndPoint: TPoint;
 | |
|       aCaretMode: TSynCaretAdjustMode; const AValue: String);
 | |
|     procedure SetVisibleSpecialChars(AValue: TSynVisibleSpecialChars);
 | |
|     procedure SurrenderPrimarySelection;
 | |
|     procedure BookMarkOptionsChanged(Sender: TObject);
 | |
|     procedure ComputeCaret(X, Y: Integer);
 | |
|     procedure DoBlockIndent;
 | |
|     procedure DoBlockUnindent;
 | |
|     procedure DoHomeKey(aMode: TSynHomeMode = synhmDefault);
 | |
|     procedure DoEndKey;
 | |
|     procedure DoTabKey;
 | |
|     function FindHookedCmdEvent(AHandlerProc: THookedCommandEvent): integer;
 | |
|     function GetBlockBegin: TPoint;
 | |
|     function GetBlockEnd: TPoint;
 | |
|     function GetBracketHighlightStyle: TSynEditBracketHighlightStyle;
 | |
|     function GetCanPaste: Boolean;
 | |
|     function GetCanRedo: Boolean;
 | |
|     function GetCanUndo: Boolean;
 | |
|     function GetCaretXY: TPoint;
 | |
|     function GetFoldedCodeColor: TSynSelectedColor;
 | |
|     function GetMarkup(Index: integer): TSynEditMarkup;
 | |
|     function GetMarkupByClass(Index: TSynEditMarkupClass): TSynEditMarkup;
 | |
|     function GetCaretX : Integer;
 | |
|     function GetCaretY : Integer;
 | |
|     function GetCaretUndo: TSynEditUndoItem;
 | |
|     function GetHighlightAllColor : TSynSelectedColor;
 | |
|     function GetIncrementColor : TSynSelectedColor;
 | |
|     function GetLineHighlightColor: TSynSelectedColor;
 | |
|     function GetOnGutterClick : TGutterClickEvent;
 | |
|     function GetSelectedColor : TSynSelectedColor;
 | |
|     function GetBracketMatchColor : TSynSelectedColor;
 | |
|     function GetMouseLinkColor : TSynSelectedColor;
 | |
|     function GetTrimSpaceType: TSynEditStringTrimmingType;
 | |
|     procedure SetBracketHighlightStyle(
 | |
|       const AValue: TSynEditBracketHighlightStyle);
 | |
|     procedure SetOnGutterClick(const AValue : TGutterClickEvent);
 | |
|     procedure SetSelectedColor(const AValue : TSynSelectedColor);
 | |
|     procedure SetSpecialLineColors(const AValue : TSpecialLineColorsEvent);
 | |
|     procedure SetSpecialLineMarkup(const AValue : TSpecialLineMarkupEvent);
 | |
|     function GetHookedCommandHandlersCount: integer;
 | |
|     function GetLineText: string;
 | |
|     function GetCharLen(const Line: string; CharStartPos: integer): integer;
 | |
|     function GetLogicalCaretXY: TPoint;
 | |
|     procedure SetLogicalCaretXY(const NewLogCaretXY: TPoint);
 | |
|     procedure SetBeautifier(NewBeautifier: TSynCustomBeautifier);
 | |
|     function GetMaxUndo: Integer;
 | |
|     function GetSelAvail: Boolean;
 | |
|     function GetSelText: string;
 | |
|     procedure SetTrimSpaceType(const AValue: TSynEditStringTrimmingType);
 | |
|     function SynGetText: string;
 | |
|     procedure GutterChanged(Sender: TObject);
 | |
|     procedure GutterResized(Sender: TObject);
 | |
|     // x-pixel pos of first char on canvas
 | |
|     function  TextLeftPixelOffset(IncludeGutterTextDist: Boolean = True): Integer;
 | |
|     function  TextRightPixelOffset: Integer;
 | |
|     function IsPointInSelection(Value: TPoint): boolean;
 | |
|     procedure LockUndo;
 | |
|     procedure MoveCaretHorz(DX: integer);
 | |
|     procedure MoveCaretVert(DY: integer);
 | |
|     procedure PrimarySelectionRequest(const RequestedFormatID: TClipboardFormat;
 | |
|       Data: TStream);
 | |
|     procedure ScanRanges(ATextChanged: Boolean = True);
 | |
|     procedure IdleScanRanges(Sender: TObject; var Done: Boolean);
 | |
|     procedure DoBlockSelectionChanged(Sender: TObject);
 | |
|     procedure SetBlockBegin(Value: TPoint);
 | |
|     procedure SetBlockEnd(Value: TPoint);
 | |
|     procedure SetBlockIndent(const AValue: integer);
 | |
|     procedure SetCaretAndSelection(const ptCaret, ptBefore, ptAfter: TPoint;
 | |
|                                    Mode: TSynSelectionMode = smCurrent;
 | |
|                                    MakeSelectionVisible: Boolean = False
 | |
|                                    );
 | |
|     procedure SetCaretX(const Value: Integer);
 | |
|     procedure SetCaretY(const Value: Integer);
 | |
|     procedure SetExtraLineSpacing(const Value: integer);
 | |
|     procedure SetGutter(const Value: TSynGutter);
 | |
|     procedure SetRightGutter(const AValue: TSynGutter);
 | |
|     procedure SetHideSelection(const Value: boolean);
 | |
|     procedure SetHighlighter(const Value: TSynCustomHighlighter);
 | |
|     procedure RemoveHooksFromHighlighter;
 | |
|     procedure SetInsertCaret(const Value: TSynEditCaretType);
 | |
|     procedure SetInsertMode(const Value: boolean);
 | |
|     procedure SetKeystrokes(const Value: TSynEditKeyStrokes);
 | |
|     procedure SetExtraCharSpacing(const Value: integer);
 | |
|     procedure SetLastMouseCaret(const AValue: TPoint);
 | |
|     function  CurrentMaxLeftChar: Integer;
 | |
|     function  CurrentMaxLineLen: Integer;
 | |
|     procedure SetLeftChar(Value: Integer);
 | |
|     procedure SetLineText(Value: string);
 | |
|     procedure SetMaxLeftChar(Value: integer);
 | |
|     procedure SetMaxUndo(const Value: Integer);
 | |
|     procedure SetModified(Value: boolean);
 | |
|     procedure SetOptions(Value: TSynEditorOptions);
 | |
|     procedure UpdateOptions;
 | |
|     procedure SetOptions2(const Value: TSynEditorOptions2);
 | |
|     procedure UpdateOptions2;
 | |
|     procedure SetMouseOptions(AValue: TSynEditorMouseOptions);
 | |
|     procedure UpdateMouseOptions;
 | |
|     procedure SetOverwriteCaret(const Value: TSynEditCaretType);
 | |
|     procedure SetRightEdge(Value: Integer);
 | |
|     procedure SetRightEdgeColor(Value: TColor);
 | |
|     procedure SetScrollBars(const Value: TScrollStyle);
 | |
|     function  GetSelectionMode : TSynSelectionMode;
 | |
|     procedure SetSelectionMode(const Value: TSynSelectionMode);
 | |
|     procedure SetSelTextExternal(const Value: string);
 | |
|     procedure SetTabWidth(Value: integer);
 | |
|     procedure SynSetText(const Value: string);
 | |
|     function  CurrentMaxTopView: Integer;
 | |
|     procedure SetTopLine(Value: Integer);
 | |
|     procedure ScrollAfterTopLineChanged;
 | |
|     procedure SetWantTabs(const Value: boolean);
 | |
|     procedure SetWordBlock(Value: TPoint);
 | |
|     procedure SetLineBlock(Value: TPoint; WithLeadSpaces: Boolean = True);
 | |
|     procedure SetParagraphBlock(Value: TPoint);
 | |
|     procedure RecalcCharsAndLinesInWin(CheckCaret: Boolean);
 | |
|     procedure StatusChanged(AChanges: TSynStatusChanges);
 | |
|     procedure UndoRedoAdded(Sender: TObject);
 | |
|     procedure ModifiedChanged(Sender: TObject);
 | |
|     procedure UnlockUndo;
 | |
|     procedure UpdateCaret(IgnorePaintLock: Boolean = False);
 | |
|     procedure UpdateScrollBars;
 | |
|     procedure ChangeTextBuffer(NewBuffer: TSynEditStringList);
 | |
|     function  IsMarkListShared: Boolean;
 | |
|     procedure RecreateMarkList;
 | |
|     procedure DestroyMarkList;
 | |
|     procedure RemoveHandlers(ALines: TSynEditStrings = nil);
 | |
|     procedure ExtraLineCharsChanged(Sender: TObject);
 | |
|     procedure InternalBeginUndoBlock(aList: TSynEditUndoList = nil); // includes paintlock
 | |
|     procedure InternalEndUndoBlock(aList: TSynEditUndoList = nil);
 | |
|   protected
 | |
|     {$IFDEF EnableDoubleBuf}
 | |
|     BufferBitmap: TBitmap; // the double buffer
 | |
|     SavedCanvas: TCanvas; // the normal TCustomControl canvas during paint
 | |
|     {$ENDIF}
 | |
|     FTextArea: TLazSynTextArea;
 | |
|     FLeftGutterArea, FRightGutterArea: TLazSynGutterArea;
 | |
|     FPaintArea: TLazSynSurfaceManager;
 | |
| 
 | |
|     procedure Paint; override;
 | |
|     procedure StartPaintBuffer(const ClipRect: TRect);
 | |
|     procedure EndPaintBuffer(const ClipRect: TRect);
 | |
|     procedure DoOnPaint; virtual;
 | |
| 
 | |
|     procedure IncPaintLock;
 | |
|     procedure DecPaintLock;
 | |
|     procedure DoIncPaintLock(Sender: TObject);
 | |
|     procedure DoDecPaintLock(Sender: TObject);
 | |
|     procedure DoIncForeignPaintLock(Sender: TObject);
 | |
|     procedure DoDecForeignPaintLock(Sender: TObject);
 | |
|     procedure SetUpdateState(NewUpdating: Boolean; Sender: TObject); virtual;      // Called *before* paintlock, and *after* paintlock
 | |
| 
 | |
|     property PaintLockOwner: TSynEditBase read GetPaintLockOwner write SetPaintLockOwner;
 | |
|     property TextDrawer: TheTextDrawer read fTextDrawer;
 | |
| 
 | |
|   protected
 | |
|     procedure CreateHandle; override;
 | |
|     procedure CreateParams(var Params: TCreateParams); override;
 | |
|     procedure CreateWnd; override;
 | |
|     procedure DestroyWnd; override;
 | |
|     procedure Loaded; override;
 | |
|     function  GetChildOwner: TComponent; override;
 | |
|     procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
 | |
| 
 | |
|     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
 | |
|     procedure UTF8KeyPress(var Key: TUTF8Char); override;
 | |
|     procedure KeyPress(var Key: Char); override;
 | |
|     procedure KeyUp(var Key : Word; Shift : TShiftState); override;
 | |
| 
 | |
|     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y:
 | |
|       Integer); override;
 | |
|     procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
 | |
|     procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 | |
|       override;
 | |
|     procedure ScrollTimerHandler(Sender: TObject);
 | |
|     procedure DoContextPopup(MousePos: TPoint; var Handled: Boolean); override;
 | |
|     procedure FindAndHandleMouseAction(AButton: TSynMouseButton; AShift: TShiftState;
 | |
|                                 X, Y: Integer; ACCount:TSynMAClickCount;
 | |
|                                 ADir: TSynMAClickDir;
 | |
|                                 AWheelDelta: Integer = 0);
 | |
|     function DoHandleMouseAction(AnActionList: TSynEditMouseActions;
 | |
|                                  AnInfo: TSynEditMouseActionInfo): Boolean;
 | |
| 
 | |
|   protected
 | |
|     procedure SetColor(Value: TColor); override;
 | |
|     procedure DragOver(Source: TObject; X, Y: Integer;
 | |
|       State: TDragState; var Accept: Boolean); override;
 | |
|     procedure DoOnResize; override;
 | |
|     function  RealGetText: TCaption; override;
 | |
|     procedure RealSetText(const Value: TCaption); override;
 | |
|     function GetLines: TStrings; override;
 | |
|     function GetViewedTextBuffer: TSynEditStrings; override;
 | |
|     function GetFoldedTextBuffer: TObject; override;
 | |
|     function GetTextBuffer: TSynEditStrings; override;
 | |
|     procedure SetLines(Value: TStrings);  override;
 | |
|     function GetMarkupMgr: TObject; override;
 | |
|     function GetCaretObj: TSynEditCaret; override;
 | |
|     procedure FontChanged(Sender: TObject); override;
 | |
|     function GetReadOnly: boolean; virtual;
 | |
|     procedure HighlighterAttrChanged(Sender: TObject);
 | |
|     // note: FirstLine and LastLine don't need to be in correct order
 | |
|     procedure InvalidateGutterLines(FirstLine, LastLine: integer);
 | |
|     procedure InvalidateLines(FirstLine, LastLine: integer);
 | |
|     Procedure LineCountChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
 | |
|     Procedure LineTextChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
 | |
|     procedure SizeOrFontChanged(bFont: boolean);
 | |
|     procedure DoHighlightChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
 | |
|     procedure ListCleared(Sender: TObject);
 | |
|     procedure FoldChanged(Index: integer);
 | |
|     function  GetTopView : Integer;
 | |
|     procedure SetTopView(AValue : Integer);
 | |
|     procedure MarkListChange(Sender: TSynEditMark; Changes: TSynEditMarkChangeReasons);
 | |
|     procedure NotifyHookedCommandHandlers(var Command: TSynEditorCommand;
 | |
|       var AChar: TUTF8Char; Data: pointer; ATime: THookedCommandFlag); virtual;
 | |
|     function NextWordLogicalPos(ABoundary: TLazSynWordBoundary = swbWordBegin; WordEndForDelete : Boolean = false): TPoint;
 | |
|     function PrevWordLogicalPos(ABoundary: TLazSynWordBoundary = swbWordBegin): TPoint;
 | |
|     procedure RecalcCharExtent;
 | |
|     procedure RedoItem(Item: TSynEditUndoItem);
 | |
|     procedure SetCaretXY(Value: TPoint);
 | |
|     procedure CaretChanged(Sender: TObject);
 | |
|     procedure SetName(const Value: TComponentName); override;
 | |
|     procedure SetReadOnly(Value: boolean); virtual;
 | |
|     procedure SetSelTextPrimitive(PasteMode: TSynSelectionMode; Value: PChar;
 | |
|       AddToUndoList: Boolean = false);
 | |
|     procedure UndoItem(Item: TSynEditUndoItem);
 | |
|     procedure UpdateCursor;
 | |
|     procedure DoOnCommandProcessed(Command: TSynEditorCommand;
 | |
|       AChar: TUTF8Char;
 | |
|       Data: pointer); virtual;
 | |
|     // no method DoOnDropFiles, intercept the WM_DROPFILES instead
 | |
|     procedure DoOnProcessCommand(var Command: TSynEditorCommand;
 | |
|       var AChar: TUTF8Char;
 | |
|       Data: pointer); virtual;
 | |
|     function DoOnReplaceText(const ASearch, AReplace: string;
 | |
|       Line, Column: integer): TSynReplaceAction; virtual;
 | |
|     procedure DoOnStatusChange(Changes: TSynStatusChanges); virtual;
 | |
|     property LastMouseCaret: TPoint read FLastMouseCaret write SetLastMouseCaret;
 | |
|     function GetSelEnd: integer;                                                 //L505
 | |
|     function GetSelStart: integer;
 | |
|     procedure SetSelEnd(const Value: integer);
 | |
|     procedure SetSelStart(const Value: integer);
 | |
|     property TextView : TSynEditFoldedView read FFoldedLinesView;
 | |
|     property TopView: Integer read GetTopView write SetTopView;  // TopLine converted into Visible(View) lines
 | |
|     function PasteFromClipboardEx(ClipHelper: TSynClipboardStream): Boolean;
 | |
|     function FindNextUnfoldedLine(iLine: integer; Down: boolean): Integer;
 | |
|     // Todo: Reduce the argument list of Creategutter
 | |
|     function CreateGutter(AOwner : TSynEditBase; ASide: TSynGutterSide;
 | |
|                           ATextDrawer: TheTextDrawer): TSynGutter; virtual;
 | |
|   public
 | |
|     constructor Create(AOwner: TComponent); override;
 | |
|     destructor Destroy; override;
 | |
|     procedure AfterLoadFromFile;
 | |
| 
 | |
|     procedure BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF};
 | |
|     procedure BeginUpdate(WithUndoBlock: Boolean = True);
 | |
|     procedure EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF};
 | |
|     procedure EndUpdate;
 | |
| 
 | |
|   public
 | |
|     // Caret
 | |
|     function CaretXPix: Integer;
 | |
|     function CaretYPix: Integer;
 | |
|     procedure EnsureCursorPosVisible;
 | |
|     procedure MoveCaretToVisibleArea;
 | |
|     procedure MoveCaretIgnoreEOL(const NewCaret: TPoint);
 | |
|     procedure MoveLogicalCaretIgnoreEOL(const NewLogCaret: TPoint);
 | |
| 
 | |
|     property CaretX: Integer read GetCaretX write SetCaretX;
 | |
|     property CaretY: Integer read GetCaretY write SetCaretY;
 | |
|     property CaretXY: TPoint read GetCaretXY write SetCaretXY;// screen position
 | |
|     property LogicalCaretXY: TPoint read GetLogicalCaretXY write SetLogicalCaretXY;
 | |
| 
 | |
|     // Selection
 | |
|     procedure ClearSelection;
 | |
|     procedure SelectAll;
 | |
|     procedure SelectToBrace;
 | |
|     procedure SelectWord;
 | |
|     procedure SelectLine(WithLeadSpaces: Boolean = True);
 | |
|     procedure SelectParagraph;
 | |
| 
 | |
|     property BlockBegin: TPoint read GetBlockBegin write SetBlockBegin;         // Set Blockbegin. For none persistent also sets Blockend. Setting Caret may undo this and should be done before setting block
 | |
|     property BlockEnd: TPoint read GetBlockEnd write SetBlockEnd;
 | |
|     property SelStart: Integer read GetSelStart write SetSelStart;
 | |
|     property SelEnd: Integer read GetSelEnd write SetSelEnd;
 | |
|     property SelAvail: Boolean read GetSelAvail;
 | |
|     property SelText: string read GetSelText write SetSelTextExternal;
 | |
| 
 | |
|     // Text
 | |
|     procedure ClearAll;
 | |
|     procedure InsertTextAtCaret(aText: String; aCaretMode : TSynCaretAdjustMode = scamEnd);
 | |
| 
 | |
|     property TextBetweenPoints[aStartPoint, aEndPoint: TPoint]: String              // Logical Points
 | |
|       read GetTextBetweenPoints write SetTextBetweenPointsSimple;
 | |
|     property TextBetweenPointsEx[aStartPoint, aEndPoint: TPoint; CaretMode: TSynCaretAdjustMode]: String
 | |
|       write SetTextBetweenPointsEx;
 | |
|     procedure SetTextBetweenPoints(aStartPoint, aEndPoint: TPoint;
 | |
|                                    const AValue: String;
 | |
|                                    aFlags: TSynEditTextFlags = [];
 | |
|                                    aCaretMode: TSynCaretAdjustMode = scamIgnore;
 | |
|                                    aMarksMode: TSynMarksAdjustMode = smaMoveUp;
 | |
|                                    aSelectionMode: TSynSelectionMode = smNormal
 | |
|                                   );
 | |
| 
 | |
|     property LineText: string read GetLineText write SetLineText;
 | |
|     property Text: string read SynGetText write SynSetText;                     // No uncommited (trailing/trimmable) spaces
 | |
| 
 | |
|     function GetLineState(ALine: Integer): TSynLineState;
 | |
|     procedure MarkTextAsSaved;
 | |
| 
 | |
|     // BoorMark
 | |
|     procedure ClearBookMark(BookMark: Integer);
 | |
|     function  GetBookMark(BookMark: integer; var X, Y: integer): boolean;
 | |
|     procedure GotoBookMark(BookMark: Integer);
 | |
|     function  IsBookmark(BookMark: integer): boolean;
 | |
|     procedure SetBookMark(BookMark: Integer; X: Integer; Y: Integer);
 | |
|     property Marks: TSynEditMarkList read fMarkList;
 | |
| 
 | |
|     // Undo/Redo
 | |
|     procedure ClearUndo;
 | |
|     procedure Redo;
 | |
|     procedure Undo;
 | |
|     property CanRedo: boolean read GetCanRedo;
 | |
|     property CanUndo: boolean read GetCanUndo;
 | |
| 
 | |
|     // Clipboard
 | |
|     procedure CopyToClipboard;
 | |
|     procedure CutToClipboard;
 | |
|     procedure PasteFromClipboard;
 | |
|     procedure DoCopyToClipboard(SText: string; FoldInfo: String = '');
 | |
|     property CanPaste: Boolean read GetCanPaste;
 | |
| 
 | |
|     procedure DragDrop(Source: TObject; X, Y: Integer); override;
 | |
| {$IFDEF SYN_COMPILER_4_UP}
 | |
|     function ExecuteAction(ExeAction: TBasicAction): boolean; override;
 | |
| {$ENDIF}
 | |
|     procedure CommandProcessor(Command:TSynEditorCommand;
 | |
|       AChar: TUTF8Char;
 | |
|       Data:pointer); virtual;
 | |
|     procedure ExecuteCommand(Command: TSynEditorCommand;
 | |
|       const AChar: TUTF8Char; Data: pointer); virtual;
 | |
| 
 | |
|     function GetHighlighterAttriAtRowCol(XY: TPoint; out Token: string;
 | |
|       out Attri: TSynHighlighterAttributes): boolean;
 | |
|     function GetHighlighterAttriAtRowColEx(XY: TPoint; out Token: string;
 | |
|       out TokenType, Start: Integer;
 | |
|       out Attri: TSynHighlighterAttributes): boolean;                           //L505
 | |
|     procedure GetWordBoundsAtRowCol(const XY: TPoint; out StartX, EndX: integer);
 | |
|     function GetWordAtRowCol(XY: TPoint): string;
 | |
|     function NextTokenPos: TPoint; virtual; deprecated; // use next word pos instead
 | |
|     function NextWordPos: TPoint; virtual;
 | |
|     function PrevWordPos: TPoint; virtual;
 | |
|     function IdentChars: TSynIdentChars;
 | |
|     function IsIdentChar(const c: TUTF8Char): boolean;
 | |
| 
 | |
|     function IsLinkable(Y, X1, X2: Integer): Boolean;
 | |
|     procedure InvalidateGutter;
 | |
|     procedure InvalidateLine(Line: integer);
 | |
| 
 | |
|     // Byte to Char
 | |
|     function LogicalToPhysicalPos(const p: TPoint): TPoint;
 | |
|     function LogicalToPhysicalCol(const Line: String; Index, LogicalPos
 | |
|                               : integer): integer;
 | |
|     // Char to Byte
 | |
|     function PhysicalToLogicalPos(const p: TPoint): TPoint;
 | |
|     function PhysicalToLogicalCol(const Line: string;
 | |
|                                   Index, PhysicalPos: integer): integer;
 | |
|     function PhysicalLineLength(Line: String; Index: integer): integer;
 | |
| 
 | |
|     // Pixel
 | |
|     function ScreenColumnToXValue(Col: integer): integer;  // map screen column to screen pixel
 | |
|     // RowColumnToPixels: Physical coords
 | |
|     function RowColumnToPixels(RowCol: TPoint): TPoint;
 | |
|     function PixelsToRowColumn(Pixels: TPoint; aFlags: TSynCoordinateMappingFlags = [scmLimitToLines]): TPoint;
 | |
|     function PixelsToLogicalPos(const Pixels: TPoint): TPoint;
 | |
|     //
 | |
|     function ScreenRowToRow(ScreenRow: integer; LimitToLines: Boolean = True): integer;
 | |
|     function RowToScreenRow(PhysicalRow: integer): integer;
 | |
| 
 | |
|     procedure Notification(AComponent: TComponent;
 | |
|       Operation: TOperation); override;
 | |
|     procedure RegisterCommandHandler(AHandlerProc: THookedCommandEvent;
 | |
|       AHandlerData: pointer; AFlags: THookedCommandFlags = [hcfPreExec, hcfPostExec]);
 | |
|     procedure UnregisterCommandHandler(AHandlerProc: THookedCommandEvent);
 | |
| 
 | |
|     procedure RegisterMouseActionSearchHandler(AHandlerProc: TSynEditMouseActionSearchProc);
 | |
|     procedure UnregisterMouseActionSearchHandler(AHandlerProc: TSynEditMouseActionSearchProc);
 | |
|     procedure RegisterMouseActionExecHandler(AHandlerProc: TSynEditMouseActionExecProc);
 | |
|     procedure UnregisterMouseActionExecHandler(AHandlerProc: TSynEditMouseActionExecProc);
 | |
| 
 | |
|     procedure RegisterKeyTranslationHandler(AHandlerProc: THookedKeyTranslationEvent);
 | |
|     procedure UnRegisterKeyTranslationHandler(AHandlerProc: THookedKeyTranslationEvent);
 | |
| 
 | |
|     procedure RegisterStatusChangedHandler(AStatusChangeProc: TStatusChangeEvent; AChanges: TSynStatusChanges);
 | |
|     procedure UnRegisterStatusChangedHandler(AStatusChangeProc: TStatusChangeEvent);
 | |
| 
 | |
|     procedure RegisterBeforeMouseDownHandler(AHandlerProc: TMouseEvent);
 | |
|     procedure UnregisterBeforeMouseDownHandler(AHandlerProc: TMouseEvent);
 | |
| 
 | |
|     procedure RegisterBeforeKeyDownHandler(AHandlerProc: TKeyEvent);
 | |
|     procedure UnregisterBeforeKeyDownHandler(AHandlerProc: TKeyEvent);
 | |
|     procedure RegisterBeforeKeyPressHandler(AHandlerProc: TKeyPressEvent);
 | |
|     procedure UnregisterBeforeKeyPressHandler(AHandlerProc: TKeyPressEvent);
 | |
|     procedure RegisterBeforeUtf8KeyPressHandler(AHandlerProc: TUTF8KeyPressEvent);
 | |
|     procedure UnregisterBeforeUtf8KeyPressHandler(AHandlerProc: TUTF8KeyPressEvent);
 | |
| 
 | |
|     function SearchReplace(const ASearch, AReplace: string;
 | |
|       AOptions: TSynSearchOptions): integer;
 | |
|     function SearchReplaceEx(const ASearch, AReplace: string;
 | |
|       AOptions: TSynSearchOptions; AStart: TPoint): integer;
 | |
| 
 | |
|     procedure SetUseIncrementalColor(const AValue : Boolean);
 | |
|     procedure SetDefaultKeystrokes; virtual;
 | |
|     procedure ResetMouseActions;  // set mouse-actions according to current Options / may clear them
 | |
|     procedure SetOptionFlag(Flag: TSynEditorOption; Value: boolean);
 | |
|     Procedure SetHighlightSearch(const ASearch: String; AOptions: TSynSearchOptions);
 | |
| {$IFDEF SYN_COMPILER_4_UP}
 | |
|     function UpdateAction(TheAction: TBasicAction): boolean; override;
 | |
| {$ENDIF}
 | |
|     procedure WndProc(var Msg: TMessage); override;
 | |
|     procedure EraseBackground(DC: HDC); override;
 | |
|   public
 | |
|     procedure FindMatchingBracket; virtual;
 | |
|     function FindMatchingBracket(PhysStartBracket: TPoint;
 | |
|                                  StartIncludeNeighborChars, MoveCaret,
 | |
|                                  SelectBrackets, OnlyVisible: Boolean
 | |
|                                  ): TPoint; virtual;
 | |
|     //code fold
 | |
|     procedure CodeFoldAction(iLine: integer); deprecated;
 | |
|     procedure UnfoldAll;
 | |
|     procedure FoldAll(StartLevel : Integer = 0; IgnoreNested : Boolean = False);
 | |
|     property FoldState: String read GetFoldState write SetFoldState;
 | |
| 
 | |
|     procedure AddKey(Command: TSynEditorCommand; Key1: word; SS1: TShiftState;
 | |
|       Key2: word; SS2: TShiftState);
 | |
|   public
 | |
|     property CharsInWindow: Integer read GetCharsInWindow;
 | |
|     property CharWidth: integer read GetCharWidth;
 | |
|     property LeftChar: Integer read GetLeftChar write SetLeftChar;
 | |
|     property LineHeight: integer read GetLineHeight;
 | |
|     property LinesInWindow: Integer read GetLinesInWindow;
 | |
|     property MaxLeftChar: integer read fMaxLeftChar write SetMaxLeftChar
 | |
|       default 1024;
 | |
|     property TopLine: Integer read GetTopLine write SetTopLine;
 | |
| 
 | |
|     property UseIncrementalColor : Boolean write SetUseIncrementalColor;
 | |
|     property Modified: Boolean read GetModified write SetModified;
 | |
|     property PaintLock: Integer read fPaintLock;
 | |
| 
 | |
|     property UseUTF8: boolean read FUseUTF8;
 | |
|     procedure Update; override;
 | |
|     procedure Invalidate; override;
 | |
|     property ChangeStamp: int64 read GetChangeStamp;
 | |
|     procedure ShareTextBufferFrom(AShareEditor: TCustomSynEdit);
 | |
|     procedure UnShareTextBuffer;
 | |
| 
 | |
|     function PluginCount: Integer;
 | |
|     property Plugin[Index: Integer]: TLazSynEditPlugin read GetPlugin;
 | |
|     function MarkupCount: Integer;
 | |
|     property Markup[Index: integer]: TSynEditMarkup read GetMarkup;
 | |
|     property MarkupByClass[Index: TSynEditMarkupClass]: TSynEditMarkup
 | |
|       read GetMarkupByClass;
 | |
|     property TrimSpaceType: TSynEditStringTrimmingType
 | |
|       read GetTrimSpaceType write SetTrimSpaceType;
 | |
|   public
 | |
|     // Caret
 | |
|     property InsertCaret: TSynEditCaretType read FInsertCaret write SetInsertCaret default ctVerticalLine;
 | |
|     property OverwriteCaret: TSynEditCaretType read FOverwriteCaret write SetOverwriteCaret default ctBlock;
 | |
| 
 | |
|     // Selection
 | |
|     property HideSelection: boolean read fHideSelection write SetHideSelection default false;
 | |
|     property DefaultSelectionMode: TSynSelectionMode read GetDefSelectionMode write SetDefSelectionMode default smNormal;
 | |
|     property SelectionMode: TSynSelectionMode read GetSelectionMode write SetSelectionMode default smNormal;
 | |
|     property SelectedColor: TSynSelectedColor read GetSelectedColor write SetSelectedColor;
 | |
| 
 | |
|     // Colors
 | |
|     property Color default clWhite;
 | |
|     property IncrementColor: TSynSelectedColor read GetIncrementColor write SetIncrementColor;
 | |
|     property HighlightAllColor: TSynSelectedColor read GetHighlightAllColor write SetHighlightAllColor;
 | |
|     property BracketMatchColor: TSynSelectedColor read GetBracketMatchColor write SetBracketMatchColor;
 | |
|     property MouseLinkColor: TSynSelectedColor read GetMouseLinkColor write SetMouseLinkColor;
 | |
|     property LineHighlightColor: TSynSelectedColor read GetLineHighlightColor write SetLineHighlightColor;
 | |
|     property FoldedCodeColor: TSynSelectedColor read GetFoldedCodeColor write SetFoldedCodeColor;
 | |
| 
 | |
|     property Beautifier: TSynCustomBeautifier read fBeautifier write SetBeautifier;
 | |
|     property BookMarkOptions: TSynBookMarkOpt read fBookMarkOpt write fBookMarkOpt;
 | |
|     property BlockIndent: integer read FBlockIndent write SetBlockIndent default 2;
 | |
|     property BlockTabIndent: integer read FBlockTabIndent write SetBlockTabIndent default 0;
 | |
|     property ExtraCharSpacing: integer read fExtraCharSpacing write SetExtraCharSpacing default 0;
 | |
|     property ExtraLineSpacing: integer read fExtraLineSpacing write SetExtraLineSpacing default 0;
 | |
|     property Highlighter: TSynCustomHighlighter read fHighlighter write SetHighlighter;
 | |
|     property Gutter: TSynGutter read FLeftGutter write SetGutter;
 | |
|     property RightGutter: TSynGutter read FRightGutter write SetRightGutter;
 | |
|     property InsertMode: boolean read fInserting write SetInsertMode
 | |
|       default true;
 | |
|     property Keystrokes: TSynEditKeyStrokes
 | |
|       read FKeystrokes write SetKeystrokes;
 | |
|     property MouseActions: TSynEditMouseActions
 | |
|       read GetMouseActions write SetMouseActions;
 | |
|     property MouseTextActions: TSynEditMouseActions
 | |
|       read GetMouseTextActions write SetMouseTextActions;
 | |
|     property MouseSelActions: TSynEditMouseActions // Mouseactions, if mouse is over selection => fallback to normal
 | |
|       read GetMouseSelActions write SetMouseSelActions;
 | |
|     property MaxUndo: Integer read GetMaxUndo write SetMaxUndo default 1024;
 | |
|     property Options: TSynEditorOptions read FOptions write SetOptions          // See SYNEDIT_UNIMPLEMENTED_OPTIONS for deprecated Values
 | |
|       default SYNEDIT_DEFAULT_OPTIONS;
 | |
|     property Options2: TSynEditorOptions2 read FOptions2 write SetOptions2
 | |
|       default SYNEDIT_DEFAULT_OPTIONS2;
 | |
|     property MouseOptions: TSynEditorMouseOptions read FMouseOptions write SetMouseOptions
 | |
|       default SYNEDIT_DEFAULT_MOUSE_OPTIONS;
 | |
|     property ShareOptions: TSynEditorShareOptions read FShareOptions write SetShareOptions
 | |
|       default SYNEDIT_DEFAULT_SHARE_OPTIONS; experimental;
 | |
|     property VisibleSpecialChars: TSynVisibleSpecialChars read FVisibleSpecialChars write SetVisibleSpecialChars;
 | |
|     property ReadOnly: Boolean read GetReadOnly write SetReadOnly default FALSE;
 | |
|     property RightEdge: Integer read GetRightEdge write SetRightEdge default 80;
 | |
|     property RightEdgeColor: TColor read GetRightEdgeColor write SetRightEdgeColor default clSilver;
 | |
|     property ScrollBars: TScrollStyle
 | |
|       read FScrollBars write SetScrollBars default ssBoth;
 | |
|     property BracketHighlightStyle: TSynEditBracketHighlightStyle
 | |
|       read GetBracketHighlightStyle write SetBracketHighlightStyle;
 | |
|     property TabWidth: integer read fTabWidth write SetTabWidth default 8;
 | |
|     property WantTabs: boolean read fWantTabs write SetWantTabs default FALSE;
 | |
| 
 | |
|     // Events
 | |
|     property OnChange: TNotifyEvent read FOnChange write FOnChange;
 | |
|     property OnChangeUpdating: TChangeUpdatingEvent read FOnChangeUpdating write FOnChangeUpdating;
 | |
|     property OnCutCopy: TSynCopyPasteEvent read FOnCutCopy write FOnCutCopy;
 | |
|     property OnPaste: TSynCopyPasteEvent read FOnPaste write FOnPaste;
 | |
|     property OnDropFiles: TSynDropFilesEvent read fOnDropFiles write fOnDropFiles;
 | |
|     property OnGutterClick: TGutterClickEvent read GetOnGutterClick write SetOnGutterClick;
 | |
|     property OnPaint: TPaintEvent read fOnPaint write fOnPaint;
 | |
|     // OnPlaceBookmark only triggers for Bookmarks
 | |
|     property OnPlaceBookmark: TPlaceMarkEvent read FOnPlaceMark write FOnPlaceMark;
 | |
|     // OnClearBookmark only triggers for Bookmarks
 | |
|     property OnClearBookmark: TPlaceMarkEvent read FOnClearMark write FOnClearMark;
 | |
|     property OnKeyDown;
 | |
|     property OnKeyPress;
 | |
|     property OnProcessCommand: TProcessCommandEvent read FOnProcessCommand write FOnProcessCommand;
 | |
|     property OnProcessUserCommand: TProcessCommandEvent  read FOnProcessUserCommand write FOnProcessUserCommand;
 | |
|     property OnCommandProcessed: TProcessCommandEvent read fOnCommandProcessed write fOnCommandProcessed;
 | |
|     property OnReplaceText: TReplaceTextEvent read fOnReplaceText write fOnReplaceText;
 | |
|     property OnSpecialLineColors: TSpecialLineColorsEvent read FOnSpecialLineColors write SetSpecialLineColors;  deprecated;
 | |
|     property OnSpecialLineMarkup: TSpecialLineMarkupEvent read FOnSpecialLineMarkup write SetSpecialLineMarkup;
 | |
|     property OnStatusChange: TStatusChangeEvent read fOnStatusChange write fOnStatusChange;
 | |
|   end;
 | |
| 
 | |
|   TSynEdit = class(TCustomSynEdit)
 | |
|   published
 | |
|     // inherited properties
 | |
|     property Align;
 | |
|     property Beautifier;
 | |
|     property BlockIndent;
 | |
|     property BlockTabIndent;
 | |
|     property BorderSpacing;
 | |
| {$IFDEF SYN_COMPILER_4_UP}
 | |
|     property Anchors;
 | |
|     property Constraints;
 | |
| {$ENDIF}
 | |
|     property Color;
 | |
|     property Cursor default crIBeam;
 | |
|     property Enabled;
 | |
|     property Font;
 | |
|     property Height;
 | |
|     property Name;
 | |
|     property ParentColor;
 | |
|     property ParentFont;
 | |
|     property ParentShowHint;
 | |
|     property PopupMenu;
 | |
|     property ShowHint;
 | |
|     property TabOrder;
 | |
|     property TabStop default True;
 | |
|     property Tag;
 | |
|     property Visible;
 | |
|     property Width;
 | |
|     // inherited events
 | |
|     property OnClick;
 | |
|     property OnDblClick;
 | |
|     property OnTripleClick;
 | |
|     property OnQuadClick;
 | |
|     property OnDragDrop;
 | |
|     property OnDragOver;
 | |
| {$IFDEF SYN_COMPILER_4_UP}
 | |
| // ToDo Docking
 | |
|     property OnEndDock;
 | |
| {$ENDIF}
 | |
|     property OnEndDrag;
 | |
|     property OnEnter;
 | |
|     property OnExit;
 | |
|     property OnKeyDown;
 | |
|     property OnKeyPress;
 | |
|     property OnKeyUp;
 | |
|     property OnMouseDown;
 | |
|     property OnMouseMove;
 | |
|     property OnMouseUp;
 | |
|     property OnClickLink : TMouseEvent read FOnClickLink write FOnClickLink;
 | |
|     property OnMouseLink: TSynMouseLinkEvent read FOnMouseLink write FOnMouseLink;
 | |
|     property OnMouseEnter;
 | |
|     property OnMouseLeave;
 | |
| {$IFDEF SYN_COMPILER_4_UP}
 | |
| // ToDo Docking
 | |
|     property OnStartDock;
 | |
| {$ENDIF}
 | |
|     property OnStartDrag;
 | |
|     // TCustomSynEdit properties
 | |
|     property BookMarkOptions;
 | |
|     property BorderStyle default bsSingle;
 | |
|     property ExtraCharSpacing;
 | |
|     property ExtraLineSpacing;
 | |
|     property Gutter;
 | |
|     property RightGutter;
 | |
|     property HideSelection;
 | |
|     property Highlighter;
 | |
|     property InsertCaret;
 | |
|     property InsertMode;
 | |
|     property Keystrokes;
 | |
|     property MouseActions;
 | |
|     property MouseSelActions;
 | |
|     property Lines;
 | |
|     property MaxLeftChar;
 | |
|     property MaxUndo;
 | |
|     property Options;
 | |
|     property Options2;
 | |
|     property MouseOptions;
 | |
|     property VisibleSpecialChars;
 | |
|     property OverwriteCaret;
 | |
|     property ReadOnly;
 | |
|     property RightEdge;
 | |
|     property RightEdgeColor;
 | |
|     property ScrollBars;
 | |
|     property SelectedColor;
 | |
|     property IncrementColor;
 | |
|     property HighlightAllColor;
 | |
|     property BracketHighlightStyle;
 | |
|     property BracketMatchColor;
 | |
|     property FoldedCodeColor;
 | |
|     property MouseLinkColor;
 | |
|     property LineHighlightColor;
 | |
|     property DefaultSelectionMode;
 | |
|     property SelectionMode;
 | |
|     property TabWidth;
 | |
|     property WantTabs;
 | |
|     // TCustomSynEdit events
 | |
|     property OnChange;
 | |
|     property OnChangeUpdating;
 | |
|     property OnCutCopy;
 | |
|     property OnPaste;
 | |
|     property OnClearBookmark;                                                   // djlp 2000-08-29
 | |
|     property OnCommandProcessed;
 | |
|     property OnDropFiles;
 | |
|     property OnGutterClick;
 | |
|     property OnPaint;
 | |
|     property OnPlaceBookmark;
 | |
|     property OnProcessCommand;
 | |
|     property OnProcessUserCommand;
 | |
|     property OnReplaceText;
 | |
|     property OnShowHint;
 | |
|     property OnSpecialLineColors; deprecated;
 | |
|     property OnSpecialLineMarkup;
 | |
|     property OnStatusChange;
 | |
|   end;
 | |
| 
 | |
| procedure Register;
 | |
| 
 | |
| implementation
 | |
| 
 | |
| const
 | |
|   GutterTextDist = 2; //Pixel
 | |
| 
 | |
| type
 | |
| 
 | |
|   { TSynEditMarkListInternal }
 | |
| 
 | |
|   TSynEditMarkListInternal = class(TSynEditMarkList)
 | |
|   private
 | |
|     function GetLinesView: TSynEditStrings;
 | |
|     procedure SetLinesView(const AValue: TSynEditStrings);
 | |
|   protected
 | |
|     procedure AddOwnerEdit(AEdit: TSynEditBase);
 | |
|     procedure RemoveOwnerEdit(AEdit: TSynEditBase);
 | |
|     property LinesView: TSynEditStrings read GetLinesView write SetLinesView;
 | |
|   end;
 | |
| 
 | |
|   TSynStatusChangedHandlerList = Class(TSynFilteredMethodList)
 | |
|   public
 | |
|     procedure Add(AHandler: TStatusChangeEvent; Changes: TSynStatusChanges);
 | |
|     procedure Remove(AHandler: TStatusChangeEvent);
 | |
|     procedure CallStatusChangedHandlers(Sender: TObject; Changes: TSynStatusChanges);
 | |
|   end;
 | |
| 
 | |
|   { TSynEditUndoCaret }
 | |
| 
 | |
|   TSynEditUndoCaret = class(TSynEditUndoItem)
 | |
|   private
 | |
|     FCaretPos: TPoint;
 | |
|   protected
 | |
|     function IsEqualContent(AnItem: TSynEditUndoItem): Boolean; override;
 | |
|     function DebugString: String; override;
 | |
|   public
 | |
|     constructor Create(CaretPos: TPoint);
 | |
|     function IsCaretInfo: Boolean; override;
 | |
|     function PerformUndo(Caller: TObject): Boolean; override;
 | |
|   end;
 | |
| 
 | |
|   { TSynEditUndoSelCaret }
 | |
| 
 | |
|   TSynEditUndoSelCaret = class(TSynEditUndoItem)
 | |
|   private
 | |
|     FCaretPos, FBeginPos, FEndPos: TPoint;
 | |
|     FBlockMode: TSynSelectionMode;
 | |
|   protected
 | |
|     function IsEqualContent(AnItem: TSynEditUndoItem): Boolean; override;
 | |
|     function DebugString: String; override;
 | |
|   public
 | |
|     function IsCaretInfo: Boolean; override;
 | |
|     constructor Create(CaretPos, BeginPos, EndPos: TPoint; BlockMode: TSynSelectionMode);
 | |
|     function PerformUndo(Caller: TObject): Boolean; override;
 | |
|   end;
 | |
| 
 | |
|   { TSynEditUndoIndent }
 | |
| 
 | |
|   TSynEditUndoIndent = class(TSynEditUndoItem)
 | |
|   public
 | |
|     FPosY1, FPosY2, FCnt, FTabCnt: Integer;
 | |
|   public
 | |
|     constructor Create(APosY, EPosY, ACnt, ATabCnt: Integer);
 | |
|     function PerformUndo(Caller: TObject): Boolean; override;
 | |
|   end;
 | |
| 
 | |
|   { TSynEditUndoUnIndent }
 | |
| 
 | |
|   TSynEditUndoUnIndent = class(TSynEditUndoItem)
 | |
|   public
 | |
|     FPosY1, FPosY2: Integer;
 | |
|     FText: String;
 | |
|   public
 | |
|     constructor Create(APosY, EPosY: Integer; AText: String);
 | |
|     function PerformUndo(Caller: TObject): Boolean; override;
 | |
|   end;
 | |
| 
 | |
|   { TSynEditMouseGlobalActions }
 | |
| 
 | |
|   TSynEditMouseGlobalActions = class(TSynEditMouseInternalActions)
 | |
|   protected
 | |
|     procedure InitForOptions(AnOptions: TSynEditorMouseOptions); override;
 | |
|   end;
 | |
| 
 | |
|   { TSynEditMouseTextActions }
 | |
| 
 | |
|   TSynEditMouseTextActions = class(TSynEditMouseInternalActions)
 | |
|   protected
 | |
|     procedure InitForOptions(AnOptions: TSynEditorMouseOptions); override;
 | |
|   end;
 | |
| 
 | |
|   { TSynEditMouseSelActions }
 | |
| 
 | |
|   TSynEditMouseSelActions = class(TSynEditMouseInternalActions)
 | |
|   protected
 | |
|     procedure InitForOptions(AnOptions: TSynEditorMouseOptions); override;
 | |
|   end;
 | |
| 
 | |
|   { THookedCommandHandlerEntry }
 | |
| 
 | |
|   THookedCommandHandlerEntry = class(TObject)
 | |
|   private
 | |
|     FEvent: THookedCommandEvent;
 | |
|     FData: pointer;
 | |
|     FFlags: THookedCommandFlags;
 | |
|     function Equals(AEvent: THookedCommandEvent): boolean; reintroduce;
 | |
|   public
 | |
|     constructor Create(AEvent: THookedCommandEvent; AData: pointer; AFlags: THookedCommandFlags);
 | |
|   end;
 | |
| 
 | |
| { TSynEditUndoCaret }
 | |
| 
 | |
| function TSynEditUndoCaret.IsEqualContent(AnItem: TSynEditUndoItem): Boolean;
 | |
| begin
 | |
|   Result := (FCaretPos.x = TSynEditUndoCaret(AnItem).FCaretPos.x)
 | |
|         and (FCaretPos.y = TSynEditUndoCaret(AnItem).FCaretPos.y);
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoCaret.DebugString: String;
 | |
| begin
 | |
|   Result := 'CaretPos='+dbgs(FCaretPos);
 | |
| end;
 | |
| 
 | |
| constructor TSynEditUndoCaret.Create(CaretPos: TPoint);
 | |
| begin
 | |
|   FCaretPos := CaretPos;
 | |
|   {$IFDEF SynUndoDebugItems}debugln(['---  Undo Insert ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoCaret.IsCaretInfo: Boolean;
 | |
| begin
 | |
|   Result := True;
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoCaret.PerformUndo(Caller: TObject): Boolean;
 | |
| begin
 | |
|   Result := Caller is TCustomSynEdit;
 | |
|   if Result then
 | |
|     {$IFDEF SynUndoDebugItems}debugln(['---  Undo Perform ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
 | |
|     with TCustomSynEdit(Caller) do begin
 | |
|       FCaret.LineCharPos := FCaretPos;
 | |
|       FTheLinesView.CurUndoList.AddChange(TSynEditUndoCaret.Create(FCaretPos));
 | |
|     end;
 | |
| end;
 | |
| 
 | |
| { TSynEditUndoSelCaret }
 | |
| 
 | |
| constructor TSynEditUndoSelCaret.Create(CaretPos, BeginPos, EndPos: TPoint;
 | |
|   BlockMode: TSynSelectionMode);
 | |
| begin
 | |
|   FCaretPos := CaretPos;
 | |
|   FBeginPos := BeginPos;
 | |
|   FEndPos   := EndPos;
 | |
|   FBlockMode := BlockMode;
 | |
|   {$IFDEF SynUndoDebugItems}debugln(['---  Undo Insert ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoSelCaret.IsEqualContent(AnItem: TSynEditUndoItem): Boolean;
 | |
| begin
 | |
|   Result := (FCaretPos.x = TSynEditUndoSelCaret(AnItem).FCaretPos.x)
 | |
|         and (FCaretPos.y = TSynEditUndoSelCaret(AnItem).FCaretPos.y)
 | |
|         and (FBeginPos.x = TSynEditUndoSelCaret(AnItem).FBeginPos.x)
 | |
|         and (FBeginPos.y = TSynEditUndoSelCaret(AnItem).FBeginPos.y)
 | |
|         and (FEndPos.x = TSynEditUndoSelCaret(AnItem).FEndPos.x)
 | |
|         and (FEndPos.y = TSynEditUndoSelCaret(AnItem).FEndPos.y)
 | |
|         and (FBlockMode = TSynEditUndoSelCaret(AnItem).FBlockMode);
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoSelCaret.DebugString: String;
 | |
| begin
 | |
|   Result := 'CaretPos='+dbgs(FCaretPos) + ' Begin=' + dbgs(FBeginPos) + ' End=' + dbgs(FEndPos) + ' Mode=' + dbgs(ord(FBlockMode));
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoSelCaret.IsCaretInfo: Boolean;
 | |
| begin
 | |
|   Result := True;
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoSelCaret.PerformUndo(Caller: TObject): Boolean;
 | |
| begin
 | |
|   Result := Caller is TCustomSynEdit;
 | |
|   if Result then
 | |
|     {$IFDEF SynUndoDebugItems}debugln(['---  Undo Perform ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
 | |
|     with TCustomSynEdit(Caller) do begin
 | |
|       SetCaretAndSelection(FCaretPos, FBeginPos, FEndPos, FBlockMode, True);
 | |
|       FTheLinesView.CurUndoList.AddChange(TSynEditUndoSelCaret.Create(FCaretPos, FBeginPos,
 | |
|                                                      FEndPos, FBlockMode));
 | |
|     end;
 | |
| end;
 | |
| 
 | |
| { TSynEditUndoIndent }
 | |
| 
 | |
| constructor TSynEditUndoIndent.Create(APosY, EPosY, ACnt, ATabCnt: Integer);
 | |
| begin
 | |
|   FPosY1 := APosY;
 | |
|   FPosY2 := EPosY;
 | |
|   FCnt    :=  ACnt;
 | |
|   FTabCnt := ATabCnt;
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoIndent.PerformUndo(Caller: TObject): Boolean;
 | |
| begin
 | |
|   Result := False;
 | |
| end;
 | |
| 
 | |
| { TSynEditUndoUnIndent }
 | |
| 
 | |
| constructor TSynEditUndoUnIndent.Create(APosY, EPosY: Integer; AText: String);
 | |
| begin
 | |
|   FPosY1 := APosY;
 | |
|   FPosY2 := EPosY;
 | |
|   FText :=  AText;
 | |
| end;
 | |
| 
 | |
| function TSynEditUndoUnIndent.PerformUndo(Caller: TObject): Boolean;
 | |
| begin
 | |
|   Result := False;
 | |
| end;
 | |
| 
 | |
| function Roundoff(X: Extended): Longint;
 | |
| begin
 | |
|   if (x >= 0) then begin
 | |
|     Result := TruncToInt(x + 0.5)
 | |
|   end else begin
 | |
|     Result := TruncToInt(x - 0.5);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { TSynEditMouseGlobalActions }
 | |
| 
 | |
| procedure TSynEditMouseGlobalActions.InitForOptions(AnOptions: TSynEditorMouseOptions);
 | |
| begin
 | |
|   AddCommand(emcWheelScrollDown,       False,  mbXWheelDown, ccAny, cdDown, [], []);
 | |
|   AddCommand(emcWheelScrollUp,         False,  mbXWheelUp, ccAny, cdDown, [], []);
 | |
| 
 | |
|   if emCtrlWheelZoom in AnOptions then begin
 | |
|     AddCommand(emcWheelZoomOut,        False,  mbXWheelDown, ccAny, cdDown, [ssCtrl], [ssCtrl]);
 | |
|     AddCommand(emcWheelZoomIn,         False,  mbXWheelUp,   ccAny, cdDown, [ssCtrl], [ssCtrl]);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { TSynEditMouseTextActions }
 | |
| 
 | |
| procedure TSynEditMouseTextActions.InitForOptions(AnOptions: TSynEditorMouseOptions);
 | |
| var
 | |
|   rmc: Boolean;
 | |
| begin
 | |
|   Clear;
 | |
|   rmc := (emRightMouseMovesCursor in AnOptions);
 | |
|   //// eoRightMouseMovesCursor
 | |
|   //if (eoRightMouseMovesCursor in ChangedOptions) then begin
 | |
|   //  for i := FMouseActions.Count-1 downto 0 do
 | |
|   //    if FMouseActions[i].Button = mbXRight then
 | |
|   //      FMouseActions[i].MoveCaret := (eoRightMouseMovesCursor in fOptions);
 | |
|   //end;
 | |
| 
 | |
|   AddCommand(emcStartSelections,       True,  mbXLeft, ccSingle, cdDown, [],        [ssShift, ssAlt], emcoSelectionStart);
 | |
|   AddCommand(emcStartSelections,       True,  mbXLeft, ccSingle, cdDown, [ssShift], [ssShift, ssAlt], emcoSelectionContinue);
 | |
|   if (emAltSetsColumnMode in AnOptions) then begin
 | |
|     AddCommand(emcStartColumnSelections, True,  mbXLeft, ccSingle, cdDown, [ssAlt],          [ssShift, ssAlt], emcoSelectionStart);
 | |
|     AddCommand(emcStartColumnSelections, True,  mbXLeft, ccSingle, cdDown, [ssShift, ssAlt], [ssShift, ssAlt], emcoSelectionContinue);
 | |
|   end;
 | |
|   if (emShowCtrlMouseLinks in AnOptions) then
 | |
|     AddCommand(emcMouseLink,             False, mbXLeft, ccSingle, cdUp, [SYNEDIT_LINK_MODIFIER], [ssShift, ssAlt, ssCtrl]);
 | |
| 
 | |
|   if (emDoubleClickSelectsLine in AnOptions) then begin
 | |
|     AddCommand(emcSelectLine,            True,  mbXLeft, ccDouble, cdDown, [], []);
 | |
|     AddCommand(emcSelectPara,            True,  mbXLeft, ccTriple, cdDown, [], []);
 | |
|   end
 | |
|   else begin
 | |
|     AddCommand(emcSelectWord,            True,  mbXLeft, ccDouble, cdDown, [], []);
 | |
|     AddCommand(emcSelectLine,            True,  mbXLeft, ccTriple, cdDown, [], []);
 | |
|   end;
 | |
|   AddCommand(emcSelectPara,            True,  mbXLeft, ccQuad,   cdDown, [], []);
 | |
| 
 | |
|   AddCommand(emcContextMenu,           rmc,   mbXRight, ccSingle, cdUp, [], [], emcoSelectionCaretMoveNever);
 | |
| 
 | |
|   AddCommand(emcPasteSelection,        True,  mbXMiddle, ccSingle, cdDown, [], []);
 | |
| end;
 | |
| 
 | |
| { TSynEditMouseSelActions }
 | |
| 
 | |
| procedure TSynEditMouseSelActions.InitForOptions(AnOptions: TSynEditorMouseOptions);
 | |
| begin
 | |
|   Clear;
 | |
|   //rmc := (eoRightMouseMovesCursor in AnOptions);
 | |
| 
 | |
|   if (emDragDropEditing in AnOptions) then
 | |
|     AddCommand(emcStartDragMove, False, mbXLeft, ccSingle, cdDown, [], []);
 | |
| end;
 | |
| 
 | |
| { THookedCommandHandlerEntry }
 | |
| 
 | |
| constructor THookedCommandHandlerEntry.Create(AEvent: THookedCommandEvent; AData: pointer;
 | |
|   AFlags: THookedCommandFlags);
 | |
| begin
 | |
|   inherited Create;
 | |
|   fEvent := AEvent;
 | |
|   fData := AData;
 | |
|   FFlags := AFlags;
 | |
| end;
 | |
| 
 | |
| function THookedCommandHandlerEntry.Equals(AEvent: THookedCommandEvent): boolean;
 | |
| begin
 | |
|   with TMethod(fEvent) do
 | |
|     Result := (Code = TMethod(AEvent).Code) and (Data = TMethod(AEvent).Data);
 | |
| end;
 | |
| 
 | |
| procedure InitSynDefaultFont;
 | |
| begin
 | |
|   if SynDefaultFontName <> '' then exit;
 | |
|   Screen.Fonts;
 | |
|   {$UNDEF SynDefaultFont}
 | |
|   {$IFDEF LCLgtk}
 | |
|     SynDefaultFontName   := '-adobe-courier-medium-r-normal-*-*-140-*-*-*-*-iso10646-1';
 | |
|     SynDefaultFontHeight := 14;
 | |
|     {$DEFINE SynDefaultFont}
 | |
|   {$ENDIF}
 | |
|   {$IFDEF LCLcarbon}
 | |
|     SynDefaultFontName   := 'Monaco'; // Note: carbon is case sensitive
 | |
|     SynDefaultFontHeight := 12;
 | |
|     {$DEFINE SynDefaultFont}
 | |
|   {$ENDIF}
 | |
|   // LCLgtk2 and LCLQt use default settings
 | |
|   {$IFnDEF SynDefaultFont}
 | |
|     SynDefaultFontName   := 'Courier New';
 | |
|     SynDefaultFontHeight := -13;
 | |
|   {$ENDIF}
 | |
|   if Screen.Fonts.IndexOf(SynDefaultFontName) >= 0 then
 | |
|     exit;
 | |
|   if Screen.Fonts.IndexOf('DejaVu Sans Mono') >= 0 then begin
 | |
|     SynDefaultFontName   := 'DejaVu Sans Mono';
 | |
|     SynDefaultFontHeight := 13;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { TCustomSynEdit }
 | |
| 
 | |
| procedure TCustomSynEdit.AquirePrimarySelection;
 | |
| var
 | |
|   FormatList: Array [0..1] of TClipboardFormat;
 | |
| begin
 | |
|   if (not SelAvail)
 | |
|   or (PrimarySelection.OnRequest=@PrimarySelectionRequest) then exit;
 | |
|   FormatList[0] := CF_TEXT;
 | |
|   FormatList[1] := TSynClipboardStream.ClipboardFormatId;
 | |
|   try
 | |
|     PrimarySelection.SetSupportedFormats(2, @FormatList[0]);
 | |
|     PrimarySelection.OnRequest:=@PrimarySelectionRequest;
 | |
|   except
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetChangeStamp: int64;
 | |
| begin
 | |
|   Result := TSynEditStringList(FLines).TextChangeStamp;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCharsInWindow: Integer;
 | |
| begin
 | |
|   Result := FTextArea.CharsInWindow;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCharWidth: integer;
 | |
| begin
 | |
|   Result := FTextArea.CharWidth;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetDefSelectionMode: TSynSelectionMode;
 | |
| begin
 | |
|   Result := FBlockSelection.SelectionMode;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetFoldState: String;
 | |
| begin
 | |
|   Result := FFoldedLinesView.GetFoldDescription(0, 0, -1, -1, True);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetLeftChar: Integer;
 | |
| begin
 | |
|   Result := FTextArea.LeftChar;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetLineHeight: integer;
 | |
| begin
 | |
|   Result := FTextArea.LineHeight;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetLinesInWindow: Integer;
 | |
| begin
 | |
|   Result := FTextArea.LinesInWindow;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetModified: Boolean;
 | |
| begin
 | |
|   Result := TSynEditStringList(FLines).Modified;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetMouseActions: TSynEditMouseActions;
 | |
| begin
 | |
|   Result := FMouseActions.UserActions;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetMouseSelActions: TSynEditMouseActions;
 | |
| begin
 | |
|   Result := FMouseSelActions.UserActions;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetMouseTextActions: TSynEditMouseActions;
 | |
| begin
 | |
|   Result := FMouseTextActions.UserActions;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetPaintLockOwner: TSynEditBase;
 | |
| begin
 | |
|   Result := TSynEditStringList(FLines).PaintLockOwner;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetPlugin(Index: Integer): TLazSynEditPlugin;
 | |
| begin
 | |
|   Result := TLazSynEditPlugin(fPlugins[Index]);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetRightEdge: Integer;
 | |
| begin
 | |
|   Result := FTextArea.RightEdgeColumn;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetRightEdgeColor: TColor;
 | |
| begin
 | |
|   Result := FTextArea.RightEdgeColor;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetTextBetweenPoints(aStartPoint, aEndPoint: TPoint): String;
 | |
| begin
 | |
|   FInternalBlockSelection.SelectionMode := smNormal;
 | |
|   FInternalBlockSelection.StartLineBytePos := aStartPoint;
 | |
|   FInternalBlockSelection.EndLineBytePos := aEndPoint;
 | |
|   Result := FInternalBlockSelection.SelText;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetTopLine: Integer;
 | |
| begin
 | |
|   Result := FFoldedLinesView.ViewPosToTextIndex(FTextArea.TopLine) + 1;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetBlockTabIndent(AValue: integer);
 | |
| begin
 | |
|   if FBlockTabIndent = AValue then Exit;
 | |
|   FBlockTabIndent := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetBracketMatchColor(AValue: TSynSelectedColor);
 | |
| begin
 | |
|   fMarkupBracket.MarkupInfo.Assign(AValue);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetDefSelectionMode(const AValue: TSynSelectionMode);
 | |
| begin
 | |
|   FBlockSelection.SelectionMode := AValue; // Includes active
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetFoldedCodeColor(AValue: TSynSelectedColor);
 | |
| begin
 | |
|   FFoldedLinesView.MarkupInfoFoldedCode.Assign(AValue);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SurrenderPrimarySelection;
 | |
| begin
 | |
|   if PrimarySelection.OnRequest=@PrimarySelectionRequest then
 | |
|     PrimarySelection.OnRequest:=nil;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PixelsToRowColumn(Pixels: TPoint; aFlags: TSynCoordinateMappingFlags = [scmLimitToLines]): TPoint;
 | |
| // converts the client area coordinate
 | |
| // to Caret position (physical position, (1,1) based)
 | |
| // To get the text/logical position use PixelsToLogicalPos
 | |
| begin
 | |
|   Result := FTextArea.PixelsToRowColumn(Pixels, aFlags);
 | |
|   Result := Point(Result.X, ScreenRowToRow(Result.Y, scmLimitToLines in aFlags));
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PixelsToLogicalPos(const Pixels: TPoint): TPoint;
 | |
| begin
 | |
|   Result:=PhysicalToLogicalPos(PixelsToRowColumn(Pixels));
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.ScreenRowToRow(ScreenRow: integer; LimitToLines: Boolean = True): integer;
 | |
| // ScreenRow is 0-base
 | |
| // result is 1-based
 | |
| begin
 | |
|   Result := FFoldedLinesView.ScreenLineToTextIndex(ScreenRow)+1;
 | |
|   if LimitToLines and (Result >= Lines.Count) then
 | |
|     Result := Lines.Count;
 | |
| //  DebugLn(['=== SrceenRow TO Row   In:',ScreenRow,'  out:',Result, ' topline=',TopLine, '  view topline=',FFoldedLinesView.TopLine]);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.RowToScreenRow(PhysicalRow: integer): integer;
 | |
| // returns -1 for lines above visible screen (<TopLine)
 | |
| // 0 for the first line
 | |
| // 0 to LinesInWindow for visible lines (incl last partial visble line)
 | |
| // and returns LinesInWindow+1 for lines below visible screen
 | |
| begin
 | |
|   Result := FFoldedLinesView.TextIndexToScreenLine(PhysicalRow-1);
 | |
|   if Result < -1 then Result := -1;
 | |
|   if Result > LinesInWindow+1 then Result := LinesInWindow+1;
 | |
| //  DebugLn(['=== Row TO ScreenRow   In:',PhysicalRow,'  out:',Result]);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.RowColumnToPixels(RowCol: TPoint): TPoint;
 | |
| // converts screen position (1,1) based
 | |
| // to client area coordinate (0,0 based on canvas)
 | |
| begin
 | |
|   RowCol.Y := RowToScreenRow(RowCol.Y);
 | |
|   Result := FTextArea.RowColumnToPixels(RowCol);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ComputeCaret(X, Y: Integer);
 | |
| // set caret to pixel position
 | |
| begin
 | |
|   FCaret.LineCharPos := PixelsToRowColumn(Point(X,Y));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoCopyToClipboard(SText: string; FoldInfo: String = '');
 | |
| var
 | |
|   ClipHelper: TSynClipboardStream;
 | |
|   PasteAction: TSynCopyPasteAction;
 | |
|   PMode: TSynSelectionMode;
 | |
| begin
 | |
|   PasteAction := scaContinue;
 | |
|   if length(FoldInfo) = 0 then PasteAction := scaPlainText;
 | |
|   PMode :=  SelectionMode;
 | |
|   if assigned(FOnCutCopy) then begin
 | |
|     FOnCutCopy(self, SText, PMode, FBlockSelection.FirstLineBytePos, PasteAction);
 | |
|     if PasteAction = scaAbort then
 | |
|       exit;;
 | |
|   end;
 | |
| 
 | |
|   if SText = '' then exit;
 | |
|   Clipboard.Clear;
 | |
|   ClipHelper := TSynClipboardStream.Create;
 | |
|   try
 | |
|     ClipHelper.Text := SText;
 | |
|     ClipHelper.SelectionMode := PMode; // TODO if scaPlainText and smNormal, then avoid synedits own clipboard format
 | |
| 
 | |
|     if PasteAction = scaContinue then begin
 | |
|       // Fold
 | |
|       if length(FoldInfo) > 0 then
 | |
|         ClipHelper.AddTag(synClipTagFold, @FoldInfo[1], length(FoldInfo));
 | |
|     end;
 | |
| 
 | |
|     if not ClipHelper.WriteToClipboard(Clipboard) then begin
 | |
|       {$IFDEF SynClipboardExceptions}raise ESynEditError.Create('Clipboard copy operation failed');{$ENDIF}
 | |
|     end;
 | |
|   finally
 | |
|     ClipHelper.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.CopyToClipboard;
 | |
| var
 | |
|   FInfo: String;
 | |
| begin
 | |
|   if SelAvail then begin
 | |
|     if eoFoldedCopyPaste in fOptions2 then
 | |
|       FInfo := FFoldedLinesView.GetFoldDescription(
 | |
|         FBlockSelection.FirstLineBytePos.Y - 1, FBlockSelection.FirstLineBytePos.X,
 | |
|         FBlockSelection.LastLineBytePos.Y - 1,  FBlockSelection.LastLineBytePos.X);
 | |
|     DoCopyToClipboard(SelText, FInfo);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.CutToClipboard;
 | |
| var
 | |
|   FInfo: String;
 | |
| begin
 | |
|   if SelAvail then begin
 | |
|     if eoFoldedCopyPaste in fOptions2 then
 | |
|       FInfo := FFoldedLinesView.GetFoldDescription(
 | |
|         FBlockSelection.FirstLineBytePos.Y - 1, FBlockSelection.FirstLineBytePos.X,
 | |
|         FBlockSelection.LastLineBytePos.Y - 1,  FBlockSelection.LastLineBytePos.X);
 | |
|     DoCopyToClipboard(SelText, FInfo);
 | |
|     SetSelTextExternal('');
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| constructor TCustomSynEdit.Create(AOwner: TComponent);
 | |
| begin
 | |
|   inherited Create(AOwner);
 | |
|   SetInline(True);
 | |
|   ControlStyle:=ControlStyle+[csOwnedChildrenNotSelectable];
 | |
|   FScrollBarUpdateLock := 0;
 | |
|   FPaintLock := 0;
 | |
|   FUndoBlockAtPaintLock := 0;
 | |
| 
 | |
|   FStatusChangedList := TSynStatusChangedHandlerList.Create;
 | |
| 
 | |
|   FDefaultBeautifier := TSynBeautifier.Create(self);
 | |
|   FBeautifier := FDefaultBeautifier;
 | |
| 
 | |
|   FLines := TSynEditStringList.Create;
 | |
|   TSynEditStringList(FLines).AttachSynEdit(Self);
 | |
| 
 | |
|   FCaret := TSynEditCaret.Create;
 | |
|   FCaret.MaxLeftChar := @CurrentMaxLineLen;
 | |
|   FCaret.AddChangeHandler({$IFDEF FPC}@{$ENDIF}CaretChanged);
 | |
|   FInternalCaret := TSynEditCaret.Create;
 | |
|   FInternalCaret.MaxLeftChar := @CurrentMaxLineLen;
 | |
| 
 | |
|   // Create the lines/views
 | |
|   FTrimmedLinesView := TSynEditStringTrimmingList.Create(fLines, fCaret);
 | |
| 
 | |
|   FDoubleWidthChrLinesView := SynEditStringDoubleWidthChars.Create
 | |
|                                                             (FTrimmedLinesView);
 | |
| 
 | |
|   // ftab, currently has LengthOfLongestLine, therefore must be after DoubleWidthChar
 | |
|   FTabbedLinesView := TSynEditStringTabExpander.Create(FDoubleWidthChrLinesView);
 | |
| 
 | |
|   // Pointer to the First/Lowest View
 | |
|   // TODO: this should be Folded...
 | |
|   FTheLinesView := FTabbedLinesView;
 | |
|   FTopLinesView := FTrimmedLinesView;
 | |
| 
 | |
|   FFoldedLinesView := TSynEditFoldedView.Create(FTheLinesView, fCaret);
 | |
|   FFoldedLinesView.OnFoldChanged := {$IFDEF FPC}@{$ENDIF}FoldChanged;
 | |
|   FFoldedLinesView.OnLineInvalidate := {$IFDEF FPC}@{$ENDIF}InvalidateGutterLines;
 | |
|   FFoldedLinesView.DisplayView.NextView := FTheLinesView.DisplayView;
 | |
| 
 | |
|   FDisplayView := FFoldedLinesView.DisplayView;
 | |
| 
 | |
|   // External Accessor
 | |
|   FStrings := TSynEditLines.Create(TSynEditStringList(FLines), {$IFDEF FPC}@{$ENDIF}MarkTextAsSaved);
 | |
| 
 | |
|   FCaret.Lines := FTheLinesView;
 | |
|   FInternalCaret.Lines := FTheLinesView;
 | |
|   FFontDummy := TFont.Create;
 | |
|   FOldWidth := -1;
 | |
|   FOldHeight := -1;
 | |
| 
 | |
|   with TSynEditStringList(fLines) do begin
 | |
|     AddChangeHandler(senrLineCount, {$IFDEF FPC}@{$ENDIF}LineCountChanged);
 | |
|     AddChangeHandler(senrLineChange, {$IFDEF FPC}@{$ENDIF}LineTextChanged);
 | |
|     AddChangeHandler(senrHighlightChanged, {$IFDEF FPC}@{$ENDIF}DoHighlightChanged);
 | |
|     AddNotifyHandler(senrCleared, {$IFDEF FPC}@{$ENDIF}ListCleared);
 | |
|     AddNotifyHandler(senrUndoRedoAdded, {$IFDEF FPC}@{$ENDIF}Self.UndoRedoAdded);
 | |
|     AddNotifyHandler(senrModifiedChanged, {$IFDEF FPC}@{$ENDIF}ModifiedChanged);
 | |
|     AddNotifyHandler(senrIncPaintLock, {$IFDEF FPC}@{$ENDIF}DoIncPaintLock);
 | |
|     AddNotifyHandler(senrDecPaintLock, {$IFDEF FPC}@{$ENDIF}DoDecPaintLock);
 | |
|     AddNotifyHandler(senrIncOwnedPaintLock, {$IFDEF FPC}@{$ENDIF}DoIncForeignPaintLock);
 | |
|     AddNotifyHandler(senrDecOwnedPaintLock, {$IFDEF FPC}@{$ENDIF}DoDecForeignPaintLock);
 | |
|   end;
 | |
| 
 | |
|   FScreenCaret := TSynEditScreenCaret.Create(Self);
 | |
|   FScreenCaret.OnExtraLineCharsChanged := {$IFDEF FPC}@{$ENDIF}ExtraLineCharsChanged;
 | |
| 
 | |
|   FUndoList := TSynEditStringList(fLines).UndoList;
 | |
|   FRedoList := TSynEditStringList(fLines).RedoList;
 | |
|   FUndoList.OnNeedCaretUndo := {$IFDEF FPC}@{$ENDIF}GetCaretUndo;
 | |
|   {$IFDEF SynUndoDebugCalls}
 | |
|   fUndoList.DebugName := 'UNDO';
 | |
|   fRedoList.DebugName := 'REDO';
 | |
|   {$ENDIF}
 | |
| 
 | |
|   FBlockSelection := TSynEditSelection.Create(FTheLinesView, True);
 | |
|   FBlockSelection.Caret := FCaret;
 | |
|   FBlockSelection.InvalidateLinesMethod := {$IFDEF FPC}@{$ENDIF}InvalidateLines;
 | |
|   FBlockSelection.AddChangeHandler({$IFDEF FPC}@{$ENDIF}DoBlockSelectionChanged);
 | |
| 
 | |
|   FInternalBlockSelection := TSynEditSelection.Create(FTheLinesView, False);
 | |
|   FInternalBlockSelection.InvalidateLinesMethod := {$IFDEF FPC}@{$ENDIF}InvalidateLines;
 | |
|   // No need for caret, on interanl block
 | |
| 
 | |
|   FFoldedLinesView.BlockSelection := FBlockSelection;
 | |
| 
 | |
|   FWordBreaker := TSynWordBreaker.Create;
 | |
| 
 | |
|   RecreateMarkList;
 | |
| 
 | |
|   {$IFNDEF EnableDoubleBuf}
 | |
|   DoubleBuffered := True;
 | |
|   {$ENDIF}
 | |
| 
 | |
|   fTextDrawer := TheTextDrawer.Create([fsBold], fFontDummy);
 | |
|   FPaintLineColor := TSynSelectedColor.Create;
 | |
|   FPaintLineColor2 := TSynSelectedColor.Create;
 | |
|   fBookMarkOpt := TSynBookMarkOpt.Create(Self);
 | |
|   fBookMarkOpt.OnChange := {$IFDEF FPC}@{$ENDIF}BookMarkOptionsChanged;
 | |
| 
 | |
|   FLeftGutter := CreateGutter(self, gsLeft, FTextDrawer);
 | |
|   FLeftGutter.RegisterChangeHandler({$IFDEF FPC}@{$ENDIF}GutterChanged);
 | |
|   FLeftGutter.RegisterResizeHandler({$IFDEF FPC}@{$ENDIF}GutterResized);
 | |
|   FRightGutter := CreateGutter(self, gsRight, FTextDrawer);
 | |
|   FRightGutter.RegisterChangeHandler({$IFDEF FPC}@{$ENDIF}GutterChanged);
 | |
|   FRightGutter.RegisterResizeHandler({$IFDEF FPC}@{$ENDIF}GutterResized);
 | |
| 
 | |
|   ControlStyle := ControlStyle + [csOpaque, csSetCaption, csTripleClicks, csQuadClicks];
 | |
|   Height := 150;
 | |
|   Width := 200;
 | |
|   Cursor := crIBeam;
 | |
|   fPlugins := TList.Create;
 | |
|   FHookedKeyTranslationList := TSynHookedKeyTranslationList.Create;
 | |
|   // needed before setting color
 | |
|   fMarkupHighCaret := TSynEditMarkupHighlightAllCaret.Create(self);
 | |
|   fMarkupHighCaret.Selection := FBlockSelection;
 | |
|   fMarkupHighAll   := TSynEditMarkupHighlightAll.Create(self);
 | |
|   fMarkupBracket   := TSynEditMarkupBracket.Create(self);
 | |
|   fMarkupWordGroup := TSynEditMarkupWordGroup.Create(self);
 | |
|   fMarkupCtrlMouse := TSynEditMarkupCtrlMouseLink.Create(self);
 | |
|   fMarkupSpecialLine := TSynEditMarkupSpecialLine.Create(self);
 | |
|   fMarkupSelection := TSynEditMarkupSelection.Create(self, FBlockSelection);
 | |
|   fMarkupSpecialChar := TSynEditMarkupSpecialChar.Create(self);
 | |
| 
 | |
|   fMarkupManager := TSynEditMarkupManager.Create(self);
 | |
|   fMarkupManager.AddMarkUp(fMarkupSpecialChar);
 | |
|   fMarkupManager.AddMarkUp(fMarkupSpecialLine);
 | |
|   fMarkupManager.AddMarkUp(fMarkupHighCaret);
 | |
|   fMarkupManager.AddMarkUp(fMarkupHighAll);
 | |
|   fMarkupManager.AddMarkUp(fMarkupCtrlMouse);
 | |
|   fMarkupManager.AddMarkUp(fMarkupBracket);
 | |
|   fMarkupManager.AddMarkUp(fMarkupWordGroup);
 | |
|   fMarkupManager.AddMarkUp(fMarkupSelection);
 | |
|   fMarkupManager.Lines := FTheLinesView;
 | |
|   fMarkupManager.Caret := FCaret;
 | |
|   fMarkupManager.InvalidateLinesMethod := @InvalidateLines;
 | |
| 
 | |
|   {$IFDEF WinIME}
 | |
|   {$IFDEF WinIMEFull}
 | |
|   FImeHandler := LazSynImeFull.Create(Self);
 | |
|   {$ELSE}
 | |
|   FImeHandler := LazSynImeSimple.Create(Self);
 | |
|   LazSynImeSimple(FImeHandler).TextDrawer := FTextDrawer;
 | |
|   {$ENDIF}
 | |
|   FImeHandler.InvalidateLinesMethod := @InvalidateLines;
 | |
|   {$ENDIF}
 | |
| 
 | |
|   fFontDummy.Name := SynDefaultFontName;
 | |
|   fFontDummy.Height := SynDefaultFontHeight;
 | |
|   fFontDummy.Pitch := SynDefaultFontPitch;
 | |
|   fFontDummy.Quality := SynDefaultFontQuality;
 | |
|   FLastSetFontSize := fFontDummy.Height;
 | |
|   fLastMouseCaret := Point(-1,-1);
 | |
|   FLastMousePoint := Point(-1,-1);
 | |
|   fBlockIndent := 2;
 | |
| 
 | |
|   FTextArea := TLazSynTextArea.Create(Self, FTextDrawer);
 | |
|   FTextArea.RightEdgeVisible := not(eoHideRightMargin in SYNEDIT_DEFAULT_OPTIONS);
 | |
|   FTextArea.ExtraCharSpacing := 0;
 | |
|   FTextArea.ExtraLineSpacing := 0;
 | |
|   FTextArea.MarkupManager := fMarkupManager;
 | |
|   FTextArea.TheLinesView := FTheLinesView;
 | |
|   FTextArea.Highlighter := nil;
 | |
| 
 | |
|   FLeftGutterArea := TLazSynGutterArea.Create(Self);
 | |
|   FLeftGutterArea.TextArea := FTextArea;
 | |
|   FLeftGutterArea.Gutter := FLeftGutter;
 | |
| 
 | |
|   FRightGutterArea := TLazSynGutterArea.Create(Self);
 | |
|   FRightGutterArea.TextArea := FTextArea;
 | |
|   FRightGutterArea.Gutter := FRightGutter;
 | |
| 
 | |
|   FPaintArea := TLazSynSurfaceManager.Create(Self);
 | |
|   FPaintArea.TextArea := FTextArea;
 | |
|   FPaintArea.LeftGutterArea := FLeftGutterArea;
 | |
|   FPaintArea.RightGutterArea := FRightGutterArea;
 | |
|   FPaintArea.DisplayView := FDisplayView;
 | |
| 
 | |
|   Color := clWhite;
 | |
|   Font.Assign(fFontDummy);
 | |
|   Font.OnChange := {$IFDEF FPC}@{$ENDIF}FontChanged;
 | |
|   FontChanged(nil);
 | |
|   ParentFont := False;
 | |
|   ParentColor := False;
 | |
|   TabStop := True;
 | |
|   fInserting := True;
 | |
|   fMaxLeftChar := 1024;
 | |
|   ScrollBars := ssBoth;
 | |
|   BorderStyle := bsSingle;
 | |
|   fInsertCaret := ctVerticalLine;
 | |
|   fOverwriteCaret := ctBlock;
 | |
|   FKeystrokes := TSynEditKeyStrokes.Create(Self);
 | |
|   FCurrentComboKeyStrokes := nil;
 | |
|   if assigned(Owner) and not (csLoading in Owner.ComponentState) then begin
 | |
|     SetDefaultKeystrokes;
 | |
|   end;
 | |
| 
 | |
|   FMouseActions     := TSynEditMouseGlobalActions.Create(Self);
 | |
|   FMouseSelActions  := TSynEditMouseSelActions.Create(Self);
 | |
|   FMouseTextActions := TSynEditMouseTextActions.Create(Self);
 | |
|   FMouseActionSearchHandlerList := TSynEditMouseActionSearchList.Create;
 | |
|   FMouseActionExecHandlerList  := TSynEditMouseActionExecList.Create;
 | |
| 
 | |
|   fWantTabs := False;
 | |
|   fTabWidth := 8;
 | |
|   FOldTopView := 1;
 | |
|   FFoldedLinesView.TopLine := 1;
 | |
|   // find / replace
 | |
|   fTSearch := TSynEditSearch.Create;
 | |
|   FOptions := SYNEDIT_DEFAULT_OPTIONS;
 | |
|   FOptions2 := SYNEDIT_DEFAULT_OPTIONS2;
 | |
|   FMouseOptions := SYNEDIT_DEFAULT_MOUSE_OPTIONS;
 | |
|   FShareOptions := SYNEDIT_DEFAULT_SHARE_OPTIONS;
 | |
|   FVisibleSpecialChars := SYNEDIT_DEFAULT_VISIBLESPECIALCHARS;
 | |
|   fMarkupSpecialChar.VisibleSpecialChars := SYNEDIT_DEFAULT_VISIBLESPECIALCHARS;
 | |
|   UpdateOptions;
 | |
|   UpdateOptions2;
 | |
|   UpdateMouseOptions;
 | |
|   fScrollTimer := TTimer.Create(Self);
 | |
|   fScrollTimer.Enabled := False;
 | |
|   fScrollTimer.Interval := 100;
 | |
|   fScrollTimer.OnTimer := {$IFDEF FPC}@{$ENDIF}ScrollTimerHandler;
 | |
| 
 | |
|   // Accessibility
 | |
|   AccessibleRole := larTextEditorMultiline;
 | |
|   AccessibleValue := Self.Text;
 | |
|   AccessibleDescription := 'source code editor';
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetChildOwner: TComponent;
 | |
| begin
 | |
|   result := self;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.GetChildren(Proc: TGetChildProc; Root: TComponent);
 | |
| begin
 | |
|   if root = self then begin
 | |
|     Proc(FLeftGutter.Parts);
 | |
|     // only save right gutter, if it has gutter-parts
 | |
|     // move to parts-class
 | |
|     if FRightGutter.Parts.Count > 0 then
 | |
|       Proc(FRightGutter.Parts);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.CreateParams(var Params: TCreateParams);
 | |
| (*
 | |
| const
 | |
|   ScrollBar: array[TScrollStyle] of DWORD = (0, WS_HSCROLL, WS_VSCROLL,
 | |
|     WS_HSCROLL or WS_VSCROLL, WS_HSCROLL, WS_VSCROLL, WS_HSCROLL or WS_VSCROLL);
 | |
|   BorderStyles: array[TBorderStyle] of DWORD = (0, WS_BORDER);
 | |
|   ClassStylesOff = CS_VREDRAW or CS_HREDRAW;
 | |
| *)
 | |
| begin
 | |
|   inherited CreateParams(Params);
 | |
| (*
 | |
|   with Params do begin
 | |
|     {$IFOPT R+}{$DEFINE RangeCheckOn}{$R-}{$ENDIF}
 | |
|     WindowClass.Style := WindowClass.Style and not Cardinal(ClassStylesOff);
 | |
|     Style := Style or ScrollBar[FScrollBars] or BorderStyles[BorderStyle]
 | |
|       or WS_CLIPCHILDREN;
 | |
|     {$IFDEF RangeCheckOn}{$R+}{$ENDIF}
 | |
|     if NewStyleControls {$IFNDEF SYN_LAZARUS}and Ctl3D{$ENDIF} and (BorderStyle = bsSingle) then begin
 | |
|       Style := Style and not Cardinal(WS_BORDER);
 | |
|       ExStyle := ExStyle or WS_EX_CLIENTEDGE;
 | |
|     end;
 | |
|   end;
 | |
| *)
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.IncPaintLock;
 | |
| begin
 | |
|   if FIsInDecPaintLock then exit;
 | |
|   if (PaintLockOwner = nil) then begin
 | |
|     PaintLockOwner := Self;
 | |
|     FLines.SendNotification(senrIncOwnedPaintLock, Self);  // DoIncForeignPaintLock
 | |
|   end;
 | |
|   inc(FPaintLockOwnerCnt);
 | |
|   if FPaintLockOwnerCnt = 1 then
 | |
|     FLines.BeginUpdate(Self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DecPaintLock;
 | |
| begin
 | |
|   if FIsInDecPaintLock then exit;
 | |
|   if FPaintLockOwnerCnt = 1 then
 | |
|     FLines.EndUpdate(Self);
 | |
|   dec(FPaintLockOwnerCnt);
 | |
|   if (PaintLockOwner = Self) and (FPaintLockOwnerCnt = 0) then begin
 | |
|     FLines.SendNotification(senrDecOwnedPaintLock, Self);  // DoDecForeignPaintLock
 | |
|     PaintLockOwner := nil;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoIncForeignPaintLock(Sender: TObject);
 | |
| begin
 | |
|   if Sender = Self then exit;
 | |
|   FCaret.IncAutoMoveOnEdit;
 | |
|   FBlockSelection.IncPersistentLock;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoDecForeignPaintLock(Sender: TObject);
 | |
| begin
 | |
|   if Sender = Self then exit;
 | |
|   FBlockSelection.DecPersistentLock;
 | |
|   FCaret.DecAutoMoveOnEdit;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetUpdateState(NewUpdating: Boolean; Sender: TObject);
 | |
| begin
 | |
|   if assigned(FOnChangeUpdating) then
 | |
|     FOnChangeUpdating(Self, NewUpdating);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoIncPaintLock(Sender: TObject);
 | |
| begin
 | |
|   if FIsInDecPaintLock then exit;
 | |
|   if FPaintLock = 0 then begin
 | |
|     SetUpdateState(True, Self);
 | |
|     FInvalidateRect := Rect(-1, -1, -2, -2);
 | |
|     FOldTopView := TopView;
 | |
|     FLastTextChangeStamp := TSynEditStringList(FLines).TextChangeStamp;
 | |
|   end;
 | |
|   inc(FPaintLock);
 | |
|   FMarkupManager.IncPaintLock;
 | |
|   FFoldedLinesView.Lock; //DecPaintLock triggers ScanFrom, and folds must wait
 | |
|   FTrimmedLinesView.Lock; // Lock before caret
 | |
|   FBlockSelection.Lock;
 | |
|   FCaret.Lock;
 | |
|   FScreenCaret.Lock;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoDecPaintLock(Sender: TObject);
 | |
| begin
 | |
|   if FIsInDecPaintLock then exit;
 | |
|   FIsInDecPaintLock := True;
 | |
|   try
 | |
|     if (FUndoBlockAtPaintLock >= FPaintLock) then begin
 | |
|       if (FUndoBlockAtPaintLock > FPaintLock) then
 | |
|         debugln(['***** SYNEDIT: Fixing auto-undo-block FUndoBlockAtPaintLock=',FUndoBlockAtPaintLock,' FPaintLock=',FPaintLock]);
 | |
|       FUndoBlockAtPaintLock := 0;
 | |
|       EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TCustomSynEdit.DoDecPaintLock'){$ENDIF};
 | |
|     end;
 | |
|     if (FPaintLock=1) and HandleAllocated then begin
 | |
|       ScanRanges(FLastTextChangeStamp <> TSynEditStringList(FLines).TextChangeStamp);
 | |
|       if sfAfterLoadFromFileNeeded in fStateFlags then
 | |
|         AfterLoadFromFile;
 | |
|       if FChangedLinesStart > 0 then begin
 | |
|         InvalidateLines(FChangedLinesStart, FChangedLinesEnd);
 | |
|         InvalidateGutterLines(FChangedLinesStart, FChangedLinesEnd);
 | |
|       end;
 | |
|       FChangedLinesStart:=0;
 | |
|       FChangedLinesEnd:=0;
 | |
|     end;
 | |
|     FCaret.Unlock;            // Maybe after FFoldedLinesView
 | |
|     FBlockSelection.Unlock;
 | |
|     FTrimmedLinesView.UnLock; // Must be unlocked after caret // May Change lines
 | |
|     FFoldedLinesView.UnLock;  // after ScanFrom, but before UpdateCaret
 | |
|     FMarkupManager.DecPaintLock;
 | |
|     Dec(FPaintLock);
 | |
|     if (FPaintLock = 0) and HandleAllocated then begin
 | |
|       ScrollAfterTopLineChanged;
 | |
|       if sfScrollbarChanged in fStateFlags then
 | |
|         UpdateScrollbars;
 | |
|       // must be past UpdateScrollbars; but before UpdateCaret (for ScrollBar-Auto-show)
 | |
|       if sfEnsureCursorPos in fStateFlags then
 | |
|         EnsureCursorPosVisible;              // TODO: This may call SetTopLine, change order
 | |
|                                              // This does Paintlock, should be before final decrease
 | |
|       // Must be after EnsureCursorPosVisible (as it does MoveCaretToVisibleArea)
 | |
|       if FCaret.LinePos > FLines.Count then
 | |
|         FCaret.LinePos := FLines.Count;
 | |
|       if sfCaretChanged in fStateFlags then
 | |
|         UpdateCaret;
 | |
|       //if sfScrollbarChanged in fStateFlags then
 | |
|       //  UpdateScrollbars;
 | |
|       fMarkupHighCaret.CheckState; // Todo: need a global lock, including the markup
 | |
|                                    // Todo: Markup can do invalidation, should be before ScrollAfterTopLineChanged;
 | |
|     end;
 | |
|     if (FPaintLock = 0) then begin
 | |
|       FBlockSelection.AutoExtend := False;
 | |
|       if fStatusChanges <> [] then
 | |
|         DoOnStatusChange(fStatusChanges);
 | |
|     end;
 | |
|   finally
 | |
|     FScreenCaret.UnLock;
 | |
|     FIsInDecPaintLock := False;
 | |
|     if FPaintLock = 0 then begin
 | |
|       SetUpdateState(False, Self);
 | |
|       if FInvalidateRect.Bottom > FInvalidateRect.Top then begin
 | |
|         InvalidateRect(Handle, @FInvalidateRect, False);
 | |
|         {$IFDEF SynCheckPaintLock}
 | |
|         debugln('Returning from Paintlock, wich had Paint called while active');
 | |
|         DumpStack;
 | |
|         {$ENDIF}
 | |
|       end;
 | |
|       if sfSelChanged in FStateFlags then
 | |
|         SelAvailChange(nil);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| destructor TCustomSynEdit.Destroy;
 | |
| var
 | |
|   i: integer;
 | |
|   p: TList;
 | |
| begin
 | |
|   Application.RemoveOnIdleHandler(@IdleScanRanges);
 | |
|   SurrenderPrimarySelection;
 | |
|   Highlighter := nil;
 | |
|   Beautifier:=nil;
 | |
|   FFoldedLinesView.BlockSelection := nil;
 | |
| 
 | |
|   if fPlugins <> nil then begin
 | |
|     p := FPlugins;
 | |
|     FPlugins := nil;
 | |
|     for i := p.Count - 1 downto 0 do
 | |
|       TLazSynEditPlugin(p[i]).DoEditorDestroyed(Self);
 | |
|     p.Free;
 | |
|   end;
 | |
| 
 | |
|   // free listeners while other fields are still valid
 | |
|   if Assigned(fHookedCommandHandlers) then begin
 | |
|     for i := 0 to fHookedCommandHandlers.Count - 1 do
 | |
|       THookedCommandHandlerEntry(fHookedCommandHandlers[i]).Free;
 | |
|     FreeAndNil(fHookedCommandHandlers);
 | |
|   end;
 | |
| 
 | |
|   RemoveHandlers;
 | |
|   FLeftGutter.UnRegisterChangeHandler({$IFDEF FPC}@{$ENDIF}GutterChanged);
 | |
|   FLeftGutter.UnRegisterResizeHandler({$IFDEF FPC}@{$ENDIF}GutterResized);
 | |
|   FRightGutter.UnRegisterChangeHandler({$IFDEF FPC}@{$ENDIF}GutterChanged);
 | |
|   FRightGutter.UnRegisterResizeHandler({$IFDEF FPC}@{$ENDIF}GutterResized);
 | |
| 
 | |
|   FreeAndNil(FHookedKeyTranslationList);
 | |
|   fHookedCommandHandlers:=nil;
 | |
|   fPlugins:=nil;
 | |
|   FCaret.Lines := nil;
 | |
|   FInternalCaret.Lines := nil;
 | |
|   FMarkList.UnRegisterChangeHandler({$IFDEF FPC}@{$ENDIF}MarkListChange);
 | |
|   FreeAndNil(FPaintArea);
 | |
|   FreeAndNil(FLeftGutterArea);
 | |
|   FreeAndNil(FRightGutterArea);
 | |
|   FreeAndNil(FTextArea);
 | |
|   FreeAndNil(fTSearch);
 | |
|   FreeAndNil(fMarkupManager);
 | |
|   FreeAndNil(fBookMarkOpt);
 | |
|   FreeAndNil(fKeyStrokes);
 | |
|   FreeAndNil(FMouseActionSearchHandlerList);
 | |
|   FreeAndNil(FMouseActionExecHandlerList);
 | |
|   FreeAndNil(FMouseActions);
 | |
|   FreeAndNil(FMouseSelActions);
 | |
|   FreeAndNil(FMouseTextActions);
 | |
|   FreeAndNil(FLeftGutter);
 | |
|   FreeAndNil(FRightGutter);
 | |
|   FreeAndNil(FPaintLineColor);
 | |
|   FreeAndNil(FPaintLineColor2);
 | |
|   {$IFDEF WinIME}
 | |
|   FreeAndNil(FImeHandler);
 | |
|   {$ENDIF}
 | |
|   FreeAndNil(fTextDrawer);
 | |
|   FreeAndNil(fFontDummy);
 | |
|   DestroyMarkList; // before detach from FLines
 | |
|   FreeAndNil(FWordBreaker);
 | |
|   FreeAndNil(FFoldedLinesView); // has reference to caret
 | |
|   FreeAndNil(FInternalBlockSelection);
 | |
|   FreeAndNil(FBlockSelection);
 | |
|   FreeAndNil(FStrings);
 | |
|   FreeAndNil(FTabbedLinesView);
 | |
|   FreeAndNil(FTrimmedLinesView); // has reference to caret
 | |
|   FreeAndNil(FDoubleWidthChrLinesView);
 | |
|   TSynEditStringList(FLines).DetachSynEdit(Self);
 | |
|   if TSynEditStringList(FLines).AttachedSynEditCount = 0 then
 | |
|     FreeAndNil(fLines);
 | |
|   FreeAndNil(fCaret);
 | |
|   FreeAndNil(fInternalCaret);
 | |
|   FreeAndNil(FScreenCaret);
 | |
|   FreeAndNil(FStatusChangedList);
 | |
|   FBeautifier := nil;
 | |
|   FreeAndNil(FDefaultBeautifier);
 | |
|   FreeAndNil(FKeyDownEventList);
 | |
|   FreeAndNil(FMouseDownEventList);
 | |
|   FreeAndNil(FKeyPressEventList);
 | |
|   FreeAndNil(FUtf8KeyPressEventList);
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetBlockBegin: TPoint;
 | |
| begin
 | |
|   Result := FBlockSelection.FirstLineBytePos;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetBlockEnd: TPoint;
 | |
| begin
 | |
|   Result := FBlockSelection.LastLineBytePos;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetBracketHighlightStyle: TSynEditBracketHighlightStyle;
 | |
| begin
 | |
|   Result := fMarkupBracket.HighlightStyle;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.CaretXPix: Integer;
 | |
| var
 | |
|   p: TPoint;
 | |
| begin
 | |
|   p := Point(CaretX, CaretY);
 | |
|   Result := RowColumnToPixels(p).X;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.CaretYPix: Integer;
 | |
| begin
 | |
|   Result := RowColumnToPixels(Point(1, CaretY)).Y;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.FontChanged(Sender: TObject);
 | |
| begin // TODO: inherited ?
 | |
|   FPaintArea.ForegroundColor := Font.Color;
 | |
|   FLastSetFontSize := Font.Height;
 | |
|   RecalcCharExtent;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetTextBuffer: TSynEditStrings;
 | |
| begin
 | |
|   Result := FLines;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetLineText: string;
 | |
| begin
 | |
|   Result := FCaret.LineText;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetMarkupByClass(Index: TSynEditMarkupClass): TSynEditMarkup;
 | |
| begin
 | |
|   Result := fMarkupManager.MarkupByClass[Index];
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetHighlightAllColor : TSynSelectedColor;
 | |
| begin
 | |
|   result := fMarkupHighAll.MarkupInfo;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetIncrementColor : TSynSelectedColor;
 | |
| begin
 | |
|   result := fMarkupSelection.MarkupInfoIncr;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetLineHighlightColor: TSynSelectedColor;
 | |
| begin
 | |
|   Result := fMarkupSpecialLine.MarkupLineHighlightInfo;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetOnGutterClick : TGutterClickEvent;
 | |
| begin
 | |
|   Result := FLeftGutter.OnGutterClick;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetSelectedColor : TSynSelectedColor;
 | |
| begin
 | |
|   result := fMarkupSelection.MarkupInfoSeletion;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetSelectedColor(const AValue : TSynSelectedColor);
 | |
| begin
 | |
|   fMarkupSelection.MarkupInfoSeletion.Assign(AValue);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetSpecialLineColors(const AValue : TSpecialLineColorsEvent);
 | |
| begin
 | |
|   fOnSpecialLineColors:=AValue;
 | |
|   fMarkupSpecialLine.OnSpecialLineColors := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetSpecialLineMarkup(const AValue : TSpecialLineMarkupEvent);
 | |
| begin
 | |
|   FOnSpecialLineMarkup:=AValue;
 | |
|   fMarkupSpecialLine.OnSpecialLineMarkup := AValue;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetBracketMatchColor : TSynSelectedColor;
 | |
| begin
 | |
|   Result := fMarkupBracket.MarkupInfo;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetMouseLinkColor : TSynSelectedColor;
 | |
| begin
 | |
|   Result := fMarkupCtrlMouse.MarkupInfo;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetTrimSpaceType: TSynEditStringTrimmingType;
 | |
| begin
 | |
|   Result := FTrimmedLinesView.TrimType;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetViewedTextBuffer: TSynEditStrings;
 | |
| begin
 | |
|   Result := FTheLinesView;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetFoldedTextBuffer: TObject;
 | |
| begin
 | |
|   Result := FFoldedLinesView;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetBracketHighlightStyle(
 | |
|   const AValue: TSynEditBracketHighlightStyle);
 | |
| begin
 | |
|   fMarkupBracket.HighlightStyle := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetOnGutterClick(const AValue : TGutterClickEvent);
 | |
| begin
 | |
|   FLeftGutter.OnGutterClick := AValue; // Todo: the IDE uses this for the left gutter only
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetUseIncrementalColor(const AValue : Boolean);
 | |
| begin
 | |
|   fMarkupSelection.UseIncrementalColor:=AValue;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCharLen(const Line: string; CharStartPos: integer
 | |
|   ): integer;
 | |
| begin
 | |
|   if UseUTF8 and (length(Line)>=CharStartPos) then
 | |
|     Result:=UTF8CharacterLength(@Line[CharStartPos])
 | |
|   else
 | |
|     Result:=1;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetLogicalCaretXY: TPoint;
 | |
| begin
 | |
|   Result := FCaret.LineBytePos;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetLogicalCaretXY(const NewLogCaretXY: TPoint);
 | |
| begin
 | |
|   FCaret.ChangeOnTouch;
 | |
|   FCaret.LineBytePos := NewLogCaretXY;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetBeautifier(NewBeautifier: TSynCustomBeautifier);
 | |
| begin
 | |
|   if fBeautifier = NewBeautifier then exit;
 | |
|   if NewBeautifier = nil then
 | |
|     fBeautifier := FDefaultBeautifier
 | |
|   else
 | |
|     fBeautifier := NewBeautifier;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetSelAvail: Boolean;
 | |
| begin
 | |
|   Result := FBlockSelection.SelAvail;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetSelText: string;
 | |
| begin
 | |
|   Result := FBlockSelection.SelText;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetTrimSpaceType(const AValue: TSynEditStringTrimmingType);
 | |
| begin
 | |
|   FTrimmedLinesView.TrimType := AValue;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.SynGetText: string;
 | |
| begin
 | |
|   Result := fLines.Text;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.RealGetText: TCaption;
 | |
| begin
 | |
|   if FLines<>nil then
 | |
|     Result := FLines.Text
 | |
|   else
 | |
|     Result := '';
 | |
| end;
 | |
| 
 | |
| {$IFDEF WinIME}
 | |
| procedure TCustomSynEdit.WMImeRequest(var Msg: TMessage);
 | |
| begin
 | |
|   FImeHandler.WMImeRequest(Msg);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetImeHandler(AValue: LazSynIme);
 | |
| begin
 | |
|   if FImeHandler = AValue then Exit;
 | |
|   FreeAndNil(FImeHandler);
 | |
|   FImeHandler := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMImeNotify(var Msg: TMessage);
 | |
| begin
 | |
|     FImeHandler.WMImeNotify(Msg);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMImeComposition(var Msg: TMessage);
 | |
| begin
 | |
|   FImeHandler.WMImeComposition(Msg);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMImeStartComposition(var Msg: TMessage);
 | |
| begin
 | |
|   FImeHandler.WMImeStartComposition(Msg);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMImeEndComposition(var Msg: TMessage);
 | |
| begin
 | |
|   FImeHandler.WMImeEndComposition(Msg);
 | |
| end;
 | |
| {$ENDIF}
 | |
| 
 | |
| procedure TCustomSynEdit.InvalidateGutter;
 | |
| begin
 | |
|   InvalidateGutterLines(-1, -1);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.InvalidateGutterLines(FirstLine, LastLine: integer);   // Todo: move to gutter
 | |
| var
 | |
|   TopFoldLine: LongInt;
 | |
| begin
 | |
|   if sfPainting in fStateFlags then exit;
 | |
|   if Visible and HandleAllocated then begin
 | |
|     {$IFDEF VerboseSynEditInvalidate}
 | |
|     DebugLnEnter(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self), ' FirstLine=',FirstLine, ' LastLine=',LastLine]);
 | |
|     {$ENDIF}
 | |
|     if (FirstLine = -1) and (LastLine = -1) then begin
 | |
|       FPaintArea.InvalidateGutterLines(-1, -1);
 | |
|     end else begin
 | |
|       // pretend we haven't scrolled
 | |
|       TopFoldLine := FFoldedLinesView.TopLine;
 | |
|       if FOldTopView <> TopView then
 | |
|         FFoldedLinesView.TopLine := FOldTopView;
 | |
| 
 | |
|       if (LastLine <> -1) and (LastLine < FirstLine) then
 | |
|         SwapInt(FirstLine, LastLine);
 | |
| 
 | |
|       FPaintArea.InvalidateGutterLines(FirstLine-1, LastLine-1);
 | |
| 
 | |
|       FFoldedLinesView.TopLine := TopFoldLine;
 | |
|     end;
 | |
|     {$IFDEF VerboseSynEditInvalidate}
 | |
|     DebugLnExit(['TCustomSynEdit.InvalidateGutterLines ',DbgSName(self)]);
 | |
|     {$ENDIF}
 | |
|     end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.InvalidateLines(FirstLine, LastLine: integer);
 | |
| var
 | |
|   TopFoldLine: LongInt;
 | |
| begin
 | |
|   if sfPainting in fStateFlags then exit;
 | |
|   if Visible and HandleAllocated then begin
 | |
|     {$IFDEF VerboseSynEditInvalidate}
 | |
|     DebugLnEnter(['TCustomSynEdit.InvalidateTextLines ',DbgSName(self), ' FirstLine=',FirstLine, ' LastLine=',LastLine]);
 | |
|     {$ENDIF}
 | |
|     if (FirstLine = -1) and (LastLine = -1) then begin
 | |
|       FPaintArea.InvalidateTextLines(-1, -1);
 | |
|     end else begin
 | |
|       // pretend we haven't scrolled
 | |
|       TopFoldLine := FFoldedLinesView.TopLine;
 | |
|       if FOldTopView <> TopView then
 | |
|         FFoldedLinesView.TopLine := FOldTopView;
 | |
| 
 | |
|       if (LastLine <> -1) and (LastLine < FirstLine) then
 | |
|         SwapInt(FirstLine, LastLine);
 | |
| 
 | |
|       FPaintArea.InvalidateTextLines(FirstLine-1, LastLine-1);
 | |
| 
 | |
|       FFoldedLinesView.TopLine := TopFoldLine;
 | |
|     end;
 | |
|     {$IFDEF VerboseSynEditInvalidate}
 | |
|     DebugLnExit(['TCustomSynEdit.InvalidateTextLines ',DbgSName(self)]);
 | |
|     {$ENDIF}
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.KeyDown(var Key: Word; Shift: TShiftState);
 | |
| var
 | |
|   Data: pointer;
 | |
|   C: char;
 | |
|   Cmd: TSynEditorCommand;
 | |
|   IsStartOfCombo, Handled: boolean;
 | |
| begin
 | |
|   FInMouseClickEvent := False;
 | |
|   {$IFDEF VerboseKeys}
 | |
|   DebugLn('[TCustomSynEdit.KeyDown] ',dbgs(Key),' ',dbgs(Shift));
 | |
|   {$ENDIF}
 | |
| 
 | |
|   // Run even before OnKeyDown
 | |
|   if FKeyDownEventList <> nil then
 | |
|     FKeyDownEventList.CallKeyDownHandlers(Self, Key, Shift);
 | |
|   if Key=0 then exit;
 | |
| 
 | |
|   inherited;
 | |
|   if assigned(fMarkupCtrlMouse) then
 | |
|     fMarkupCtrlMouse.UpdateCtrlState(Shift);
 | |
| 
 | |
|   if Key in [VK_SHIFT, VK_CONTROL, VK_MENU,
 | |
|              VK_LSHIFT, VK_LCONTROL, VK_LMENU,
 | |
|              VK_RSHIFT, VK_RCONTROL, VK_RMENU,
 | |
|              VK_LWIN, VK_RWIN]
 | |
|   then
 | |
|     exit;
 | |
| 
 | |
|   Data := nil;
 | |
|   C := #0;
 | |
|   try
 | |
|     // If the translations requires Data, memory will be allocated for it via a
 | |
|     // GetMem call.  The client must call FreeMem on Data if it is not NIL.
 | |
|     IsStartOfCombo := False;
 | |
|     Handled := False;
 | |
| 
 | |
|     // Check 2nd stroke in SynEdit.KeyStrokes
 | |
|     if FCurrentComboKeyStrokes <> nil then begin
 | |
|       // Run hooked first, it might want to "steal" the key(s)
 | |
|       Cmd := 0;
 | |
|       FHookedKeyTranslationList.CallHookedKeyTranslationHandlers(self,
 | |
|         Key, Shift, Data, IsStartOfCombo, Handled, Cmd, FCurrentComboKeyStrokes);
 | |
| 
 | |
|       if not Handled then begin
 | |
|         Cmd := KeyStrokes.FindKeycodeEx(Key, Shift, Data, IsStartOfCombo, True, FCurrentComboKeyStrokes);
 | |
|         if IsStartOfCombo then
 | |
|           FCurrentComboKeyStrokes := FKeyStrokes;
 | |
|         Handled := (Cmd <> ecNone) or IsStartOfCombo;
 | |
|       end;
 | |
| 
 | |
|       if not IsStartOfCombo then begin
 | |
|         FCurrentComboKeyStrokes.ResetKeyCombo;
 | |
|         FCurrentComboKeyStrokes := nil;
 | |
|       end;
 | |
|     end;
 | |
|     assert(Handled or (FCurrentComboKeyStrokes=nil), 'FCurrentComboKeyStrokes<>nil, should be handled');
 | |
| 
 | |
|     // Check 1st/single stroke in Hooked KeyStrokes
 | |
|     if not Handled then begin
 | |
|       FCurrentComboKeyStrokes := nil;
 | |
|       FHookedKeyTranslationList.CallHookedKeyTranslationHandlers(self,
 | |
|         Key, Shift, Data, IsStartOfCombo, Handled, Cmd, FCurrentComboKeyStrokes);
 | |
|       if (not IsStartOfCombo) and (FCurrentComboKeyStrokes <> nil) then
 | |
|         FCurrentComboKeyStrokes.ResetKeyCombo; // should not happen
 | |
|     end;
 | |
|     // Check 1st/single stroke in SynEdit.KeyStrokes
 | |
|     if not Handled then begin
 | |
|       FKeyStrokes.ResetKeyCombo;
 | |
|       Cmd := KeyStrokes.FindKeycodeEx(Key, Shift, Data, IsStartOfCombo);
 | |
|       if IsStartOfCombo then
 | |
|         FCurrentComboKeyStrokes := FKeyStrokes;
 | |
|     end;
 | |
| 
 | |
|     if Cmd <> ecNone then begin
 | |
|       // Reset FCurrentComboKeyStrokes => no open combo
 | |
|       assert(FCurrentComboKeyStrokes=nil, 'FCurrentComboKeyStrokes<>nil, should be ecNone');
 | |
|       if FCurrentComboKeyStrokes <> nil then
 | |
|         FCurrentComboKeyStrokes.ResetKeyCombo;
 | |
|       FCurrentComboKeyStrokes := nil;
 | |
| 
 | |
|       Include(FStateFlags, sfHideCursor);
 | |
|       LastMouseCaret := Point(-1,-1);                                           // includes update cursor
 | |
|       //DebugLn(['[TCustomSynEdit.KeyDown] key translated ',cmd]);
 | |
|       Key := 0; // eat it.
 | |
|       Include(fStateFlags, sfIgnoreNextChar);
 | |
|       CommandProcessor(Cmd, C, Data);
 | |
|     end else if IsStartOfCombo then begin
 | |
|       // this key could be the start of a two-key-combo shortcut
 | |
|       Key := 0; // eat it.
 | |
|       Include(fStateFlags, sfIgnoreNextChar);
 | |
|     end else
 | |
|       Exclude(fStateFlags, sfIgnoreNextChar);
 | |
|   finally
 | |
|     if Data <> nil then
 | |
|       FreeMem(Data);
 | |
|   end;
 | |
|   UpdateCursor;
 | |
|   SelAvailChange(nil);
 | |
|   //DebugLn('[TCustomSynEdit.KeyDown] END ',dbgs(Key),' ',dbgs(Shift));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.KeyUp(var Key: Word; Shift: TShiftState);
 | |
| begin
 | |
|   {$IFDEF VerboseKeys}
 | |
|   DebugLn(['[TCustomSynEdit.KeyUp] ',Key
 | |
|     ,' Shift=',ssShift in Shift,' Ctrl=',ssCtrl in Shift,' Alt=',ssAlt in Shift]);
 | |
|   {$ENDIF}
 | |
|   inherited KeyUp(Key, Shift);
 | |
| 
 | |
|   if sfIgnoreNextChar in fStateFlags then
 | |
|     Exclude(FStateFlags, sfIgnoreNextChar);
 | |
| 
 | |
|   if assigned(fMarkupCtrlMouse) then
 | |
|     fMarkupCtrlMouse.UpdateCtrlState(Shift);
 | |
|   UpdateCursor;
 | |
| end;
 | |
| 
 | |
| 
 | |
| procedure TCustomSynEdit.Loaded;
 | |
| begin
 | |
|   inherited Loaded;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UTF8KeyPress(var Key: TUTF8Char);
 | |
| begin
 | |
|   if Key='' then exit;
 | |
| 
 | |
|   // Run even before OnKeyPress
 | |
|   if FUtf8KeyPressEventList <> nil then
 | |
|     FUtf8KeyPressEventList.CallUtf8KeyPressHandlers(Self, Key);
 | |
|   if Key='' then exit;
 | |
| 
 | |
|   // don't fire the event if key is to be ignored
 | |
|   if not (sfIgnoreNextChar in fStateFlags) then begin
 | |
|     Include(FStateFlags, sfHideCursor);
 | |
|     if Assigned(OnUTF8KeyPress) then OnUTF8KeyPress(Self, Key);
 | |
|     // The key will be handled in UTFKeyPress always and KeyPress won't be called
 | |
|     // so we we fire the OnKeyPress here
 | |
|     if (ord(key[1])< %11000000) and (key[1]<>#0) and Assigned(OnKeyPress) then
 | |
|       OnKeyPress(Self, Key[1]);
 | |
|     {$IFDEF VerboseKeys}
 | |
|     DebugLn('TCustomSynEdit.UTF8KeyPress ',DbgSName(Self),' Key="',DbgStr(Key),'" UseUTF8=',dbgs(UseUTF8));
 | |
|     {$ENDIF}
 | |
|     CommandProcessor(ecChar, Key, nil);
 | |
|     // Check if ecChar has handled the Key; Todo: move the condition, in one common place
 | |
|     if not ReadOnly and ((Key = #13) or (Key >= #32)) and (Key <> #127) then
 | |
|       Key:='';
 | |
|   end else begin
 | |
|     // don't ignore further keys
 | |
|     Exclude(fStateFlags, sfIgnoreNextChar);
 | |
|     // Key was handled anyway, so eat it!
 | |
|     Key:='';
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.KeyPress(var Key: Char);
 | |
| begin
 | |
|   if Key=#0 then exit;
 | |
| 
 | |
|   // Run even before OnKeyPress
 | |
|   if FKeyPressEventList <> nil then
 | |
|     FKeyPressEventList.CallKeyPressHandlers(Self, Key);
 | |
|   if Key=#0 then exit;
 | |
| 
 | |
|   // don't fire the event if key is to be ignored
 | |
|   if not (sfIgnoreNextChar in fStateFlags) then begin
 | |
|     Include(FStateFlags, sfHideCursor);
 | |
|     {$IFDEF VerboseKeys}
 | |
|     DebugLn('TCustomSynEdit.KeyPress ',DbgSName(Self),' Key="',DbgStr(Key),'" UseUTF8=',dbgs(UseUTF8));
 | |
|     {$ENDIF}
 | |
|     if Assigned(OnKeyPress) then OnKeyPress(Self, Key);
 | |
|     CommandProcessor(ecChar, Key, nil);
 | |
|     // Check if ecChar has handled the Key; Todo: move the condition, in one common place
 | |
|     if not ReadOnly and ((Key = #13) or (Key >= #32)) and (Key <> #127) then
 | |
|       Key:=#0;
 | |
|   end else begin
 | |
|     // don't ignore further keys
 | |
|     Exclude(fStateFlags, sfIgnoreNextChar);
 | |
|     // Key was handled anyway, so eat it!
 | |
|     Key:=#0;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.DoHandleMouseAction(AnActionList: TSynEditMouseActions;
 | |
|   AnInfo: TSynEditMouseActionInfo): Boolean;
 | |
| var
 | |
|   CaretDone: Boolean;
 | |
|   AnAction: TSynEditMouseAction;
 | |
| 
 | |
|   procedure MoveCaret;
 | |
|   begin
 | |
|    FCaret.LineCharPos := AnInfo.NewCaret.LineCharPos;
 | |
|    CaretDone := True;
 | |
|   end;
 | |
| 
 | |
|   function GetWheelScrollAmount(APageSize: integer): integer;
 | |
|   const
 | |
|     WHEEL_DELTA = 120;
 | |
|     //WHEEL_PAGESCROLL = MAXDWORD;
 | |
|   var
 | |
|     WClicks, WLines: Integer;
 | |
|   begin
 | |
|     Inc(FMouseWheelAccumulator, AnInfo.WheelDelta);
 | |
|     Inc(FMouseWheelLinesAccumulator, MinMax(Mouse.WheelScrollLines, 1, APageSize) * AnInfo.WheelDelta);
 | |
|     WClicks := FMouseWheelAccumulator div WHEEL_DELTA;
 | |
|     WLines  := FMouseWheelLinesAccumulator div WHEEL_DELTA;
 | |
|     dec(FMouseWheelAccumulator, WClicks * WHEEL_DELTA);
 | |
|     dec(FMouseWheelLinesAccumulator, WLines * WHEEL_DELTA);
 | |
| 
 | |
|     case AnAction.Option of
 | |
|       emcoWheelScrollSystem:
 | |
|         begin
 | |
|           Result := Abs(WLines);
 | |
|         end;
 | |
|       emcoWheelScrollLines:
 | |
|         begin
 | |
|           Result := Abs(WClicks);
 | |
|           If Result = 0 then
 | |
|             exit;
 | |
|           if AnAction.Option2 > 0 then
 | |
|             Result := Result * AnAction.Option2;
 | |
|           if (Result > APageSize) then
 | |
|             Result := APageSize;
 | |
|           exit;
 | |
|         end;
 | |
|       emcoWheelScrollPages:
 | |
|           Result := Abs(WClicks) * APageSize;
 | |
|       emcoWheelScrollPagesLessOne:
 | |
|           Result := Abs(WClicks) * (APageSize - 1);
 | |
|       else
 | |
|         begin
 | |
|           Result := Abs(WLines);
 | |
|           exit;
 | |
|         end;
 | |
|     end;
 | |
| 
 | |
|     If Result = 0 then
 | |
|       exit;
 | |
| 
 | |
|     if AnAction.Option2 > 0 then
 | |
|       Result := MulDiv(Result, AnAction.Option2, 100);
 | |
|     if (Result > APageSize) then
 | |
|       Result := APageSize;
 | |
|     if (Result < 1) then
 | |
|       Result := 1;
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   ACommand: TSynEditorMouseCommand;
 | |
|   Handled: Boolean;
 | |
|   ClipHelper: TSynClipboardStream;
 | |
|   i, j: integer;
 | |
| begin
 | |
|   AnAction := nil;
 | |
|   Result := False;
 | |
|   while not Result do begin
 | |
|     AnAction := AnActionList.FindCommand(AnInfo, AnAction);
 | |
| 
 | |
|     if AnAction = nil then exit(False);
 | |
|     ACommand := AnAction.Command;
 | |
|     AnInfo.CaretDone := False;
 | |
| 
 | |
|     // Opening the context menu must not unset the block selection
 | |
|     // Therefore if a non persistent block is given, it shall ignore the caret move.
 | |
|     if (ACommand = emcContextMenu) and FBlockSelection.SelAvail and
 | |
|        not FBlockSelection.Persistent then
 | |
|     begin
 | |
|       case AnAction.Option of
 | |
|         emcoSelectionCaretMoveOutside:
 | |
|           AnInfo.CaretDone :=
 | |
|             (CompareCarets(AnInfo.NewCaret.LineBytePos, FBlockSelection.FirstLineBytePos) <= 0) and
 | |
|             (CompareCarets(AnInfo.NewCaret.LineBytePos, FBlockSelection.LastLineBytePos) >= 0);
 | |
|         emcoSelectionCaretMoveAlways:
 | |
|           AnInfo.CaretDone := False;
 | |
|         else
 | |
|           AnInfo.CaretDone := True;
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
|     // Plugins/External
 | |
|     Result := FMouseActionExecHandlerList.CallExecHandlers(AnAction, AnInfo);
 | |
|     // Gutter
 | |
|     if not Result then
 | |
|       Result := FLeftGutter.DoHandleMouseAction(AnAction, AnInfo);
 | |
|     if not Result then
 | |
|       Result := FRightGutter.DoHandleMouseAction(AnAction, AnInfo);
 | |
| 
 | |
|     if Result then begin
 | |
|       if (not AnInfo.CaretDone) and AnAction.MoveCaret then
 | |
|         MoveCaret;
 | |
|       if (AnAction.IgnoreUpClick) then
 | |
|         AnInfo.IgnoreUpClick := True;
 | |
|       exit;
 | |
|     end;
 | |
| 
 | |
|     Result := True;
 | |
|     CaretDone := AnInfo.CaretDone;
 | |
|     MouseCapture := False;
 | |
| 
 | |
|     if (ACommand = emcWheelScrollDown)
 | |
|     then begin
 | |
|       // sroll dependant on visible scrollbar / or not at all
 | |
|       if  (fStateFlags * [sfVertScrollbarVisible, sfHorizScrollbarVisible] = [sfHorizScrollbarVisible])
 | |
|       then ACommand := emcWheelHorizScrollDown
 | |
|       else ACommand := emcWheelVertScrollDown;
 | |
|     end;
 | |
| 
 | |
|     if (ACommand = emcWheelScrollUp)
 | |
|     then begin
 | |
|       // sroll dependant on visible scrollbar / or not at all
 | |
|       if  (fStateFlags * [sfVertScrollbarVisible, sfHorizScrollbarVisible] = [sfHorizScrollbarVisible])
 | |
|       then ACommand := emcWheelHorizScrollUp
 | |
|       else ACommand := emcWheelVertScrollUp;
 | |
|     end;
 | |
| 
 | |
|     case ACommand of
 | |
|       emcNone: ; // do nothing, but result := true
 | |
|       emcStartSelections, emcStartColumnSelections, emcStartLineSelections:
 | |
|         begin
 | |
|           FBlockSelection.AutoExtend := AnAction.Option = emcoSelectionContinue;
 | |
|           FCaret.ChangeOnTouch;
 | |
|           MoveCaret;
 | |
|           case ACommand of
 | |
|             emcStartColumnSelections:
 | |
|               FMouseSelectionMode := smColumn;
 | |
|             emcStartLineSelections:
 | |
|                 FMouseSelectionMode := smLine;
 | |
|             else
 | |
|               FMouseSelectionMode := FBlockSelection.SelectionMode;
 | |
|           end;
 | |
|           if (AnAction.Option = emcoSelectionContinue) then begin
 | |
|             // only set ActiveSelectionMode if we continue an existing selection
 | |
|             // Otherwise we are just setting the caret, selection will start on mouse move
 | |
|             FBlockSelection.ActiveSelectionMode := FMouseSelectionMode;
 | |
|             Include(fStateFlags, sfMouseDoneSelecting);
 | |
|           end;
 | |
|           MouseCapture := True;
 | |
|           Include(fStateFlags, sfWaitForMouseSelecting);
 | |
|         end;
 | |
|       emcSelectWord:
 | |
|         begin
 | |
|           if AnAction.MoveCaret then
 | |
|             MoveCaret;
 | |
|           SetWordBlock(AnInfo.NewCaret.LineBytePos);
 | |
|           MouseCapture := FALSE;
 | |
|         end;
 | |
|       emcSelectLine:
 | |
|         begin
 | |
|           if AnAction.MoveCaret then
 | |
|             MoveCaret;
 | |
|           SetLineBlock(AnInfo.NewCaret.LineBytePos, AnAction.Option = emcoSelectLineFull);
 | |
|           MouseCapture := FALSE;
 | |
|         end;
 | |
|       emcSelectPara:
 | |
|         begin
 | |
|           if AnAction.MoveCaret then
 | |
|             MoveCaret;
 | |
|           SetParagraphBlock(AnInfo.NewCaret.LineBytePos);
 | |
|           MouseCapture := FALSE;
 | |
|         end;
 | |
|       emcStartDragMove:
 | |
|         begin
 | |
|           if SelAvail and (SelectionMode = smNormal) then begin
 | |
|             Include(fStateFlags, sfWaitForDragging);
 | |
|             MouseCapture := True;
 | |
|           end
 | |
|           else
 | |
|             Result := False; // Currently only drags smNormal
 | |
|         end;
 | |
|       emcPasteSelection:
 | |
|         begin
 | |
|           ClipHelper := TSynClipboardStream.Create;
 | |
|           try
 | |
|             ClipHelper.ReadFromClipboard(PrimarySelection);
 | |
|             if ClipHelper.TextP <> nil then begin
 | |
|               MoveCaret;
 | |
|               if (not FBlockSelection.Persistent) then
 | |
|                 FBlockSelection.Clear;
 | |
|               Result := PasteFromClipboardEx(ClipHelper);
 | |
|             end
 | |
|             else
 | |
|               Result := False;
 | |
|           finally
 | |
|             ClipHelper.Free;
 | |
|           end;
 | |
|         end;
 | |
|       emcMouseLink:
 | |
|         begin
 | |
|           if assigned(fMarkupCtrlMouse) and fMarkupCtrlMouse.IsMouseOverLink and
 | |
|              assigned(FOnClickLink)
 | |
|           then
 | |
|             FOnClickLink(Self, SynMouseButtonBackMap[AnInfo.Button], AnInfo.Shift, AnInfo.MouseX, AnInfo.MouseY)
 | |
|           else
 | |
|             Result := False;
 | |
|         end;
 | |
|       emcContextMenu:
 | |
|         begin
 | |
|           Handled := False;
 | |
|           if AnAction.MoveCaret and (not CaretDone) then begin
 | |
|             MoveCaret;
 | |
|           end;
 | |
|           inherited DoContextPopup(Point(AnInfo.MouseX, AnInfo.MouseY), Handled);
 | |
|           // Open PopUpMenu after DecPaintlock
 | |
|           if not Handled then
 | |
|             FMouseClickDoPopUp := True;
 | |
|         end;
 | |
|       emcSynEditCommand:
 | |
|         begin
 | |
|           if AnAction.MoveCaret then
 | |
|             MoveCaret;
 | |
|           CommandProcessor(AnAction.Option, #0, nil);
 | |
|         end;
 | |
|       emcWheelHorizScrollDown, emcWheelHorizScrollUp:
 | |
|         begin
 | |
|           i := GetWheelScrollAmount(CharsInWindow);
 | |
|           if ACommand = emcWheelHorizScrollUp then i := -i;
 | |
|           if i <> 0 then LeftChar := LeftChar + i;
 | |
|         end;
 | |
|       emcWheelVertScrollDown, emcWheelVertScrollUp:
 | |
|         begin
 | |
|           i := GetWheelScrollAmount(LinesInWindow);
 | |
|           if ACommand = emcWheelVertScrollUp then i := -i;
 | |
|           if i <> 0 then TopView := TopView + i;
 | |
|         end;
 | |
|       emcWheelZoomOut, emcWheelZoomIn:
 | |
|         begin
 | |
|           if ( (ACommand = emcWheelZoomOut) and (abs(Font.Height) < 3) ) or
 | |
|              ( (ACommand = emcWheelZoomIn) and (abs(Font.Height) > 50) )
 | |
|           then begin
 | |
|             Result := False;
 | |
|           end
 | |
|           else begin
 | |
|             j := 1;
 | |
|             if ACommand = emcWheelZoomIn then j := -1;
 | |
|             i := FLastSetFontSize;
 | |
|             if Font.Height < 0
 | |
|             then Font.Height := Font.Height + j
 | |
|             else Font.Height := Font.Height - j;
 | |
|             FLastSetFontSize := i;
 | |
|           end;
 | |
|         end;
 | |
|       emcWheelZoomNorm:
 | |
|         begin
 | |
|           Font.Height := FLastSetFontSize;
 | |
|         end;
 | |
|       else
 | |
|         Result := False; // ACommand was not handled => Fallback to parent Context
 | |
|     end;
 | |
| 
 | |
|     if Result and (not CaretDone) and AnAction.MoveCaret then
 | |
|       MoveCaret;
 | |
|     if Result and (AnAction.IgnoreUpClick) then
 | |
|       AnInfo.IgnoreUpClick := True;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetColor(Value: TColor);
 | |
| begin
 | |
|   inherited SetColor(Value);
 | |
|   FPaintArea.BackgroundColor := Color;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.FindAndHandleMouseAction(AButton: TSynMouseButton;
 | |
|   AShift: TShiftState; X, Y: Integer; ACCount:TSynMAClickCount;
 | |
|   ADir: TSynMAClickDir; AWheelDelta: Integer = 0);
 | |
| var
 | |
|   Info: TSynEditMouseActionInfo;
 | |
| begin
 | |
|   FInternalCaret.AssignFrom(FCaret);
 | |
|   FInternalCaret.LineCharPos := PixelsToRowColumn(Point(X,Y));
 | |
|   with Info do begin
 | |
|     NewCaret := FInternalCaret;
 | |
|     Button := AButton;
 | |
|     Shift := AShift;
 | |
|     MouseX := X;
 | |
|     MouseY := Y;
 | |
|     WheelDelta := AWheelDelta;
 | |
|     CCount := ACCount;
 | |
|     Dir := ADir;
 | |
|     IgnoreUpClick := False;
 | |
|   end;
 | |
|   try
 | |
|     // Check plugins/external handlers
 | |
|     if FMouseActionSearchHandlerList.CallSearchHandlers(Info,
 | |
|                                          {$IFDEF FPC}@{$ENDIF}DoHandleMouseAction)
 | |
|     then
 | |
|       exit;
 | |
| 
 | |
|     if FLeftGutter.Visible and (X < FLeftGutter.Width) then begin
 | |
|       // mouse event occured in Gutter ?
 | |
|       if FLeftGutter.MaybeHandleMouseAction(Info, {$IFDEF FPC}@{$ENDIF}DoHandleMouseAction) then
 | |
|         exit;
 | |
|     end
 | |
|     else
 | |
|     if FRightGutter.Visible and (X > ClientWidth - FRightGutter.Width) then begin
 | |
|       // mouse event occured in Gutter ?
 | |
|       if FRightGutter.MaybeHandleMouseAction(Info, {$IFDEF FPC}@{$ENDIF}DoHandleMouseAction) then
 | |
|         exit;
 | |
|     end
 | |
|     else
 | |
|     begin
 | |
|       // mouse event occured in selected block ?
 | |
|       if SelAvail and (X >= FTextArea.Bounds.Left) and (X < FTextArea.Bounds.Right) and
 | |
|          (Y >= FTextArea.Bounds.Top) and (Y < FTextArea.Bounds.Bottom) and
 | |
|          IsPointInSelection(FInternalCaret.LineBytePos)
 | |
|       then
 | |
|         if DoHandleMouseAction(FMouseSelActions.GetActionsForOptions(FMouseOptions), Info) then
 | |
|           exit;
 | |
|       // mouse event occured in text?
 | |
|       if DoHandleMouseAction(FMouseTextActions.GetActionsForOptions(FMouseOptions), Info) then
 | |
|         exit;
 | |
|     end;
 | |
| 
 | |
|     DoHandleMouseAction(FMouseActions.GetActionsForOptions(FMouseOptions), Info);
 | |
|   finally
 | |
|     if Info.IgnoreUpClick then
 | |
|       include(fStateFlags, sfIgnoreUpClick);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
 | |
|   X, Y: Integer);
 | |
| var
 | |
|   CType: TSynMAClickCount;
 | |
| begin
 | |
|   //DebugLn(['TCustomSynEdit.MouseDown START Mouse=',X,',',Y,' Caret=',CaretX,',',CaretY,', BlockBegin=',BlockBegin.X,',',BlockBegin.Y,' BlockEnd=',BlockEnd.X,',',BlockEnd.Y]);
 | |
|   Exclude(FStateFlags, sfHideCursor);
 | |
|   FInMouseClickEvent := True;
 | |
| 
 | |
|   if FMouseDownEventList <> nil then
 | |
|     FMouseDownEventList.CallMouseDownHandlers(Self, Button, Shift, X, Y);
 | |
| 
 | |
|   if (X>=ClientWidth-ScrollBarWidth) or (Y>=ClientHeight-ScrollBarWidth) then
 | |
|   begin
 | |
|     inherited MouseDown(Button, Shift, X, Y);
 | |
|     exit;
 | |
|   end;
 | |
| 
 | |
|   LastMouseCaret:=PixelsToRowColumn(Point(X,Y));
 | |
|   fMouseDownX := X;
 | |
|   fMouseDownY := Y;
 | |
| 
 | |
|   fStateFlags := fStateFlags - [sfDblClicked, sfTripleClicked, sfQuadClicked,
 | |
|                                 sfLeftGutterClick, sfRightGutterClick,
 | |
|                                 sfWaitForMouseSelecting, sfMouseSelecting, sfMouseDoneSelecting,
 | |
|                                 sfWaitForDragging, sfIgnoreUpClick
 | |
|                                ];
 | |
| 
 | |
|   if ssQuad in Shift then begin
 | |
|     CType := ccQuad;
 | |
|     Include(fStateFlags, sfQuadClicked);
 | |
|   end
 | |
|   else if ssTriple in Shift then begin
 | |
|     CType := ccTriple;
 | |
|     Include(fStateFlags, sfTripleClicked);
 | |
|   end
 | |
|   else if ssDouble in Shift then begin
 | |
|     CType := ccDouble;
 | |
|     Include(fStateFlags, sfDblClicked);
 | |
|   end
 | |
|   else
 | |
|     CType := ccSingle;
 | |
| 
 | |
|   FMouseClickDoPopUp := False;
 | |
|   IncPaintLock;
 | |
|   try
 | |
|     if (X < TextLeftPixelOffset(False)) then begin
 | |
|       Include(fStateFlags, sfLeftGutterClick);
 | |
|       FLeftGutter.MouseDown(Button, Shift, X, Y);
 | |
|     end;
 | |
|     if (X > ClientWidth - TextRightPixelOffset - ScrollBarWidth) then begin
 | |
|       Include(fStateFlags, sfRightGutterClick);
 | |
|       FRightGutter.MouseDown(Button, Shift, X, Y);
 | |
|     end;
 | |
|     FindAndHandleMouseAction(SynMouseButtonMap[Button], Shift, X, Y, CType, cdDown);
 | |
|   finally
 | |
|     DecPaintLock;
 | |
|   end;
 | |
|   if FMouseClickDoPopUp and (PopupMenu <> nil) then begin
 | |
|     PopupMenu.PopupComponent:=self;
 | |
|     PopupMenu.PopUp;
 | |
|   end;
 | |
| 
 | |
|   inherited MouseDown(Button, Shift, X, Y);
 | |
|   LCLIntf.SetFocus(Handle);
 | |
|   UpdateCaret;
 | |
|   SelAvailChange(nil);
 | |
|   //debugln('TCustomSynEdit.MouseDown END sfWaitForDragging=',dbgs(sfWaitForDragging in fStateFlags),' ');
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MouseMove(Shift: TShiftState; X, Y: Integer);
 | |
| begin
 | |
|   Exclude(FStateFlags, sfHideCursor);
 | |
|   inherited MouseMove(Shift, x, y);
 | |
|   if (sfLeftGutterClick in fStateFlags) then
 | |
|     FLeftGutter.MouseMove(Shift, X, Y);
 | |
|   if (sfRightGutterClick in fStateFlags) then
 | |
|     FRightGutter.MouseMove(Shift, X, Y);
 | |
| 
 | |
|   FLastMousePoint := Point(X,Y);
 | |
|   LastMouseCaret := PixelsToRowColumn(Point(X,Y));
 | |
|   UpdateCursor;
 | |
| 
 | |
|   if (sfWaitForMouseSelecting in fStateFlags) and MouseCapture and
 | |
|      ( (abs(fMouseDownX-X) >= MinMax(CharWidth div 2, 2, 4)) or
 | |
|        (abs(fMouseDownY-Y) >= MinMax(LineHeight div 2, 2, 4)) )
 | |
|   then
 | |
|     FStateFlags := FStateFlags - [sfWaitForMouseSelecting] + [sfMouseSelecting];
 | |
| 
 | |
|   //debugln('TCustomSynEdit.MouseMove sfWaitForDragging=',dbgs(sfWaitForDragging in fStateFlags),' MouseCapture=',dbgs(MouseCapture),' GetCaptureControl=',DbgSName(GetCaptureControl));
 | |
|   if MouseCapture and (sfWaitForDragging in fStateFlags) then begin
 | |
|     if (Abs(fMouseDownX - X) >= GetSystemMetrics(SM_CXDRAG))
 | |
|       or (Abs(fMouseDownY - Y) >= GetSystemMetrics(SM_CYDRAG))
 | |
|     then begin
 | |
|       FStateFlags := FStateFlags
 | |
|                    -[sfWaitForDragging, sfWaitForMouseSelecting, sfMouseSelecting]
 | |
|                    + [sfIsDragging];
 | |
|       //debugln('TCustomSynEdit.MouseMove BeginDrag');
 | |
|       BeginDrag(true);
 | |
|     end;
 | |
|   end
 | |
|   else
 | |
|   if (fStateFlags * [sfMouseSelecting, sfIsDragging] <> []) and MouseCapture
 | |
|   then begin
 | |
|     //DebugLn(' TCustomSynEdit.MouseMove CAPTURE Mouse=',dbgs(X),',',dbgs(Y),' Caret=',dbgs(CaretXY),', BlockBegin=',dbgs(BlockBegin),' BlockEnd=',dbgs(BlockEnd));
 | |
|     if sfIsDragging in fStateFlags then
 | |
|       FBlockSelection.IncPersistentLock;
 | |
|     FInternalCaret.AssignFrom(FCaret);
 | |
|     FInternalCaret.LineCharPos := PixelsToRowColumn(Point(X,Y));
 | |
| 
 | |
|     // compare to Bounds => Padding area does not scroll
 | |
|     if ( (X >= FTextArea.Bounds.Left)   or (LeftChar <= 1) ) and
 | |
|        ( (X <  FTextArea.Bounds.Right)  or (LeftChar >= CurrentMaxLeftChar) ) and
 | |
|        ( (Y >= FTextArea.Bounds.Top)    or (TopView <= 1) ) and
 | |
|        ( (Y <  FTextArea.Bounds.Bottom) or (TopView >= CurrentMaxTopView) )
 | |
|     then begin
 | |
|       if (sfMouseSelecting in fStateFlags) and not FInternalCaret.IsAtPos(FCaret) then
 | |
|         Include(fStateFlags, sfMouseDoneSelecting);
 | |
|       FBlockSelection.AutoExtend := sfMouseSelecting in fStateFlags;
 | |
|       FCaret.LineBytePos := FInternalCaret.LineBytePos;
 | |
|       FBlockSelection.AutoExtend := False;
 | |
|     end
 | |
|     else begin
 | |
|       // begin scrolling?
 | |
|       if X < FTextArea.Bounds.Left then
 | |
|         FScrollDeltaX := Min((X - FTextArea.Bounds.Left - CharWidth) div CharWidth, -1)
 | |
|       else if x >= FTextArea.Bounds.Right then
 | |
|         FScrollDeltaX := Max((X - FTextArea.Bounds.Right + 1 + CharWidth) div CharWidth, 1)
 | |
|       else
 | |
|         FScrollDeltaX := 0;
 | |
| 
 | |
|       if Y < FTextArea.Bounds.Top then
 | |
|         FScrollDeltaY := Min((Y - FTextArea.Bounds.Top - LineHeight) div LineHeight, -1)
 | |
|       else if Y >= FTextArea.Bounds.Bottom then
 | |
|         FScrollDeltaY := Max((Y - FTextArea.Bounds.Bottom + 1 + LineHeight) div LineHeight, 1)
 | |
|       else
 | |
|         FScrollDeltaY := 0;
 | |
| 
 | |
|       fScrollTimer.Enabled := (fScrollDeltaX <> 0) or (fScrollDeltaY <> 0);
 | |
|       if (sfMouseSelecting in fStateFlags) and ((fScrollDeltaX <> 0) or (fScrollDeltaY <> 0)) then
 | |
|         Include(fStateFlags, sfMouseDoneSelecting);
 | |
|     end;
 | |
|     if sfMouseDoneSelecting in fStateFlags then
 | |
|       FBlockSelection.ActiveSelectionMode := FMouseSelectionMode;
 | |
|     if sfIsDragging in fStateFlags then
 | |
|       FBlockSelection.DecPersistentLock;
 | |
|   end
 | |
|   else
 | |
|   if MouseCapture and (fStateFlags * [sfIsDragging, sfWaitForMouseSelecting] = [])
 | |
|   then begin
 | |
|     MouseCapture:=false;
 | |
|     fScrollTimer.Enabled := False;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ScrollTimerHandler(Sender: TObject);
 | |
| var
 | |
|   C: TPoint;
 | |
|   CurMousePos: TPoint;
 | |
|   X, Y: Integer;
 | |
| begin
 | |
|   // changes to line / column in one go
 | |
|   if sfIsDragging in fStateFlags then
 | |
|     FBlockSelection.IncPersistentLock;
 | |
|   DoIncPaintLock(Self); // No editing is taking place
 | |
|   try
 | |
|     CurMousePos:=Point(0,0);
 | |
|     GetCursorPos(CurMousePos);
 | |
|     CurMousePos:=ScreenToClient(CurMousePos);
 | |
|     C := PixelsToLogicalPos(CurMousePos);
 | |
| 
 | |
|     // recalculate scroll deltas
 | |
|     if CurMousePos.X < FTextArea.Bounds.Left then
 | |
|       FScrollDeltaX := Min((CurMousePos.X - FTextArea.Bounds.Left - CharWidth) div CharWidth, -1)
 | |
|     else if CurMousePos.x >= FTextArea.Bounds.Right then
 | |
|       FScrollDeltaX := Max((CurMousePos.X - FTextArea.Bounds.Right + 1 + CharWidth) div CharWidth, 1)
 | |
|     else
 | |
|       FScrollDeltaX := 0;
 | |
| 
 | |
|     if CurMousePos.Y < FTextArea.Bounds.Top then
 | |
|       FScrollDeltaY := Min((CurMousePos.Y - FTextArea.Bounds.Top - LineHeight) div LineHeight, -1)
 | |
|     else if CurMousePos.Y >= FTextArea.Bounds.Bottom then
 | |
|       FScrollDeltaY := Max((CurMousePos.Y - FTextArea.Bounds.Bottom + 1 + LineHeight) div LineHeight, 1)
 | |
|     else
 | |
|       FScrollDeltaY := 0;
 | |
| 
 | |
|     fScrollTimer.Enabled := (fScrollDeltaX <> 0) or (fScrollDeltaY <> 0);
 | |
|     // now scroll
 | |
|     if fScrollDeltaX <> 0 then begin
 | |
|       LeftChar := LeftChar + fScrollDeltaX;
 | |
|       X := LeftChar;
 | |
|       if fScrollDeltaX > 0 then  // scrolling right?
 | |
|         Inc(X, CharsInWindow);
 | |
|       FCaret.LineCharPos := Point(X, C.Y);
 | |
|       if (not(sfIsDragging in fStateFlags)) then
 | |
|         SetBlockEnd(LogicalCaretXY);
 | |
|     end;
 | |
|     if fScrollDeltaY <> 0 then begin
 | |
|       if GetKeyState(VK_SHIFT) < 0 then
 | |
|         TopView := TopView + fScrollDeltaY * LinesInWindow
 | |
|       else
 | |
|         TopView := TopView + fScrollDeltaY;
 | |
|       if fScrollDeltaY > 0
 | |
|       then Y := FFoldedLinesView.TextIndex[LinesInWindow-1]+1  // scrolling down
 | |
|       else Y := TopLine;  // scrolling up
 | |
|       if Y < 1   // past end of file
 | |
|       then y := FCaret.LinePos;
 | |
|       FCaret.LineCharPos := Point(C.X, Y);
 | |
|       if (not(sfIsDragging in fStateFlags)) then
 | |
|         SetBlockEnd(LogicalCaretXY);
 | |
|     end;
 | |
|   finally
 | |
|     DoDecPaintLock(Self);
 | |
|     if sfIsDragging in fStateFlags then
 | |
|       FBlockSelection.DecPersistentLock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoContextPopup(MousePos: TPoint; var Handled: Boolean);
 | |
| begin
 | |
|   Handled := FInMouseClickEvent;
 | |
|   if not Handled then
 | |
|     Exclude(FStateFlags, sfHideCursor);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
 | |
|   X, Y: Integer);
 | |
| var
 | |
|   wasDragging, wasSelecting, ignoreUp : Boolean;
 | |
|   CType: TSynMAClickCount;
 | |
| begin
 | |
|   Exclude(FStateFlags, sfHideCursor);
 | |
| //DebugLn('TCustomSynEdit.MouseUp Mouse=',X,',',Y,' Caret=',CaretX,',',CaretY,', BlockBegin=',BlockBegin.X,',',BlockBegin.Y,' BlockEnd=',BlockEnd.X,',',BlockEnd.Y);
 | |
|   FInMouseClickEvent := True;
 | |
|   wasDragging := (sfIsDragging in fStateFlags);
 | |
|   wasSelecting := (sfMouseDoneSelecting in fStateFlags);
 | |
|   ignoreUp := (sfIgnoreUpClick in fStateFlags);
 | |
|   Exclude(fStateFlags, sfIsDragging);
 | |
|   Exclude(fStateFlags, sfWaitForMouseSelecting);
 | |
|   Exclude(fStateFlags, sfMouseSelecting);
 | |
|   Exclude(fStateFlags, sfMouseDoneSelecting);
 | |
|   Exclude(fStateFlags, sfIgnoreUpClick);
 | |
|   fScrollTimer.Enabled := False;
 | |
|   inherited MouseUp(Button, Shift, X, Y);
 | |
|   MouseCapture := False;
 | |
| 
 | |
|   if sfQuadClicked in fStateFlags then begin
 | |
|     CType := ccQuad;
 | |
|     Include(fStateFlags, sfQuadClicked);
 | |
|   end
 | |
|   else if sfTripleClicked in fStateFlags then begin
 | |
|     CType := ccTriple;
 | |
|     Include(fStateFlags, sfTripleClicked);
 | |
|   end
 | |
|   else if sfDblClicked in fStateFlags then begin
 | |
|     CType := ccDouble;
 | |
|     Include(fStateFlags, sfDblClicked);
 | |
|   end
 | |
|   else
 | |
|     CType := ccSingle;
 | |
|   fStateFlags:=fStateFlags - [sfDblClicked,sfTripleClicked,sfQuadClicked];
 | |
| 
 | |
|   if sfWaitForDragging in fStateFlags then
 | |
|   begin
 | |
|     ComputeCaret(X, Y);
 | |
|     SetBlockBegin(LogicalCaretXY);
 | |
|     SetBlockEnd(LogicalCaretXY);
 | |
|     Exclude(fStateFlags, sfWaitForDragging);
 | |
|   end;
 | |
| 
 | |
|   if (X>=ClientWidth-ScrollBarWidth) or (Y>=ClientHeight-ScrollBarWidth) then
 | |
|     exit;
 | |
|   LastMouseCaret:=PixelsToRowColumn(Point(X,Y));
 | |
| 
 | |
|   if wasDragging or wasSelecting or ignoreUp then exit;
 | |
| 
 | |
|   FMouseClickDoPopUp := False;
 | |
|   IncPaintLock;
 | |
|   try
 | |
|     if (sfLeftGutterClick in fStateFlags) then begin
 | |
|       FLeftGutter.MouseUp(Button, Shift, X, Y);
 | |
|       Exclude(fStateFlags, sfLeftGutterClick);
 | |
|     end;
 | |
|     if (sfRightGutterClick in fStateFlags) then begin
 | |
|       FRightGutter.MouseUp(Button, Shift, X, Y);
 | |
|       Exclude(fStateFlags, sfRightGutterClick);
 | |
|     end;
 | |
|     FindAndHandleMouseAction(SynMouseButtonMap[Button], Shift, X, Y, CType, cdUp);
 | |
|   finally
 | |
|     DecPaintLock;
 | |
|   end;
 | |
|   if FMouseClickDoPopUp and (PopupMenu <> nil) then begin
 | |
|     PopupMenu.PopupComponent:=self;
 | |
|     PopupMenu.PopUp;
 | |
|   end;
 | |
|   SelAvailChange(nil);
 | |
|   //DebugLn('TCustomSynEdit.MouseUp END Mouse=',X,',',Y,' Caret=',CaretX,',',CaretY,', BlockBegin=',BlockBegin.X,',',BlockBegin.Y,' BlockEnd=',BlockEnd.X,',',BlockEnd.Y);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.Paint;
 | |
| var
 | |
|   rcClip: TRect;
 | |
| begin
 | |
|   // Get the invalidated rect. Compute the invalid area in lines / columns.
 | |
|   rcClip := Canvas.ClipRect;
 | |
| 
 | |
|   If FPaintLock > 0 then begin
 | |
|     debugln(['Warning: SynEdit.Paint called during PaintLock']);
 | |
|     {$IFDEF SynCheckPaintLock}
 | |
|     DumpStack;
 | |
|     {$ENDIF}
 | |
|     // Ensure this will be repainted after PaintLock
 | |
|     if FInvalidateRect.Top < 0 then
 | |
|       FInvalidateRect := rcClip
 | |
|     else
 | |
|       types.UnionRect(FInvalidateRect, FInvalidateRect, rcClip);
 | |
|     // Just paint the background
 | |
|     SetBkColor(Canvas.Handle, ColorToRGB(Color));
 | |
|     InternalFillRect(Canvas.Handle, rcClip);
 | |
|     if rcClip.Left <= TextLeftPixelOffset(False) then begin
 | |
|       rcClip.Right := TextLeftPixelOffset(False)+1;
 | |
|       SetBkColor(Canvas.Handle, ColorToRGB(FLeftGutter.Color));
 | |
|       InternalFillRect(Canvas.Handle, rcClip);
 | |
|     end;
 | |
|     exit;
 | |
|   end;
 | |
| 
 | |
|   {$IFDEF EnableDoubleBuf}
 | |
|   //rcClip:=Rect(0,0,ClientWidth,ClientHeight);
 | |
|   StartPaintBuffer(rcClip);
 | |
|   {$ENDIF}
 | |
|   {$IFDEF SYNSCROLLDEBUG}
 | |
|   debugln(['PAINT ',DbgSName(self),' sfHasScrolled=',dbgs(sfHasScrolled in fStateFlags),' rect=',dbgs(rcClip)]);
 | |
|   {$ENDIF}
 | |
| 
 | |
|   Include(fStateFlags,sfPainting);
 | |
|   Exclude(fStateFlags, sfHasScrolled);
 | |
|   // Now paint everything while the caret is hidden.
 | |
|   FScreenCaret.Hide;
 | |
|   try
 | |
|     FPaintArea.Paint(Canvas, rcClip);
 | |
|     DoOnPaint;
 | |
|   finally
 | |
|     {$IFDEF EnableDoubleBuf}
 | |
|     EndPaintBuffer(rcClip);
 | |
|     {$ENDIF}
 | |
|     UpdateCaret;
 | |
|     Exclude(fStateFlags,sfPainting);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.CodeFoldAction(iLine: integer);
 | |
| // iLine is 1 based as parameter
 | |
| begin
 | |
|   if (iLine<=0) or (iLine>FTheLinesView.Count) then exit;
 | |
|   dec(iLine);
 | |
| //DebugLn(['****** FoldAction at ',iLine,' scrline=',FFoldedLinesView.TextIndexToScreenLine(iLine), ' type ', SynEditCodeFoldTypeNames[FFoldedLinesView.FoldType[FFoldedLinesView.TextIndexToScreenLine(iLine)]],  '  view topline=',FFoldedLinesView.TopLine  ]);
 | |
|   if FFoldedLinesView.FoldType[FFoldedLinesView.TextIndexToScreenLine(iLine)]
 | |
|      * [cfCollapsedFold, cfCollapsedHide] <> []
 | |
|   then
 | |
|     FFoldedLinesView.UnFoldAtTextIndex(iLine)
 | |
|   else
 | |
|   if FFoldedLinesView.FoldType[FFoldedLinesView.TextIndexToScreenLine(iLine)]
 | |
|      * [cfFoldStart] <> []
 | |
|   then
 | |
|     FFoldedLinesView.FoldAtTextIndex(iLine);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.FindNextUnfoldedLine(iLine: integer; Down: boolean
 | |
|   ): Integer;
 | |
| // iLine is 1 based
 | |
| begin
 | |
|   Result:=iLine;
 | |
|   if Down then
 | |
|     while (Result<FTheLinesView.Count) and (FFoldedLinesView.FoldedAtTextIndex[Result-1]) do
 | |
|       inc(Result)
 | |
|   else
 | |
|     while (Result>1) and (FFoldedLinesView.FoldedAtTextIndex[Result-1]) do
 | |
|       dec(Result);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.CreateGutter(AOwner : TSynEditBase; ASide: TSynGutterSide;
 | |
|   ATextDrawer: TheTextDrawer): TSynGutter;
 | |
| begin
 | |
|   Result := TSynGutter.Create(AOwner, ASide, ATextDrawer);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnfoldAll;
 | |
| begin
 | |
|   FFoldedLinesView.UnfoldAll;
 | |
|   Invalidate;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.FoldAll(StartLevel : Integer = 0; IgnoreNested : Boolean = False);
 | |
| begin
 | |
|   FFoldedLinesView.FoldAll(StartLevel, IgnoreNested);
 | |
|   Invalidate;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.StartPaintBuffer(const ClipRect: TRect);
 | |
| {$IFDEF EnableDoubleBuf}
 | |
| var
 | |
|   NewBufferWidth: Integer;
 | |
|   NewBufferHeight: Integer;
 | |
| {$ENDIF}
 | |
| begin
 | |
|   {$IFDEF EnableDoubleBuf}
 | |
|   if (SavedCanvas<>nil) then RaiseGDBException('');
 | |
|   if BufferBitmap=nil then
 | |
|     BufferBitmap:=TBitmap.Create;
 | |
|   NewBufferWidth:=BufferBitmap.Width;
 | |
|   NewBufferHeight:=BufferBitmap.Height;
 | |
|   if NewBufferWidth<ClipRect.Right then
 | |
|     NewBufferWidth:=ClipRect.Right;
 | |
|   if NewBufferHeight<ClipRect.Bottom then
 | |
|     NewBufferHeight:=ClipRect.Bottom;
 | |
|   BufferBitmap.Width:=NewBufferWidth;
 | |
|   BufferBitmap.Height:=NewBufferHeight;
 | |
|   SavedCanvas:=Canvas;
 | |
|   Canvas:=BufferBitmap.Canvas;
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.EndPaintBuffer(const ClipRect: TRect);
 | |
| begin
 | |
|   {$IFDEF EnableDoubleBuf}
 | |
|   if (SavedCanvas=nil) then RaiseGDBException('');
 | |
|   if not (SavedCanvas is TControlCanvas) then RaiseGDBException('');
 | |
|   Canvas:=SavedCanvas;
 | |
|   SavedCanvas:=nil;
 | |
|   Canvas.CopyRect(ClipRect,BufferBitmap.Canvas,ClipRect);
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.NextWordLogicalPos(ABoundary: TLazSynWordBoundary;
 | |
|   WordEndForDelete: Boolean): TPoint;
 | |
| var
 | |
|   i, j, CX, CY, NX, OX, LineLen: integer;
 | |
|   Line, ULine: string;
 | |
|   CWidth: TPhysicalCharWidths;
 | |
|   r, InclCurrent: Boolean;
 | |
| begin
 | |
|   Result := LogicalCaretXY;
 | |
|   CX := Result.X;
 | |
|   CY := Result.Y;
 | |
| 
 | |
|   if (CY < 1) then begin
 | |
|     Result.X := 1;
 | |
|     Result.Y := 1;
 | |
|     exit;
 | |
|   end;
 | |
|   i := FTheLinesView.Count;
 | |
|   if (CY > i) or ((CY = i) and (CX > length(FTheLinesView[i-1]))) then begin
 | |
|     Result.Y := i;
 | |
|     Result.X := length(FTheLinesView[Result.Y-1]) + 1;
 | |
|     exit;
 | |
|   end;
 | |
| 
 | |
|   Line := FTheLinesView[CY - 1];
 | |
|   InclCurrent := False;
 | |
|   LineLen := Length(Line);
 | |
|   if CX > LineLen then begin
 | |
|     Line := FTheLinesView[CY];
 | |
|     LineLen := Length(Line);
 | |
|     Inc(CY);
 | |
|     CX := 1;
 | |
|     InclCurrent := True;
 | |
|   end;
 | |
| 
 | |
|   case ABoundary of
 | |
|     swbWordBegin: begin
 | |
|         CX := WordBreaker.NextWordStart(Line,  CX, InclCurrent);
 | |
|         if (CX <= 0) and not InclCurrent then CX := LineLen + 1;
 | |
|         if (CX <= 0) and InclCurrent then CX := 1;
 | |
|       end;
 | |
|     swbWordEnd: begin
 | |
|         CX := WordBreaker.NextWordEnd(Line,  CX);
 | |
|         if (CX <= 0) then CX := LineLen + 1;
 | |
|       end;
 | |
|     swbTokenBegin: begin
 | |
|         if not (   InclCurrent and
 | |
|                    ((CX <= 1) or (Line[CX-1] in FWordBreaker.WhiteChars)) and
 | |
|                    ((CX > LineLen) or not(Line[CX] in FWordBreaker.WhiteChars))   )
 | |
|         then
 | |
|           CX := WordBreaker.NextBoundary(Line,  CX);
 | |
|         if (CX > 0) and (CX <= LineLen) and (Line[CX] in FWordBreaker.WhiteChars) then
 | |
|           CX := WordBreaker.NextBoundary(Line,  CX);
 | |
|         if (CX <= 0) then CX := LineLen + 1;
 | |
|       end;
 | |
|     swbTokenEnd: begin
 | |
|         CX := WordBreaker.NextBoundary(Line,  CX);
 | |
|         if (CX > 1) and (Line[CX-1] in FWordBreaker.WhiteChars) then
 | |
|           CX := WordBreaker.NextBoundary(Line,  CX);
 | |
|         if (CX <= 0) then CX := LineLen + 1;
 | |
|       end;
 | |
|     swbCaseChange: begin
 | |
|         NX := WordBreaker.NextWordStart(Line,  CX, InclCurrent);
 | |
|         if (NX <= 0) and not InclCurrent then NX := LineLen + 1;
 | |
|         if (NX <= 0) and InclCurrent then NX := 1;
 | |
| 
 | |
|         ULine := LazUTF8.UTF8UpperCase(Line);
 | |
|         CWidth := FTheLinesView.GetPhysicalCharWidths(CY - 1); // for utf 8
 | |
|         OX := CX;
 | |
|         i := Length(ULine);
 | |
|         // skip upper
 | |
|         While (CX < NX) and (CX <= i) do begin          // check entire next utf-8 char to be equal
 | |
|           r := (CX = OX) or (CX <= 1) or (Line[CX-1] <> '_') or ((CX <= i) and (Line[CX] = '_'));
 | |
|           j := CX;
 | |
|           repeat
 | |
|             r := r and (Line[j] = ULine[j]);
 | |
|             inc(j);
 | |
|           until (j > i) or (CWidth[j-1] <> 0);
 | |
|           if not r then break;
 | |
|           CX := j;
 | |
|         end;
 | |
|         // skip lowercase
 | |
|         ULine := LazUTF8.UTF8LowerCase(Line);
 | |
|         While (CX < NX) and (CX <= i) do begin          // check entire next utf-8 char to be equal
 | |
|           r := (CX = OX) or (CX <= 1) or (Line[CX-1] <> '_') or ((CX <= i) and (Line[CX] = '_'));
 | |
|           j := CX;
 | |
|           repeat
 | |
|             r := r and (Line[j] = ULine[j]);
 | |
|             inc(j);
 | |
|           until (j > i) or (CWidth[j-1] <> 0);
 | |
|           if not r then break;
 | |
|           CX := j;
 | |
|         end;
 | |
|       end;
 | |
|   end;
 | |
| 
 | |
|   Result := Point(CX, CY);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PrevWordLogicalPos(ABoundary: TLazSynWordBoundary): TPoint;
 | |
| 
 | |
|   procedure CheckLineStart(var CX, CY: integer);
 | |
|   var
 | |
|     Line: String;
 | |
|   begin
 | |
|     if CX <= 0 then
 | |
|       if CY > 1 then begin
 | |
|         // just position at the end of the previous line
 | |
|         // Todo skip spaces
 | |
|         Dec(CY);
 | |
|         Line := FTheLinesView[CY - 1];
 | |
|         CX := Length(Line) + 1;
 | |
|       end
 | |
|       else
 | |
|         CX := 1;
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   i, j, CX, CY, NX, OX: integer;
 | |
|   Line, ULine: string;
 | |
|   CWidth: TPhysicalCharWidths;
 | |
|   r: Boolean;
 | |
| begin
 | |
|   Result := LogicalCaretXY;
 | |
|   CX := Result.X;
 | |
|   CY := Result.Y;
 | |
| 
 | |
|   if (CY < 1) then begin
 | |
|     Result.X := 1;
 | |
|     Result.Y := 1;
 | |
|     exit;
 | |
|   end;
 | |
|   if (CY > FTheLinesView.Count) then begin
 | |
|     Result.Y := FTheLinesView.Count;
 | |
|     Result.X := length(FTheLinesView[Result.Y-1]) + 1;
 | |
|     exit;
 | |
|   end;
 | |
| 
 | |
|   Line := FTheLinesView[CY - 1];
 | |
| 
 | |
|   case ABoundary of
 | |
|     swbWordBegin: begin
 | |
|         CX := WordBreaker.PrevWordStart(Line,  Min(CX, Length(Line) + 1));
 | |
|         CheckLineStart(CX, CY);
 | |
|       end;
 | |
|     swbWordEnd: begin
 | |
|         CX := WordBreaker.PrevWordEnd(Line,  Min(CX, Length(Line) + 1));
 | |
|         CheckLineStart(CX, CY);
 | |
|       end;
 | |
|     swbTokenBegin: begin
 | |
|         CX := WordBreaker.PrevBoundary(Line,  Min(CX, Length(Line) + 1));
 | |
|         if (CX > 0) and (Line[CX] in FWordBreaker.WhiteChars) then
 | |
|           CX := WordBreaker.PrevBoundary(Line,  Min(CX, Length(Line) + 1));
 | |
|         if CX = 1 then CX := -1;
 | |
|         CheckLineStart(CX, CY);
 | |
|       end;
 | |
|     swbTokenEnd: begin
 | |
|         CX := WordBreaker.PrevBoundary(Line,  Min(CX, Length(Line) + 1));
 | |
|         if (CX > 1) and (Line[CX-1] in FWordBreaker.WhiteChars) then
 | |
|           CX := WordBreaker.PrevBoundary(Line,  Min(CX, Length(Line) + 1));
 | |
|         if CX = 1 then CX := -1;
 | |
|         CheckLineStart(CX, CY);
 | |
|       end;
 | |
|     swbCaseChange: begin
 | |
|         NX := WordBreaker.PrevWordStart(Line,  Min(CX, Length(Line) + 1));
 | |
| 
 | |
|         ULine := LazUTF8.UTF8LowerCase(Line);
 | |
|         CWidth := FTheLinesView.GetPhysicalCharWidths(CY - 1); // for utf 8
 | |
|         OX := CX;
 | |
|         i := Length(ULine);
 | |
|         if CX > i + 1 then CX := i + 1;
 | |
|         // skip lowercase
 | |
|         While (CX > NX) and (CX - 1 > 0) do begin          // check entire previous utf-8 char to be equal
 | |
|           r := (CX = OX) or (Line[CX - 1] <> '_') or ((CX <= i) and (Line[CX] = '_'));
 | |
|           j := CX;
 | |
|           repeat
 | |
|             dec(j);
 | |
|             r := r and (Line[j] = ULine[j]);
 | |
|           until (j < 1) or (CWidth[j-1] <> 0);
 | |
|           if not r then break;
 | |
|           CX := j;
 | |
|         end;
 | |
|         // skip upper
 | |
|         While (CX > NX) and (CX - 1 > 0) do begin          // check entire previous utf-8 char to be not equal
 | |
|           j := CX;
 | |
|           r := true;
 | |
|           repeat
 | |
|             dec(j);
 | |
|             r := r and (Line[j] = ULine[j]);
 | |
|           until (j < 1) or (CWidth[j-1] <> 0);
 | |
|           r := r or not( (CX = OX) or (Line[CX - 1] <> '_') or ((CX <= i) and (Line[CX] = '_')) );
 | |
|           if r then break;
 | |
|           CX := j;
 | |
|         end;
 | |
|         if (CX - 1 < 1) then
 | |
|           CX := NX;
 | |
|         CheckLineStart(CX, CY);
 | |
|       end;
 | |
|   end;
 | |
| 
 | |
|   Result := Point(CX, CY);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.EraseBackground(DC: HDC);
 | |
| begin
 | |
|   // we are painting everything ourselves, so not need to erase background
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.Update;
 | |
| begin
 | |
|   Invalidate;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.Invalidate;
 | |
| begin
 | |
|   {$IFDEF VerboseSynEditInvalidate}
 | |
|   DebugLn(['TCustomSynEdit.Invalidate ',DbgSName(self)]);
 | |
|   {$ENDIF}
 | |
|   inherited Invalidate;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PluginCount: Integer;
 | |
| begin
 | |
|   Result := fPlugins.Count;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.MarkupCount: Integer;
 | |
| begin
 | |
|   Result := FMarkupManager.Count;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.PasteFromClipboard;
 | |
| var
 | |
|   ClipHelper: TSynClipboardStream;
 | |
| begin
 | |
|   ClipHelper := TSynClipboardStream.Create;
 | |
|   try
 | |
|     ClipHelper.ReadFromClipboard(Clipboard);
 | |
|     PasteFromClipboardEx(ClipHelper);
 | |
|   finally
 | |
|     ClipHelper.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PasteFromClipboardEx(ClipHelper: TSynClipboardStream) : Boolean;
 | |
| var
 | |
|   PTxt: PChar;
 | |
|   PStr: String;
 | |
|   PMode: TSynSelectionMode;
 | |
|   InsStart: TPoint;
 | |
|   PasteAction: TSynCopyPasteAction;
 | |
| begin
 | |
|   Result := False;
 | |
|   InternalBeginUndoBlock;
 | |
|   try
 | |
|     PTxt := ClipHelper.TextP;
 | |
|     PMode := ClipHelper.SelectionMode;
 | |
|     PasteAction := scaContinue;
 | |
|     if assigned(FOnPaste) then begin
 | |
|       if ClipHelper.IsPlainText then PasteAction := scaPlainText;
 | |
|       InsStart := FCaret.LineBytePos;
 | |
|       if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then
 | |
|         InsStart := FBlockSelection.FirstLineBytePos;
 | |
|       PStr := PTxt;
 | |
|       FOnPaste(self, PStr, PMode, InsStart, PasteAction);
 | |
|       PTxt := PChar(PStr);
 | |
|       if (PStr = '') or (PasteAction = scaAbort) then
 | |
|         exit;
 | |
|     end;
 | |
| 
 | |
|     if ClipHelper.TextP = nil then
 | |
|       exit;
 | |
| 
 | |
|     Result := True;
 | |
|     if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then
 | |
|       FBlockSelection.SelText := '';
 | |
|     InsStart := FCaret.LineBytePos;
 | |
|     FInternalBlockSelection.StartLineBytePos := InsStart;
 | |
|     FInternalBlockSelection.SetSelTextPrimitive(PMode, PTxt);
 | |
|     FCaret.LineBytePos := FInternalBlockSelection.StartLineBytePos;
 | |
| 
 | |
|     if PasteAction = scaPlainText then
 | |
|       exit;
 | |
| 
 | |
|     if eoFoldedCopyPaste in fOptions2 then begin
 | |
|       PTxt := ClipHelper.GetTagPointer(synClipTagFold);
 | |
|       if PTxt <> nil then begin
 | |
|         ScanRanges;
 | |
|         FFoldedLinesView.ApplyFoldDescription(InsStart.Y -1, InsStart.X,
 | |
|             FInternalBlockSelection.StartLinePos-1, FInternalBlockSelection.StartBytePos,
 | |
|             PTxt, ClipHelper.GetTagLen(synClipTagFold));
 | |
|       end;
 | |
|     end;
 | |
|   finally
 | |
|     InternalEndUndoBlock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SelectAll;
 | |
| var
 | |
|   LastPt: TPoint;
 | |
| begin
 | |
|   DoIncPaintLock(Self); // No editing is taking place
 | |
|   LastPt := Point(1, FTheLinesView.Count);
 | |
|   if LastPt.y > 0 then
 | |
|     Inc(LastPt.x, Length(FTheLinesView[LastPt.y - 1]))
 | |
|   else
 | |
|     LastPt.y  := 1;
 | |
|   SetCaretAndSelection(LogicalToPhysicalPos(LastPt), Point(1, 1), LastPt);
 | |
|   FBlockSelection.ActiveSelectionMode := smNormal;
 | |
|   DoDecPaintLock(Self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetHighlightSearch(const ASearch : String; AOptions : TSynSearchOptions);
 | |
| begin
 | |
|   fMarkupHighAll.SearchOptions := AOptions;
 | |
|   fMarkupHighAll.SearchString := ASearch;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SelectToBrace;
 | |
| begin
 | |
|   FindMatchingBracket(CaretXY,true,true,true,false);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SelectWord;
 | |
| begin
 | |
|   SetWordBlock(LogicalCaretXY);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SelectLine(WithLeadSpaces: Boolean = True);
 | |
| begin
 | |
|   SetLineBlock(CaretXY, WithLeadSpaces);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SelectParagraph;
 | |
| begin
 | |
|   SetParagraphBlock(CaretXY);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoBlockSelectionChanged(Sender : TObject);
 | |
| begin
 | |
|   StatusChanged([scSelection]);
 | |
|   if HandleAllocated and Focused then
 | |
|     SelAvailChange(nil);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetBlockBegin(Value: TPoint); // logical position (byte)
 | |
| begin
 | |
|   fBlockSelection.StartLineBytePos := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetBlockEnd(Value: TPoint); // logical position (byte)
 | |
| begin
 | |
|   fBlockSelection.EndLineBytePos := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetBlockIndent(const AValue: integer);
 | |
| begin
 | |
|   if fBlockIndent=AValue then exit;
 | |
|   fBlockIndent:=AValue;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCaretX : Integer;
 | |
| begin
 | |
|   Result:= FCaret.CharPos;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCaretY : Integer;
 | |
| begin
 | |
|   Result:= FCaret.LinePos;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCaretUndo: TSynEditUndoItem;
 | |
| begin
 | |
|   if SelAvail then
 | |
|     Result := TSynEditUndoSelCaret.Create(FCaret.LineCharPos,
 | |
|        FBlockSelection.StartLineBytePos, FBlockSelection.EndLineBytePos,
 | |
|        FBlockSelection.ActiveSelectionMode)
 | |
|   else
 | |
|     Result := TSynEditUndoCaret.Create(FCaret.LineCharPos);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetMarkup(Index: integer): TSynEditMarkup;
 | |
| begin
 | |
|   Result := fMarkupManager.Markup[Index];
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetCaretX(const Value: Integer);
 | |
| begin
 | |
|   FCaret.ChangeOnTouch; // setting the caret always clears selection (even setting to current pos / no change)
 | |
|   FCaret.CharPos := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetCaretY(const Value: Integer);
 | |
| begin
 | |
|   FCaret.ChangeOnTouch; // setting the caret always clears selection (even setting to current pos / no change)
 | |
|   FCaret.LinePos := Value;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCaretXY: TPoint;
 | |
| begin
 | |
|   Result := FCaret.LineCharPos;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetFoldedCodeColor: TSynSelectedColor;
 | |
| begin
 | |
|   Result := FFoldedLinesView.MarkupInfoFoldedCode;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetLines: TStrings;
 | |
| begin
 | |
|   Result := FStrings;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetCaretXY(Value: TPoint);
 | |
| // physical position (screen)
 | |
| begin
 | |
|   FCaret.ChangeOnTouch; // setting the caret always clears selection (even setting to current pos / no change)
 | |
|   FCaret.LineCharPos:= Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.CaretChanged(Sender: TObject);
 | |
| begin
 | |
|   Include(fStateFlags, sfCaretChanged);
 | |
|   if FCaret.OldCharPos <> FCaret.CharPos then
 | |
|     Include(fStatusChanges, scCaretX);
 | |
|   if FCaret.OldLinePos <> FCaret.LinePos then begin
 | |
|     Include(fStatusChanges, scCaretY);
 | |
|     InvalidateGutterLines(FCaret.OldLinePos, FCaret.OldLinePos);
 | |
|     InvalidateGutterLines(FCaret.LinePos, FCaret.LinePos);
 | |
|   end;
 | |
|   EnsureCursorPosVisible;
 | |
|   if fPaintLock = 0 then
 | |
|     fMarkupHighCaret.CheckState; // Todo need a global lock, including the markup
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.CurrentMaxLeftChar: Integer;
 | |
| begin
 | |
|   if not HandleAllocated then // don't know chars in window yet
 | |
|     exit(MaxInt);
 | |
|   Result := FTheLinesView.LengthOfLongestLine;
 | |
|   if (eoScrollPastEol in Options) and (Result < fMaxLeftChar) then
 | |
|     Result := fMaxLeftChar;
 | |
|   Result := Result - CharsInWindow + 1 + FScreenCaret.ExtraLineChars;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.CurrentMaxLineLen: Integer;
 | |
| begin
 | |
|   if not HandleAllocated then // don't know chars in window yet
 | |
|     exit(MaxInt);
 | |
|   Result := FTheLinesView.LengthOfLongestLine + 1;
 | |
|   if (eoScrollPastEol in Options) and (Result < fMaxLeftChar) then
 | |
|     Result := fMaxLeftChar;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetLeftChar(Value: Integer);
 | |
| begin
 | |
|   Value := Min(Value, CurrentMaxLeftChar);
 | |
|   Value := Max(Value, 1);
 | |
|   if Value <> FTextArea.LeftChar then begin
 | |
|     FTextArea.LeftChar := Value;
 | |
|     UpdateScrollBars;
 | |
|     InvalidateLines(-1, -1);
 | |
|     StatusChanged([scLeftChar]);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetLines(Value: TStrings);
 | |
| begin
 | |
|   if HandleAllocated then
 | |
|     FStrings.Assign(Value);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetMarkupMgr: TObject;
 | |
| begin
 | |
|   Result := fMarkupManager;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCaretObj: TSynEditCaret;
 | |
| begin
 | |
|   Result := FCaret;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetLineText(Value: string);
 | |
| begin
 | |
|   FCaret.LineText := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetName(const Value: TComponentName);
 | |
| var
 | |
|   TextToName: boolean;
 | |
| begin
 | |
|   TextToName := (ComponentState * [csDesigning, csLoading] = [csDesigning])
 | |
|     and (TrimRight(Text) = Name);
 | |
|   inherited SetName(Value);
 | |
|   if TextToName then
 | |
|     Text := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.CreateHandle;
 | |
| begin
 | |
|   Application.RemoveOnIdleHandler(@IdleScanRanges);
 | |
|   DoIncPaintLock(nil);
 | |
|   try
 | |
|     inherited CreateHandle;   //SizeOrFontChanged will be called
 | |
|     FLeftGutter.RecalcBounds;
 | |
|     FRightGutter.RecalcBounds;
 | |
|     UpdateScrollBars;
 | |
|   finally
 | |
|     DoDecPaintLock(nil);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetScrollBars(const Value: TScrollStyle);
 | |
| begin
 | |
|   if (FScrollBars <> Value) then begin
 | |
|     FScrollBars := Value;
 | |
|     UpdateScrollBars;
 | |
|     Invalidate;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetSelTextPrimitive(PasteMode: TSynSelectionMode;
 | |
|   Value: PChar; AddToUndoList: Boolean = false);
 | |
| Begin
 | |
|   IncPaintLock;
 | |
|   if not AddToUndoList then begin
 | |
|     fUndoList.Lock;
 | |
|     fRedoList.Lock;
 | |
|   end;
 | |
|   try
 | |
|     FBlockSelection.SetSelTextPrimitive(PasteMode, Value);
 | |
|   finally
 | |
|     if not AddToUndoList then begin
 | |
|       fUndoList.Unlock;
 | |
|       fRedoList.Unlock;
 | |
|     end;
 | |
|     DecPaintLock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetSelTextExternal(const Value: string);
 | |
| begin
 | |
|   // undo entry added
 | |
|   InternalBeginUndoBlock;
 | |
|   try
 | |
|     FBlockSelection.SelText := Value;
 | |
|   finally
 | |
|     InternalEndUndoBlock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SynSetText(const Value: string);
 | |
| begin
 | |
|   FLines.Text := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RealSetText(const Value: TCaption);
 | |
| begin
 | |
|   FLines.Text := Value; // Do not trim
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.CurrentMaxTopView: Integer;
 | |
| begin
 | |
|   Result := FFoldedLinesView.Count;
 | |
|   if not(eoScrollPastEof in Options) then
 | |
|     Result := Result + 1 - Max(0, LinesInWindow);
 | |
|   Result := Max(Result, 1);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetTopLine(Value: Integer);
 | |
| var
 | |
|   NewTopView: Integer;
 | |
| begin
 | |
|   // TODO : Above hidden line only if folded, if hidden then use below
 | |
|   if FFoldedLinesView.FoldedAtTextIndex[Value-1] then
 | |
|     Value := FindNextUnfoldedLine(Value, False);
 | |
|   if FFoldedLinesView.FoldedAtTextIndex[Value-1] then
 | |
|     Value := FindNextUnfoldedLine(Value, True);
 | |
| 
 | |
|   NewTopView := FFoldedLinesView.TextIndexToViewPos(Value-1);
 | |
|   if NewTopView <> TopView then begin
 | |
|     TopView := NewTopView;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ScrollAfterTopLineChanged;
 | |
| var
 | |
|   Delta: Integer;
 | |
|   srect: TRect;
 | |
| begin
 | |
|   if (sfPainting in fStateFlags) or (fPaintLock <> 0) or (not HandleAllocated) then
 | |
|     exit;
 | |
|   Delta := FOldTopView - TopView;
 | |
|   {$IFDEF SYNSCROLLDEBUG}
 | |
|   if (sfHasScrolled in fStateFlags) then debugln(['ScrollAfterTopLineChanged with sfHasScrolled Delta=',Delta,' topline=',TopLine, '  FOldTopView=',FOldTopView ]);
 | |
|   {$ENDIF}
 | |
|   if Delta <> 0 then begin
 | |
|     // TODO: SW_SMOOTHSCROLL --> can't get it work
 | |
|     if (Abs(Delta) >= LinesInWindow) or (sfHasScrolled in FStateFlags) then begin
 | |
|       {$IFDEF SYNSCROLLDEBUG}
 | |
|       debugln(['ScrollAfterTopLineChanged does invalidet Delta=',Delta]);
 | |
|       {$ENDIF}
 | |
|       Invalidate;
 | |
|     end else
 | |
|     begin
 | |
|       srect := FPaintArea.Bounds;
 | |
|       srect.Top := FTextArea.TextBounds.Top;
 | |
|       srect.Bottom := FTextArea.TextBounds.Bottom;
 | |
|       if ScrollWindowEx(Handle, 0, LineHeight * Delta, @srect, @srect, 0, nil, SW_INVALIDATE)
 | |
|       then begin
 | |
|         {$IFDEF SYNSCROLLDEBUG}
 | |
|         debugln(['ScrollAfterTopLineChanged did scroll Delta=',Delta]);
 | |
|         {$ENDIF}
 | |
|         include(fStateFlags, sfHasScrolled);
 | |
|       end else begin
 | |
|         Invalidate;    // scrollwindow failed, invalidate all
 | |
|         {$IFDEF SYNSCROLLDEBUG}
 | |
|         debugln(['ScrollAfterTopLineChanged does invalidet (scroll failed) Delta=',Delta]);
 | |
|         {$ENDIF}
 | |
|       end;
 | |
|     end;
 | |
|   end;
 | |
|   FOldTopView := TopView;
 | |
|   if (Delta <> 0) and (eoAlwaysVisibleCaret in fOptions2) then
 | |
|     MoveCaretToVisibleArea;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MoveCaretToVisibleArea;
 | |
| // scroll to make the caret visible
 | |
| var
 | |
|   NewCaretXY: TPoint;
 | |
|   MaxY: LongInt;
 | |
| begin
 | |
|   {$IFDEF SYNDEBUG}
 | |
|   if (sfEnsureCursorPos in fStateFlags) then
 | |
|     debugln('SynEdit. skip MoveCaretToVisibleArea');
 | |
|   {$ENDIF}
 | |
|   if (not HandleAllocated) or (sfEnsureCursorPos in fStateFlags) then
 | |
|     exit;
 | |
| 
 | |
|   NewCaretXY:=CaretXY;
 | |
|   if NewCaretXY.X < LeftChar then
 | |
|     NewCaretXY.X := LeftChar
 | |
|   else if NewCaretXY.X > LeftChar + CharsInWindow - FScreenCaret.ExtraLineChars then
 | |
|     NewCaretXY.X := LeftChar + CharsInWindow - FScreenCaret.ExtraLineChars;
 | |
|   if NewCaretXY.Y < TopLine then
 | |
|     NewCaretXY.Y := TopLine
 | |
|   else begin
 | |
|     MaxY:= ScreenRowToRow(Max(0,LinesInWindow-1));
 | |
|     if NewCaretXY.Y > MaxY then
 | |
|       NewCaretXY.Y := MaxY;
 | |
|   end;
 | |
|   if CompareCarets(CaretXY,NewCaretXY)<>0 then
 | |
|   begin
 | |
|     //DebugLn(['TCustomSynEdit.MoveCaretToVisibleArea Old=',dbgs(CaretXY),' New=',dbgs(NewCaretXY)]);
 | |
|     FCaret.LineCharPos:=NewCaretXY;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MoveCaretIgnoreEOL(const NewCaret: TPoint);
 | |
| begin
 | |
|   FCaret.IncForcePastEOL;
 | |
|   FCaret.LineCharPos := NewCaret;
 | |
|   FCaret.DecForcePastEOL;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MoveLogicalCaretIgnoreEOL(const NewLogCaret: TPoint);
 | |
| begin
 | |
|   MoveCaretIgnoreEOL(LogicalToPhysicalPos(NewLogCaret));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UpdateCaret(IgnorePaintLock: Boolean = False);
 | |
| var
 | |
|   x, y: Integer;
 | |
| begin
 | |
|   if ( (PaintLock <> 0) and not IgnorePaintLock ) or (not HandleAllocated)
 | |
|   then begin
 | |
|     Include(fStateFlags, sfCaretChanged);
 | |
|   end else begin
 | |
|     Exclude(fStateFlags, sfCaretChanged);
 | |
|     if eoAlwaysVisibleCaret in fOptions2 then
 | |
|       MoveCaretToVisibleArea;
 | |
| 
 | |
|     x := CaretXPix;
 | |
|     y := CaretYPix;
 | |
|     FScreenCaret.DisplayPos := Point(x, y);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UpdateScrollBars;
 | |
| var
 | |
|   ScrollInfo: TScrollInfo;
 | |
| begin
 | |
|   if FScrollBarUpdateLock <> 0 then exit;
 | |
|   if not HandleAllocated or (PaintLock <> 0) then
 | |
|     Include(fStateFlags, sfScrollbarChanged)
 | |
|   else begin
 | |
|     Exclude(fStateFlags, sfScrollbarChanged);
 | |
|     ScrollInfo.cbSize := SizeOf(ScrollInfo);
 | |
|     ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL and not SIF_TRACKPOS;
 | |
|     ScrollInfo.nMin := 1;
 | |
|     ScrollInfo.nTrackPos := 0;
 | |
| 
 | |
|     // Horizontal
 | |
|     ScrollInfo.nMax := FTheLinesView.LengthOfLongestLine + 1;
 | |
|     if (eoScrollPastEol in Options) and (ScrollInfo.nMax < fMaxLeftChar + 1) then
 | |
|       ScrollInfo.nMax := fMaxLeftChar + 1;
 | |
|     inc(ScrollInfo.nMax, FScreenCaret.ExtraLineChars);
 | |
|     if ((fScrollBars in [ssBoth, ssHorizontal]) or
 | |
|         ((fScrollBars in [ssAutoBoth, ssAutoHorizontal]) and (ScrollInfo.nMax - 1 > CharsInWindow))
 | |
|        ) xor (sfHorizScrollbarVisible in fStateFlags)
 | |
|     then begin
 | |
|       if (sfHorizScrollbarVisible in fStateFlags)
 | |
|         then exclude(fStateFlags, sfHorizScrollbarVisible)
 | |
|         else include(fStateFlags, sfHorizScrollbarVisible);
 | |
|       if fStateFlags * [sfEnsureCursorPos, sfEnsureCursorPosAtResize] <> [] then
 | |
|         include(fStateFlags, sfEnsureCursorPosAtResize);
 | |
|       ShowScrollBar(Handle, SB_Horz, sfHorizScrollbarVisible in fStateFlags);
 | |
|       RecalcCharsAndLinesInWin(True);
 | |
|     end;
 | |
|     {$IFnDEF SynNewScrollBarUpdate}
 | |
|     if (sfHorizScrollbarVisible in fStateFlags) then begin
 | |
|     {$ENDIF}
 | |
|     ScrollInfo.nPage := CharsInWindow;
 | |
|     ScrollInfo.nPos := LeftChar;
 | |
|     SetScrollInfo(Handle, SB_HORZ, ScrollInfo, True);
 | |
|     {$IFNDEF LCLWin32} {$IFnDEF SynScrollBarWorkaround}
 | |
|     if not (sfHorizScrollbarVisible in fStateFlags) then
 | |
|       ShowScrollBar(Handle, SB_Horz, False);
 | |
|     {$ENDIF} {$ENDIF}
 | |
|     {$IFnDEF SynNewScrollBarUpdate}
 | |
|     end;
 | |
|     {$ENDIF}
 | |
|     //DebugLn('[TCustomSynEdit.UpdateScrollbars] nMin=',ScrollInfo.nMin,' nMax=',ScrollInfo.nMax,
 | |
|     //' nPage=',ScrollInfo.nPage,' nPos=',ScrollInfo.nPos,' ClientW=',ClientWidth);
 | |
| 
 | |
|     // Vertical
 | |
|     ScrollInfo.nMax := FFoldedLinesView.Count+1;
 | |
|     if (eoScrollPastEof in Options) then
 | |
|       Inc(ScrollInfo.nMax, LinesInWindow - 1);
 | |
|     if ((fScrollBars in [ssBoth, ssVertical]) or
 | |
|         ((fScrollBars in [ssAutoBoth, ssAutoVertical]) and (ScrollInfo.nMax - 1 > LinesInWindow))
 | |
|        ) xor (sfVertScrollbarVisible in fStateFlags)
 | |
|     then begin
 | |
|       if (sfVertScrollbarVisible in fStateFlags)
 | |
|         then exclude(fStateFlags, sfVertScrollbarVisible)
 | |
|         else include(fStateFlags, sfVertScrollbarVisible);
 | |
|       if fStateFlags * [sfEnsureCursorPos, sfEnsureCursorPosAtResize] <> [] then
 | |
|         include(fStateFlags, sfEnsureCursorPosAtResize);
 | |
|       ShowScrollBar(Handle, SB_Vert, sfVertScrollbarVisible in fStateFlags);
 | |
|       RecalcCharsAndLinesInWin(True);
 | |
|     end;
 | |
|     {$IFnDEF SynNewScrollBarUpdate}
 | |
|     if (sfVertScrollbarVisible in fStateFlags) then begin
 | |
|     {$ENDIF}
 | |
|     ScrollInfo.nPage := LinesInWindow;
 | |
|     ScrollInfo.nPos := TopView;
 | |
|     SetScrollInfo(Handle, SB_VERT, ScrollInfo, True);
 | |
|     {$IFNDEF LCLWin32} {$IFnDEF SynScrollBarWorkaround}
 | |
|     if not (sfVertScrollbarVisible in fStateFlags) then
 | |
|       ShowScrollBar(Handle, SB_Vert, False);
 | |
|     {$ENDIF} {$ENDIF}
 | |
|     {$IFnDEF SynNewScrollBarUpdate}
 | |
|     end;
 | |
|     {$ENDIF}
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SelAvailChange(Sender: TObject);
 | |
| begin
 | |
|   if PaintLock > 0 then begin
 | |
|     Include(FStateFlags, sfSelChanged);
 | |
|     exit;
 | |
|   end;
 | |
|   Exclude(FStateFlags, sfSelChanged);
 | |
|   if SelAvail
 | |
|   then AquirePrimarySelection
 | |
|   else SurrenderPrimarySelection;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMDropFiles(var Msg: TMessage);
 | |
| {$IFNDEF SYN_LAZARUS}
 | |
| // ToDo DropFiles
 | |
| var
 | |
|   i, iNumberDropped: integer;
 | |
|   szPathName: array[0..260] of char;
 | |
|   Point: TPoint;
 | |
|   FilesList: TStringList;
 | |
| {$ENDIF}
 | |
| begin
 | |
|   {$IFDEF SYN_LAZARUS}
 | |
|   LastMouseCaret:=Point(-1,-1);
 | |
|   {$ELSE}
 | |
|   try
 | |
|     if Assigned(fOnDropFiles) then begin
 | |
|       FilesList := TStringList.Create;
 | |
|       try
 | |
|         iNumberDropped := DragQueryFile(THandle(Msg.wParam), Cardinal(-1),
 | |
|           nil, 0);
 | |
|         DragQueryPoint(THandle(Msg.wParam), Point);
 | |
| 
 | |
|         for i := 0 to iNumberDropped - 1 do begin
 | |
|           DragQueryFile(THandle(Msg.wParam), i, szPathName,
 | |
|             SizeOf(szPathName));
 | |
|           FilesList.Add(szPathName);
 | |
|         end;
 | |
|         fOnDropFiles(Self, Point.X, Point.Y, FilesList);
 | |
|       finally
 | |
|         FilesList.Free;
 | |
|       end;
 | |
|     end;
 | |
|   finally
 | |
|     Msg.Result := 0;
 | |
|     DragFinish(THandle(Msg.wParam));
 | |
|   end;
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMExit(var Message: TLMExit);
 | |
| begin
 | |
|   LastMouseCaret:=Point(-1,-1);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMEraseBkgnd(var Msg: TMessage);
 | |
| begin
 | |
|   Msg.Result := 1;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMGetDlgCode(var Msg: TWMGetDlgCode);
 | |
| begin
 | |
|   inherited;
 | |
|   Msg.Result := DLGC_WANTARROWS or DLGC_WANTCHARS or DLGC_WANTALLKEYS;
 | |
|   if fWantTabs and (GetKeyState(VK_CONTROL) >= 0) then
 | |
|     Msg.Result := Msg.Result or DLGC_WANTTAB;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMHScroll(var Msg: {$IFDEF SYN_LAZARUS}TLMScroll{$ELSE}TWMScroll{$ENDIF});
 | |
| begin
 | |
|   case Msg.ScrollCode of
 | |
|       // Scrolls to start / end of the line
 | |
|     SB_TOP: LeftChar := 1;
 | |
|     SB_BOTTOM: LeftChar := CurrentMaxLeftChar;
 | |
|       // Scrolls one char left / right
 | |
|     SB_LINEDOWN: LeftChar := LeftChar + 1;
 | |
|     SB_LINEUP: LeftChar := LeftChar - 1;
 | |
|       // Scrolls one page of chars left / right
 | |
|     SB_PAGEDOWN: LeftChar := LeftChar
 | |
|       + Max(1, (CharsInWindow - Ord(eoScrollByOneLess in fOptions)));
 | |
|     SB_PAGEUP: LeftChar := LeftChar
 | |
|       - Max(1, (CharsInWindow - Ord(eoScrollByOneLess in fOptions)));
 | |
|       // Scrolls to the current scroll bar position
 | |
|     SB_THUMBPOSITION,
 | |
|     SB_THUMBTRACK: LeftChar := Msg.Pos;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMKillFocus(var Msg: TWMKillFocus);
 | |
| begin
 | |
|   if fCaret = nil then exit; // This SynEdit is in Destroy
 | |
|   Exclude(FStateFlags, sfHideCursor);
 | |
|   inherited;
 | |
|   {$IFDEF VerboseFocus}
 | |
|   DebugLn(['[TCustomSynEdit.WMKillFocus] A ',Name, ' time=', dbgs(Now*86640)]);
 | |
|   {$ENDIF}
 | |
|   LastMouseCaret:=Point(-1,-1);
 | |
|   // Todo: Under Windows, keeping the Caret only works, if no other component creates a caret
 | |
|   if not (eoPersistentCaret in fOptions) then begin
 | |
|     FScreenCaret.Visible := False;
 | |
|     FScreenCaret.DestroyCaret;
 | |
|   end;
 | |
|   if FHideSelection and SelAvail then
 | |
|     Invalidate;
 | |
|   {$IFDEF WinIME}
 | |
|   FImeHandler.FocusKilled;
 | |
|   {$ENDIF}
 | |
|   inherited;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMSetFocus(var Msg: TLMSetFocus);
 | |
| begin
 | |
|   if fCaret = nil then exit; // This SynEdit is in Destroy
 | |
|   Exclude(FStateFlags, sfHideCursor);
 | |
|   LastMouseCaret:=Point(-1,-1);
 | |
|   {$IFDEF VerboseFocus}
 | |
|   DebugLn(['[TCustomSynEdit.WMSetFocus] A ',Name,':',ClassName, ' time=', dbgs(Now*86640)]);
 | |
|   {$ENDIF}
 | |
|   FScreenCaret.DestroyCaret; // Ensure recreation. On Windows only one caret exists, and it must be moved to the focused editor
 | |
|   FScreenCaret.Visible := not(eoNoCaret in FOptions);
 | |
|   //if FHideSelection and SelAvail then
 | |
|   //  Invalidate;
 | |
|   inherited;
 | |
|   //DebugLn('[TCustomSynEdit.WMSetFocus] END');
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoOnResize;
 | |
| begin
 | |
|   inherited;
 | |
|   if (not HandleAllocated) or ((ClientWidth = FOldWidth) and (ClientHeight = FOldHeight)) then exit;
 | |
|   FOldWidth := ClientWidth;
 | |
|   FOldHeight := ClientHeight;
 | |
|   inc(FScrollBarUpdateLock);
 | |
|   FScreenCaret.Lock;
 | |
|   try
 | |
|     FLeftGutter.RecalcBounds;
 | |
|     FRightGutter.RecalcBounds;
 | |
|     SizeOrFontChanged(FALSE);
 | |
|     if sfEnsureCursorPosAtResize in fStateFlags then
 | |
|       EnsureCursorPosVisible;
 | |
|     Exclude(fStateFlags, sfEnsureCursorPosAtResize);
 | |
|   finally
 | |
|     FScreenCaret.UnLock;
 | |
|     dec(FScrollBarUpdateLock);
 | |
|     UpdateScrollBars;
 | |
|   end;
 | |
|   //debugln('TCustomSynEdit.Resize ',dbgs(Width),',',dbgs(Height),',',dbgs(ClientWidth),',',dbgs(ClientHeight));
 | |
|   // SetLeftChar(LeftChar);                                                     //mh 2000-10-19
 | |
| end;
 | |
| 
 | |
| var
 | |
|   ScrollHintWnd: THintWindow;
 | |
| 
 | |
| function GetScrollHint: THintWindow;
 | |
| begin
 | |
|   if ScrollHintWnd = nil then begin
 | |
|     ScrollHintWnd := HintWindowClass.Create(Application);
 | |
|     ScrollHintWnd.Name:='SynEditScrollHintWnd';
 | |
|     ScrollHintWnd.Visible := FALSE;
 | |
|   end;
 | |
|   Result := ScrollHintWnd;
 | |
|   Result.AutoHide := True;  // Because SB_ENDSCROLL never happens under LCL-GTK2
 | |
|   Result.HideInterval := 1500;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMVScroll(var Msg: {$IFDEF SYN_LAZARUS}TLMScroll{$ELSE}TWMScroll{$ENDIF});
 | |
| var
 | |
|   s: ShortString;
 | |
|   rc: TRect;
 | |
|   pt: TPoint;
 | |
|   ScrollHint: THintWindow;
 | |
| begin
 | |
|   //debugln('TCustomSynEdit.WMVScroll A ',DbgSName(Self),' Msg.ScrollCode=',dbgs(Msg.ScrollCode),' SB_PAGEDOWN=',dbgs(SB_PAGEDOWN),' SB_PAGEUP=',dbgs(SB_PAGEUP));
 | |
|   case Msg.ScrollCode of
 | |
|       // Scrolls to start / end of the text
 | |
|     SB_TOP: TopView := 1;
 | |
|     SB_BOTTOM: TopView := FFoldedLinesView.Count;
 | |
|       // Scrolls one line up / down
 | |
|     SB_LINEDOWN: TopView := TopView + 1;
 | |
|     SB_LINEUP: TopView := TopView - 1;
 | |
|       // Scrolls one page of lines up / down
 | |
|     SB_PAGEDOWN: TopView := TopView
 | |
|       + Max(1, (LinesInWindow - Ord(eoScrollByOneLess in fOptions))); // TODO: scroll half page ?
 | |
|     SB_PAGEUP: TopView := TopView
 | |
|       - Max(1, (LinesInWindow - Ord(eoScrollByOneLess in fOptions)));
 | |
|       // Scrolls to the current scroll bar position
 | |
|     SB_THUMBPOSITION,
 | |
|     SB_THUMBTRACK:
 | |
|       begin
 | |
|         TopView := Msg.Pos;
 | |
| 
 | |
|         if eoShowScrollHint in fOptions then begin
 | |
|           ScrollHint := GetScrollHint;
 | |
|           if not ScrollHint.Visible then begin
 | |
|             ScrollHint.Color := Application.HintColor;
 | |
|             ScrollHint.Visible := TRUE;
 | |
|           end;
 | |
|           s := Format('line %d', [TopLine]);
 | |
|           rc := ScrollHint.CalcHintRect(200, s, nil);
 | |
|           pt := ClientToScreen(Point(
 | |
|                 ClientWidth-ScrollBarWidth
 | |
|                         - rc.Right - 4, 10));
 | |
|           if eoScrollHintFollows in fOptions then
 | |
|             pt.y := Mouse.CursorPos.y - (rc.Bottom div 2);
 | |
|           OffsetRect(rc, pt.x, pt.y);
 | |
|           ScrollHint.ActivateHint(rc, s);
 | |
|           ScrollHint.Invalidate;
 | |
|           ScrollHint.Update;
 | |
|         end;
 | |
|       end;
 | |
|       // Ends scrolling
 | |
|     SB_ENDSCROLL:
 | |
|       if eoShowScrollHint in fOptions then
 | |
|         with GetScrollHint do begin
 | |
|           Visible := FALSE;
 | |
|           ActivateHint(Rect(0, 0, 0, 0), '');
 | |
|         end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ScanRanges(ATextChanged: Boolean = True);
 | |
| begin
 | |
|   if not HandleAllocated then begin
 | |
|     Application.RemoveOnIdleHandler(@IdleScanRanges); // avoid duplicate add
 | |
|     if assigned(FHighlighter) then
 | |
|       Application.AddOnIdleHandler(@IdleScanRanges, False);
 | |
|     exit;
 | |
|   end;
 | |
|   if not assigned(FHighlighter) then begin
 | |
|     if ATextChanged then begin
 | |
|       fMarkupManager.TextChanged(FChangedLinesStart, FChangedLinesEnd);
 | |
|       // TODO: see TSynEditFoldedView.LineCountChanged, this is only needed, because NeedFixFrom does not always work
 | |
|       FFoldedLinesView.FixFoldingAtTextIndex(FChangedLinesStart, FChangedLinesEnd);
 | |
|     end;
 | |
|     TopView := TopView;
 | |
|     exit;
 | |
|   end;
 | |
|   FHighlighter.CurrentLines := FLines; // Trailing spaces are not needed
 | |
|   FHighlighter.ScanRanges;
 | |
| 
 | |
|   // Todo: text may not have changed
 | |
|   if ATextChanged then
 | |
|     fMarkupManager.TextChanged(FChangedLinesStart, FChangedLinesEnd);
 | |
|   TopView := TopView;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.IdleScanRanges(Sender: TObject; var Done: Boolean);
 | |
| begin
 | |
|   Application.RemoveOnIdleHandler(@IdleScanRanges);
 | |
|   if not assigned(FHighlighter) then
 | |
|     exit;
 | |
| 
 | |
|   FHighlighter.CurrentLines := FLines; // Trailing spaces are not needed
 | |
|   if not FHighlighter.IdleScanRanges then
 | |
|     exit;
 | |
| 
 | |
|   // Move to the end; give others a change too
 | |
|   Application.AddOnIdleHandler(@IdleScanRanges, False);
 | |
|   Done := False;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.LineCountChanged(Sender: TSynEditStrings;
 | |
|   AIndex, ACount: Integer);
 | |
| begin
 | |
|   {$IFDEF SynFoldDebug}debugln(['FOLD-- LineCountChanged Aindex', AIndex, '  ACount=', ACount]);{$ENDIF}
 | |
|   if (AIndex < FBeautifyStartLineIdx) or (FBeautifyStartLineIdx < 0) then
 | |
|     FBeautifyStartLineIdx := AIndex;
 | |
|   if ACount > 0 then begin
 | |
|     if (AIndex > FBeautifyEndLineIdx) then
 | |
|       FBeautifyEndLineIdx := AIndex + ACount - 1
 | |
|     else
 | |
|       FBeautifyEndLineIdx := FBeautifyEndLineIdx + ACount;
 | |
|   end else begin
 | |
|     FBeautifyEndLineIdx := FBeautifyEndLineIdx + ACount;
 | |
|     if (FBeautifyEndLineIdx < AIndex) then
 | |
|       FBeautifyEndLineIdx := AIndex;
 | |
|   end;
 | |
| 
 | |
|   if PaintLock>0 then begin
 | |
|     // FChangedLinesStart is also given to Markup.TextChanged; but it is not used there
 | |
|     if (FChangedLinesStart<1) or (FChangedLinesStart>AIndex+1) then
 | |
|       FChangedLinesStart:=AIndex+1;
 | |
|     FChangedLinesEnd := -1; // Invalidate the rest of lines
 | |
|   end else begin
 | |
|     ScanRanges;
 | |
|     InvalidateLines(AIndex + 1, -1);
 | |
|     InvalidateGutterLines(AIndex + 1, -1);
 | |
|     if FCaret.LinePos > FLines.Count then FCaret.LinePos := FLines.Count;
 | |
|   end;
 | |
|   if TopLine > AIndex + 1 then
 | |
|     TopLine := TopLine + ACount // will call UpdateScrollBars
 | |
|   else
 | |
|     UpdateScrollBars;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.LineTextChanged(Sender: TSynEditStrings;
 | |
|   AIndex, ACount: Integer);
 | |
| begin
 | |
|   {$IFDEF SynFoldDebug}debugln(['FOLD-- LineTextChanged Aindex', AIndex, '  ACount=', ACount]);{$ENDIF}
 | |
|   if (AIndex < FBeautifyStartLineIdx) or (FBeautifyStartLineIdx < 0) then
 | |
|     FBeautifyStartLineIdx := AIndex;
 | |
|   if (AIndex + ACount - 1 > FBeautifyEndLineIdx) then
 | |
|     FBeautifyEndLineIdx := AIndex + ACount - 1;
 | |
| 
 | |
|   if PaintLock>0 then begin
 | |
|     if (FChangedLinesStart<1) or (FChangedLinesStart>AIndex+1) then
 | |
|       FChangedLinesStart:=AIndex+1;
 | |
|     if (FChangedLinesEnd >= 0) and (FChangedLinesEnd<AIndex+1) then
 | |
|       FChangedLinesEnd:=AIndex + 1 + MaX(ACount, 0);
 | |
|   end else begin
 | |
|     ScanRanges;
 | |
|     InvalidateLines(AIndex + 1, AIndex + ACount);
 | |
|     InvalidateGutterLines(AIndex + 1, AIndex + ACount);
 | |
|   end;
 | |
|   UpdateScrollBars;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoHighlightChanged(Sender: TSynEditStrings; AIndex,
 | |
|   ACount: Integer);
 | |
| begin
 | |
|   InvalidateLines(AIndex + 1, AIndex + 1 + ACount);
 | |
|   InvalidateGutterLines(AIndex + 1, AIndex + 1 + ACount);
 | |
|   FFoldedLinesView.FixFoldingAtTextIndex(AIndex, AIndex + ACount);
 | |
|   if FPendingFoldState <> '' then
 | |
|     SetFoldState(FPendingFoldState);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ListCleared(Sender: TObject);
 | |
| begin
 | |
|   ClearUndo;
 | |
|   // invalidate the *whole* client area
 | |
|   Invalidate;
 | |
|   // set caret and selected block to start of text
 | |
|   SetBlockBegin(Point(1, 1));
 | |
|   SetCaretXY(Point(1, 1));
 | |
|   // scroll to start of text
 | |
|   TopView := 1;
 | |
|   LeftChar := 1;
 | |
|   StatusChanged(scTextCleared);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.FoldChanged(Index : integer);
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   {$IFDEF SynFoldDebug}debugln(['FOLD-- FoldChanged; Index=', Index, ' TopView=', TopView, '  ScreenRowToRow(LinesInWindow + 1)=', ScreenRowToRow(LinesInWindow + 1)]);{$ENDIF}
 | |
|   TopView := TopView;
 | |
|   i := FFoldedLinesView.CollapsedLineForFoldAtLine(CaretY);
 | |
|   if i > 0 then begin
 | |
|     SetCaretXY(Point(1, i));
 | |
|     UpdateCaret;
 | |
|   end
 | |
|   else
 | |
|   if eoAlwaysVisibleCaret in fOptions2 then
 | |
|     MoveCaretToVisibleArea;
 | |
|   UpdateScrollBars;
 | |
|   if Index + 1 > Max(1, ScreenRowToRow(LinesInWindow + 1)) then exit;
 | |
|   if Index + 1 < TopLine then Index := TopLine - 1;
 | |
|   InvalidateLines(Index + 1, -1);
 | |
|   InvalidateGutterLines(Index + 1, -1);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetTopView(AValue : Integer);
 | |
| begin
 | |
|   //  don't use MinMax here, it will fail in design mode (Lines.Count is zero,
 | |
|   // but the painting code relies on TopLine >= 1)
 | |
|   {$IFDEF SYNSCROLLDEBUG}
 | |
|   if (fPaintLock = 0) and (not FIsInDecPaintLock) then debugln(['SetTopView outside Paintlock New=',AValue, ' Old=', FFoldedLinesView.TopLine]);
 | |
|   if (sfHasScrolled in fStateFlags) then debugln(['SetTopView with sfHasScrolled Value=',AValue, '  FOldTopView=',FOldTopView ]);
 | |
|   {$ENDIF}
 | |
|   TSynEditStringList(FLines).SendCachedNotify; // TODO: review
 | |
| 
 | |
|   AValue := Min(AValue, CurrentMaxTopView);
 | |
|   AValue := Max(AValue, 1);
 | |
| 
 | |
|   (* ToDo: FFoldedLinesView.TopLine := AValue;
 | |
|     Required, if "TopView := TopView" or "TopLine := TopLine" is called,
 | |
|     after ScanRanges (used to be: LineCountChanged / LineTextChanged)
 | |
|   *)
 | |
|   FFoldedLinesView.TopLine := AValue;
 | |
| 
 | |
|   if FTextArea.TopLine <> AValue then begin
 | |
|     FTextArea.TopLine := AValue;
 | |
|     UpdateScrollBars;
 | |
|     // call MarkupMgr before ScrollAfterTopLineChanged, in case we aren't in a PaintLock
 | |
|     fMarkupManager.TopLine := TopLine;
 | |
|     if (sfPainting in fStateFlags) then debugln('SetTopline inside paint');
 | |
|     ScrollAfterTopLineChanged;
 | |
|     StatusChanged([scTopLine]);
 | |
|   end
 | |
|   else
 | |
|     fMarkupManager.TopLine := TopLine;
 | |
| 
 | |
|   {$IFDEF SYNSCROLLDEBUG}
 | |
|   if (fPaintLock = 0) and (not FIsInDecPaintLock) then debugln('SetTopline outside Paintlock EXIT');
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetTopView : Integer;
 | |
| begin
 | |
|   Result := FTextArea.TopLine;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetWordBlock(Value: TPoint);
 | |
| var
 | |
|   TempString: string;
 | |
|   x: Integer;
 | |
| begin
 | |
|   { Value is the position of the Caret in bytes }
 | |
|   Value.y := MinMax(Value.y, 1, FTheLinesView.Count);
 | |
|   TempString := FTheLinesView[Value.Y - 1];
 | |
|   if TempString = '' then exit;
 | |
|   x := MinMax(Value.x, 1, Length(TempString)+1);
 | |
| 
 | |
|   Value.X := WordBreaker.PrevWordStart(TempString, x, True);
 | |
|   if Value.X < 0 then
 | |
|     Value.X := WordBreaker.NextWordStart(TempString, x);
 | |
|   if Value.X < 0 then
 | |
|     exit;
 | |
| 
 | |
|   DoIncPaintLock(Self); // No editing is taking place
 | |
|   FBlockSelection.StartLineBytePos := Value;
 | |
|   Value.X := WordBreaker.NextWordEnd(TempString, Value.X);
 | |
|   FBlockSelection.EndLineBytePos := Value;
 | |
|   FBlockSelection.ActiveSelectionMode := smNormal;
 | |
|   FCaret.LineBytePos := Value;
 | |
|   DoDecPaintLock(Self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetLineBlock(Value: TPoint; WithLeadSpaces: Boolean = True);
 | |
| var
 | |
|   ALine: string;
 | |
|   x, x2: Integer;
 | |
| begin
 | |
|   DoIncPaintLock(Self); // No editing is taking place
 | |
|   FBlockSelection.StartLineBytePos := Point(1,MinMax(Value.y, 1, FTheLinesView.Count));
 | |
|   FBlockSelection.EndLineBytePos := Point(1,MinMax(Value.y+1, 1, FTheLinesView.Count));
 | |
|   if (FBlockSelection.StartLinePos >= 1)
 | |
|   and (FBlockSelection.StartLinePos <= FTheLinesView.Count) then begin
 | |
|     ALine:=FTheLinesView[FBlockSelection.StartLinePos - 1];
 | |
|     x2:=length(ALine)+1;
 | |
|     if not WithLeadSpaces then begin
 | |
|       x := FBlockSelection.StartBytePos;
 | |
|       while (x<length(ALine)) and (ALine[x] in [' ',#9]) do
 | |
|         inc(x);
 | |
|       FBlockSelection.StartLineBytePos := Point(x,MinMax(Value.y, 1, FTheLinesView.Count));
 | |
|       while (x2 > x) and (ALine[X2-1] in [' ',#9]) do
 | |
|         dec(x2);
 | |
|     end;
 | |
|     FBlockSelection.EndLineBytePos := Point(x2, MinMax(Value.y, 1, FTheLinesView.Count));
 | |
|   end;
 | |
|   FBlockSelection.ActiveSelectionMode := smNormal;
 | |
|   LogicalCaretXY := FBlockSelection.EndLineBytePos;
 | |
|   //DebugLn(' FFF2 ',Value.X,',',Value.Y,' BlockBegin=',BlockBegin.X,',',BlockBegin.Y,' BlockEnd=',BlockEnd.X,',',BlockEnd.Y);
 | |
|   DoDecPaintLock(Self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetParagraphBlock(Value: TPoint);
 | |
| var
 | |
|   ParagraphStartLine, ParagraphEndLine, ParagraphEndX: integer;
 | |
| 
 | |
| begin
 | |
|   DoIncPaintLock(Self); // No editing is taking place
 | |
|   ParagraphStartLine := MinMax(Value.y,   1, FTheLinesView.Count);
 | |
|   ParagraphEndLine   := MinMax(Value.y+1, 1, FTheLinesView.Count);
 | |
|   ParagraphEndX := 1;
 | |
|   while (ParagraphStartLine > 1) and
 | |
|         (Trim(FTheLinesView[ParagraphStartLine-1])<>'')
 | |
|   do
 | |
|     dec(ParagraphStartLine);
 | |
|   while (ParagraphEndLine <= FTheLinesView.Count) and
 | |
|         (Trim(FTheLinesView[ParagraphEndLine-1])<>'')
 | |
|   do
 | |
|     inc(ParagraphEndLine);
 | |
|   if (ParagraphEndLine > FTheLinesView.Count) then begin
 | |
|     dec(ParagraphEndLine);
 | |
|     ParagraphEndX := length(FTheLinesView[ParagraphEndLine-1]) + 1;
 | |
|   end;
 | |
|   FBlockSelection.StartLineBytePos := Point(1, ParagraphStartLine);
 | |
|   FBlockSelection.EndLineBytePos   := Point(ParagraphEndX, ParagraphEndLine);
 | |
|   FBlockSelection.ActiveSelectionMode := smNormal;
 | |
|   CaretXY := FBlockSelection.EndLineBytePos;
 | |
|   //DebugLn(' FFF3 ',Value.X,',',Value.Y,' BlockBegin=',BlockBegin.X,',',BlockBegin.Y,' BlockEnd=',BlockEnd.X,',',BlockEnd.Y);
 | |
|   DoDecPaintLock(Self);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCanUndo: Boolean;
 | |
| begin
 | |
|   result := fUndoList.CanUndo;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCanRedo: Boolean;
 | |
| begin
 | |
|   result := fRedoList.CanUndo;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetCanPaste:Boolean;
 | |
| begin
 | |
|   Result := Clipboard.HasFormat(CF_TEXT)
 | |
|     or Clipboard.HasFormat(TSynClipboardStream.ClipboardFormatId)
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.Redo;
 | |
| var
 | |
|   Item: TSynEditUndoItem;
 | |
|   Group: TSynEditUndoGroup;
 | |
| begin
 | |
|   Group := fRedoList.PopItem;
 | |
|   if Group <> nil then begin;
 | |
|     {$IFDEF SynUndoDebugCalls}
 | |
|     DebugLnEnter(['>> TCustomSynEdit.Redo ',DbgSName(self), ' ', dbgs(Self), ' Group', dbgs(Group), ' cnt=', Group.Count]);
 | |
|     {$ENDIF}
 | |
|     IncPaintLock;
 | |
|     FTheLinesView.IsRedoing := True;
 | |
|     Item := Group.Pop;
 | |
|     if Item <> nil then begin
 | |
|       InternalBeginUndoBlock;
 | |
|       fUndoList.CurrentGroup.Reason := Group.Reason;
 | |
|       fUndoList.IsInsideRedo := True;
 | |
|       try
 | |
|         repeat
 | |
|           RedoItem(Item);
 | |
|           Item := Group.Pop;
 | |
|         until (Item = nil);
 | |
|       finally
 | |
|         InternalEndUndoBlock;
 | |
|       end;
 | |
|     end;
 | |
|     FTheLinesView.IsRedoing := False;
 | |
|     Group.Free;
 | |
|     if fRedoList.IsTopMarkedAsUnmodified then
 | |
|       fUndoList.MarkTopAsUnmodified;
 | |
|     DecPaintLock;
 | |
|     {$IFDEF SynUndoDebugCalls}
 | |
|     DebugLnExit(['<< TCustomSynEdit.Redo ',DbgSName(self), ' ', dbgs(Self)]);
 | |
|   end else begin
 | |
|     DebugLn(['<< TCustomSynEdit.Redo - NO GROUP ',DbgSName(self), ' ', dbgs(Self)]);
 | |
|     {$ENDIF}
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RedoItem(Item: TSynEditUndoItem);
 | |
| var
 | |
|   Line, StrToDelete: PChar;
 | |
|   x, y, Len, Len2: integer;
 | |
| 
 | |
|   function GetLeadWSLen : integer;
 | |
|   var
 | |
|     Run : PChar;
 | |
|   begin
 | |
|     Run := Line;
 | |
|     while (Run[0] in [' ', #9]) do
 | |
|       Inc(Run);
 | |
|     Result := Run - Line;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   if Assigned(Item) then
 | |
|   try
 | |
|     FCaret.IncForcePastEOL;
 | |
|     if Item.ClassType = TSynEditUndoIndent then
 | |
|     begin // re-insert the column
 | |
|       SetCaretAndSelection(LogicalToPhysicalPos(Point(1,TSynEditUndoIndent(Item).FPosY1)),
 | |
|         Point(1, TSynEditUndoIndent(Item).FPosY1), Point(2, TSynEditUndoIndent(Item).FPosY2),
 | |
|         smNormal);
 | |
|       x := FBlockIndent;
 | |
|       y := FBlockTabIndent;
 | |
|       FBlockIndent := TSynEditUndoIndent(Item).FCnt;
 | |
|       FBlockTabIndent := TSynEditUndoIndent(Item).FTabCnt;
 | |
|       DoBlockIndent;
 | |
|       FBlockIndent := x;
 | |
|       FBlockTabIndent := y;
 | |
|     end
 | |
|     else
 | |
|     if Item.ClassType = TSynEditUndoUnIndent then
 | |
|     begin // re-delete the (raggered) column
 | |
|       // add to undo list
 | |
|       fUndoList.AddChange(TSynEditUndoUnIndent.Create(TSynEditUndoUnIndent(Item).FPosY1,
 | |
|          TSynEditUndoUnIndent(Item).FPosY2, TSynEditUndoUnIndent(Item).FText));
 | |
|       // Delete string
 | |
|       fUndoList.Lock;
 | |
|       StrToDelete := PChar(TSynEditUndoUnIndent(Item).FText);
 | |
|       x := -1;
 | |
|       for y := TSynEditUndoUnIndent(Item).FPosY1 to TSynEditUndoUnIndent(Item).FPosY2 do begin
 | |
|         Line := PChar(FTheLinesView[y - 1]);
 | |
|         Len := GetLeadWSLen;
 | |
|         Len2 := GetEOL(StrToDelete) - StrToDelete;
 | |
|         if (Len2 > 0) and (Len >= Len2) then
 | |
|           FTheLinesView.EditDelete(1+Len-Len2, y, Len2);
 | |
|         inc(StrToDelete, Len2+1);
 | |
|       end;
 | |
|       fUndoList.Unlock;
 | |
|     end
 | |
|     else
 | |
|       if not Item.PerformUndo(self) then
 | |
|         FTheLinesView.EditRedo(Item);
 | |
|   finally
 | |
|     FCaret.DecForcePastEOL;
 | |
|     Item.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UpdateCursor;
 | |
| begin
 | |
|   if (sfHideCursor in FStateFlags) and (eoAutoHideCursor in fOptions2) then begin
 | |
|     SetCursor(crNone);
 | |
|     exit;
 | |
|   end;
 | |
| 
 | |
|   if (FLastMousePoint.X >= FTextArea.Bounds.Left) and (FLastMousePoint.X <  FTextArea.Bounds.Right) and
 | |
|      (FLastMousePoint.Y >= FTextArea.Bounds.Top) and (FLastMousePoint.Y < FTextArea.Bounds.Bottom)
 | |
|   then begin
 | |
|     if Assigned(FMarkupCtrlMouse) and (FMarkupCtrlMouse.Cursor <> crDefault) then
 | |
|       Cursor := FMarkupCtrlMouse.Cursor
 | |
|     else
 | |
|       Cursor := crIBeam;
 | |
|   end
 | |
|   else
 | |
|     Cursor := crDefault;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.Undo;
 | |
| var
 | |
|   Item: TSynEditUndoItem;
 | |
|   Group: TSynEditUndoGroup;
 | |
| begin
 | |
|   Group := fUndoList.PopItem;
 | |
|   if Group <> nil then begin;
 | |
|     {$IFDEF SynUndoDebugCalls}
 | |
|     DebugLnEnter(['>> TCustomSynEdit.Undo ',DbgSName(self), ' ', dbgs(Self), ' Group', dbgs(Group), ' cnt=', Group.Count]);
 | |
|     {$ENDIF}
 | |
|     IncPaintLock;
 | |
|     FTheLinesView.IsUndoing := True;
 | |
|     Item := Group.Pop;
 | |
|     if Item <> nil then begin
 | |
|       InternalBeginUndoBlock(fRedoList);
 | |
|       fRedoList.CurrentGroup.Reason := Group.Reason;
 | |
|       fUndoList.Lock;
 | |
|       try
 | |
|         repeat
 | |
|           UndoItem(Item);
 | |
|           Item := Group.Pop;
 | |
|         until (Item = nil);
 | |
|       finally
 | |
|         // Todo: Decide what do to, If there are any trimable spaces.
 | |
|         FTrimmedLinesView.ForceTrim;
 | |
|         fUndoList.UnLock;
 | |
|         InternalEndUndoBlock(fRedoList);
 | |
|       end;
 | |
|     end;
 | |
|     FTheLinesView.IsUndoing := False;
 | |
|     Group.Free;
 | |
|     if fUndoList.IsTopMarkedAsUnmodified then
 | |
|       fRedoList.MarkTopAsUnmodified;
 | |
|     DecPaintLock;
 | |
|     {$IFDEF SynUndoDebugCalls}
 | |
|     DebugLnExit(['<< TCustomSynEdit.Undo ',DbgSName(self), ' ', dbgs(Self)]);
 | |
|   end else begin
 | |
|     DebugLn(['<< TCustomSynEdit.Undo - NO GROUP ',DbgSName(self), ' ', dbgs(Self)]);
 | |
|     {$ENDIF}
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UndoItem(Item: TSynEditUndoItem);
 | |
| var
 | |
|   Line, OldText: PChar;
 | |
|   y, Len, Len2, LenT: integer;
 | |
| 
 | |
|   function GetLeadWSLen : integer;
 | |
|   var
 | |
|     Run : PChar;
 | |
|   begin
 | |
|     Run := Line;
 | |
|     while (Run[0] in [' ', #9]) do
 | |
|       Inc(Run);
 | |
|     Result := Run - Line;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   if Assigned(Item) then try
 | |
|     FCaret.IncForcePastEOL;
 | |
| 
 | |
|     if Item.ClassType = TSynEditUndoIndent then
 | |
|     begin
 | |
|       // add to redo list
 | |
|       fRedoList.AddChange(TSynEditUndoIndent.Create(TSynEditUndoIndent(Item).FPosY1,
 | |
|          TSynEditUndoIndent(Item).FPosY2, TSynEditUndoIndent(Item).FCnt, TSynEditUndoIndent(Item).FTabCnt));
 | |
|       // quick unintend (must all be spaces, as inserted...)
 | |
|       fRedoList.Lock;
 | |
|       Len2 := TSynEditUndoIndent(Item).FCnt;
 | |
|       LenT := TSynEditUndoIndent(Item).FTabCnt;
 | |
|       for y := TSynEditUndoIndent(Item).FPosY1 to TSynEditUndoIndent(Item).FPosY2 do begin
 | |
|         Line := PChar(FTheLinesView[y - 1]);
 | |
|         Len := GetLeadWSLen;
 | |
|         FTheLinesView.EditDelete(Len+1-Len2, y, Len2);
 | |
|         FTheLinesView.EditDelete(1, y, LenT);
 | |
|       end;
 | |
|       fRedoList.Unlock;
 | |
|     end
 | |
|     else
 | |
| 
 | |
|     if Item.ClassType = TSynEditUndoUnIndent then
 | |
|     begin
 | |
|       fRedoList.AddChange(TSynEditUndoUnIndent.Create(TSynEditUndoUnIndent(Item).FPosY1,
 | |
|           TSynEditUndoUnIndent(Item).FPosY2, TSynEditUndoUnIndent(Item).FText));
 | |
|       // reinsert the string
 | |
|       fRedoList.Lock;
 | |
|       OldText := PChar(TSynEditUndoUnIndent(Item).FText);
 | |
|       for y := TSynEditUndoUnIndent(Item).FPosY1 to TSynEditUndoUnIndent(Item).FPosY2 do begin
 | |
|         Line := PChar(FTheLinesView[y - 1]);
 | |
|         Len := GetLeadWSLen;
 | |
|         Len2 := GetEOL(OldText) - OldText;
 | |
|         if Len2 > 0 then
 | |
|           FTheLinesView.EditInsert(Len+1, y, copy(OldText, 1, Len2));
 | |
|         inc(OldText, Len2+1);
 | |
|       end;
 | |
|       fRedoList.Unlock;
 | |
|     end
 | |
| 
 | |
|     else
 | |
|       if not Item.PerformUndo(self) then
 | |
|         FTheLinesView.EditUndo(Item);
 | |
|   finally
 | |
|     FTrimmedLinesView.UndoTrimmedSpaces := False;
 | |
|     FCaret.DecForcePastEOL;
 | |
|     Item.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetFoldState(const AValue: String);
 | |
| begin
 | |
|   if assigned(fHighlighter) then begin
 | |
|     fHighlighter.CurrentLines := FTheLinesView;
 | |
|     if fHighlighter.NeedScan then begin
 | |
|       FPendingFoldState := AValue;
 | |
|       exit;
 | |
|     end;
 | |
|   end;
 | |
|   if sfAfterLoadFromFileNeeded in fStateFlags then begin
 | |
|     FPendingFoldState := AValue;
 | |
|     exit;
 | |
|   end;
 | |
|   FFoldedLinesView.Lock;
 | |
|   FFoldedLinesView.ApplyFoldDescription(0, 0, -1, -1, PChar(AValue), length(AValue), True);
 | |
|   TopView := TopView; // Todo: reset TopView on foldedview
 | |
|   FFoldedLinesView.UnLock;
 | |
|   FPendingFoldState := '';
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetHighlightAllColor(AValue: TSynSelectedColor);
 | |
| begin
 | |
|   fMarkupHighAll.MarkupInfo.Assign(AValue);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetIncrementColor(AValue: TSynSelectedColor);
 | |
| begin
 | |
|   fMarkupSelection.MarkupInfoIncr.Assign(AValue);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetLineHighlightColor(AValue: TSynSelectedColor);
 | |
| begin
 | |
|   fMarkupSpecialLine.MarkupLineHighlightInfo.Assign(AValue);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetMouseActions(const AValue: TSynEditMouseActions);
 | |
| begin
 | |
|   FMouseActions.UserActions := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetMouseLinkColor(AValue: TSynSelectedColor);
 | |
| begin
 | |
|   fMarkupCtrlMouse.MarkupInfo.Assign(AValue);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetMouseSelActions(const AValue: TSynEditMouseActions);
 | |
| begin
 | |
|   FMouseSelActions.UserActions := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetMouseTextActions(AValue: TSynEditMouseActions);
 | |
| begin
 | |
|   FMouseTextActions.UserActions := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetPaintLockOwner(const AValue: TSynEditBase);
 | |
| begin
 | |
|   TSynEditStringList(FLines).PaintLockOwner := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetShareOptions(const AValue: TSynEditorShareOptions);
 | |
| var
 | |
|   ChangedOptions: TSynEditorShareOptions;
 | |
|   OldMarkList: TSynEditMarkList;
 | |
|   it: TSynEditMarkIterator;
 | |
|   MListShared: Boolean;
 | |
| begin
 | |
|   if FShareOptions = AValue then exit;
 | |
| 
 | |
|   ChangedOptions:=(FShareOptions - AValue) + (AValue - FShareOptions);
 | |
|   FShareOptions := AValue;
 | |
| 
 | |
|   if (eosShareMarks in ChangedOptions) then begin
 | |
|     MListShared := IsMarkListShared;
 | |
|     if ( (FShareOptions * [eosShareMarks] = []) and MListShared ) or
 | |
|        ( (eosShareMarks  in FShareOptions) and (not MListShared) and
 | |
|          (TSynEditStringList(FLines).AttachedSynEditCount > 1) )
 | |
|     then begin
 | |
|       OldMarkList := FMarkList;
 | |
|       FMarkList := nil;
 | |
|       RecreateMarkList;
 | |
|       it := TSynEditMarkIterator.Create(OldMarkList);
 | |
|       it.GotoBOL;
 | |
|       while it.Next do begin
 | |
|         // Todo: prevent notifications
 | |
|         if it.Mark.OwnerEdit = Self then
 | |
|           FMarkList.Add(it.Mark);
 | |
|       end;
 | |
|       it.Free;
 | |
|       FreeAndNil(FMarkList);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ChangeTextBuffer(NewBuffer: TSynEditStringList);
 | |
| var
 | |
|   OldBuffer: TSynEditStringList;
 | |
|   LView: TSynEditStrings;
 | |
|   i: Integer;
 | |
| begin
 | |
|   FLines.SendNotification(senrTextBufferChanging, FLines); // Send the old buffer
 | |
|   DestroyMarkList;
 | |
| 
 | |
|   // Detach Highlighter
 | |
|   if FHighlighter <> nil then
 | |
|     FHighlighter.DetachFromLines(FLines);
 | |
| 
 | |
|   // Set the New Lines
 | |
|   OldBuffer := TSynEditStringList(FLines);
 | |
| 
 | |
|   Flines := NewBuffer;
 | |
|   TSynEditStringList(FLines).AttachSynEdit(Self);
 | |
|   TSynEditStringsLinked(FTopLinesView).NextLines := FLines;
 | |
| 
 | |
|   // Todo: Todo Refactor all classes with events, so they an be told to re-attach
 | |
|   NewBuffer.CopyHanlders(OldBuffer, self);
 | |
|   LView := FTheLinesView;
 | |
|   while (LView is TSynEditStringsLinked) and (LView <> FLines) do begin
 | |
|     NewBuffer.CopyHanlders(OldBuffer, LView);
 | |
|     LView := TSynEditStringsLinked(LView).NextLines;
 | |
|   end;
 | |
|   NewBuffer.CopyHanlders(OldBuffer, FFoldedLinesView);
 | |
|   //NewBuffer.CopyHanlders(OldBuffer, FMarkList);
 | |
|   NewBuffer.CopyHanlders(OldBuffer, FCaret);
 | |
|   NewBuffer.CopyHanlders(OldBuffer, FInternalCaret);
 | |
|   NewBuffer.CopyHanlders(OldBuffer, FBlockSelection);
 | |
|   NewBuffer.CopyHanlders(OldBuffer, FInternalBlockSelection);
 | |
|   NewBuffer.CopyHanlders(OldBuffer, fMarkupManager);
 | |
|   for i := 0 to fMarkupManager.Count - 1 do
 | |
|     NewBuffer.CopyHanlders(OldBuffer, fMarkupManager.Markup[i]);
 | |
|   for i := 0 to FPlugins.Count - 1 do
 | |
|     NewBuffer.CopyHanlders(OldBuffer, Plugin[i]);
 | |
| 
 | |
|   FUndoList := NewBuffer.UndoList;
 | |
|   FRedoList := NewBuffer.RedoList;
 | |
| 
 | |
|   // Recreate te public access to FLines
 | |
|   FreeAndNil(FStrings);
 | |
|   FStrings := TSynEditLines.Create(TSynEditStringList(FLines), {$IFDEF FPC}@{$ENDIF}MarkTextAsSaved);
 | |
| 
 | |
|   // Flines has been set to the new buffer; and self is attached to the new FLines
 | |
|   // FTheLinesView points to new FLines
 | |
|   RecreateMarkList;
 | |
| 
 | |
|   // Attach Highlighter
 | |
|   if FHighlighter <> nil then
 | |
|     FHighlighter.AttachToLines(FLines);
 | |
| 
 | |
|   RemoveHandlers(OldBuffer);
 | |
|   OldBuffer.DetachSynEdit(Self);
 | |
|   FLines.SendNotification(senrTextBufferChanged, OldBuffer); // Send the old buffer
 | |
|   OldBuffer.SendNotification(senrTextBufferChanged, OldBuffer); // Send the old buffer
 | |
|   if OldBuffer.AttachedSynEditCount = 0 then
 | |
|     OldBuffer.Free;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.IsMarkListShared: Boolean;
 | |
| var
 | |
|   i, j: Integer;
 | |
| begin
 | |
|   j := 0;
 | |
|   i := TSynEditStringList(FLines).AttachedSynEditCount - 1;
 | |
|   while (i >= 0) and (j <= 1) do begin
 | |
|     if TCustomSynEdit(TSynEditStringList(FLines).AttachedSynEdits[i]).FMarkList = FMarkList then
 | |
|       inc(j);
 | |
|     dec(i);
 | |
|   end;
 | |
|   Result := j > 1;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RecreateMarkList;
 | |
| var
 | |
|   s: TSynEditBase;
 | |
|   i: Integer;
 | |
| begin
 | |
|   DestroyMarkList;
 | |
| 
 | |
|   if (TSynEditStringList(FLines).AttachedSynEditCount > 1) and
 | |
|      (eosShareMarks in FShareOptions)
 | |
|   then begin
 | |
|     s := TSynEditStringList(FLines).AttachedSynEdits[0];
 | |
|     if s = Self then
 | |
|       s := TSynEditStringList(FLines).AttachedSynEdits[1];
 | |
|     FMarkList := TCustomSynEdit(s).FMarkList;
 | |
|     TSynEditMarkListInternal(fMarkList).AddOwnerEdit(Self);
 | |
|     for i := 0 to 9 do
 | |
|       FBookMarks[i] := TCustomSynEdit(s).fBookMarks[i];
 | |
|   end
 | |
|   else begin
 | |
|     FMarkList := TSynEditMarkListInternal.Create(self, FTheLinesView);
 | |
|     for i := 0 to 9 do
 | |
|       FBookMarks[i] := nil;
 | |
|   end;
 | |
| 
 | |
|   FMarkList.RegisterChangeHandler({$IFDEF FPC}@{$ENDIF}MarkListChange,
 | |
|     [low(TSynEditMarkChangeReason)..high(TSynEditMarkChangeReason)]);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DestroyMarkList;
 | |
| var
 | |
|   it: TSynEditMarkIterator;
 | |
|   s: TSynEditBase;
 | |
| begin
 | |
|   if FMarkList = nil then
 | |
|     exit;
 | |
| 
 | |
|   TSynEditMarkListInternal(fMarkList).RemoveOwnerEdit(Self);
 | |
|   FMarkList.UnRegisterChangeHandler({$IFDEF FPC}@{$ENDIF}MarkListChange);
 | |
| 
 | |
|   if IsMarkListShared then begin
 | |
|     s := TSynEditStringList(FLines).AttachedSynEdits[0];
 | |
|     if s = Self then
 | |
|       s := TSynEditStringList(FLines).AttachedSynEdits[1]; // TODO: find one that shares the MarkList (if someday partial sharing of Marks is avail)
 | |
| 
 | |
|     if TSynEditMarkListInternal(FMarkList).LinesView = FTheLinesView then
 | |
|       TSynEditMarkListInternal(FMarkList).LinesView := TCustomSynEdit(s).FTheLinesView;
 | |
| 
 | |
|     it := TSynEditMarkIterator.Create(FMarkList);
 | |
|     it.GotoBOL;
 | |
|     while it.Next do begin
 | |
|       // Todo: prevent notifications
 | |
|       if it.Mark.OwnerEdit = Self then
 | |
|         it.Mark.OwnerEdit := s;
 | |
|     end;
 | |
|     it.Free;
 | |
|     FMarkList := nil;
 | |
|   end
 | |
|   else
 | |
|     FreeAndNil(FMarkList);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ShareTextBufferFrom(AShareEditor: TCustomSynEdit);
 | |
| begin
 | |
|   if fPaintLock <> 0 then RaiseGDBException('Cannot change TextBuffer while paintlocked');
 | |
| 
 | |
|   ChangeTextBuffer(TSynEditStringList(AShareEditor.FLines));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnShareTextBuffer;
 | |
| begin
 | |
|   if fPaintLock <> 0 then RaiseGDBException('Cannot change TextBuffer while paintlocked');
 | |
|   if TSynEditStringList(FLines).AttachedSynEditCount = 1 then
 | |
|     exit;
 | |
| 
 | |
|   ChangeTextBuffer(TSynEditStringList.Create);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RemoveHandlers(ALines: TSynEditStrings = nil);
 | |
| var
 | |
|   LView: TSynEditStrings;
 | |
|   i: Integer;
 | |
| begin
 | |
|   if not assigned(ALines) then
 | |
|     ALines := FLines;
 | |
| 
 | |
|   // Todo: aggregated objects, should be responsible themself
 | |
|   TSynEditStringList(ALines).RemoveHanlders(self);
 | |
|   LView := FTheLinesView;
 | |
|   while (LView is TSynEditStringsLinked) and (LView <> ALines) do begin
 | |
|     TSynEditStringList(ALines).RemoveHanlders(LView);
 | |
|     LView := TSynEditStringsLinked(LView).NextLines;
 | |
|   end;
 | |
|   TSynEditStringList(ALines).RemoveHanlders(FFoldedLinesView);
 | |
|   TSynEditStringList(ALines).RemoveHanlders(FCaret);
 | |
|   TSynEditStringList(ALines).RemoveHanlders(FInternalCaret);
 | |
|   TSynEditStringList(ALines).RemoveHanlders(FBlockSelection);
 | |
|   TSynEditStringList(ALines).RemoveHanlders(FInternalBlockSelection);
 | |
|   TSynEditStringList(ALines).RemoveHanlders(fMarkupManager);
 | |
|   for i := 0 to fMarkupManager.Count - 1 do
 | |
|     TSynEditStringList(ALines).RemoveHanlders(fMarkupManager.Markup[i]);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ExtraLineCharsChanged(Sender: TObject);
 | |
| begin
 | |
|   UpdateScrollBars;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetTextBetweenPointsSimple(aStartPoint, aEndPoint: TPoint;
 | |
|   const AValue: String);
 | |
| begin
 | |
|   InternalBeginUndoBlock;
 | |
|   try
 | |
|     FInternalBlockSelection.SelectionMode := smNormal;
 | |
|     FInternalBlockSelection.StartLineBytePos := aStartPoint;
 | |
|     FInternalBlockSelection.EndLineBytePos := aEndPoint;
 | |
|     FInternalBlockSelection.SelText := AValue;
 | |
|   finally
 | |
|     InternalEndUndoBlock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetTextBetweenPointsEx(aStartPoint, aEndPoint: TPoint;
 | |
|   aCaretMode: TSynCaretAdjustMode; const AValue: String);
 | |
| begin
 | |
|   SetTextBetweenPoints(aStartPoint, aEndPoint, AValue, [], aCaretMode);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetTextBetweenPoints(aStartPoint, aEndPoint: TPoint;
 | |
|   const AValue: String; aFlags: TSynEditTextFlags; aCaretMode: TSynCaretAdjustMode;
 | |
|   aMarksMode: TSynMarksAdjustMode; aSelectionMode: TSynSelectionMode);
 | |
| begin
 | |
|   InternalBeginUndoBlock;
 | |
|   try
 | |
|     if aCaretMode = scamAdjust then
 | |
|       FCaret.IncAutoMoveOnEdit;
 | |
| 
 | |
|     if aSelectionMode = smCurrent then
 | |
|       FInternalBlockSelection.SelectionMode    := FBlockSelection.ActiveSelectionMode
 | |
|     else
 | |
|       FInternalBlockSelection.SelectionMode    := aSelectionMode;
 | |
|     FInternalBlockSelection.StartLineBytePos := aStartPoint;
 | |
|     FInternalBlockSelection.EndLineBytePos   := aEndPoint;
 | |
|     aStartPoint := FInternalBlockSelection.FirstLineBytePos;
 | |
| 
 | |
|     if aCaretMode = scamBegin then
 | |
|       FCaret.LineBytePos := aStartPoint;
 | |
| 
 | |
|     FInternalBlockSelection.SetSelTextPrimitive(FInternalBlockSelection.ActiveSelectionMode,
 | |
|                                                 PChar(AValue),
 | |
|                                                 aMarksMode = smaKeep
 | |
|                                                );
 | |
|     if aCaretMode = scamEnd then
 | |
|       FCaret.LineBytePos := FInternalBlockSelection.StartLineBytePos;
 | |
|     if setSelect in aFlags then begin
 | |
|       FBlockSelection.StartLineBytePos    := aStartPoint;
 | |
|       FBlockSelection.ActiveSelectionMode := FInternalBlockSelection.SelectionMode;
 | |
|       FBlockSelection.EndLineBytePos      := FInternalBlockSelection.StartLineBytePos;
 | |
|       if FBlockSelection.ActiveSelectionMode = smLine then
 | |
|         FBlockSelection.EndLineBytePos := Point(FBlockSelection.StartBytePos + 1, FBlockSelection.EndLinePos - 1);
 | |
|     end;
 | |
|   finally
 | |
|     if aCaretMode = scamAdjust then
 | |
|       FCaret.DecAutoMoveOnEdit;
 | |
|     InternalEndUndoBlock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetVisibleSpecialChars(AValue: TSynVisibleSpecialChars);
 | |
| begin
 | |
|   if FVisibleSpecialChars = AValue then Exit;
 | |
|   FVisibleSpecialChars := AValue;
 | |
|   fMarkupSpecialChar.VisibleSpecialChars := AValue;
 | |
|   if eoShowSpecialChars in Options
 | |
|   then FPaintArea.VisibleSpecialChars := AValue
 | |
|   else FPaintArea.VisibleSpecialChars := [];
 | |
|   if eoShowSpecialChars in Options then Invalidate;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetLineState(ALine: Integer): TSynLineState;
 | |
| begin
 | |
|   with TSynEditStringList(fLines) do
 | |
|     if [sfModified, sfSaved] * Flags[ALine] = [sfModified] then
 | |
|       Result := slsUnsaved
 | |
|     else
 | |
|     if [sfModified, sfSaved] * Flags[ALine] = [sfModified, sfSaved] then
 | |
|       Result := slsSaved
 | |
|     else
 | |
|       Result := slsNone;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ClearBookMark(BookMark: Integer);
 | |
| begin
 | |
|   if (BookMark in [0..9]) and assigned(fBookMarks[BookMark]) then
 | |
|     FBookMarks[BookMark].Free;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.GotoBookMark(BookMark: Integer);
 | |
| var
 | |
|   LogCaret: TPoint;
 | |
| begin
 | |
|   if (BookMark in [0..9]) and assigned(fBookMarks[BookMark])
 | |
|     and (fBookMarks[BookMark].Line <= fLines.Count)
 | |
|   then begin
 | |
|     LogCaret:=Point(fBookMarks[BookMark].Column, fBookMarks[BookMark].Line);
 | |
|     DoIncPaintLock(Self); // No editing is taking place
 | |
|     FCaret.LineBytePos := LogCaret;
 | |
|     SetBlockEnd(LogCaret);
 | |
|     SetBlockBegin(LogCaret);
 | |
|     DoDecPaintLock(Self);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.IsLinkable(Y, X1, X2: Integer): Boolean;
 | |
| begin
 | |
|   Result := X1 <> X2;
 | |
|   if Result and Assigned(FOnMouseLink) then
 | |
|     FOnMouseLink(Self, X1, Y, Result);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetBookMark(BookMark: Integer; X: Integer; Y: Integer);
 | |
| var
 | |
|   i: Integer;
 | |
|   mark: TSynEditMark;
 | |
| begin
 | |
|   if (BookMark in [0..9]) and (Y >= 1) and (Y <= Max(1, fLines.Count)) then
 | |
|   begin
 | |
|     mark := TSynEditMark.Create(self);
 | |
|     X := PhysicalToLogicalPos(Point(X, Y)).x;
 | |
|     with mark do begin
 | |
|       Line := Y;
 | |
|       Column := X;
 | |
|       ImageIndex := Bookmark;
 | |
|       BookmarkNumber := Bookmark;
 | |
|       Visible := true;
 | |
|       InternalImage := (fBookMarkOpt.BookmarkImages = nil);
 | |
|     end;
 | |
|     for i := 0 to 9 do
 | |
|       if assigned(fBookMarks[i]) and (fBookMarks[i].Line = Y) then
 | |
|         ClearBookmark(i);
 | |
|     if assigned(fBookMarks[BookMark]) then
 | |
|       ClearBookmark(BookMark);
 | |
|     FMarkList.Add(mark);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WndProc(var Msg: TMessage);
 | |
| // Prevent Alt-Backspace from beeping
 | |
| const
 | |
|   ALT_KEY_DOWN = $20000000;
 | |
| begin
 | |
|   if (Msg.Msg = WM_SYSCHAR) and (Msg.wParam = VK_BACK) and
 | |
|     (Msg.lParam and ALT_KEY_DOWN <> 0)
 | |
|   then
 | |
|     Msg.Msg := 0
 | |
|   else
 | |
|     inherited;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.InsertTextAtCaret(aText: String; aCaretMode : TSynCaretAdjustMode = scamEnd);
 | |
| begin
 | |
|   TextBetweenPointsEx[FCaret.LineBytePos, FCaret.LineBytePos, aCaretMode] := aText;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DragOver(Source: TObject; X, Y: Integer;
 | |
|   State: TDragState; var Accept: Boolean);
 | |
| begin
 | |
|   inherited;
 | |
|   LastMouseCaret:=Point(-1,-1);
 | |
|   if (Source is TCustomSynEdit) and not TCustomSynEdit(Source).ReadOnly then
 | |
|   begin
 | |
|     Accept := True;
 | |
|     //Ctrl is pressed => change cursor to indicate copy instead of move
 | |
|     if GetKeyState(VK_CONTROL) < 0 then
 | |
|       DragCursor := crMultiDrag
 | |
|     else
 | |
|       DragCursor := crDrag;
 | |
|     FBlockSelection.IncPersistentLock;
 | |
|     if State = dsDragLeave then //restore prev caret position
 | |
|       ComputeCaret(FMouseDownX, FMouseDownY)
 | |
|     else //position caret under the mouse cursor
 | |
|       ComputeCaret(X, Y);
 | |
|     FBlockSelection.DecPersistentLock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DragDrop(Source: TObject; X, Y: Integer);
 | |
| var
 | |
|   NewCaret: TPoint;
 | |
|   DoDrop, DropAfter, DropMove: boolean;
 | |
|   BB, BE: TPoint;
 | |
|   DragDropText: string;
 | |
|   Adjust: integer;
 | |
|   FoldInfo: String;
 | |
|   BlockSel: TSynEditSelection;
 | |
| begin
 | |
|   if not ReadOnly  and (Source is TCustomSynEdit)
 | |
|     and TCustomSynEdit(Source).SelAvail
 | |
|   then begin
 | |
|     IncPaintLock;
 | |
|     try
 | |
|       inherited;
 | |
|       ComputeCaret(X, Y);
 | |
|       NewCaret := CaretXY;
 | |
|       // if from other control then move when SHIFT, else copy
 | |
|       // if from Self then copy when CTRL, else move
 | |
|       if Source <> Self then begin
 | |
|         DropMove := GetKeyState(VK_SHIFT) < 0;
 | |
|         DoDrop := TRUE;
 | |
|         DropAfter := FALSE;
 | |
|       end else begin
 | |
|         DropMove := GetKeyState(VK_CONTROL) >= 0;
 | |
|         BB := BlockBegin;
 | |
|         BE := BlockEnd;
 | |
|         DropAfter := (NewCaret.Y > BE.Y)
 | |
|           or ((NewCaret.Y = BE.Y) and (NewCaret.X > BE.X));
 | |
|         DoDrop := DropAfter or (NewCaret.Y < BB.Y)
 | |
|           or ((NewCaret.Y = BB.Y) and (NewCaret.X < BB.X));
 | |
|       end;
 | |
|       if DoDrop then begin
 | |
|         InternalBeginUndoBlock;                                                         //mh 2000-11-20
 | |
|         try
 | |
|           DragDropText := TCustomSynEdit(Source).SelText;
 | |
|           BlockSel := TCustomSynEdit(Source).FBlockSelection;
 | |
|           if eoFoldedCopyPaste in fOptions2 then
 | |
|             FoldInfo :=  TCustomSynEdit(Source).FFoldedLinesView.GetFoldDescription(
 | |
|                   BlockSel.FirstLineBytePos.Y - 1, BlockSel.FirstLineBytePos.X,
 | |
|                   BlockSel.LastLineBytePos.Y - 1,  BlockSel.LastLineBytePos.X);
 | |
|           // delete the selected text if necessary
 | |
|           if DropMove then begin
 | |
|             if Source <> Self then
 | |
|               TCustomSynEdit(Source).SelText := ''
 | |
|             else begin
 | |
|               SetSelTextExternal('');
 | |
|               // adjust horizontal drop position
 | |
|               if DropAfter and (NewCaret.Y = BE.Y) then begin
 | |
|                 if BB.Y = BE.Y then
 | |
|                   Adjust := BE.X - BB.X
 | |
|                 else
 | |
|                   Adjust := BE.X - 1;
 | |
|                 Dec(NewCaret.X, Adjust);
 | |
|               end;
 | |
|               // adjust vertical drop position
 | |
|               if DropAfter and (BE.Y > BB.Y) then
 | |
|                 Dec(NewCaret.Y, BE.Y - BB.Y);
 | |
|             end;
 | |
|           end;
 | |
|           // insert the selected text
 | |
|           FCaret.IncForcePastEOL;
 | |
|           try
 | |
|             CaretXY := NewCaret;
 | |
|             BlockBegin := NewCaret;
 | |
|             SetSelTextPrimitive(smNormal, PChar(DragDropText), true);
 | |
|             if FoldInfo <> '' then begin
 | |
|               ScanRanges;
 | |
|               FFoldedLinesView.ApplyFoldDescription(NewCaret.Y -1, NewCaret.X,
 | |
|                     FBlockSelection.StartLinePos-1, FBlockSelection.StartBytePos,
 | |
|                     PChar(FoldInfo), length(FoldInfo));
 | |
|             end;
 | |
|           finally
 | |
|             FCaret.DecForcePastEOL;
 | |
|           end;
 | |
|           FCaret.LineCharPos := NewCaret;
 | |
|           BlockBegin := PhysicalToLogicalPos(NewCaret);
 | |
|           BlockEnd := LogicalCaretXY;
 | |
|         finally
 | |
|           InternalEndUndoBlock;
 | |
|         end;
 | |
|       end;
 | |
|     finally
 | |
|       DecPaintLock;
 | |
|     end;
 | |
|   end else
 | |
|     inherited;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetRightEdge(Value: Integer);
 | |
| begin
 | |
|   // Todo: check and invalidate in text area
 | |
|   if FTextArea.RightEdgeColumn <> Value then begin
 | |
|     FPaintArea.RightEdgeColumn := Value;
 | |
|     if FTextArea.RightEdgeVisible then
 | |
|       Invalidate;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetRightEdgeColor(Value: TColor);
 | |
| var
 | |
|   nX: integer;
 | |
|   rcInval: TRect;
 | |
| begin
 | |
|   // Todo: check and invalidate in text area
 | |
|   if RightEdgeColor <> Value then begin
 | |
|     FPaintArea.RightEdgeColor := Value;
 | |
|     if HandleAllocated then begin
 | |
|       nX := FTextArea.ScreenColumnToXValue(FTextArea.RightEdgeColumn + 1);
 | |
|       rcInval := Rect(nX - 1, 0, nX + 1, ClientHeight-ScrollBarWidth);
 | |
|       {$IFDEF VerboseSynEditInvalidate}
 | |
|       DebugLn(['TCustomSynEdit.SetRightEdgeColor ',dbgs(rcInval)]);
 | |
|       {$ENDIF}
 | |
|       InvalidateRect(Handle, @rcInval, FALSE);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetMaxUndo: Integer;
 | |
| begin
 | |
|   result := fUndoList.MaxUndoActions;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetMaxUndo(const Value: Integer);
 | |
| begin
 | |
|   if Value > -1 then begin
 | |
|     fUndoList.MaxUndoActions := Value;
 | |
|     fRedoList.MaxUndoActions := Value;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.Notification(AComponent: TComponent;
 | |
|   Operation: TOperation);
 | |
| begin
 | |
|   inherited Notification(AComponent, Operation);
 | |
|   if Operation = opRemove then begin
 | |
|     if AComponent = fHighlighter then begin
 | |
|       fHighlighter.DetachFromLines(FLines);
 | |
|       fHighlighter := nil;
 | |
|       fMarkupHighCaret.Highlighter := nil;
 | |
|       fMarkupWordGroup.Highlighter := nil;
 | |
|       FFoldedLinesView.Highlighter := nil;
 | |
|       FPaintArea.Highlighter := nil;
 | |
|       if not (csDestroying in ComponentState) then begin
 | |
|         RecalcCharExtent;
 | |
|         Invalidate;
 | |
|       end;
 | |
|     end;
 | |
|     if (fBookmarkOpt <> nil) then
 | |
|       if (AComponent = fBookmarkOpt.BookmarkImages) then begin
 | |
|         fBookmarkOpt.BookmarkImages := nil;
 | |
|         InvalidateGutterLines(-1, -1);
 | |
|       end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RemoveHooksFromHighlighter;
 | |
| begin
 | |
|   if not Assigned(fHighlighter) then
 | |
|     exit;
 | |
|   fHighlighter.UnhookAttrChangeEvent({$IFDEF FPC}@{$ENDIF}HighlighterAttrChanged);
 | |
|   fHighlighter.DetachFromLines(FLines);
 | |
|   fHighlighter.RemoveFreeNotification(self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetHighlighter(const Value: TSynCustomHighlighter);
 | |
| begin
 | |
|   if Value <> fHighlighter then begin
 | |
|     FPendingFoldState := '';
 | |
|     RemoveHooksFromHighlighter;
 | |
|     if Assigned(Value) then begin
 | |
|       Value.HookAttrChangeEvent(
 | |
|         {$IFDEF FPC}@{$ENDIF}HighlighterAttrChanged);
 | |
|       Value.FreeNotification(Self);
 | |
|       Value.AttachToLines(FLines);
 | |
|     end;
 | |
|     fHighlighter := Value;
 | |
|     IncPaintLock;
 | |
|     try
 | |
|       // Ensure to free all copies in SynEit.Notification too
 | |
|       fMarkupHighCaret.Highlighter := Value;
 | |
|       fMarkupWordGroup.Highlighter := Value;
 | |
|       FFoldedLinesView.Highlighter := Value;
 | |
|       FPaintArea.Highlighter := Value;
 | |
|       FWordBreaker.Reset;
 | |
|       if fHighlighter<>nil then begin
 | |
|         fTSearch.IdentChars := fHighlighter.IdentChars;
 | |
|         FWordBreaker.IdentChars     := fHighlighter.IdentChars;
 | |
|         FWordBreaker.WordBreakChars := fHighlighter.WordBreakChars;
 | |
|       end else begin
 | |
|         fTSearch.ResetIdentChars;
 | |
|       end;
 | |
|       RecalcCharExtent;
 | |
|       ScanRanges; // Todo: Skip if paintlocked
 | |
|       // There may not have been a scan
 | |
|       if fHighlighter <> nil then
 | |
|         FHighlighter.CurrentLines := FLines;
 | |
|       FLines.SendNotification(senrHighlightChanged, FLines, -1, -1);
 | |
|     finally
 | |
|       DecPaintLock;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetHideSelection(const Value: boolean);
 | |
| begin
 | |
|   if fHideSelection <> Value then begin
 | |
|     FHideSelection := Value;
 | |
|     Invalidate;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetInsertMode(const Value: boolean);
 | |
| begin
 | |
|   if fInserting <> Value then begin
 | |
|     fInserting := Value;
 | |
|     if InsertMode then
 | |
|       FScreenCaret.DisplayType := FInsertCaret
 | |
|     else
 | |
|       FScreenCaret.DisplayType := FOverwriteCaret;
 | |
|     StatusChanged([scInsertMode]);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetInsertCaret(const Value: TSynEditCaretType);
 | |
| begin
 | |
|   if FInsertCaret <> Value then begin
 | |
|     FInsertCaret := Value;
 | |
|     if InsertMode then
 | |
|       FScreenCaret.DisplayType := fInsertCaret;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetOverwriteCaret(const Value: TSynEditCaretType);
 | |
| begin
 | |
|   if FOverwriteCaret <> Value then begin
 | |
|     FOverwriteCaret := Value;
 | |
|     if not InsertMode then
 | |
|       FScreenCaret.DisplayType := fOverwriteCaret;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetMaxLeftChar(Value: integer);
 | |
| begin
 | |
|   Value := MinMax(Value, 1, MAX_SCROLL); // horz scrolling is only 16 bit
 | |
|   if fMaxLeftChar <> Value then begin
 | |
|     fMaxLeftChar := Value;
 | |
|     Invalidate;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.EnsureCursorPosVisible;
 | |
| var
 | |
|   PhysCaretXY: TPoint;
 | |
|   MinX: Integer;
 | |
|   MaxX: Integer;
 | |
|   PhysBlockBeginXY: TPoint;
 | |
|   PhysBlockEndXY: TPoint;
 | |
| begin
 | |
|   if (PaintLockOwner <> nil) and (PaintLockOwner <> Self) and
 | |
|      (not (eoAlwaysVisibleCaret in fOptions2))
 | |
|   then
 | |
|     exit;
 | |
| 
 | |
|   if (not HandleAllocated) or (fPaintLock > 0) or
 | |
|      (FWinControlFlags * [wcfInitializing, wcfCreatingHandle] <> [])
 | |
|   then begin
 | |
|     include(fStateFlags, sfEnsureCursorPos);
 | |
|     exit;
 | |
|   end;
 | |
| 
 | |
|   exclude(fStateFlags, sfEnsureCursorPos);
 | |
|   DoIncPaintLock(Self); // No editing is taking place
 | |
|   try
 | |
|     // Make sure X is visible
 | |
|     //DebugLn('[TCustomSynEdit.EnsureCursorPosVisible] A CaretX=',CaretX,' LeftChar=',LeftChar,' CharsInWindow=',CharsInWindow,' ClientWidth=',ClientWidth);
 | |
|     PhysCaretXY:=CaretXY;
 | |
|     // try to make the current selection visible as well
 | |
|     MinX:=PhysCaretXY.X;
 | |
|     MaxX:=PhysCaretXY.X;
 | |
|     if SelAvail then begin
 | |
|       PhysBlockBeginXY:=LogicalToPhysicalPos(BlockBegin);
 | |
|       PhysBlockEndXY:=LogicalToPhysicalPos(BlockEnd);
 | |
|       if (PhysBlockBeginXY.X<>PhysBlockEndXY.X)
 | |
|       or (PhysBlockBeginXY.Y<>PhysBlockEndXY.Y) then begin
 | |
|         if (FBlockSelection.ActiveSelectionMode <> smColumn) and
 | |
|            (PhysBlockBeginXY.Y<>PhysBlockEndXY.Y) then
 | |
|           PhysBlockBeginXY.X:=1;
 | |
|         if MinX>PhysBlockBeginXY.X then
 | |
|           MinX:=Max(PhysBlockBeginXY.X,PhysCaretXY.X-CharsInWindow+1);
 | |
|         if MinX>PhysBlockEndXY.X then
 | |
|           MinX:=Max(PhysBlockEndXY.X,PhysCaretXY.X-CharsInWindow+1);
 | |
|         if MaxX<PhysBlockBeginXY.X then
 | |
|           MaxX:=Min(PhysBlockBeginXY.X,MinX+CharsInWindow-1);
 | |
|         if MaxX<PhysBlockEndXY.X then
 | |
|           MaxX:=Min(PhysBlockEndXY.X,MinX+CharsInWindow-1);
 | |
|       end;
 | |
|     end;
 | |
|     {DebugLn('TCustomSynEdit.EnsureCursorPosVisible A CaretX=',dbgs(PhysCaretXY.X),
 | |
|       ' BlockX=',dbgs(PhysBlockBeginXY.X)+'-'+dbgs(PhysBlockEndXY.X),
 | |
|       ' CharsInWindow='+dbgs(CharsInWindow), MinX='+dbgs(MinX),' MaxX='+dbgs(MaxX),
 | |
|       ' LeftChar='+dbgs(LeftChar), '');}
 | |
|     if MinX < LeftChar then
 | |
|       LeftChar := MinX
 | |
|     else if LeftChar < MaxX - (Max(1, CharsInWindow) - 1 - FScreenCaret.ExtraLineChars) then
 | |
|       LeftChar := MaxX - (Max(1, CharsInWindow) - 1 - FScreenCaret.ExtraLineChars)
 | |
|     else
 | |
|       LeftChar := LeftChar;                                                     //mh 2000-10-19
 | |
|     //DebugLn(['TCustomSynEdit.EnsureCursorPosVisible B LeftChar=',LeftChar,' MinX=',MinX,' MaxX=',MaxX,' CharsInWindow=',CharsInWindow]);
 | |
|     // Make sure Y is visible
 | |
|     if CaretY < TopLine then
 | |
|       TopLine := CaretY
 | |
|     else if CaretY > ScreenRowToRow(Max(1, LinesInWindow) - 1) then             //mh 2000-10-19
 | |
|       TopLine := FFoldedLinesView.TextPosAddLines(CaretY, -Max(0, LinesInWindow-1))
 | |
|     else
 | |
|       TopView := TopView;                                                       //mh 2000-10-19
 | |
|   finally
 | |
|     DoDecPaintLock(Self);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetKeystrokes(const Value: TSynEditKeyStrokes);
 | |
| begin
 | |
|   if Value = nil then
 | |
|     FKeystrokes.Clear
 | |
|   else
 | |
|     FKeystrokes.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetExtraCharSpacing(const Value: integer);
 | |
| begin
 | |
|   if fExtraCharSpacing=Value then exit;
 | |
|   fExtraCharSpacing := Value;
 | |
|   FPaintArea.ExtraCharSpacing := Value;
 | |
|   FontChanged(self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetLastMouseCaret(const AValue: TPoint);
 | |
| begin
 | |
|   if (FLastMouseCaret.X=AValue.X) and (FLastMouseCaret.Y=AValue.Y) then exit;
 | |
|   FLastMouseCaret:=AValue;
 | |
|   if assigned(fMarkupCtrlMouse) then
 | |
|     fMarkupCtrlMouse.LastMouseCaret := AValue;
 | |
|   UpdateCursor;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetDefaultKeystrokes;
 | |
| begin
 | |
|   FKeystrokes.ResetDefaults;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ResetMouseActions;
 | |
| begin
 | |
|   FMouseActions.Options := FMouseOptions;
 | |
|   FMouseActions.ResetUserActions;
 | |
|   FMouseSelActions.Options := FMouseOptions;
 | |
|   FMouseSelActions.ResetUserActions;
 | |
|   FMouseTextActions.Options := FMouseOptions;
 | |
|   FMouseTextActions.ResetUserActions;
 | |
| 
 | |
|   FLeftGutter.ResetMouseActions;
 | |
|   FRightGutter.ResetMouseActions;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.CommandProcessor(Command: TSynEditorCommand;
 | |
|   AChar: TUTF8Char;
 | |
|   Data: pointer);
 | |
| var
 | |
|   InitialCmd: TSynEditorCommand;
 | |
| begin
 | |
|   {$IFDEF VerboseKeys}
 | |
|   DebugLn(['[TCustomSynEdit.CommandProcessor] ',Command
 | |
|     ,' AChar=',AChar,' Data=',DbgS(Data)]);
 | |
|   {$ENDIF}
 | |
|   // first the program event handler gets a chance to process the command
 | |
|   InitialCmd := Command;
 | |
|   NotifyHookedCommandHandlers(Command, AChar, Data, hcfInit);
 | |
|   DoOnProcessCommand(Command, AChar, Data);
 | |
|   if Command <> ecNone then begin
 | |
|     try
 | |
|       InternalBeginUndoBlock;
 | |
|       FBeautifyStartLineIdx := -1;
 | |
|       FBeautifyEndLineIdx := -1;
 | |
|       if assigned(FBeautifier) then begin
 | |
|         FBeautifier.AutoIndent := (eoAutoIndent in FOptions);
 | |
|         FBeautifier.BeforeCommand(self, FTheLinesView, FCaret, Command, InitialCmd);
 | |
|       end;
 | |
|       // notify hooked command handlers before the command is executed inside of
 | |
|       // the class
 | |
|       if Command <> ecNone then
 | |
|         NotifyHookedCommandHandlers(Command, AChar, Data, hcfPreExec);
 | |
|       // internal command handler
 | |
|       if (Command <> ecNone) and (Command < ecUserFirst) then
 | |
|         ExecuteCommand(Command, AChar, Data);
 | |
|       // notify hooked command handlers after the command was executed inside of
 | |
|       // the class
 | |
|       if Command <> ecNone then
 | |
|         NotifyHookedCommandHandlers(Command, AChar, Data, hcfPostExec);
 | |
|       if Command <> ecNone then
 | |
|         DoOnCommandProcessed(Command, AChar, Data);
 | |
| 
 | |
|       if assigned(FBeautifier) then begin
 | |
|         tsyneditstringlist(FLines).FlushNotificationCache;
 | |
|         FBeautifier.AutoIndent := (eoAutoIndent in FOptions);
 | |
|         FBeautifier.AfterCommand(self, FTheLinesView, FCaret, Command, InitialCmd,
 | |
|                                  FBeautifyStartLineIdx+1, FBeautifyEndLineIdx+1);
 | |
|       end;
 | |
|     finally
 | |
|       InternalEndUndoBlock;
 | |
|       {$IFDEF SynCheckPaintLock}
 | |
|       if (FPaintLock > 0) and (FInvalidateRect.Bottom > FInvalidateRect.Top) then begin
 | |
|         debugln(['TCustomSynEdit.CommandProcessor: Paint called while locked  InitialCmd=', InitialCmd, ' Command=', Command]);
 | |
|         DumpStack;
 | |
|       end;
 | |
|       {$ENDIF}
 | |
|     end;
 | |
|   end;
 | |
|   NotifyHookedCommandHandlers(Command, AChar, Data, hcfFinish);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ExecuteCommand(Command: TSynEditorCommand;
 | |
|   const AChar: TUTF8Char; Data: pointer);
 | |
| const
 | |
|   SEL_MODE: array[ecNormalSelect..ecLineSelect] of TSynSelectionMode = (
 | |
|     smNormal, smColumn, smLine);
 | |
| var
 | |
|   CX: Integer;
 | |
|   Len: Integer;
 | |
|   Temp: string;
 | |
|   Helper: string;
 | |
|   moveBkm: boolean;
 | |
|   WP: TPoint;
 | |
|   Caret: TPoint;
 | |
|   CaretNew: TPoint;
 | |
|   counter: Integer;
 | |
|   LogCounter: integer;
 | |
|   LogCaretXY: TPoint;
 | |
|   CY: Integer;
 | |
| 
 | |
| begin
 | |
|   IncPaintLock;
 | |
|   try
 | |
|     fUndoList.CurrentReason := Command;
 | |
| 
 | |
|     if Command in [ecSelColCmdRangeStart..ecSelColCmdRangeEnd] then
 | |
|       FBlockSelection.ActiveSelectionMode := smColumn;
 | |
|     if Command in [ecSelCmdRangeStart..ecSelCmdRangeEnd] then
 | |
|       FBlockSelection.ActiveSelectionMode := FBlockSelection.SelectionMode;
 | |
| 
 | |
|     FBlockSelection.AutoExtend := Command in [ecSelectionStart..ecSelectionEnd];
 | |
|     FCaret.ChangeOnTouch;
 | |
| 
 | |
|     case Command of
 | |
| // horizontal caret movement or selection
 | |
|       ecLeft, ecSelLeft, ecColSelLeft:
 | |
|         begin
 | |
|           if (eoCaretSkipsSelection in Options2) and (Command=ecLeft)
 | |
|           and SelAvail and FCaret.IsAtLineByte(FBlockSelection.LastLineBytePos) then begin
 | |
|             FBlockSelection.IgnoreNextCaretMove;
 | |
|             FCaret.LineBytePos := FBlockSelection.FirstLineBytePos;
 | |
|           end
 | |
|           else
 | |
|             MoveCaretHorz(-1);
 | |
|         end;
 | |
|       ecRight, ecSelRight, ecColSelRight:
 | |
|         begin
 | |
|           if (eoCaretSkipsSelection in Options2) and (Command=ecRight)
 | |
|           and SelAvail and FCaret.IsAtLineByte(FBlockSelection.FirstLineBytePos) then begin
 | |
|             FBlockSelection.IgnoreNextCaretMove;
 | |
|             FCaret.LineBytePos := FBlockSelection.LastLineBytePos;
 | |
|           end
 | |
|           else
 | |
|             MoveCaretHorz(1);
 | |
|         end;
 | |
|       ecPageLeft, ecSelPageLeft, ecColSelPageLeft:
 | |
|         begin
 | |
|           MoveCaretHorz(-Max(1, CharsInWindow));
 | |
|         end;
 | |
|       ecPageRight, ecSelPageRight, ecColSelPageRight:
 | |
|         begin
 | |
|           MoveCaretHorz(Max(1, CharsInWindow));
 | |
|         end;
 | |
|       ecLineStart, ecSelLineStart, ecColSelLineStart:
 | |
|         begin
 | |
|           DoHomeKey;
 | |
|         end;
 | |
|       ecLineTextStart, ecSelLineTextStart, ecColSelLineTextStart:
 | |
|         begin
 | |
|           DoHomeKey(synhmFirstWord);
 | |
|         end;
 | |
|       ecLineEnd, ecSelLineEnd, ecColSelLineEnd:
 | |
|         begin
 | |
|           DoEndKey;
 | |
|         end;
 | |
| // vertical caret movement or selection
 | |
|       ecUp, ecSelUp, ecColSelUp:
 | |
|         begin
 | |
|           MoveCaretVert(-1);
 | |
|         end;
 | |
|       ecDown, ecSelDown, ecColSelDown:
 | |
|         begin
 | |
|           MoveCaretVert(1);
 | |
|         end;
 | |
|       ecPageUp, ecSelPageUp, ecPageDown, ecSelPageDown, ecColSelPageUp, ecColSelPageDown:
 | |
|         begin
 | |
|           counter := LinesInWindow;
 | |
|           if (eoHalfPageScroll in fOptions) then counter:=counter div 2;
 | |
|           if eoScrollByOneLess in fOptions then
 | |
|             Dec(counter);
 | |
|           counter := Max(1, counter);
 | |
|           if (Command in [ecPageUp, ecSelPageUp, ecColSelPageUp]) then
 | |
|             counter := -counter;
 | |
|           TopView := TopView + counter;
 | |
|           MoveCaretVert(counter);
 | |
|         end;
 | |
|       ecPageTop, ecSelPageTop, ecColSelPageTop:
 | |
|         begin
 | |
|           FCaret.LinePos := TopLine;
 | |
|         end;
 | |
|       ecPageBottom, ecSelPageBottom, ecColSelPageBottom:
 | |
|         begin
 | |
|           FCaret.LinePos := ScreenRowToRow(LinesInWindow - 1);
 | |
|         end;
 | |
|       ecEditorTop, ecSelEditorTop:
 | |
|         begin
 | |
|           FCaret.LineCharPos := Point(1, FFoldedLinesView.ViewPosToTextIndex(1)+1);
 | |
|         end;
 | |
|       ecEditorBottom, ecSelEditorBottom:
 | |
|         begin
 | |
|           CaretNew := Point(1, FFoldedLinesView.ViewPosToTextIndex(FFoldedLinesView.Count)+1);
 | |
|           if (CaretNew.Y > 0) then
 | |
|             CaretNew.X := Length(FTheLinesView[CaretNew.Y - 1]) + 1;
 | |
|           FCaret.LineCharPos := CaretNew;
 | |
|         end;
 | |
|       ecColSelEditorTop:
 | |
|         begin
 | |
|           FCaret.LinePos := FFoldedLinesView.ViewPosToTextIndex(1)+1;
 | |
|         end;
 | |
|       ecColSelEditorBottom:
 | |
|         begin
 | |
|           FCaret.LinePos := FFoldedLinesView.ViewPosToTextIndex(FFoldedLinesView.Count)+1;
 | |
|         end;
 | |
|         
 | |
| // goto special line / column position
 | |
|       ecGotoXY, ecSelGotoXY:
 | |
|         if Assigned(Data) then begin
 | |
|           FCaret.LineCharPos := PPoint(Data)^;
 | |
|         end;
 | |
| // word selection
 | |
|       ecWordLeft, ecSelWordLeft, ecColSelWordLeft,
 | |
|       ecWordEndLeft, ecSelWordEndLeft, ecHalfWordLeft, ecSelHalfWordLeft:
 | |
|         begin
 | |
|           case Command of
 | |
|             ecWordEndLeft, ecSelWordEndLeft:   CaretNew := PrevWordLogicalPos(swbWordEnd);
 | |
|             ecHalfWordLeft, ecSelHalfWordLeft: CaretNew := PrevWordLogicalPos(swbCaseChange);
 | |
|             else                               CaretNew := PrevWordLogicalPos;
 | |
|           end;
 | |
|           if FFoldedLinesView.FoldedAtTextIndex[CaretNew.Y - 1] then begin
 | |
|             CY := FindNextUnfoldedLine(CaretNew.Y, False);
 | |
|             CaretNew := Point(1 + Length(FTheLinesView[CY-1]), CY);
 | |
|           end;
 | |
|           FCaret.LineBytePos := CaretNew;
 | |
|         end;
 | |
|       ecWordRight, ecSelWordRight, ecColSelWordRight,
 | |
|       ecWordEndRight, ecSelWordEndRight, ecHalfWordRight, ecSelHalfWordRight:
 | |
|         begin
 | |
|           case Command of
 | |
|             ecWordEndRight, ecSelWordEndRight:   CaretNew := NextWordLogicalPos(swbWordEnd);
 | |
|             ecHalfWordRight, ecSelHalfWordRight: CaretNew := NextWordLogicalPos(swbCaseChange);
 | |
|             else                                 CaretNew := NextWordLogicalPos;
 | |
|           end;
 | |
|           if FFoldedLinesView.FoldedAtTextIndex[CaretNew.Y - 1] then
 | |
|             CaretNew := Point(1, FindNextUnfoldedLine(CaretNew.Y, True));
 | |
|           FCaret.LineBytePos := CaretNew;
 | |
|         end;
 | |
|       ecSelectAll:
 | |
|         begin
 | |
|           SelectAll;
 | |
|         end;
 | |
| {begin}                                                                         //mh 2000-10-30
 | |
|       ecDeleteLastChar:
 | |
|         if not ReadOnly then begin
 | |
|           if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then
 | |
|             SetSelTextExternal('')
 | |
|           else begin
 | |
|             Temp := LineText;
 | |
|             Len := Length(Temp);
 | |
|             LogCaretXY:=LogicalCaretXY;
 | |
|             Caret := CaretXY;
 | |
|             //debugln('ecDeleteLastChar B Temp="',DbgStr(Temp),'" CaretX=',dbgs(CaretX),' LogCaretXY=',dbgs(LogCaretXY));
 | |
|             if LogCaretXY.X > Len +1
 | |
|             then begin
 | |
|               // past EOL; only move caret one column
 | |
|               FCaret.IncForcePastEOL;
 | |
|               CaretX := CaretX - 1;
 | |
|               FCaret.DecForcePastEOL;
 | |
|             end else if CaretX = 1 then begin
 | |
|               // join this line with the last line if possible
 | |
|               if CaretY > 1 then begin
 | |
|                 CaretY := CaretY - 1;
 | |
|                 CaretX := PhysicalLineLength(FTheLinesView[CaretY - 1],
 | |
|                                              CaretY - 1) + 1;
 | |
|                 FTheLinesView.EditLineJoin(CaretY);
 | |
|               end;
 | |
|             end else begin
 | |
|                 // delete char
 | |
|               {$IFDEF USE_UTF8BIDI_LCL}
 | |
|               CaretX := CaretX - 1;
 | |
|               FTheLinesView.EditDelete(CaretX, LogCaretXY.Y, 1);
 | |
|               {$ELSE USE_UTF8BIDI_LCL}
 | |
|               LogCaretXY.X:=PhysicalToLogicalCol(Temp, CaretY-1, CaretX - 1);
 | |
|               LogCounter:=GetCharLen(Temp,LogCaretXY.X);
 | |
|               CaretX := LogicalToPhysicalCol(Temp, CaretY-1, LogCaretXY.X);
 | |
|               FTheLinesView.EditDelete(FCaret.BytePos, LogCaretXY.Y, LogCounter);
 | |
|               {$ENDIF USE_UTF8BIDI_LCL}
 | |
|               //end;
 | |
|             end;
 | |
| 
 | |
|           end;
 | |
|         end;
 | |
|       ecDeleteChar:
 | |
|         if not ReadOnly then begin
 | |
|           if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then
 | |
|             SetSelTextExternal('')
 | |
|           else begin
 | |
|             Temp := LineText;
 | |
|             Len := Length(Temp);
 | |
|             LogCaretXY:=LogicalCaretXY;
 | |
|             if LogCaretXY.X <= Len then
 | |
|             begin
 | |
|               // delete char
 | |
|               Counter:=GetCharLen(Temp,LogCaretXY.X);
 | |
|               FTheLinesView.EditDelete(LogCaretXY.X, CaretY, Counter);
 | |
|               SetLogicalCaretXY(LogCaretXY);
 | |
|             end else begin
 | |
|               // join line with the line after
 | |
|               if CaretY < FTheLinesView.Count then begin
 | |
|                 Helper := StringOfChar(' ', LogCaretXY.X - 1 - Len);
 | |
|                 FTheLinesView.EditLineJoin(CaretY, Helper);
 | |
|               end;
 | |
|             end;
 | |
|           end;
 | |
|         end;
 | |
|       ecDeleteWord, ecDeleteEOL:
 | |
|         if not ReadOnly then begin
 | |
|           Len := LogicalToPhysicalCol(LineText, CaretY-1,Length(LineText)+1)-1;
 | |
|           Helper := '';
 | |
|           Caret := CaretXY;
 | |
|           if Command = ecDeleteWord then begin
 | |
|             if CaretX > Len + 1 then begin
 | |
|               Helper := StringOfChar(' ', CaretX - 1 - Len);
 | |
|               CaretX := 1 + Len;
 | |
|             end;
 | |
|             // if we are not in a word, delete word + spaces (up to next token)
 | |
|             if WordBreaker.IsAtWordStart(LineText, LogicalCaretXY.X) or
 | |
|                WordBreaker.IsAtWordEnd(LineText, LogicalCaretXY.X) or
 | |
|                (not WordBreaker.IsInWord(LineText, LogicalCaretXY.X)) or
 | |
|                (LogicalCaretXY.X > Length(LineText))
 | |
|             then
 | |
|               WP := NextWordLogicalPos(swbTokenBegin, True)
 | |
|             else
 | |
|               // if we are inside a word, delete to word-end
 | |
|               WP := NextWordLogicalPos(swbWordEnd, True);
 | |
|           end else
 | |
|             WP := Point(Len + 1, CaretY);
 | |
|           if (WP.X <> FCaret.BytePos) or (WP.Y <> FCaret.LinePos) then begin
 | |
|             FInternalBlockSelection.StartLineBytePos := WP;
 | |
|             FInternalBlockSelection.EndLineBytePos := LogicalCaretXY;
 | |
|             FInternalBlockSelection.ActiveSelectionMode := smNormal;
 | |
|             FInternalBlockSelection.SetSelTextPrimitive(smNormal, nil);
 | |
|             if Helper <> '' then
 | |
|               FTabbedLinesView.EditInsert(CaretX, CaretY, Helper);
 | |
|             FCaret.BytePos := FInternalBlockSelection.StartBytePos + length(Helper);
 | |
|           end;
 | |
|         end;
 | |
|       ecDeleteLastWord, ecDeleteBOL:
 | |
|         if not ReadOnly then begin
 | |
|           if Command = ecDeleteLastWord then
 | |
|             WP := PrevWordLogicalPos
 | |
|           else
 | |
|             WP := Point(1, CaretY);
 | |
|           if (WP.X <> FCaret.BytePos) or (WP.Y <> FCaret.LinePos) then begin
 | |
|             FInternalBlockSelection.StartLineBytePos := WP;
 | |
|             FInternalBlockSelection.EndLineBytePos := LogicalCaretXY;
 | |
|             FInternalBlockSelection.ActiveSelectionMode := smNormal;
 | |
|             FInternalBlockSelection.SetSelTextPrimitive(smNormal, nil);
 | |
|             FCaret.LineBytePos := WP;
 | |
|           end;
 | |
|         end;
 | |
|       ecDeleteLine:
 | |
|         if not ReadOnly
 | |
|         then begin
 | |
|           CY := FCaret.LinePos;
 | |
|           if (Cy < FTheLinesView.Count) then
 | |
|             FTheLinesView.EditLinesDelete(CaretY, 1)
 | |
|           else
 | |
|           if (Cy = FTheLinesView.Count) and (FTheLinesView[CY-1] <> '') then
 | |
|             FTheLinesView.EditDelete(1, Cy, length(FTheLinesView[Cy-1]));
 | |
|           CaretXY := Point(1, CaretY); // like seen in the Delphi editor
 | |
|         end;
 | |
|       ecClearAll:
 | |
|         begin
 | |
|           if not ReadOnly then ClearAll;
 | |
|         end;
 | |
|       ecInsertLine,
 | |
|       ecLineBreak:
 | |
|         if not ReadOnly then begin
 | |
|           if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then
 | |
|             SetSelTextExternal('');
 | |
|           Temp := LineText;
 | |
|           LogCaretXY:=LogicalCaretXY;
 | |
|           Len := Length(Temp);
 | |
|           if LogCaretXY.X > Len + 1 then
 | |
|             LogCaretXY.X := Len + 1;
 | |
|           FTheLinesView.EditLineBreak(LogCaretXY.X, LogCaretXY.Y);
 | |
|           if Command = ecLineBreak then
 | |
|             CaretXY := Point(1, CaretY + 1)
 | |
|           else
 | |
|             CaretXY := CaretXY;
 | |
|         end;
 | |
|       ecTab:
 | |
|         if not ReadOnly then
 | |
|         try
 | |
|           FCaret.IncForcePastEOL;
 | |
|           DoTabKey;
 | |
|         finally
 | |
|           FCaret.DecForcePastEOL;
 | |
|         end;
 | |
|       ecShiftTab:
 | |
|         if not ReadOnly then
 | |
|           if SelAvail and (eoTabIndent in Options) then
 | |
|             DoBlockUnindent;
 | |
|       ecMatchBracket:
 | |
|         FindMatchingBracket;
 | |
|       ecChar:
 | |
|         if not ReadOnly and (AChar >= #32) and (AChar <> #127) then begin
 | |
|           if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then begin
 | |
|             SetSelTextExternal(AChar);
 | |
|           end else begin
 | |
|             try
 | |
|               FCaret.IncForcePastEOL;
 | |
|               FCaret.IncForceAdjustToNextChar;
 | |
| 
 | |
|               LogCaretXY := FCaret.LineBytePos;
 | |
|               Temp := LineText;
 | |
|               Len := Length(Temp);
 | |
|               if (not fInserting) and (LogCaretXY.X - 1 < Len) then begin
 | |
|                 counter := GetCharLen(Temp,LogCaretXY.X);
 | |
|                 FTheLinesView.EditDelete(LogCaretXY.X, LogCaretXY.Y, counter);
 | |
|                 Len := Len - counter;
 | |
|               end;
 | |
| 
 | |
|               {$IFDEF USE_UTF8BIDI_LCL}
 | |
|               // TODO: improve utf8bidi for tabs
 | |
|               Len := VLength(LineText, drLTR);
 | |
|               (*if Len < CaretX then
 | |
|                 Temp := StringOfChar(' ', CaretX - Len)
 | |
|               else
 | |
|                 Temp := '' *)
 | |
|               FTheLinesView.EditInsert(CaretX, LogCaretXY.Y, (*Temp +*) AChar);
 | |
|               {$ELSE}
 | |
|               (*if Len < LogCaretXY.X - 1 then begin
 | |
|                 Temp := StringOfChar(' ', LogCaretXY.X - 1 - Len);
 | |
|                 LogCaretXY.X := Len + 1;
 | |
|               end
 | |
|               else
 | |
|                 temp := '';*)
 | |
|               FTheLinesView.EditInsert(LogCaretXY.X, LogCaretXY.Y, (*Temp +*) AChar);
 | |
|               {$ENDIF}
 | |
| 
 | |
|               CaretX := CaretX + 1;
 | |
|               if CaretX >= LeftChar + CharsInWindow then
 | |
|                 LeftChar := LeftChar + Min(25, CharsInWindow - 1);
 | |
|             finally
 | |
|               FCaret.DecForceAdjustToNextChar;
 | |
|               FCaret.DecForcePastEOL;
 | |
|             end;
 | |
|           end;
 | |
|         end
 | |
|         else if not ReadOnly and (AChar = #13) then begin
 | |
|           // ecLineBreak is not assigned
 | |
|           // Insert a linebreak, but do not apply any other functionality (such as indent)
 | |
|           if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then
 | |
|             SetSelTextExternal('');
 | |
|           LogCaretXY:=LogicalCaretXY;
 | |
|           FTheLinesView.EditLineBreak(LogCaretXY.X, LogCaretXY.Y);
 | |
|           CaretXY := Point(1, CaretY + 1);
 | |
|           EnsureCursorPosVisible;
 | |
|         end;
 | |
|       ecUndo:
 | |
|         begin
 | |
|           if not ReadOnly then Undo;
 | |
|         end;
 | |
|       ecRedo:
 | |
|         begin
 | |
|           if not ReadOnly then Redo;
 | |
|         end;
 | |
|       ecGotoMarker0..ecGotoMarker9:
 | |
|         begin
 | |
|           if BookMarkOptions.EnableKeys then
 | |
|             GotoBookMark(Command - ecGotoMarker0);
 | |
|         end;
 | |
|       ecSetMarker0..ecSetMarker9,ecToggleMarker0..ecToggleMarker9:
 | |
|         begin
 | |
|           if BookMarkOptions.EnableKeys then begin
 | |
|             if (Command >= ecSetMarker0) and (Command <= ecSetMarker9) then
 | |
|               CX := Command - ecSetMarker0
 | |
|             else
 | |
|               CX := Command - ecToggleMarker0;
 | |
|             if assigned(fBookMarks[CX]) then begin
 | |
|               moveBkm := ((Command >= ecSetMarker0) and (Command <= ecSetMarker9))
 | |
|                          or (fBookMarks[CX].Line <> CaretY);
 | |
|               ClearBookMark(CX);
 | |
|               if moveBkm then
 | |
|                 SetBookMark(CX, CaretX, CaretY);
 | |
|             end else
 | |
|               SetBookMark(CX, CaretX, CaretY);
 | |
|           end; // if BookMarkOptions.EnableKeys
 | |
|         end;
 | |
|       ecCut:
 | |
|         begin
 | |
|           if (not ReadOnly) and SelAvail then
 | |
|             CutToClipboard;
 | |
|         end;
 | |
|       ecCopy:
 | |
|         begin
 | |
|           CopyToClipboard;
 | |
|         end;
 | |
|       ecPaste:
 | |
|         begin
 | |
|           if not ReadOnly then PasteFromClipboard;
 | |
|         end;
 | |
|       ecScrollUp:
 | |
|         begin
 | |
|           TopView := TopView - 1;
 | |
|           if CaretY > ScreenRowToRow(LinesInWindow-1) then
 | |
|             CaretY := ScreenRowToRow(LinesInWindow-1);
 | |
|         end;
 | |
|       ecScrollDown:
 | |
|         begin
 | |
|           TopView := TopView + 1;
 | |
|           if CaretY < TopLine then
 | |
|             CaretY := TopLine;
 | |
|         end;
 | |
|       ecScrollLeft:
 | |
|         begin
 | |
|           LeftChar := LeftChar - 1;
 | |
|           if CaretX > LeftChar + CharsInWindow then
 | |
|             CaretX := LeftChar + CharsInWindow;
 | |
|           Update;
 | |
|         end;
 | |
|       ecScrollRight:
 | |
|         begin
 | |
|           LeftChar := LeftChar + 1;
 | |
|           if CaretX < LeftChar then
 | |
|             CaretX := LeftChar;
 | |
|           Update;
 | |
|         end;
 | |
|       ecInsertMode:
 | |
|         begin
 | |
|           InsertMode := TRUE;
 | |
|         end;
 | |
|       ecOverwriteMode:
 | |
|         begin
 | |
|           InsertMode := FALSE;
 | |
|         end;
 | |
|       ecToggleMode:
 | |
|         begin
 | |
|           InsertMode := not InsertMode;
 | |
|         end;
 | |
|       ecBlockSetBegin:
 | |
|         begin
 | |
|           FBlockSelection.Hide :=
 | |
|             CompareCarets(FCaret.LineBytePos, FBlockSelection.EndLineBytePos) <= 0;
 | |
|           FBlockSelection.StartLineBytePosAdjusted := FCaret.LineBytePos;
 | |
|         end;
 | |
|       ecBlockSetEnd:
 | |
|         begin
 | |
|           FBlockSelection.Hide :=
 | |
|             CompareCarets(FCaret.LineBytePos, FBlockSelection.StartLineBytePos) >= 0;
 | |
|           FBlockSelection.EndLineBytePos := FCaret.LineBytePos;
 | |
|         end;
 | |
|       ecBlockToggleHide:
 | |
|         begin
 | |
|           FBlockSelection.Hide := not FBlockSelection.Hide;
 | |
|         end;
 | |
|       ecBlockHide:
 | |
|         begin
 | |
|           FBlockSelection.Hide := True;
 | |
|         end;
 | |
|       ecBlockShow:
 | |
|         begin
 | |
|           FBlockSelection.Hide := False;
 | |
|         end;
 | |
|       ecBlockMove:
 | |
|         begin
 | |
|           if SelAvail then begin
 | |
|             helper := FBlockSelection.SelText;
 | |
|             FInternalBlockSelection.AssignFrom(FBlockSelection);
 | |
|             FBlockSelection.IncPersistentLock;
 | |
|             FBlockSelection.StartLineBytePos := FCaret.LineBytePos;             // Track the Adjustment of the insert position
 | |
|             FInternalBlockSelection.SelText := '';
 | |
|             FCaret.LineBytePos := FBlockSelection.StartLineBytePos;
 | |
|             Caret := FCaret.LineBytePos;
 | |
|             FBlockSelection.SelText := Helper;
 | |
|             FBlockSelection.DecPersistentLock;
 | |
|             CaretNew := FCaret.LineBytePos;
 | |
|             FBlockSelection.StartLineBytePos := Caret;
 | |
|             FBlockSelection.EndLineBytePos := CaretNew;
 | |
|           end;
 | |
|         end;
 | |
|       ecBlockCopy:
 | |
|         begin
 | |
|           if SelAvail then
 | |
|             InsertTextAtCaret(FBlockSelection.SelText, scamEnd);
 | |
|         end;
 | |
|       ecBlockDelete:
 | |
|         begin
 | |
|           if SelAvail then
 | |
|             FBlockSelection.SelText := '';
 | |
|         end;
 | |
|       ecBlockGotoBegin:
 | |
|         begin
 | |
|           FCaret.LineBytePos := FBlockSelection.FirstLineBytePos;
 | |
|         end;
 | |
|       ecBlockGotoEnd:
 | |
|         begin
 | |
|           FCaret.LineBytePos := FBlockSelection.LastLineBytePos;
 | |
|         end;
 | |
| 
 | |
|       ecBlockIndent:
 | |
|         if not ReadOnly then DoBlockIndent;
 | |
|       ecBlockUnindent:
 | |
|         if not ReadOnly then DoBlockUnindent;
 | |
|       ecNormalSelect,
 | |
|       ecColumnSelect,
 | |
|       ecLineSelect:
 | |
|         begin
 | |
|           DefaultSelectionMode := SEL_MODE[Command];
 | |
|         end;
 | |
|       EcFoldLevel1..EcFoldLevel9:
 | |
|         FoldAll(Command - EcFoldLevel1);
 | |
|       EcFoldLevel0:
 | |
|         UnfoldAll;
 | |
|       EcFoldCurrent:
 | |
|         begin
 | |
|           CY := FFoldedLinesView.ExpandedLineForBlockAtLine(CaretY);
 | |
|           if CY > 0 then begin
 | |
|             FFoldedLinesView.FoldAtTextIndex(CY-1);
 | |
|             SetCaretXY(Point(1, CY));
 | |
|           end;
 | |
|         end;
 | |
|       EcUnFoldCurrent:
 | |
|           FFoldedLinesView.UnFoldAtTextIndex(CaretY-1);
 | |
|       EcToggleMarkupWord:
 | |
|           FMarkupHighCaret.ToggleCurrentWord;
 | |
|     end;
 | |
|   finally
 | |
|     DecPaintLock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoOnCommandProcessed(Command: TSynEditorCommand;
 | |
|   AChar: TUTF8Char;
 | |
|   Data: pointer);
 | |
| begin
 | |
|   if Assigned(fOnCommandProcessed) then
 | |
|     fOnCommandProcessed(Self, Command, AChar, Data);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoOnProcessCommand(var Command: TSynEditorCommand;
 | |
|   var AChar: TUTF8Char; Data: pointer);
 | |
| begin
 | |
|   //DebugLn(['TCustomSynEdit.DoOnProcessCommand Command=',Command]);
 | |
|   if Command < ecUserFirst then begin
 | |
|     if Assigned(FOnProcessCommand) then
 | |
|       FOnProcessCommand(Self, Command, AChar, Data);
 | |
|   end else begin
 | |
|     if Assigned(FOnProcessUserCommand) then
 | |
|       FOnProcessUserCommand(Self, Command, AChar, Data);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ClearAll;
 | |
| begin
 | |
|   InternalBeginUndoBlock;
 | |
|   try
 | |
|     SelectAll;
 | |
|     SelText:='';
 | |
|   finally
 | |
|     InternalEndUndoBlock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ClearSelection;
 | |
| begin
 | |
|   if SelAvail then
 | |
|     SelText := '';
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetSelectionMode : TSynSelectionMode;
 | |
| begin
 | |
|   Result := fBlockSelection.ActiveSelectionMode;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetSelectionMode(const Value: TSynSelectionMode);
 | |
| begin
 | |
|   fBlockSelection.ActiveSelectionMode := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.InternalBeginUndoBlock(aList: TSynEditUndoList);
 | |
| begin
 | |
|   if aList = nil then aList := fUndoList;
 | |
|   {$IFDEF SynUndoDebugBeginEnd}
 | |
|   DebugLnEnter(['>> TCustomSynEdit.InternalBeginUndoBlock', DbgSName(self), ' ', dbgs(Self), ' aList=', aList, ' FPaintLock=', FPaintLock, ' InGroupCount=',aList.InGroupCount]);
 | |
|   {$ENDIF}
 | |
|   aList.OnNeedCaretUndo := {$IFDEF FPC}@{$ENDIF}GetCaretUndo;
 | |
|   aList.BeginBlock;
 | |
|   IncPaintLock;
 | |
|   FFoldedLinesView.Lock;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.InternalEndUndoBlock(aList: TSynEditUndoList);
 | |
| begin
 | |
|   if aList = nil then aList := fUndoList;
 | |
|   // Write all trimming info to the end of the undo block,
 | |
|   // so it will be undone first, and other UndoItems do see the expected spaces
 | |
|   FFoldedLinesView.UnLock;
 | |
|    // must be last => May call MoveCaretToVisibleArea, which must only happen
 | |
|    // after unfold
 | |
|   DecPaintLock;
 | |
|   aList.EndBlock; // Todo: Doing this after DecPaintLock, can cause duplicate calls to StatusChanged(scModified)
 | |
|   {$IFDEF SynUndoDebugBeginEnd}
 | |
|   DebugLnEnter(['<< TCustomSynEdit.InternalEndUndoBlock', DbgSName(self), ' ', dbgs(Self), ' aList=', aList, ' FPaintLock=', FPaintLock, ' InGroupCount=',aList.InGroupCount]);
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF};
 | |
| begin
 | |
|   {$IFDEF SynUndoDebugBeginEnd}
 | |
|   DebugLnEnter(['>> TCustomSynEdit.BeginUndoBlock ', DbgSName(self), ' ', dbgs(Self), ' Caller=', ACaller, ' FPaintLock=', FPaintLock, ' InGroupCount=',fUndoList.InGroupCount, '  FIsInDecPaintLock=',dbgs(FIsInDecPaintLock)]);
 | |
|   if ACaller = '' then DumpStack;
 | |
|   {$ENDIF}
 | |
|   fUndoList.OnNeedCaretUndo := {$IFDEF FPC}@{$ENDIF}GetCaretUndo;
 | |
|   fUndoList.BeginBlock;
 | |
|   ////FFoldedLinesView.Lock;
 | |
|   //FTrimmedLinesView.Lock;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.BeginUpdate(WithUndoBlock: Boolean = True);
 | |
| begin
 | |
|   IncPaintLock;
 | |
|   {$IFDEF SynUndoDebugBeginEnd}
 | |
|   if WithUndoBlock and (FPaintLock = 0) and (FUndoBlockAtPaintLock = 0) then
 | |
|     DebugLn(['************** TCustomSynEdit.BeginUpdate  PAINTLOCK NOT INCREASED  ', DbgSName(self), ' ', dbgs(Self),  ' FPaintLock=', FPaintLock, ' InGroupCount=',fUndoList.InGroupCount, '  FIsInDecPaintLock=',dbgs(FIsInDecPaintLock)]);
 | |
|   {$ENDIF}
 | |
|   if WithUndoBlock and (FPaintLock > 0) and (FUndoBlockAtPaintLock = 0) then begin
 | |
|     FUndoBlockAtPaintLock := FPaintLock;
 | |
|     BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('SynEdit.BeginUpdate'){$ENDIF};
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF};
 | |
| begin
 | |
|   // Write all trimming info to the end of the undo block,
 | |
|   // so it will be undone first, and other UndoItems do see the expected spaces
 | |
|   //FTrimmedLinesView.UnLock;
 | |
|   ////FFoldedLinesView.UnLock;
 | |
|   fUndoList.EndBlock;
 | |
|   {$IFDEF SynUndoDebugBeginEnd}
 | |
|   DebugLnEnter(['<< TCustomSynEdit.EndUndoBlock', DbgSName(self), ' ', dbgs(Self), ' Caller=', ACaller, ' FPaintLock=', FPaintLock, ' InGroupCount=',fUndoList.InGroupCount, '  FIsInDecPaintLock=',dbgs(FIsInDecPaintLock)]);
 | |
|   //if ACaller = '' then DumpStack;
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.EndUpdate;
 | |
| begin
 | |
|   DecPaintLock;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.AddKey(Command: TSynEditorCommand;
 | |
|   Key1: word; SS1: TShiftState; Key2: word; SS2: TShiftState);
 | |
| var
 | |
|   Key: TSynEditKeyStroke;
 | |
| begin
 | |
|   Key := Keystrokes.Add;
 | |
|   Key.Command := Command;
 | |
|   Key.Key := Key1;
 | |
|   Key.Shift := SS1;
 | |
|   Key.Key2 := Key2;
 | |
|   Key.Shift2 := SS2;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.AfterLoadFromFile;
 | |
| begin
 | |
|   if (not HandleAllocated) or
 | |
|      ( (FPaintLock > 0) and not((FPaintLock = 1) and FIsInDecPaintLock) )
 | |
|   then begin
 | |
|     Include(fStateFlags, sfAfterLoadFromFileNeeded);
 | |
|     exit;
 | |
|   end;
 | |
|   Exclude(fStateFlags, sfAfterLoadFromFileNeeded);
 | |
|   if assigned(FFoldedLinesView) then begin
 | |
|     ScanRanges;
 | |
|     FFoldedLinesView.UnfoldAll;
 | |
|     FFoldedLinesView.CollapseDefaultFolds;
 | |
|     if FPendingFoldState <> '' then
 | |
|       SetFoldState(FPendingFoldState);
 | |
|     TopView := TopView;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MarkListChange(Sender: TSynEditMark; Changes: TSynEditMarkChangeReasons);
 | |
| begin
 | |
|   if (smcrAdded in Changes) and Sender.IsBookmark then begin
 | |
|     FBookMarks[Sender.BookmarkNumber] := Sender;
 | |
|     if Assigned(FOnPlaceMark) then
 | |
|       FOnPlaceMark(Self, Sender);
 | |
|   end;
 | |
|   if (smcrRemoved in Changes) and Sender.IsBookmark then begin
 | |
|     FBookMarks[Sender.BookmarkNumber] := nil;
 | |
|     if Assigned(FOnPlaceMark) then
 | |
|       FOnClearMark(Self, Sender);
 | |
|   end;
 | |
| 
 | |
|   if (not Sender.Visible) and (not (smcrVisible in Changes)) then
 | |
|     exit;
 | |
| 
 | |
|   if smcrLine in Changes then begin
 | |
|     InvalidateLine(Sender.OldLine); // TODO: only if mark has special line color, or other code markup
 | |
|     InvalidateGutterLines(Sender.OldLine, Sender.OldLine);
 | |
|   end;
 | |
|   InvalidateLine(Sender.Line);  // TODO: only if mark has special line color, or other code markup
 | |
|   InvalidateGutterLines(Sender.Line, Sender.Line);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetSelStart: integer;                                   //L505 begin
 | |
| 
 | |
|   function llen(const data: string): integer;
 | |
|   begin
 | |
|     result := length(Data) + length(LineEnding);
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   loop: integer;
 | |
|   p: TPoint;
 | |
| begin
 | |
|   if SelAvail then
 | |
|   begin
 | |
|     p:=BlockBegin;
 | |
|   end
 | |
|   else
 | |
|   begin
 | |
|     p:=LogicalCaretXY;
 | |
|   end;
 | |
| 
 | |
|   result := 0;
 | |
|   loop := 0;
 | |
|   while (loop < (p.Y - 1)) and (loop < FTheLinesView.Count) do
 | |
|   begin
 | |
|     result := result + llen(FTheLinesView[loop]);
 | |
|     inc(loop);
 | |
|   end;
 | |
|   if loop < FTheLinesView.Count then
 | |
|     result := result + Min(p.X, length(FTheLinesView[loop]) + 1);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetSelStart(const Value: integer);
 | |
| 
 | |
|   function llen(const data: string): integer;
 | |
|   begin
 | |
|     result := length(Data) + length(LineEnding);
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   loop: integer;
 | |
|   count: integer;
 | |
| begin
 | |
|   loop := 0;
 | |
|   count := 0;
 | |
|   while (loop < FTheLinesView.Count) and (count + llen(FTheLinesView[loop]) < value) do begin
 | |
|     count := count + llen(FTheLinesView[loop]);
 | |
|     inc(loop);
 | |
|   end;
 | |
| {  CaretX := value - count;
 | |
|   CaretY := loop + 1;
 | |
| 
 | |
|   fBlockBegin.X := CaretX;
 | |
|   fBlockBegin.Y := CaretY;}
 | |
| 
 | |
|   //This seems the same as above, but uses the other fixes inside of SetCaretXY
 | |
|   //to adjust the cursor pos correctly.
 | |
|   FCaret.LineBytePos := Point(value - count, loop + 1);
 | |
|   BlockBegin := Point(value - count, loop + 1);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetSelEnd: integer;
 | |
| 
 | |
|   function llen(const data: string): integer;
 | |
|   begin
 | |
|     result := length(Data) + length(LineEnding);
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   loop: integer;
 | |
|   p: TPoint;
 | |
| begin
 | |
|   if SelAvail then
 | |
|   begin
 | |
|     p := BlockEnd;
 | |
|   end else begin
 | |
|     p := LogicalCaretXY;
 | |
|   end;
 | |
| 
 | |
|   result := 0;
 | |
|   loop := 0;
 | |
|   while (loop < (p.y - 1)) and (loop < FTheLinesView.Count) do begin
 | |
|     Result := result + llen(FTheLinesView[loop]);
 | |
|     inc(loop);
 | |
|   end;
 | |
|   if loop<FTheLinesView.Count then
 | |
|     result := result + p.x;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetSelEnd(const Value: integer);
 | |
| 
 | |
|   function llen(const data: string): integer;
 | |
|   begin
 | |
|     result := length(Data) + length(LineEnding);
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   p: TPoint;
 | |
|   loop: integer;
 | |
|   count: integer;
 | |
| begin
 | |
|   loop := 0;
 | |
|   count := 0;
 | |
|   while (loop < FTheLinesView.Count) and (count + llen(FTheLinesView[loop]) < value) do begin
 | |
|     count := count + llen(FTheLinesView.strings[loop]);
 | |
|     inc(loop);
 | |
|   end;
 | |
|   p.x := value - count; p.y := loop + 1;
 | |
|   BlockEnd := p;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetExtraLineSpacing(const Value: integer);
 | |
| begin
 | |
|   if fExtraLineSpacing=Value then exit;
 | |
|   fExtraLineSpacing := Value;
 | |
|   FPaintArea.ExtraLineSpacing := Value;
 | |
|   FontChanged(self);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetBookMark(BookMark: integer; var X, Y: integer):
 | |
|   boolean;
 | |
| var
 | |
|   i: integer;
 | |
| begin
 | |
|   Result := false;
 | |
|   if assigned(Marks) then
 | |
|     for i := 0 to Marks.Count - 1 do
 | |
|       if Marks[i].IsBookmark and (Marks[i].BookmarkNumber = BookMark) then begin
 | |
|         X := Marks[i].Column;
 | |
|         Y := Marks[i].Line;
 | |
|         X := LogicalToPhysicalPos(Point(X, Y)).x;
 | |
|         Result := true;
 | |
|         Exit;
 | |
|       end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.IsBookmark(BookMark: integer): boolean;
 | |
| var
 | |
|   x, y: integer;
 | |
| begin
 | |
|   Result := GetBookMark(BookMark, x{%H-}, y{%H-});
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MarkTextAsSaved;
 | |
| begin
 | |
|   TSynEditStringList(fLines).MarkSaved;
 | |
|   if FLeftGutter.Visible and FLeftGutter.ChangesPart(0).Visible then
 | |
|     InvalidateGutter; // Todo: Make the ChangeGutterPart an observer
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ClearUndo;
 | |
| begin
 | |
|   fUndoList.Clear;
 | |
|   fRedoList.Clear;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetGutter(const Value: TSynGutter);
 | |
| begin
 | |
|   FLeftGutter.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetRightGutter(const AValue: TSynGutter);
 | |
| begin
 | |
|   FRightGutter.Assign(AValue);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.GutterChanged(Sender: TObject);
 | |
| begin
 | |
|   if (csLoading in ComponentState) then exit;
 | |
|   InvalidateGutter; //Todo: move to gutter
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.GutterResized(Sender: TObject);
 | |
| begin
 | |
|   if (csLoading in ComponentState) then exit;
 | |
| 
 | |
|   GutterChanged(Sender);
 | |
| 
 | |
|   if HandleAllocated then begin
 | |
|     RecalcCharsAndLinesInWin(False);
 | |
|     UpdateScrollBars;
 | |
|     Invalidate;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.TextLeftPixelOffset(IncludeGutterTextDist: Boolean): Integer;
 | |
| begin
 | |
|   if FLeftGutter.Visible then begin
 | |
|     Result := FLeftGutter.Width;
 | |
|     if IncludeGutterTextDist then
 | |
|       inc(Result, GutterTextDist);
 | |
|   end
 | |
|   else begin
 | |
|     Result := 0;
 | |
|     if IncludeGutterTextDist then
 | |
|       inc(Result, 1);  // include space for caret at pos.x=1 (if FOffsetX = -1)
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.TextRightPixelOffset: Integer;
 | |
| begin
 | |
|   if FRightGutter.Visible then
 | |
|     Result := FRightGutter.Width
 | |
|   else
 | |
|     Result := 0;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.LockUndo;
 | |
| begin
 | |
|   fUndoList.Lock;
 | |
|   fRedoList.Lock
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnlockUndo;
 | |
| begin
 | |
|   fUndoList.Unlock;
 | |
|   fRedoList.Unlock;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.WMMouseWheel(var Message: TLMMouseEvent);
 | |
| var
 | |
|   lState: TShiftState;
 | |
| begin
 | |
|   if ((sfHorizScrollbarVisible in fStateFlags) and (Message.Y > ClientHeight)) or
 | |
|      ((sfVertScrollbarVisible in fStateFlags) and (Message.X > ClientWidth))
 | |
|    then begin
 | |
|      inherited;
 | |
|      exit;
 | |
|    end;
 | |
| 
 | |
|   lState := Message.State - [ssCaps, ssNum, ssScroll]; // Remove unreliable states, see http://bugs.freepascal.org/view.php?id=20065
 | |
| 
 | |
|   FMouseClickDoPopUp := False;
 | |
|   IncPaintLock;
 | |
|   try
 | |
|     if Message.WheelDelta > 0 then begin
 | |
|       FindAndHandleMouseAction(mbXWheelUp, lState, Message.X, Message.Y, ccSingle, cdDown, Message.WheelDelta);
 | |
|     end
 | |
|     else begin
 | |
|       // send megative delta
 | |
|       FindAndHandleMouseAction(mbXWheelDown, lState, Message.X, Message.Y, ccSingle, cdDown, Message.WheelDelta);
 | |
|     end;
 | |
|   finally
 | |
|     DecPaintLock;
 | |
|   end;
 | |
| 
 | |
|   if FMouseClickDoPopUp and (PopupMenu <> nil) then begin
 | |
|     PopupMenu.PopupComponent:=self;
 | |
|     PopupMenu.PopUp;
 | |
|   end;
 | |
| 
 | |
|   Message.Result := 1 // handled, skip further handling by interface
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetWantTabs(const Value: boolean);
 | |
| begin
 | |
|   fWantTabs := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetTabWidth(Value: integer);
 | |
| begin
 | |
|   Value := MinMax(Value, 1{0}, 256);
 | |
|   if (Value <> fTabWidth) then begin
 | |
|     fTabWidth := Value;
 | |
|     FTabbedLinesView.TabWidth := Value;
 | |
|     Invalidate; // to redraw text containing tab chars
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| // find / replace
 | |
| function TCustomSynEdit.SearchReplace(const ASearch, AReplace: string;
 | |
|   AOptions: TSynSearchOptions): integer;
 | |
| var
 | |
|   StartPos: TPoint;
 | |
| begin
 | |
|   if (ssoFindContinue in AOptions) and SelAvail then begin
 | |
|     if ssoBackwards in AOptions then
 | |
|       StartPos := BlockBegin
 | |
|     else
 | |
|       StartPos := BlockEnd;
 | |
|   end
 | |
|   else
 | |
|     StartPos := LogicalCaretXY;
 | |
|   Result := SearchReplaceEx(ASearch, AReplace, AOptions, StartPos);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.SearchReplaceEx(const ASearch, AReplace: string;
 | |
|   AOptions: TSynSearchOptions; AStart: TPoint): integer;
 | |
| var
 | |
|   ptStart, ptEnd: TPoint; // start and end of the search range
 | |
|   ptCurrent: TPoint; // current search position
 | |
|   nFound: integer;
 | |
|   bBackward, bFromCursor: boolean;
 | |
|   bPrompt: boolean;
 | |
|   bReplace, bReplaceAll: boolean;
 | |
|   nAction: TSynReplaceAction;
 | |
|   CurReplace: string;
 | |
|   ptFoundStart, ptFoundEnd: TPoint;
 | |
| 
 | |
|   function InValidSearchRange(First, Last: integer): boolean;
 | |
|   begin
 | |
|     Result := TRUE;
 | |
|     case FBlockSelection.ActiveSelectionMode of
 | |
|       smNormal:
 | |
|         if ((ptCurrent.Y = ptStart.Y) and (First < ptStart.X)) or
 | |
|           ((ptCurrent.Y = ptEnd.Y) and (Last > ptEnd.X)) then Result := FALSE;
 | |
|       smColumn:
 | |
|         Result := (First >= ptStart.X) and (Last <= ptEnd.X);
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   Result := 0;
 | |
|   // can't search for or replace an empty string
 | |
|   if Length(ASearch) = 0 then exit;
 | |
|   // get the text range to search in, ignore the "Search in selection only"
 | |
|   // option if nothing is selected
 | |
|   bBackward := (ssoBackwards in AOptions);
 | |
|   bPrompt := (ssoPrompt in AOptions);
 | |
|   bReplace := (ssoReplace in AOptions);
 | |
|   bReplaceAll := (ssoReplaceAll in AOptions);
 | |
|   bFromCursor := not (ssoEntireScope in AOptions);
 | |
|   if not SelAvail then Exclude(AOptions, ssoSelectedOnly);
 | |
|   if (ssoSelectedOnly in AOptions) then begin
 | |
|     ptStart := BlockBegin;
 | |
|     ptEnd := BlockEnd;
 | |
|     // search the whole line in the line selection mode
 | |
|     if (FBlockSelection.ActiveSelectionMode = smLine) then begin
 | |
|       ptStart.X := 1;
 | |
|       ptEnd.X := Length(FTheLinesView[ptEnd.Y - 1]) + 1;
 | |
|     end else if (FBlockSelection.ActiveSelectionMode = smColumn) then
 | |
|       // make sure the start column is smaller than the end column
 | |
|       if (ptStart.X > ptEnd.X) then begin
 | |
|         nFound := ptStart.X;
 | |
|         ptStart.X := ptEnd.X;
 | |
|         ptEnd.X := nFound;
 | |
|       end;
 | |
|     // ignore the cursor position when searching in the selection
 | |
|     if bBackward then ptCurrent := ptEnd else ptCurrent := ptStart;
 | |
|   end else begin
 | |
|     ptStart := Point(1, 1);
 | |
|     ptEnd.Y := FTheLinesView.Count;
 | |
|     ptEnd.X := Length(FTheLinesView[ptEnd.Y - 1]) + 1;
 | |
|     if bFromCursor then
 | |
|       if bBackward then
 | |
|         ptEnd := AStart
 | |
|       else
 | |
|         ptStart := AStart;
 | |
|     if bBackward then ptCurrent := ptEnd else ptCurrent := ptStart;
 | |
|   end;
 | |
|   // initialize the search engine
 | |
|   fTSearch.Sensitive := ssoMatchCase in AOptions;
 | |
|   fTSearch.Whole := ssoWholeWord in AOptions;
 | |
|   fTSearch.Pattern := ASearch;
 | |
|   fTSearch.RegularExpressions := ssoRegExpr in AOptions;
 | |
|   fTSearch.RegExprMultiLine := ssoRegExprMultiLine in AOptions;
 | |
|   fTSearch.Replacement:=AReplace;
 | |
|   fTSearch.Backwards:=bBackward;
 | |
|   // search while the current search position is inside of the search range
 | |
|   IncPaintLock;
 | |
|   try
 | |
|     //DebugLn(['TCustomSynEdit.SearchReplace ptStart=',dbgs(ptStart),' ptEnd=',dbgs(ptEnd),' ASearch="',dbgstr(ASearch),'" AReplace="',dbgstr(AReplace),'"']);
 | |
|     while fTSearch.FindNextOne(FTheLinesView,ptStart,ptEnd,ptFoundStart,ptFoundEnd, True) do
 | |
|     begin
 | |
|       //DebugLn(['TCustomSynEdit.SearchReplace FOUND ptStart=',dbgs(ptStart),' ptEnd=',dbgs(ptEnd),' ptFoundStart=',dbgs(ptFoundStart),' ptFoundEnd=',dbgs(ptFoundEnd)]);
 | |
|       // check if found place is entirely in range
 | |
|       if (FBlockSelection.ActiveSelectionMode <> smColumn)
 | |
|       or ((ptFoundStart.Y=ptFoundEnd.Y)
 | |
|           and (ptFoundStart.X >= ptStart.X) and (ptFoundEnd.X <= ptEnd.X)) then
 | |
|       begin
 | |
|         // pattern found
 | |
|         Inc(Result);
 | |
|         // Select the text, so the user can see it in the OnReplaceText event
 | |
|         // handler or as the search result.
 | |
|         BlockBegin := ptFoundStart;
 | |
|         if bBackward then LogicalCaretXY := BlockBegin;
 | |
|         BlockEnd := ptFoundEnd;
 | |
|         if not bBackward then LogicalCaretXY := ptFoundEnd;
 | |
|         // If it's a 'search' only we can leave the procedure now.
 | |
|         if not (bReplace or bReplaceAll) then exit;
 | |
|         // Prompt and replace or replace all.  If user chooses to replace
 | |
|         // all after prompting, turn off prompting.
 | |
|         CurReplace:=AReplace;
 | |
|         if ssoRegExpr in AOptions then
 | |
|           CurReplace:=fTSearch.RegExprReplace;
 | |
|         if bPrompt and Assigned(fOnReplaceText) then begin
 | |
|           EnsureCursorPosVisible;
 | |
|           try
 | |
|             DecPaintLock;
 | |
|             nAction := DoOnReplaceText(ASearch,CurReplace,
 | |
|                                        ptFoundStart.Y,ptFoundStart.X);
 | |
|           finally
 | |
|             IncPaintLock;
 | |
|           end;
 | |
|           if nAction = raCancel then exit;
 | |
|         end else
 | |
|           nAction := raReplace;
 | |
|         if not (nAction = raSkip) then begin
 | |
|           // user has been prompted and has requested to silently replace all
 | |
|           // so turn off prompting
 | |
|           if nAction = raReplaceAll then begin
 | |
|             bReplaceAll := True;
 | |
|             bPrompt := False;
 | |
|           end;
 | |
|           // replace text
 | |
|           //DebugLn(['TCustomSynEdit.SearchReplace OldSel="',dbgstr(SelText),'"']);
 | |
|           SetSelTextExternal(CurReplace);
 | |
|           //DebugLn(['TCustomSynEdit.SearchReplace NewSel="',dbgstr(SelText),'"']);
 | |
|           // adjust positions
 | |
|           ptEnd:=AdjustPositionAfterReplace(ptEnd,ptFoundStart,ptFoundEnd,
 | |
|                                             CurReplace);
 | |
|           ptFoundEnd:=AdjustPositionAfterReplace(ptFoundEnd,
 | |
|                                             ptFoundStart,ptFoundEnd,CurReplace);
 | |
|         end;
 | |
|         if not bReplaceAll then
 | |
|           exit;
 | |
|       end;
 | |
|       // shrink search range for next search
 | |
|       if ssoSearchInReplacement in AOptions then begin
 | |
|         if bBackward then begin
 | |
|           ptEnd:=ptFoundEnd;
 | |
|         end else begin
 | |
|           ptStart:=ptFoundStart;
 | |
|         end;
 | |
|       end else begin
 | |
|         if bBackward then begin
 | |
|           ptEnd:=ptFoundStart;
 | |
|         end else begin
 | |
|           ptStart:=ptFoundEnd;
 | |
|         end;
 | |
|       end;
 | |
|       //DebugLn(['TCustomSynEdit.SearchReplace FIND NEXT ptStart=',dbgs(ptStart),' ptEnd=',dbgs(ptEnd)]);
 | |
|     end;
 | |
|   finally
 | |
|     DecPaintLock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.IsPointInSelection(Value: TPoint): boolean;
 | |
| var
 | |
|   ptBegin, ptEnd: TPoint;
 | |
| begin
 | |
|   ptBegin := BlockBegin;
 | |
|   ptEnd := BlockEnd;
 | |
|   if (Value.Y >= ptBegin.Y) and (Value.Y <= ptEnd.Y) and
 | |
|     ((ptBegin.Y <> ptEnd.Y) or (ptBegin.X <> ptEnd.X))
 | |
|     then begin
 | |
|     if FBlockSelection.SelectionMode = smLine then
 | |
|       Result := TRUE
 | |
|     else if (FBlockSelection.ActiveSelectionMode = smColumn) then begin
 | |
|       if (ptBegin.X > ptEnd.X) then
 | |
|         Result := (Value.X >= ptEnd.X) and (Value.X < ptBegin.X)
 | |
|       else if (ptBegin.X < ptEnd.X) then
 | |
|         Result := (Value.X >= ptBegin.X) and (Value.X < ptEnd.X)
 | |
|       else
 | |
|         Result := FALSE;
 | |
|     end else
 | |
|       Result := ((Value.Y > ptBegin.Y) or (Value.X >= ptBegin.X)) and
 | |
|         ((Value.Y < ptEnd.Y) or (Value.X < ptEnd.X));
 | |
|   end else
 | |
|     Result := FALSE;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.BookMarkOptionsChanged(Sender: TObject);
 | |
| begin
 | |
|   InvalidateGutter;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetOptions(Value: TSynEditorOptions);
 | |
| var
 | |
|   ChangedOptions: TSynEditorOptions;
 | |
|   m: TSynEditorOption;
 | |
|   MOpt: TSynEditorMouseOptions;
 | |
|   f: Boolean;
 | |
| begin
 | |
|   Value := Value - SYNEDIT_UNIMPLEMENTED_OPTIONS;
 | |
|   if (Value = FOptions) then exit;
 | |
| 
 | |
|   ChangedOptions:=(FOptions-Value)+(Value-FOptions);
 | |
|   FOptions := Value;
 | |
|   UpdateOptions;
 | |
| 
 | |
|   if not (eoScrollPastEol in Options) then
 | |
|     LeftChar := LeftChar;
 | |
|   if (eoScrollPastEol in Options) or (eoScrollPastEof in Options) then begin
 | |
|     UpdateScrollBars;
 | |
|     TopView := TopView;
 | |
|   end;
 | |
|   // (un)register HWND as drop target
 | |
|   if (eoDropFiles in ChangedOptions) and not (csDesigning in ComponentState) and HandleAllocated then
 | |
|     ; // ToDo DragAcceptFiles
 | |
|   if (ChangedOptions * [eoPersistentCaret, eoNoCaret] <> []) and HandleAllocated then begin
 | |
|     UpdateCaret;
 | |
|     if Focused then
 | |
|       FScreenCaret.Visible := not(eoNoCaret in FOptions)
 | |
|     else
 | |
|       FScreenCaret.Visible := (eoPersistentCaret in FOptions) and not(eoNoCaret in FOptions);
 | |
|   end;
 | |
|   if (eoShowSpecialChars in ChangedOptions) then begin
 | |
|     if eoShowSpecialChars in FOptions
 | |
|     then FPaintArea.VisibleSpecialChars := VisibleSpecialChars
 | |
|     else FPaintArea.VisibleSpecialChars := [];
 | |
|     if HandleAllocated then
 | |
|       Invalidate;
 | |
|   end;
 | |
|   fMarkupSpecialChar.Enabled := (eoShowSpecialChars in fOptions);
 | |
| 
 | |
|   if (eoHideRightMargin in ChangedOptions) then
 | |
|     FPaintArea.RightEdgeVisible := not(eoHideRightMargin in FOptions);
 | |
| 
 | |
|   (* Deal with deprecated Mouse values
 | |
|      Those are all controlled by mouse-actions.
 | |
|      As long as the default mouse actions are set, the below will act as normal
 | |
|   *)
 | |
| 
 | |
|   MOpt := FMouseOptions;
 | |
|   f := False;
 | |
|   for m := low(SYNEDIT_OLD_MOUSE_OPTIONS_MAP) to high(SYNEDIT_OLD_MOUSE_OPTIONS_MAP) do
 | |
|     if (m in SYNEDIT_OLD_MOUSE_OPTIONS) and (m in ChangedOptions) then begin
 | |
|       f := True;
 | |
|       if (m in FOptions)
 | |
|       then MOpt := MOpt + [SYNEDIT_OLD_MOUSE_OPTIONS_MAP[m]]
 | |
|       else MOpt := MOpt - [SYNEDIT_OLD_MOUSE_OPTIONS_MAP[m]];
 | |
|     end;
 | |
|   if f then
 | |
|     MouseOptions := MOpt;
 | |
| 
 | |
|   FOptions := Value; // undo changes applied by MouseOptions
 | |
| 
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UpdateOptions;
 | |
| begin
 | |
|   FTrimmedLinesView.Enabled := eoTrimTrailingSpaces in fOptions;
 | |
|   FCaret.AllowPastEOL := (eoScrollPastEol in fOptions);
 | |
|   FCaret.KeepCaretX := (eoKeepCaretX in fOptions);
 | |
|   FBlockSelection.Enabled := not(eoNoSelection in fOptions);
 | |
|   FUndoList.GroupUndo := eoGroupUndo in fOptions;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetOptions2(const Value: TSynEditorOptions2);
 | |
| var
 | |
|   ChangedOptions: TSynEditorOptions2;
 | |
| begin
 | |
|   if (Value <> fOptions2) then begin
 | |
|     ChangedOptions:=(fOptions2 - Value) + (Value - fOptions2);
 | |
|     fOptions2 := Value;
 | |
|     UpdateOptions2;
 | |
|     if eoAlwaysVisibleCaret in fOptions2 then
 | |
|       MoveCaretToVisibleArea;
 | |
|     if (eoAutoHideCursor in ChangedOptions) and not(eoAutoHideCursor in fOptions2) then
 | |
|       UpdateCursor;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UpdateOptions2;
 | |
| begin
 | |
|   FBlockSelection.Persistent := eoPersistentBlock in fOptions2;
 | |
|   FCaret.SkipTabs := (eoCaretSkipTab in fOptions2);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetMouseOptions(AValue: TSynEditorMouseOptions);
 | |
| var
 | |
|   ChangedOptions: TSynEditorMouseOptions;
 | |
|   m: TSynEditorOption;
 | |
| begin
 | |
|   if FMouseOptions = AValue then Exit;
 | |
| 
 | |
|   ChangedOptions := (FMouseOptions-AValue)+(AValue-FMouseOptions);
 | |
|   FMouseOptions := AValue;
 | |
|   // changes take effect when MouseActions are accessed
 | |
| 
 | |
|   for m := low(SYNEDIT_OLD_MOUSE_OPTIONS_MAP) to high(SYNEDIT_OLD_MOUSE_OPTIONS_MAP) do
 | |
|     if (m in SYNEDIT_OLD_MOUSE_OPTIONS) and
 | |
|        (SYNEDIT_OLD_MOUSE_OPTIONS_MAP[m] in ChangedOptions) and
 | |
|        not(SYNEDIT_OLD_MOUSE_OPTIONS_MAP[m] in FMouseOptions)
 | |
|     then
 | |
|       FOptions := FOptions - [m];
 | |
| 
 | |
|   if (emShowCtrlMouseLinks in ChangedOptions) then begin
 | |
|     if assigned(fMarkupCtrlMouse) then
 | |
|       fMarkupCtrlMouse.UpdateCtrlMouse;
 | |
|     UpdateCursor;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UpdateMouseOptions;
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetOptionFlag(Flag: TSynEditorOption; Value: boolean);
 | |
| begin
 | |
|   if (Value <> (Flag in fOptions)) then begin
 | |
|     if Value then
 | |
|       Options := Options + [Flag]
 | |
|     else
 | |
|       Options := Options - [Flag];
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SizeOrFontChanged(bFont: boolean);
 | |
| begin
 | |
|   if HandleAllocated then begin
 | |
|     LastMouseCaret:=Point(-1,-1);
 | |
|     RecalcCharsAndLinesInWin(False);
 | |
| 
 | |
|     //DebugLn('TCustomSynEdit.SizeOrFontChanged LinesInWindow=',dbgs(LinesInWindow),' ClientHeight=',dbgs(ClientHeight),' ',dbgs(LineHeight));
 | |
|     //debugln('TCustomSynEdit.SizeOrFontChanged A ClientWidth=',dbgs(ClientWidth),' FLeftGutter.Width=',dbgs(FLeftGutter.Width),' ScrollBarWidth=',dbgs(ScrollBarWidth),' CharWidth=',dbgs(CharWidth),' CharsInWindow=',dbgs(CharsInWindow),' Width=',dbgs(Width));
 | |
|     if bFont then begin
 | |
|       UpdateScrollbars;
 | |
|       Exclude(fStateFlags, sfCaretChanged);
 | |
|       Invalidate;
 | |
|     end else
 | |
|       UpdateScrollbars;
 | |
|     Exclude(fStateFlags, sfScrollbarChanged); // TODO: Why?
 | |
|     if not (eoScrollPastEol in Options) then
 | |
|       LeftChar := LeftChar;
 | |
|     if not (eoScrollPastEof in Options) then
 | |
|       TopView := TopView;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RecalcCharsAndLinesInWin(CheckCaret: Boolean);
 | |
| var
 | |
|   OldLinesInWindow: Integer;
 | |
|   l, r: Integer;
 | |
| begin
 | |
|   if FLeftGutter.Visible
 | |
|   then l := FLeftGutter.Width
 | |
|   else l := 0;
 | |
|   if FRightGutter.Visible
 | |
|   then r := FRightGutter.Width
 | |
|   else r := 0;
 | |
| 
 | |
|   OldLinesInWindow := FTextArea.LinesInWindow;
 | |
| 
 | |
|   // TODO: lock FTextArea, so size re-calc is done once only
 | |
|   FPaintArea.SetBounds(0, 0, ClientHeight - ScrollBarWidth, ClientWidth - ScrollBarWidth);
 | |
|   FPaintArea.LeftGutterWidth := l;
 | |
|   FPaintArea.RightGutterWidth := r;
 | |
| 
 | |
|   if FLeftGutter.Visible
 | |
|   then FPaintArea.Padding[bsLeft] := GutterTextDist
 | |
|   else FPaintArea.Padding[bsLeft] := 1;
 | |
|   if FRightGutter.Visible
 | |
|   then FPaintArea.Padding[bsRight] := 0 //GutterTextDist
 | |
|   else FPaintArea.Padding[bsRight] := 0;
 | |
| 
 | |
|   //CharsInWindow := Max(1, w div CharWidth);
 | |
|   if OldLinesInWindow <> FTextArea.LinesInWindow then
 | |
|     StatusChanged([scLinesInWindow]);
 | |
| 
 | |
|   FFoldedLinesView.LinesInWindow := LinesInWindow;
 | |
|   FMarkupManager.LinesInWindow := LinesInWindow;
 | |
| 
 | |
|   FScreenCaret.Lock;
 | |
|   FScreenCaret.ClipRect := FTextArea.Bounds;
 | |
|   //FScreenCaret.ClipRect := Rect(TextLeftPixelOffset(False), 0,
 | |
|   //                              ClientWidth - TextRightPixelOffset - ScrollBarWidth + 1,
 | |
|   //                              ClientHeight - ScrollBarWidth);
 | |
|   FScreenCaret.ClipExtraPixel := FTextArea.Bounds.Right - FTextArea.Bounds.Left - CharsInWindow * CharWidth;
 | |
|   FScreenCaret.UnLock;
 | |
| 
 | |
|   if CheckCaret then begin
 | |
|     if not (eoScrollPastEol in Options) then
 | |
|       LeftChar := LeftChar;
 | |
|     if not (eoScrollPastEof in Options) then
 | |
|       TopView := TopView;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MoveCaretHorz(DX: integer);
 | |
| var
 | |
|   NewCaret: TPoint;
 | |
|   s: String;
 | |
|   PhysicalLineLen: Integer;
 | |
| begin
 | |
|   NewCaret:=Point(CaretX+DX,CaretY);
 | |
|   if NewCaret.X<1 then begin
 | |
|     if (eoScrollPastEol in fOptions) or (NewCaret.Y=1) then
 | |
|       NewCaret.X:=1
 | |
|     else begin
 | |
|       // move to end of prev line
 | |
|       NewCaret.Y:= FFoldedLinesView.TextPosAddLines(NewCaret.Y, -1);
 | |
|       s:=FTheLinesView[NewCaret.Y-1];
 | |
|       PhysicalLineLen:=LogicalToPhysicalPos(Point(length(s)+1,NewCaret.Y)).X-1;
 | |
|       NewCaret.X:=PhysicalLineLen+1;
 | |
|     end;
 | |
|   end
 | |
|   else if not (eoScrollPastEol in fOptions) then begin
 | |
|     s:=LineText;
 | |
|     PhysicalLineLen:=LogicalToPhysicalPos(Point(length(s)+1,CaretY)).X-1;
 | |
|     if (NewCaret.X > PhysicalLineLen+1) and (DX > 0) then begin
 | |
|       // move to start of next line (if it was a move to the right)
 | |
|       NewCaret.X := 1;
 | |
|       NewCaret.Y := FFoldedLinesView.TextPosAddLines(NewCaret.Y, +1);
 | |
|     end;
 | |
|   end;
 | |
|   DoIncPaintLock(Self);  // No editing is taking place
 | |
|   FCaret.IncForcePastEOL;
 | |
|   if DX > 0 then
 | |
|     FCaret.IncForceAdjustToNextChar;
 | |
|   FCaret.LineCharPos := NewCaret;
 | |
|   FCaret.DecForcePastEOL;
 | |
|   if DX > 0 then
 | |
|     FCaret.DecForceAdjustToNextChar;
 | |
|   DoDecPaintLock(Self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.MoveCaretVert(DY: integer);
 | |
| // moves Caret vertical DY unfolded lines
 | |
| var
 | |
|   NewCaret: TPoint;
 | |
|   OldCaret: TPoint;
 | |
| begin
 | |
|   OldCaret:=CaretXY;
 | |
|   NewCaret:=OldCaret;
 | |
|   NewCaret.Y:=FFoldedLinesView.TextPosAddLines(NewCaret.Y, DY);
 | |
|   DoIncPaintLock(Self); // No editing is taking place
 | |
|   FCaret.LinePos := NewCaret.Y;
 | |
|   DoDecPaintLock(Self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetCaretAndSelection(const ptCaret, ptBefore,
 | |
|   ptAfter: TPoint; Mode: TSynSelectionMode = smCurrent; MakeSelectionVisible: Boolean = False);
 | |
| // caret is physical (screen)
 | |
| // Before, After is logical (byte)
 | |
| var
 | |
|   L1, L2, LBottomLine, LCaretFirst, LCaretLast: Integer;
 | |
| begin
 | |
|   DoIncPaintLock(Self); // No editing is taking place
 | |
| 
 | |
|   CaretXY := ptCaret;
 | |
|   SetBlockBegin(ptBefore);
 | |
|   SetBlockEnd(ptAfter);
 | |
|   if Mode <> smCurrent then
 | |
|     FBlockSelection.ActiveSelectionMode := Mode;
 | |
| 
 | |
|   if MakeSelectionVisible then begin
 | |
|     //l1 := FBlockSelection.FirstLineBytePos;;
 | |
|     LBottomLine := FFoldedLinesView.TextPosAddLines(TopLine, LinesInWindow);
 | |
| 
 | |
|     LCaretFirst := CaretY;
 | |
|     LCaretLast := Max(1, FFoldedLinesView.TextPosAddLines(CaretY, 1-LinesInWindow));  // Will have caret on last visible line
 | |
| 
 | |
|     l1 := Min(LCaretFirst, FBlockSelection.FirstLineBytePos.y);
 | |
|     l2 := Max(LCaretFirst, FBlockSelection.LastLineBytePos.y);
 | |
| 
 | |
|     if CaretY < TopLine then begin
 | |
|       // Scrolling up,  Topline = L1 ; but ensure Caret
 | |
|       TopLine := Max(LCaretLast,
 | |
|                  Min(LCaretFirst,
 | |
|                      L1
 | |
|                     ));
 | |
|     end
 | |
|     else if CaretY > LBottomLine then begin
 | |
|       // Scrolling down, LastLine = L2
 | |
|       TopLine := Max(LCaretLast,
 | |
|                  Min(LCaretFirst,
 | |
|                      FFoldedLinesView.TextPosAddLines(L2, 1-LinesInWindow)
 | |
|                     ));
 | |
|     end
 | |
|     else begin
 | |
|       // Caret alreayd visible, check block
 | |
|       if l1 < TopLine then
 | |
|         TopLine := Max(LCaretLast,
 | |
|                    Min(LCaretFirst,
 | |
|                        L1
 | |
|                       ))
 | |
|       else
 | |
|       if l2 > LBottomLine then
 | |
|         TopLine := Max(LCaretLast,
 | |
|                    Min(LCaretFirst,
 | |
|                        FFoldedLinesView.TextPosAddLines(L2, 1-LinesInWindow)
 | |
|                       ));
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   DoDecPaintLock(Self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RecalcCharExtent;
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   (* Highlighter or Font changed *)
 | |
| 
 | |
|   FFontDummy.Assign(Font);
 | |
|   with FFontDummy do begin
 | |
|     // Keep GTK happy => By ensuring a change the XFLD fontname gets cleared
 | |
|     {$IFDEF LCLGTK1}
 | |
|     Pitch := fpVariable;
 | |
|     Style := [fsBold];
 | |
|     Pitch := fpDefault; // maybe Fixed
 | |
|     {$ENDIF}
 | |
|     // TODO: Clear style only, if Highlighter uses styles
 | |
|     Style := [];        // Reserved for Highlighter
 | |
|   end;
 | |
|   //debugln(['TCustomSynEdit.RecalcCharExtent ',fFontDummy.Name,' ',fFontDummy.Size]);
 | |
|   //debugln('TCustomSynEdit.RecalcCharExtent A UseUTF8=',dbgs(UseUTF8),
 | |
|   //  ' Font.CanUTF8='+dbgs(Font.CanUTF8)+' CharHeight=',dbgs(CharHeight));
 | |
| 
 | |
|   fTextDrawer.BaseFont := FFontDummy;
 | |
|   if Assigned(fHighlighter) then
 | |
|     for i := 0 to Pred(fHighlighter.AttrCount) do
 | |
|       fTextDrawer.BaseStyle := fHighlighter.Attribute[i].Style;
 | |
|   fTextDrawer.CharExtra := fExtraCharSpacing;
 | |
| 
 | |
|   FUseUTF8:=fTextDrawer.UseUTF8;
 | |
|   FLines.IsUtf8 := FUseUTF8;
 | |
| 
 | |
|   FScreenCaret.Lock;
 | |
|   try
 | |
|     FScreenCaret.CharWidth := CharWidth;
 | |
|     FScreenCaret.CharHeight := LineHeight - Max(0, ExtraLineSpacing);
 | |
|     SizeOrFontChanged(TRUE);
 | |
|   finally
 | |
|     FScreenCaret.UnLock;
 | |
|   end;
 | |
|   UpdateScrollBars;
 | |
|   //debugln('TCustomSynEdit.RecalcCharExtent UseUTF8=',dbgs(UseUTF8),' Font.CanUTF8=',dbgs(Font.CanUTF8));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.HighlighterAttrChanged(Sender: TObject);
 | |
| begin
 | |
|   RecalcCharExtent;
 | |
|   Invalidate;
 | |
|   // TODO: obey paintlock
 | |
|   if fHighlighter.AttributeChangeNeedScan then begin
 | |
|     FHighlighter.CurrentLines := FTheLinesView;
 | |
|     FHighlighter.ScanAllRanges;
 | |
|     fMarkupManager.TextChanged(0, FTheLinesView.Count - 1);
 | |
|     TopView := TopView;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.StatusChanged(AChanges: TSynStatusChanges);
 | |
| begin
 | |
|   fStatusChanges := fStatusChanges + AChanges;
 | |
|   if PaintLock = 0 then
 | |
|     DoOnStatusChange(fStatusChanges);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoTabKey;
 | |
| var
 | |
|   i, iLine: integer;
 | |
|   PrevLine,
 | |
|   Spaces: string;
 | |
|   p: PChar;
 | |
|   OldCaretX: integer;
 | |
| begin
 | |
|   if (eoTabIndent in Options) and SelAvail then begin
 | |
|     DoBlockIndent;
 | |
|     exit;
 | |
|   end;
 | |
| 
 | |
|   InternalBeginUndoBlock;
 | |
|   try
 | |
|     i := 0;
 | |
|     OldCaretX := CaretX;
 | |
|     SelText := '';
 | |
|     // With a multi-line block the caret may have advanced, avoid negative spaces
 | |
|     if CaretX > OldCaretX then
 | |
|       OldCaretX := CaretX;
 | |
|     if eoSmartTabs in fOptions then begin
 | |
|       iLine := CaretY - 1;
 | |
|       if (iLine > 0) and (iLine < FTheLinesView.Count) then begin
 | |
|         repeat
 | |
|           Dec(iLine);
 | |
|           if iLine < 0 then break;
 | |
|           PrevLine := FTheLinesView[iLine];
 | |
|         until PhysicalLineLength(PrevLine, iLine) > OldCaretX - 1;
 | |
| 
 | |
|         if iLine >= 0 then begin
 | |
|           p := @PrevLine[PhysicalToLogicalCol(PrevLine, iLine, OldCaretX)];
 | |
|           // scan over non-whitespaces
 | |
|           while not (p^ in [#0, #9, #32]) do
 | |
|             inc(p);
 | |
|           // scan over whitespaces
 | |
|           while (p^ in [#9, #32]) do
 | |
|             inc(p);
 | |
|           i := LogicalToPhysicalCol(PrevLine, iLine, p-@PrevLine[1]+1) - CaretX;
 | |
|         end;
 | |
|       end;
 | |
|     end;
 | |
|     if i <= 0 then begin
 | |
|       i := TabWidth - (CaretX - 1) mod TabWidth;
 | |
|       if i = 0 then i := TabWidth;
 | |
|     end;
 | |
|     // i now contains the needed spaces
 | |
|     Spaces := CreateTabsAndSpaces(CaretX,i,TabWidth,
 | |
|                                   not (eoTabsToSpaces in Options));
 | |
|     //debugln('TCustomSynEdit.DoTabKey Spaces="',DbgStr(Spaces),'" TabChar=',DbgStr(TabChar));
 | |
|     OldCaretX := CaretX;
 | |
|     //debugln('TCustomSynEdit.DoTabKey Before SetSelText Line="',DbgStr(GetLineText),'"');
 | |
|     SetSelTextExternal(Spaces);
 | |
|     //debugln('TCustomSynEdit.DoTabKey After SetSelText Line="',DbgStr(GetLineText),'"');
 | |
|     CaretX := OldCaretX + i;
 | |
|     //debugln('TCustomSynEdit.DoTabKey StartOfBlock=',dbgs(StartOfBlock),' fBlockEnd=',dbgs(fBlockEnd),' Spaces="',Spaces,'"');
 | |
|   finally
 | |
|     InternalEndUndoBlock;
 | |
|   end;
 | |
|   EnsureCursorPosVisible;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.CreateWnd;
 | |
| begin
 | |
|   inherited;
 | |
|   if (eoDropFiles in fOptions) and not (csDesigning in ComponentState) then
 | |
|     {$IFDEF SYN_LAZARUS}
 | |
|     // ToDo DragAcceptFiles
 | |
|     ;
 | |
|     {$ELSE}
 | |
|     DragAcceptFiles(Handle, TRUE);
 | |
|     {$ENDIF}
 | |
|   SizeOrFontChanged(true);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DestroyWnd;
 | |
| begin
 | |
|   if (eoDropFiles in fOptions) and not (csDesigning in ComponentState) then begin
 | |
|     {$IFDEF SYN_LAZARUS}
 | |
|     // ToDo DragAcceptFiles
 | |
|     ;
 | |
|     {$ELSE}
 | |
|     DragAcceptFiles(Handle, FALSE);
 | |
|     {$ENDIF}
 | |
|   end;
 | |
|   {$IFDEF EnableDoubleBuf}
 | |
|   FreeAndNil(BufferBitmap);
 | |
|   {$ENDIF}
 | |
|   SurrenderPrimarySelection;
 | |
|   inherited DestroyWnd;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoBlockIndent;
 | |
| var
 | |
|   BB,BE            : TPoint;
 | |
|   Line : PChar;
 | |
|   Len, e, y: integer;
 | |
|   Spaces, Tabs: String;
 | |
| 
 | |
|   function GetLeadWSLen : integer;
 | |
|   var
 | |
|     Run : PChar;
 | |
|   begin
 | |
|     Run := Line;
 | |
|     while (Run[0] in [' ', #9]) do
 | |
|       Inc(Run);
 | |
|     Result := Run - Line;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   IncPaintLock;
 | |
|   FBlockSelection.IncPersistentLock;
 | |
|   try
 | |
|     // build text to insert
 | |
|     if not SelAvail then begin
 | |
|       BB := CaretXY;
 | |
|       BE := CaretXY;
 | |
|       e := BE.y;
 | |
|     end else begin
 | |
|       BB := BlockBegin;
 | |
|       BE := BlockEnd;
 | |
|       if (BE.X = 1)
 | |
|       then e := BE.y - 1
 | |
|       else e := BE.y;
 | |
|     end;
 | |
| 
 | |
|     Spaces := StringOfChar(#32, FBlockIndent);
 | |
|     Tabs   := StringOfChar( #9, FBlockTabIndent);
 | |
|     fUndoList.Lock;
 | |
|     fRedoList.Lock;
 | |
|     try
 | |
|       for y := BB.Y to e do
 | |
|       begin
 | |
|         Line := PChar(FTheLinesView[y - 1]);
 | |
|         Len := GetLeadWSLen;
 | |
|         FTheLinesView.EditInsert(Len + 1, y, Spaces);
 | |
|         FTheLinesView.EditInsert(1,       y, Tabs);
 | |
|       end;
 | |
|     finally
 | |
|       fUndoList.Unlock;
 | |
|       fRedoList.Unlock;
 | |
|     end;
 | |
| 
 | |
|     fUndoList.AddChange(TSynEditUndoIndent.Create(BB.Y, e, FBlockIndent, FBlockTabIndent));
 | |
|   finally
 | |
|     FTrimmedLinesView.ForceTrim; // Otherwise it may reset the block
 | |
|     FCaret.LineBytePos := FBlockSelection.EndLineBytePos;
 | |
|     FBlockSelection.DecPersistentLock;
 | |
|     DecPaintLock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoBlockUnindent;
 | |
| const
 | |
|   LineEnd = #10;
 | |
| var
 | |
|   BB, BE: TPoint;
 | |
|   FullStrToDelete: PChar;
 | |
|   Line: PChar;
 | |
|   Len, LogP1, PhyP1, LogP2, PhyP2, y, StrToDeleteLen, e : integer;
 | |
|   i, i2, j: Integer;
 | |
|   SomethingDeleted : Boolean;
 | |
|   HasTab: Boolean;
 | |
| 
 | |
|   function GetLeadWSLen : integer;
 | |
|   var
 | |
|     Run : PChar;
 | |
|   begin
 | |
|     Run := Line;
 | |
|     HasTab := False;
 | |
|     while (Run[0] in [' ', #9]) do begin
 | |
|       HasTab := HasTab or (Run[0] = #9);
 | |
|       Inc(Run);
 | |
|     end;
 | |
|     Result := Run - Line;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   if not SelAvail then begin
 | |
|     BB := CaretXY;
 | |
|     BE := CaretXY;
 | |
|     e := BE.y;
 | |
|   end else begin
 | |
|     BB := BlockBegin;
 | |
|     BE := BlockEnd;
 | |
|     // convert selection to complete lines
 | |
|     if BE.X = 1 then
 | |
|       e := BE.y - 1
 | |
|     else
 | |
|       e := BE.y;
 | |
|   end;
 | |
| 
 | |
|   IncPaintLock;
 | |
|   FBlockSelection.IncPersistentLock;
 | |
|   // build string to delete
 | |
|   StrToDeleteLen := (fBlockIndent+length(LineEnd)) * (e - BB.y + 1) + 1;
 | |
|   //                 chars per line * lines-1    + last line + null char
 | |
|   FullStrToDelete := StrAlloc(StrToDeleteLen);
 | |
|   try
 | |
|     FullStrToDelete[0] := #0;
 | |
|     SomethingDeleted := False;
 | |
| 
 | |
|     fUndoList.Lock;
 | |
|     fRedoList.Lock;
 | |
| 
 | |
|     // before locking the undo list
 | |
|     for y := BB.Y to e do
 | |
|     begin
 | |
|       Line := PChar(FTheLinesView[y - 1]);
 | |
|       Len := GetLeadWSLen;
 | |
|       LogP1 := Len + 1;
 | |
|       if HasTab and (Len > 0) then begin
 | |
|         // LogP1, PhyP1 log and phys of the first none-whitespace
 | |
|         PhyP1 := LogicalToPhysicalPos(Point(LogP1, y)).x;
 | |
|         // LogP2, PhyP2 log and phys of the point to which to delete back
 | |
|         LogP2 := PhysicalToLogicalPos(Point( Max(PhyP1 - FBlockIndent, 1), y )).x;
 | |
|         PhyP2 := LogicalToPhysicalPos(Point(LogP2,y)).x;
 | |
| 
 | |
|         if PhyP1 - PhyP2 <> FBlockIndent then begin
 | |
|           // need tab to space
 | |
|           StrCat(FullStrToDelete, PChar(copy(Line, LogP2, LogP1 - LogP2)));
 | |
|           StrCat(FullStrToDelete, PChar(LineEnd));
 | |
|           FTheLinesView.EditDelete(LogP2, y, LogP1 - LogP2);
 | |
|           SomethingDeleted := True;
 | |
| 
 | |
|           fUndoList.Unlock;
 | |
|           fRedoList.Unlock;
 | |
|           FTheLinesView.EditInsert(LogP2, y, StringOfChar(' ', PhyP1 - PhyP2 - FBlockIndent));
 | |
|           fUndoList.Lock;
 | |
|           fRedoList.Lock;
 | |
|           continue;
 | |
|         end;
 | |
|         // tabs present, but no replacement needed (LogP1, LogP2 are correct
 | |
|       end
 | |
|       else begin
 | |
|         // no tabs present
 | |
|         LogP2 := Max(LogP1 - FBlockIndent, 1);
 | |
|       end;
 | |
| 
 | |
|       // Remove spaces (or tab)
 | |
|       if LogP1 - LogP2 > 0 then
 | |
|         StrCat(FullStrToDelete, PChar(copy(Line, LogP2, LogP1 - LogP2)));
 | |
|       StrCat(FullStrToDelete, PChar(LineEnd));
 | |
|       if LogP1 - LogP2 > 0 then
 | |
|         FTheLinesView.EditDelete(LogP2, y, LogP1 - LogP2);
 | |
|       SomethingDeleted := SomethingDeleted or (LogP1 - LogP2 > 0);
 | |
| 
 | |
|       // Todo: create FullTabStrToDelete for tabs
 | |
|       fUndoList.Unlock;
 | |
|       fRedoList.Unlock;
 | |
|       Line := PChar(FTheLinesView[y - 1]);
 | |
|       j := 0;
 | |
|       for i := 1 to FBlockTabIndent do begin
 | |
|         i2 := fTabWidth;
 | |
|         while (i2 > 0) and (Line[j] = #32) do begin
 | |
|           dec(i2);
 | |
|           inc(j);
 | |
|         end;
 | |
|         if (i2 > 0) and (Line[j] = #9) then inc(j);
 | |
|       end;
 | |
|       if j > 0 then
 | |
|         FTheLinesView.EditDelete(1, y, j);
 | |
|       fUndoList.Lock;
 | |
|       fRedoList.Lock;
 | |
| 
 | |
|     end;
 | |
| 
 | |
|     fUndoList.Unlock;
 | |
|     fRedoList.Unlock;
 | |
| 
 | |
|     if SomethingDeleted then
 | |
|       fUndoList.AddChange(TSynEditUndoUnIndent.Create(BB.Y, e, FullStrToDelete));
 | |
| 
 | |
|     FTrimmedLinesView.ForceTrim; // Otherwise it may reset the block
 | |
|   finally
 | |
|     StrDispose(FullStrToDelete);
 | |
|     FCaret.LineBytePos := FBlockSelection.EndLineBytePos;
 | |
|     FBlockSelection.DecPersistentLock;
 | |
|     DecPaintLock;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoHomeKey(aMode: TSynHomeMode = synhmDefault);
 | |
| // jump to start of line (x=1),
 | |
| // or if already there, jump to first non blank char
 | |
| // or if blank line, jump to line indent position
 | |
| // if eoEnhanceHomeKey and behind alternative point then jump first
 | |
| var
 | |
|   s: string;
 | |
|   FirstNonBlank: Integer;
 | |
|   LineStart: LongInt;
 | |
|   OldPos: TPoint;
 | |
|   NewPos: TPoint;
 | |
| begin
 | |
|   OldPos := CaretXY;
 | |
|   NewPos := OldPos;
 | |
| 
 | |
|   if not(eoEnhanceHomeKey in fOptions) and (CaretX > 1) and (aMode in [synhmDefault]) then
 | |
|   begin
 | |
|     // not at start of line -> jump to start of line
 | |
|     NewPos.X := 1;
 | |
|   end else 
 | |
|   begin
 | |
|     // calculate line start position
 | |
|     FirstNonBlank := -1;
 | |
|     if CaretY <= FTheLinesView.Count then 
 | |
|     begin
 | |
|       s := FTheLinesView[CaretXY.Y - 1];
 | |
| 
 | |
|       // search first non blank char pos
 | |
|       FirstNonBlank := 1;
 | |
|       while (FirstNonBlank <= length(s)) and (s[FirstNonBlank] in [#32, #9]) do
 | |
|         inc(FirstNonBlank);
 | |
|       if FirstNonBlank > length(s) then
 | |
|         FirstNonBlank := -1;
 | |
|     end else
 | |
|       s := '';
 | |
| 
 | |
|     if (FirstNonBlank >= 1) or (aMode in [synhmFirstWord]) then
 | |
|     begin
 | |
|       // this line is not blank
 | |
|       if FirstNonBlank < 1 then FirstNonBlank := 1;
 | |
|       LineStart := LogicalToPhysicalPos(Point(FirstNonBlank, CaretY)).x;
 | |
|     end else 
 | |
|     begin
 | |
|       // this line is blank
 | |
|       // -> use automatic line indent
 | |
|       LineStart := FBeautifier.GetDesiredIndentForLine(Self, FTheLinesView, FCaret);
 | |
|     end;
 | |
| 
 | |
|     NewPos.X:=LineStart;
 | |
|     if (eoEnhanceHomeKey in fOptions)  and (aMode in [synhmDefault]) and
 | |
|        (OldPos.X>1) and (OldPos.X<=NewPos.X)
 | |
|     then begin
 | |
|       NewPos.X:=1;
 | |
|     end;
 | |
|   end;
 | |
|   FCaret.LineCharPos := NewPos;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoEndKey;
 | |
| // jump to start of line (x=1),
 | |
| // or if already there, jump to first non blank char
 | |
| // or if blank line, jump to line indent position
 | |
| // if eoEnhanceHomeKey and behind alternative point then jump first
 | |
| var
 | |
|   s: string;
 | |
|   LastNonBlank: Integer;
 | |
|   LineEnd: LongInt;
 | |
|   OldPos: TPoint;
 | |
|   NewPos: TPoint;
 | |
| begin
 | |
|   OldPos := CaretXY;
 | |
|   NewPos := OldPos;
 | |
|   s := LineText;
 | |
| 
 | |
|   if not (eoEnhanceEndKey in fOptions2) and (FCaret.BytePos <> Length(s)+1) then begin
 | |
|     // not at end of real line -> jump to end of line
 | |
|     FCaret.BytePos := Length(s)+1;
 | |
|   end else begin
 | |
|     // calculate line end position
 | |
|     LastNonBlank := -1;
 | |
|     if s <> '' then begin
 | |
|       // search first non blank char pos
 | |
|       LastNonBlank := Length(s);
 | |
|       while (LastNonBlank > 0) and (s[LastNonBlank] in [#32, #9]) do
 | |
|         dec(LastNonBlank);
 | |
|     end;
 | |
|     if LastNonBlank >=1 then begin
 | |
|       // this line is not blank
 | |
|       LineEnd := LogicalToPhysicalPos(Point(LastNonBlank + 1, CaretY)).x;
 | |
|     end else begin
 | |
|       // this line is blank
 | |
|       // -> use automatic line indent
 | |
|       LineEnd := FBeautifier.GetDesiredIndentForLine(Self, FTheLinesView, FCaret);
 | |
|     end;
 | |
| 
 | |
|     NewPos.X:=LineEnd;
 | |
|     if (eoEnhanceEndKey in fOptions2) and (FCaret.BytePos <> Length(s)+1) and (OldPos.X >= NewPos.X)
 | |
|     then begin
 | |
|       FCaret.BytePos := Length(s)+1;
 | |
|     end
 | |
|     else
 | |
|       FCaret.LineCharPos := NewPos;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| {$IFDEF SYN_COMPILER_4_UP}
 | |
| function TCustomSynEdit.ExecuteAction(ExeAction: TBasicAction): boolean;
 | |
| begin
 | |
|   if ExeAction is TEditAction then
 | |
|   begin
 | |
|     Result := TRUE;
 | |
|     if ExeAction is TEditCut then
 | |
|       CutToClipboard
 | |
|     else if ExeAction is TEditCopy then
 | |
|       CopyToClipboard
 | |
|     else if ExeAction is TEditPaste then
 | |
|       PasteFromClipboard
 | |
| {$IFDEF SYN_COMPILER_5_UP}
 | |
|     else if ExeAction is TEditDelete then
 | |
|       ClearSelection
 | |
|     else if ExeAction is TEditUndo then
 | |
|       Undo
 | |
|     else if ExeAction is TEditSelectAll then
 | |
|       SelectAll;
 | |
| {$ENDIF}
 | |
|   end else
 | |
|     Result := inherited ExecuteAction(ExeAction);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.UpdateAction(TheAction: TBasicAction): boolean;
 | |
| begin
 | |
|   if TheAction is TEditAction then
 | |
|   begin
 | |
|     Result := Focused;
 | |
|     if Result then
 | |
|     begin
 | |
|       if (TheAction is TEditCut) or (TheAction is TEditCopy) then
 | |
|         TEditAction(TheAction).Enabled := SelAvail
 | |
|       else if TheAction is TEditPaste then
 | |
|         TEditAction(TheAction).Enabled := CanPaste
 | |
| {$IFDEF SYN_COMPILER_5_UP}
 | |
|       else if TheAction is TEditDelete then
 | |
|         TEditAction(TheAction).Enabled := TRUE
 | |
|       else if TheAction is TEditUndo then
 | |
|         TEditAction(TheAction).Enabled := CanUndo
 | |
|       else if TheAction is TEditSelectAll then
 | |
|         TEditAction(TheAction).Enabled := TRUE;
 | |
| {$ENDIF}
 | |
|     end;
 | |
|   end else
 | |
|     Result := inherited UpdateAction(TheAction);
 | |
| end;
 | |
| {$ENDIF}
 | |
| 
 | |
| procedure TCustomSynEdit.SetModified(Value: boolean);
 | |
| begin
 | |
|   TSynEditStringList(FLines).Modified := Value;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.InvalidateLine(Line: integer);
 | |
| begin
 | |
|   InvalidateLines(Line, Line);
 | |
|   InvalidateGutterLines(Line, Line);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetReadOnly: boolean;
 | |
| begin
 | |
|   Result := fReadOnly;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.SetReadOnly(Value: boolean);
 | |
| begin
 | |
|   if fReadOnly <> Value then begin
 | |
|     fReadOnly := Value;
 | |
|     StatusChanged([scReadOnly]);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.FindMatchingBracket;
 | |
| begin
 | |
|   FindMatchingBracket(CaretXY,false,true,false,false);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.FindMatchingBracket(PhysStartBracket: TPoint;
 | |
|   StartIncludeNeighborChars, MoveCaret, SelectBrackets, OnlyVisible: boolean
 | |
|   ): TPoint;
 | |
| // returns physical (screen) position of end bracket
 | |
| const
 | |
|   // keep the ' last
 | |
|   Brackets: array[0..7] of char = ('(', ')', '[', ']', '{', '}', '''', '"');
 | |
| type
 | |
|   TokenPos = Record X: Integer; Attr: Integer; end;
 | |
| var
 | |
|   Line, s1: string;
 | |
|   PosX, PosY: integer;
 | |
|   StartPt: TPoint;
 | |
|   LogicalStart: TPoint;
 | |
|   // for ContextMatch
 | |
|   BracketKind, TmpStart: Integer;
 | |
|   TmpAttr : TSynHighlighterAttributes;
 | |
|   // for IsContextBracket
 | |
|   MaxKnownTokenPos, TokenListCnt: Integer;
 | |
|   TokenPosList: Array of TokenPos;
 | |
| 
 | |
|   // remove all text, that is not of desired attribute
 | |
|   function IsContextBracket: boolean;
 | |
|   var
 | |
|     i, l: Integer;
 | |
|   begin
 | |
|     if not assigned(fHighlighter) then exit(true);
 | |
|     if PosX > MaxKnownTokenPos then begin
 | |
|       // Token is not yet known
 | |
|       l := Length(TokenPosList);
 | |
|       if l < max(CharsInWindow * 2, 32) then begin
 | |
|         l := max(CharsInWindow * 2, 32);
 | |
|         SetLength(TokenPosList, l);
 | |
|       end;
 | |
|       // Init the Highlighter only once per line
 | |
|       if MaxKnownTokenPos < 1 then begin
 | |
|         fHighlighter.CurrentLines := FTheLinesView;
 | |
|         fHighlighter.StartAtLineIndex(PosY - 1);
 | |
|         TokenListCnt := 0;
 | |
|       end
 | |
|       else
 | |
|         fHighlighter.Next;
 | |
|       i := TokenListCnt;
 | |
|       while not fHighlighter.GetEol do begin
 | |
|         TokenPosList[i].X := fHighlighter.GetTokenPos + 1;
 | |
|         TokenPosList[i].Attr := fHighlighter.GetTokenKind;
 | |
|         if TokenPosList[i].X > PosX then begin
 | |
|           TokenListCnt := i + 1;
 | |
|           MaxKnownTokenPos := TokenPosList[i].X;
 | |
|           Result := TokenPosList[i-1].Attr = BracketKind;
 | |
|           exit;
 | |
|         end;
 | |
|         inc(i);
 | |
|         if i >= l then begin
 | |
|           l := l * 4;
 | |
|           SetLength(TokenPosList, l);
 | |
|         end;
 | |
|         fHighlighter.Next;
 | |
|       end;
 | |
|       MaxKnownTokenPos := Length(Line);
 | |
|       TokenPosList[i].X := MaxKnownTokenPos;
 | |
|       TokenListCnt := i + 1;
 | |
|       Result := TokenPosList[i-1].Attr = BracketKind;
 | |
|       exit;
 | |
|     end;
 | |
|     // Token is in previously retrieved values
 | |
|     i := 1;
 | |
|     while (i < TokenListCnt) and (TokenPosList[i].X <= PosX) do
 | |
|       inc(i);
 | |
|     Result := TokenPosList[i-1].Attr = BracketKind;
 | |
|   end;
 | |
| 
 | |
|   procedure DoMatchingBracketFound;
 | |
|   var
 | |
|     EndPt, DummyPt: TPoint;
 | |
|   begin
 | |
|     // matching bracket found, set caret and bail out
 | |
|     Result := Point(PosX, PosY); // start with logical (byte) position
 | |
|     if SelectBrackets then begin
 | |
|       EndPt:=Result;
 | |
|       if (EndPt.Y < StartPt.Y)
 | |
|         or ((EndPt.Y = StartPt.Y) and (EndPt.X < StartPt.X)) then
 | |
|       begin
 | |
|         DummyPt:=StartPt;
 | |
|         StartPt:=EndPt;
 | |
|         EndPt:=DummyPt;
 | |
|       end;
 | |
|       inc(EndPt.X);
 | |
|       SetCaretAndSelection(CaretXY, StartPt, EndPt);
 | |
|     end
 | |
|     else if MoveCaret then
 | |
|       LogicalCaretXY := Result;
 | |
|   end;
 | |
| 
 | |
|   procedure DoFindMatchingQuote(q: char);
 | |
|   var
 | |
|     Test: char;
 | |
|     Len, PrevPosX, PrevCnt: integer;
 | |
|   begin
 | |
|     StartPt:=Point(PosX,PosY);
 | |
|     GetHighlighterAttriAtRowColEx(StartPt, s1, BracketKind, TmpStart, TmpAttr);
 | |
|     // Checck if we have a complete token, e.g. Highlightec returned entire "string"
 | |
|     if (TmpStart = PosX) and (Length(s1)>1) and (s1[Length(s1)] = q) then begin
 | |
|       PosX := PosX + Length(s1) - 1;
 | |
|       DoMatchingBracketFound;
 | |
|       exit;
 | |
|     end;
 | |
|     if (TmpStart + Length(s1) - 1 = PosX) and (Length(s1)>1) and (s1[1] = q) then begin
 | |
|       PosX := PosX - Length(s1) + 1;
 | |
|       DoMatchingBracketFound;
 | |
|       exit;
 | |
|     end;
 | |
| 
 | |
|     MaxKnownTokenPos := 0;
 | |
|     Len := PosX;
 | |
|     PrevPosX := -1;
 | |
|     PrevCnt := 0;
 | |
|     // search until start of line
 | |
|      while PosX > 1 do begin
 | |
|       Dec(PosX);
 | |
|       Test := Line[PosX];
 | |
|       if (Test = q) and IsContextBracket then begin
 | |
|         inc(PrevCnt);
 | |
|         if PrevPosX < 0 then PrevPosX := PosX;
 | |
|       end;
 | |
|     end;
 | |
|     // 1st, 3rd, 5th, ... are opening
 | |
|     if (PrevPosX > 0) and (PrevCnt mod 2 = 1) then begin
 | |
|       PosX := PrevPosX;
 | |
|       DoMatchingBracketFound;
 | |
|       exit;
 | |
|     end;
 | |
| 
 | |
|     PosX := Len;
 | |
|     Len := Length(Line);
 | |
|     while PosX < Len do begin
 | |
|       Inc(PosX);
 | |
|       Test := Line[PosX];
 | |
|       if (Test = q) and IsContextBracket then begin
 | |
|         DoMatchingBracketFound;
 | |
|         exit;
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
|     if (PrevPosX > 0) then begin
 | |
|       PosX := PrevPosX;
 | |
|       DoMatchingBracketFound;
 | |
|       exit;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   procedure DoFindMatchingBracket(i: integer);
 | |
|   var
 | |
|     Test, BracketInc, BracketDec: char;
 | |
|     NumBrackets, Len: integer;
 | |
|   begin
 | |
|     StartPt:=Point(PosX,PosY);
 | |
|     GetHighlighterAttriAtRowColEx(StartPt, s1, BracketKind, TmpStart, TmpAttr);
 | |
|     MaxKnownTokenPos := 0;
 | |
|     BracketInc := Brackets[i];
 | |
|     BracketDec := Brackets[i xor 1]; // 0 -> 1, 1 -> 0, ...
 | |
|     // search for the matching bracket (that is until NumBrackets = 0)
 | |
|     NumBrackets := 1;
 | |
|     if Odd(i) then begin
 | |
|       // closing bracket -> search opening bracket
 | |
|       repeat
 | |
|         // search until start of line
 | |
|         while PosX > 1 do begin
 | |
|           Dec(PosX);
 | |
|           Test := Line[PosX];
 | |
|           if (Test = BracketInc) and IsContextBracket then
 | |
|             Inc(NumBrackets)
 | |
|           else if (Test = BracketDec) and IsContextBracket then begin
 | |
|             Dec(NumBrackets);
 | |
|             if NumBrackets = 0 then begin
 | |
|               DoMatchingBracketFound;
 | |
|               exit;
 | |
|             end;
 | |
|           end;
 | |
|         end;
 | |
|         // get previous line if possible
 | |
|         if PosY = 1 then break;
 | |
|         Dec(PosY);
 | |
|         if OnlyVisible
 | |
|         and ((PosY<TopLine) or (PosY >= ScreenRowToRow(LinesInWindow)))
 | |
|         then
 | |
|           break;
 | |
|         Line := FTheLinesView[PosY - 1];
 | |
|         MaxKnownTokenPos := 0;
 | |
|         PosX := Length(Line) + 1;
 | |
|       until FALSE;
 | |
|     end else begin
 | |
|       // opening bracket -> search closing bracket
 | |
|       repeat
 | |
|         // search until end of line
 | |
|         Len := Length(Line);
 | |
|         while PosX < Len do begin
 | |
|           Inc(PosX);
 | |
|           Test := Line[PosX];
 | |
|           if (Test = BracketInc) and IsContextBracket then
 | |
|             Inc(NumBrackets)
 | |
|           else if (Test = BracketDec) and IsContextBracket then begin
 | |
|             Dec(NumBrackets);
 | |
|             if NumBrackets = 0 then begin
 | |
|               DoMatchingBracketFound;
 | |
|               exit;
 | |
|             end;
 | |
|           end;
 | |
|         end;
 | |
|         // get next line if possible
 | |
|         if PosY = FTheLinesView.Count then break;
 | |
|         Inc(PosY);
 | |
|         if OnlyVisible
 | |
|         and ((PosY < TopLine) or (PosY >= ScreenRowToRow(LinesInWindow)))
 | |
|         then
 | |
|           break;
 | |
|         Line := FTheLinesView[PosY - 1];
 | |
|         MaxKnownTokenPos := 0;
 | |
|         PosX := 0;
 | |
|       until FALSE;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   procedure DoCheckBracket;
 | |
|   var
 | |
|     i: integer;
 | |
|     Test: char;
 | |
|   begin
 | |
|     if Length(Line) >= PosX then begin
 | |
|       Test := Line[PosX];
 | |
|       // is it one of the recognized brackets?
 | |
|       for i := Low(Brackets) to High(Brackets) do begin
 | |
|         if Test = Brackets[i] then begin
 | |
|           // this is the bracket, get the matching one and the direction
 | |
|           if Brackets[i] in ['''', '"'] then
 | |
|             DoFindMatchingQuote(Brackets[i])
 | |
|           else
 | |
|             DoFindMatchingBracket(i);
 | |
|           exit;
 | |
|         end;
 | |
|       end;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   Result.X:=-1;
 | |
|   Result.Y:=-1;
 | |
| 
 | |
|   // get char at caret
 | |
|   LogicalStart:=PhysicalToLogicalPos(PhysStartBracket);
 | |
|   PosX := LogicalStart.X;
 | |
|   PosY := LogicalStart.Y;
 | |
|   if (PosY<1) or (PosY>FTheLinesView.Count) then exit;
 | |
|   if OnlyVisible
 | |
|   and ((PosY<TopLine) or (PosY >= ScreenRowToRow(LinesInWindow)))
 | |
|   then
 | |
|    exit;
 | |
| 
 | |
|   Line := FTheLinesView[PosY - 1];
 | |
|   try
 | |
|     DoCheckBracket;
 | |
|     if Result.Y>0 then exit;
 | |
|     if StartIncludeNeighborChars then begin
 | |
|       if PosX>1 then begin
 | |
|         // search in front
 | |
|         dec(PosX);
 | |
|         DoCheckBracket;
 | |
|         if Result.Y>0 then exit;
 | |
|         inc(PosX);
 | |
|       end;
 | |
|       if PosX<Length(Line) then begin
 | |
|         // search behind
 | |
|         inc(PosX);
 | |
|         DoCheckBracket;
 | |
|         if Result.Y>0 then exit;
 | |
|       end;
 | |
|     end;
 | |
|   finally
 | |
|     if Result.Y>0 then begin
 | |
|       Result:=LogicalToPhysicalPos(Result);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
|                                                                                  //L505 begin
 | |
| function TCustomSynEdit.GetHighlighterAttriAtRowCol(XY: TPoint;
 | |
|   out Token: string; out Attri: TSynHighlighterAttributes): boolean;
 | |
| var
 | |
|   TmpType, TmpStart: Integer;
 | |
| begin
 | |
|   Result := GetHighlighterAttriAtRowColEx(XY, Token, TmpType, TmpStart, Attri);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetHighlighterAttriAtRowColEx(XY: TPoint;
 | |
|   out Token: string; out TokenType, Start: Integer;
 | |
|   out Attri: TSynHighlighterAttributes): boolean;
 | |
| var
 | |
|   PosX, PosY: integer;
 | |
|   Line: string;
 | |
| begin
 | |
|   PosY := XY.Y -1;
 | |
|   if Assigned(Highlighter) and (PosY >= 0) and (PosY < FTheLinesView.Count) then
 | |
|   begin
 | |
|     Line := FTheLinesView[PosY];
 | |
|     fHighlighter.CurrentLines := FTheLinesView;
 | |
|     Highlighter.StartAtLineIndex(PosY);
 | |
|     PosX := XY.X;
 | |
|     if (PosX > 0) and (PosX <= Length(Line)) then begin
 | |
|       while not Highlighter.GetEol do begin
 | |
|         Start := Highlighter.GetTokenPos + 1;
 | |
|         Token := Highlighter.GetToken;
 | |
|         if (PosX >= Start) and (PosX < Start + Length(Token)) then begin
 | |
|           Attri := Highlighter.GetTokenAttribute;
 | |
|           TokenType := Highlighter.GetTokenKind;
 | |
|           Result := TRUE;
 | |
|           exit;
 | |
|         end;
 | |
|         Highlighter.Next;
 | |
|       end;
 | |
|     end;
 | |
|   end;
 | |
|   Token := '';
 | |
|   Attri := nil;
 | |
|   TokenType := -1;
 | |
|   Result := FALSE;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.IdentChars: TSynIdentChars;
 | |
| begin
 | |
|   Result := FWordBreaker.IdentChars;  // Maybe WordChars?
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.IsIdentChar(const c: TUTF8Char): boolean;
 | |
| begin
 | |
|   Result:=(length(c)=1) and (c[1] in IdentChars);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.GetWordBoundsAtRowCol(const XY: TPoint; out StartX,
 | |
|   EndX: integer); // all params are logical (byte) positions
 | |
| var
 | |
|   Line: string;
 | |
| begin
 | |
|   StartX:=XY.X;
 | |
|   EndX:=XY.X;
 | |
|   Line := FTheLinesView[XY.Y - 1];
 | |
|   if WordBreaker.IsInWord(Line, XY.X) then begin
 | |
|     StartX := WordBreaker.PrevWordStart(Line, XY.X, True);
 | |
|     EndX := WordBreaker.NextWordEnd(Line, XY.X, True);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetWordAtRowCol(XY: TPoint): string;
 | |
| var
 | |
|   StartX, EndX: integer;
 | |
|   Line: string;
 | |
| begin
 | |
|   GetWordBoundsAtRowCol(XY, StartX, EndX);
 | |
|   Line := FTheLinesView[XY.Y - 1];
 | |
|   Result := Copy(Line, StartX, EndX - StartX);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.NextTokenPos: TPoint;
 | |
| var
 | |
|   CX, CY, LineLen: integer;
 | |
|   Line: string;
 | |
|   CurIdentChars, WhiteChars: TSynIdentChars;
 | |
|   nTokenPos, nTokenLen: integer;
 | |
|   sToken: PChar;
 | |
|   LogCaret: TPoint;
 | |
| 
 | |
|   procedure FindFirstNonWhiteSpaceCharInNextLine;
 | |
|   begin
 | |
|     if CY < FTheLinesView.Count then begin
 | |
|       Line := FTheLinesView[CY];
 | |
|       LineLen := Length(Line);
 | |
|       Inc(CY);
 | |
|       CX:=1;
 | |
|       while (CX<=LineLen) and (Line[CX] in WhiteChars) do inc(CX);
 | |
|       if CX>LineLen then CX:=1;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   LogCaret:=LogicalCaretXY;
 | |
|   CX := LogCaret.X;
 | |
|   CY := LogCaret.Y;
 | |
|   // valid line?
 | |
|   if (CY >= 1) and (CY <= FTheLinesView.Count) then begin
 | |
|     Line := FTheLinesView[CY - 1];
 | |
|     LineLen := Length(Line);
 | |
|     WhiteChars := FWordBreaker.WhiteChars;
 | |
|     if CX > LineLen then begin
 | |
|       FindFirstNonWhiteSpaceCharInNextLine;
 | |
|     end else begin
 | |
|       if fHighlighter<>nil then begin
 | |
|         fHighlighter.CurrentLines := FTheLinesView;
 | |
|         fHighlighter.StartAtLineIndex(CY - 1);
 | |
|         while not fHighlighter.GetEol do begin
 | |
|           nTokenPos := fHighlighter.GetTokenPos; // zero-based
 | |
|           fHighlighter.GetTokenEx(sToken,nTokenLen);
 | |
|           if (CX>nTokenPos) and (CX<=nTokenPos+nTokenLen) then begin
 | |
|             CX:=nTokenPos+nTokenLen+1;
 | |
|             break;
 | |
|           end;
 | |
|           // Let the highlighter scan the next token.
 | |
|           fHighlighter.Next;
 | |
|         end;
 | |
|         if fHighlighter.GetEol then
 | |
|           FindFirstNonWhiteSpaceCharInNextLine;
 | |
|       end else begin
 | |
|         // no highlighter
 | |
|         CurIdentChars:=IdentChars;
 | |
|         // find first "whitespace" if next char is not a "whitespace"
 | |
|         if (Line[CX] in CurIdentChars) then begin
 | |
|           // in a word -> move to end of word
 | |
|           while (CX<=LineLen) and (Line[CX] in CurIdentChars) do inc(CX);
 | |
|         end;
 | |
|         if (Line[CX] in WhiteChars) then begin
 | |
|           // skip white space
 | |
|           while (CX<=LineLen) and (Line[CX] in WhiteChars) do inc(CX);
 | |
|         end;
 | |
|         // delete at least one char
 | |
|         if (CX=CaretX) then inc(CX);
 | |
|       end;
 | |
|     end;
 | |
|   end;
 | |
|   Result := LogicalToPhysicalPos(Point(CX, CY));
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.NextWordPos: TPoint;
 | |
| begin
 | |
|   Result := LogicalToPhysicalPos(NextWordLogicalPos);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PrevWordPos: TPoint;
 | |
| begin
 | |
|   Result := LogicalToPhysicalPos(PrevWordLogicalPos);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.FindHookedCmdEvent(AHandlerProc: THookedCommandEvent):
 | |
|   integer;
 | |
| var
 | |
|   Entry: THookedCommandHandlerEntry;
 | |
| begin
 | |
|   Result := GetHookedCommandHandlersCount - 1;
 | |
|   while Result >= 0 do begin
 | |
|     Entry := THookedCommandHandlerEntry(fHookedCommandHandlers[Result]);
 | |
|     if Entry.Equals(AHandlerProc) then
 | |
|       break;
 | |
|     Dec(Result);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.GetHookedCommandHandlersCount: integer;
 | |
| begin
 | |
|   if Assigned(fHookedCommandHandlers) then
 | |
|     Result := fHookedCommandHandlers.Count
 | |
|   else
 | |
|     Result := 0;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterCommandHandler(AHandlerProc: THookedCommandEvent;
 | |
|   AHandlerData: pointer; AFlags: THookedCommandFlags);
 | |
| begin
 | |
|   if not Assigned(AHandlerProc) then begin
 | |
| {$IFDEF SYN_DEVELOPMENT_CHECKS}
 | |
|     raise Exception.Create('Event handler is NIL in RegisterCommandHandler');
 | |
| {$ENDIF}
 | |
|     exit;
 | |
|   end;
 | |
|   if not Assigned(fHookedCommandHandlers) then
 | |
|     fHookedCommandHandlers := TList.Create;
 | |
|   if FindHookedCmdEvent(AHandlerProc) = -1 then
 | |
|     fHookedCommandHandlers.Add(THookedCommandHandlerEntry.Create(
 | |
|       AHandlerProc, AHandlerData, AFlags))
 | |
|   else
 | |
| {$IFDEF SYN_DEVELOPMENT_CHECKS}
 | |
|     raise Exception.CreateFmt('Event handler (%p, %p) already registered',
 | |
|       [TMethod(AHandlerProc).Data, TMethod(AHandlerProc).Code]);
 | |
| {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnregisterCommandHandler(AHandlerProc:
 | |
|   THookedCommandEvent);
 | |
| var
 | |
|   i: integer;
 | |
| begin
 | |
|   if not Assigned(AHandlerProc) then begin
 | |
| {$IFDEF SYN_DEVELOPMENT_CHECKS}
 | |
|     raise Exception.Create('Event handler is NIL in UnregisterCommandHandler');
 | |
| {$ENDIF}
 | |
|     exit;
 | |
|   end;
 | |
|   i := FindHookedCmdEvent(AHandlerProc);
 | |
|   if i > -1 then begin
 | |
|     THookedCommandHandlerEntry(fHookedCommandHandlers[i]).Free;
 | |
|     fHookedCommandHandlers.Delete(i);
 | |
|   end else
 | |
| {$IFDEF SYN_DEVELOPMENT_CHECKS}
 | |
|     raise Exception.CreateFmt('Event handler (%p, %p) is not registered',
 | |
|       [TMethod(AHandlerProc).Data, TMethod(AHandlerProc).Code]);
 | |
| {$ENDIF}
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterMouseActionSearchHandler(AHandlerProc: TSynEditMouseActionSearchProc);
 | |
| begin
 | |
|   FMouseActionSearchHandlerList.Add(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnregisterMouseActionSearchHandler(AHandlerProc: TSynEditMouseActionSearchProc);
 | |
| begin
 | |
|   FMouseActionSearchHandlerList.Remove(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterMouseActionExecHandler(AHandlerProc: TSynEditMouseActionExecProc);
 | |
| begin
 | |
|   FMouseActionExecHandlerList.Add(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnregisterMouseActionExecHandler(AHandlerProc: TSynEditMouseActionExecProc);
 | |
| begin
 | |
|   FMouseActionExecHandlerList.Remove(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterKeyTranslationHandler(AHandlerProc: THookedKeyTranslationEvent);
 | |
| begin
 | |
|   FHookedKeyTranslationList.Add(TMEthod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnRegisterKeyTranslationHandler(AHandlerProc: THookedKeyTranslationEvent);
 | |
| begin
 | |
|   FHookedKeyTranslationList.Remove(TMEthod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterStatusChangedHandler(AStatusChangeProc: TStatusChangeEvent;
 | |
|   AChanges: TSynStatusChanges);
 | |
| begin
 | |
|   TSynStatusChangedHandlerList(FStatusChangedList).Add(AStatusChangeProc, AChanges);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnRegisterStatusChangedHandler(AStatusChangeProc: TStatusChangeEvent);
 | |
| begin
 | |
|   TSynStatusChangedHandlerList(FStatusChangedList).Remove(AStatusChangeProc);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterBeforeMouseDownHandler(AHandlerProc: TMouseEvent);
 | |
| begin
 | |
|   if FMouseDownEventList = nil then
 | |
|     FMouseDownEventList := TLazSynMouseDownEventList.Create;
 | |
|   FMouseDownEventList.Add(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnregisterBeforeMouseDownHandler(AHandlerProc: TMouseEvent);
 | |
| begin
 | |
|   if FMouseDownEventList <> nil then
 | |
|     FMouseDownEventList.Remove(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterBeforeKeyDownHandler(AHandlerProc: TKeyEvent);
 | |
| begin
 | |
|   if FKeyDownEventList = nil then
 | |
|     FKeyDownEventList := TLazSynKeyDownEventList.Create;
 | |
|   FKeyDownEventList.Add(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnregisterBeforeKeyDownHandler(AHandlerProc: TKeyEvent);
 | |
| begin
 | |
|   if FKeyDownEventList <> nil then
 | |
|     FKeyDownEventList.Remove(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterBeforeKeyPressHandler(AHandlerProc: TKeyPressEvent);
 | |
| begin
 | |
|   if FKeyPressEventList = nil then
 | |
|     FKeyPressEventList := TLazSynKeyPressEventList.Create;
 | |
|   FKeyPressEventList.Add(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnregisterBeforeKeyPressHandler(AHandlerProc: TKeyPressEvent);
 | |
| begin
 | |
|   if FKeyPressEventList <> nil then
 | |
|     FKeyPressEventList.Remove(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.RegisterBeforeUtf8KeyPressHandler(AHandlerProc: TUTF8KeyPressEvent);
 | |
| begin
 | |
|   if FUtf8KeyPressEventList = nil then
 | |
|     FUtf8KeyPressEventList := TLazSynUtf8KeyPressEventList.Create;
 | |
|   FUtf8KeyPressEventList.Add(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UnregisterBeforeUtf8KeyPressHandler(AHandlerProc: TUTF8KeyPressEvent);
 | |
| begin
 | |
|   if FUtf8KeyPressEventList <> nil then
 | |
|     FUtf8KeyPressEventList.Remove(TMethod(AHandlerProc));
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.NotifyHookedCommandHandlers(var Command: TSynEditorCommand;
 | |
|   var AChar: TUTF8Char; Data: pointer; ATime: THookedCommandFlag);
 | |
| var
 | |
|   Handled: boolean;
 | |
|   i: integer;
 | |
|   Entry: THookedCommandHandlerEntry;
 | |
| begin
 | |
|   Handled := FALSE;
 | |
|   for i := 0 to GetHookedCommandHandlersCount - 1 do begin
 | |
|     Entry := THookedCommandHandlerEntry(fHookedCommandHandlers[i]);
 | |
|     if not(ATime in Entry.FFlags) then continue;
 | |
|     // NOTE: Command should NOT be set to ecNone, because this might interfere
 | |
|     // with other handlers.  Set Handled to False instead (and check its value
 | |
|     // to not process the command twice).
 | |
|     Entry.fEvent(Self, ATime in [hcfPostExec, hcfFinish], Handled, Command, AChar, Data,
 | |
|       Entry.fData);
 | |
|   end;
 | |
|   if Handled then
 | |
|     Command := ecNone;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoOnPaint;
 | |
| begin
 | |
|   if Assigned(fOnPaint) then begin
 | |
|     Canvas.Font.Assign(Font);
 | |
|     Canvas.Brush.Color := Color;
 | |
|     fOnPaint(Self, Canvas);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.DoOnReplaceText(const ASearch, AReplace: string;
 | |
|   Line, Column: integer): TSynReplaceAction;
 | |
| begin
 | |
|   Result := raCancel;
 | |
|   if Assigned(fOnReplaceText) then
 | |
|     fOnReplaceText(Self, ASearch, AReplace, Line, Column, Result);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.DoOnStatusChange(Changes: TSynStatusChanges);
 | |
| begin
 | |
|   TSynStatusChangedHandlerList(FStatusChangedList).CallStatusChangedHandlers(Self, Changes);
 | |
|   if Assigned(fOnStatusChange) then begin
 | |
|     fOnStatusChange(Self, fStatusChanges);
 | |
|     fStatusChanges := [];
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.UndoRedoAdded(Sender: TObject);
 | |
| begin
 | |
|   // Todo: Check Paintlock, otherwise move to LinesChanged, LineCountChanged
 | |
|   if Assigned(fOnChange) then
 | |
|     fOnChange(Self);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.ModifiedChanged(Sender: TObject); 
 | |
| begin
 | |
|   StatusChanged([scModified]);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.LogicalToPhysicalPos(const p: TPoint): TPoint;
 | |
| begin
 | |
|   Result := FTheLinesView.LogicalToPhysicalPos(p);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.LogicalToPhysicalCol(const Line: String;
 | |
|   Index, LogicalPos: integer): integer;
 | |
| // LogicalPos is 1-based
 | |
| // Index 0-based LineNumber
 | |
| begin
 | |
|   Result := FTheLinesView.LogicalToPhysicalCol(Line, Index,
 | |
|                                                                 LogicalPos);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PhysicalLineLength(Line: String; Index: integer): integer;
 | |
| begin
 | |
|   Result:=LogicalToPhysicalCol(Line, Index, length(Line)+1) - 1
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PhysicalToLogicalPos(const p: TPoint): TPoint;
 | |
| begin
 | |
|   Result := FTheLinesView.PhysicalToLogicalPos(p);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.PhysicalToLogicalCol(const Line: string;
 | |
|   Index, PhysicalPos: integer): integer;
 | |
| begin
 | |
|   Result := FTheLinesView.PhysicalToLogicalCol(Line, Index,
 | |
|                                                                 PhysicalPos);
 | |
| end;
 | |
| 
 | |
| function TCustomSynEdit.ScreenColumnToXValue(Col : integer) : integer;
 | |
| begin
 | |
|   Result := FTextArea.ScreenColumnToXValue(Col);
 | |
| end;
 | |
| 
 | |
| procedure TCustomSynEdit.PrimarySelectionRequest(
 | |
|   const RequestedFormatID: TClipboardFormat;  Data: TStream);
 | |
| var
 | |
|   s: string;
 | |
|   ClipHelper: TSynClipboardStream;
 | |
| begin
 | |
|   if (not SelAvail) then exit;
 | |
|   s:=SelText;
 | |
|   if s = ''  then
 | |
|     exit;
 | |
|   if RequestedFormatID = CF_TEXT then begin
 | |
|     Data.Write(s[1],length(s));
 | |
|   end
 | |
|   else
 | |
|   if RequestedFormatID = TSynClipboardStream.ClipboardFormatId then begin
 | |
|     ClipHelper := TSynClipboardStream.Create;
 | |
|     try
 | |
|       ClipHelper.SelectionMode := SelectionMode;
 | |
|       // InternalText, so we don't need a 2nd call for CF_TEXT
 | |
|       ClipHelper.InternalText := s;
 | |
|       // Fold
 | |
|       if eoFoldedCopyPaste in fOptions2 then
 | |
|         s := FFoldedLinesView.GetFoldDescription(
 | |
|           FBlockSelection.FirstLineBytePos.Y - 1, FBlockSelection.FirstLineBytePos.X,
 | |
|           FBlockSelection.LastLineBytePos.Y - 1,  FBlockSelection.LastLineBytePos.X);
 | |
|       if length(s) > 0 then
 | |
|         ClipHelper.AddTag(synClipTagFold, @s[1], length(s));
 | |
|       Data.Write(ClipHelper.Memory^, ClipHelper.Size);
 | |
|     finally
 | |
|       ClipHelper.Free;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { TLazSynEditPlugin }
 | |
| 
 | |
| constructor TLazSynEditPlugin.Create(AOwner: TComponent);
 | |
| begin
 | |
|   if AOwner is TCustomSynEdit then begin
 | |
|     inherited Create(nil);
 | |
|     Editor := TCustomSynEdit(AOwner);
 | |
|   end
 | |
|   else
 | |
|     inherited Create(AOwner);
 | |
| end;
 | |
| 
 | |
| destructor TLazSynEditPlugin.Destroy;
 | |
| begin
 | |
|   Editor := nil;
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| procedure TLazSynEditPlugin.BeforeEditorChange;
 | |
| begin
 | |
|   if (Editor <> nil) then begin
 | |
|     DoEditorRemoving(Editor);
 | |
|     UnRegisterFromEditor(Editor);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TLazSynEditPlugin.AfterEditorChange;
 | |
| begin
 | |
|   if Editor <> nil then begin
 | |
|     RegisterToEditor(Editor);
 | |
|     DoEditorAdded(Editor);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TLazSynEditPlugin.RegisterToEditor(AValue: TCustomSynEdit);
 | |
| begin
 | |
|   if AValue.fPlugins <> nil then
 | |
|     AValue.fPlugins.Add(Self);
 | |
| end;
 | |
| 
 | |
| procedure TLazSynEditPlugin.UnRegisterFromEditor(AValue: TCustomSynEdit);
 | |
| begin
 | |
|   if AValue.fPlugins <> nil then
 | |
|     AValue.fPlugins.Remove(Self);
 | |
| end;
 | |
| 
 | |
| procedure TLazSynEditPlugin.SetEditor(const AValue: TCustomSynEdit);
 | |
| begin
 | |
|   if AValue = FriendEdit then exit;
 | |
| 
 | |
|   BeforeEditorChange;
 | |
|   FriendEdit := AValue;
 | |
|   AfterEditorChange;
 | |
| end;
 | |
| 
 | |
| function TLazSynEditPlugin.GetEditor: TCustomSynEdit;
 | |
| begin
 | |
|   Result := FriendEdit as TCustomSynEdit;
 | |
| end;
 | |
| 
 | |
| function TLazSynEditPlugin.OwnedByEditor: Boolean;
 | |
| begin
 | |
|   Result := Owner = nil;
 | |
| end;
 | |
| 
 | |
| procedure TLazSynEditPlugin.DoEditorDestroyed(const AValue: TCustomSynEdit);
 | |
| begin
 | |
|   if Editor <> AValue then exit;
 | |
|   if OwnedByEditor then
 | |
|     Free
 | |
|   else
 | |
|     Editor := nil;
 | |
| end;
 | |
| 
 | |
| procedure TLazSynEditPlugin.DoEditorAdded(AValue: TCustomSynEdit);
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| procedure TLazSynEditPlugin.DoEditorRemoving(AValue: TCustomSynEdit);
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| procedure Register;
 | |
| begin
 | |
|   RegisterClasses([TSynGutterPartList, TSynRightGutterPartList,
 | |
|                    TSynGutterSeparator, TSynGutterCodeFolding,
 | |
|                    TSynGutterLineNumber, TSynGutterChanges, TSynGutterMarks,
 | |
|                    TSynGutterLineOverview]);
 | |
| 
 | |
|   RegisterPropertyToSkip(TSynSelectedColor, 'OnChange', '', '');
 | |
|   RegisterPropertyToSkip(TSynSelectedColor, 'StartX', '', '');
 | |
|   RegisterPropertyToSkip(TSynSelectedColor, 'EndX', '', '');
 | |
| 
 | |
|   RegisterPropertyToSkip(TSynGutter, 'ShowCodeFolding', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'CodeFoldingWidth', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'ShowChanges', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'ShowLineNumbers', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'ShowOnlyLineNumbersMultiplesOf', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'ZeroStart', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'MarkupInfoLineNumber', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'MarkupInfoModifiedLine', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'MarkupInfoCodeFoldingTree', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'LeadingZeros', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'DigitCount', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'AllowSkipGutterSeparatorDraw', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'GutterParts', '', '');
 | |
|   RegisterPropertyToSkip(TSynGutter, 'OnChange', '', '');
 | |
|   RegisterPropertyToSkip(TSynEdit, 'CFDividerDrawLevel', '', '');
 | |
| end;
 | |
| 
 | |
| { TSynHookedKeyTranslationList }
 | |
| 
 | |
| procedure TSynHookedKeyTranslationList.CallHookedKeyTranslationHandlers(Sender: TObject;
 | |
|   Code: word; SState: TShiftState; var Data: pointer; var IsStartOfCombo: boolean;
 | |
|   var Handled: boolean; var Command: TSynEditorCommand;
 | |
|   var ComboKeyStrokes: TSynEditKeyStrokes);
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   if ComboKeyStrokes <> nil then begin
 | |
|     // Finish Combo
 | |
|     for i := 0 to Count - 1 do
 | |
|       THookedKeyTranslationEvent(Items[i])(Sender, Code, SState, Data,
 | |
|         IsStartOfCombo, Handled, Command, True, ComboKeyStrokes);
 | |
|   end
 | |
|   else begin
 | |
|     // New Stroke
 | |
|     for i := 0 to Count - 1 do
 | |
|       THookedKeyTranslationEvent(Items[i])(Sender, Code, SState, Data,
 | |
|         IsStartOfCombo, Handled, Command, False, ComboKeyStrokes);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { TLazSynUtf8KeyPressEventList }
 | |
| 
 | |
| procedure TLazSynUtf8KeyPressEventList.CallUtf8KeyPressHandlers(Sender: TObject;
 | |
|   var UTF8Key: TUTF8Char);
 | |
| var
 | |
|   i: LongInt;
 | |
| begin
 | |
|   i:=Count;
 | |
|   while NextDownIndex(i) do
 | |
|     TUTF8KeyPressEvent(Items[i])(Sender, UTF8Key);
 | |
| end;
 | |
| 
 | |
| { TLazSynKeyPressEventList }
 | |
| 
 | |
| procedure TLazSynKeyPressEventList.CallKeyPressHandlers(Sender: TObject; var Key: char);
 | |
| var
 | |
|   i: LongInt;
 | |
| begin
 | |
|   i:=Count;
 | |
|   while NextDownIndex(i) do
 | |
|     TKeyPressEvent(Items[i])(Sender, Key);
 | |
| end;
 | |
| 
 | |
| { TLazSynMouseDownEventList }
 | |
| 
 | |
| procedure TLazSynMouseDownEventList.CallMouseDownHandlers(Sender: TObject;
 | |
|   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 | |
| var
 | |
|   i: LongInt;
 | |
| begin
 | |
|   i:=Count;
 | |
|   while NextDownIndex(i) do
 | |
|     TMouseEvent(Items[i])(Sender, Button, Shift, X, Y);
 | |
| end;
 | |
| 
 | |
| { TLazSynKeyDownEventList }
 | |
| 
 | |
| procedure TLazSynKeyDownEventList.CallKeyDownHandlers(Sender: TObject; var Key: Word;
 | |
|   Shift: TShiftState);
 | |
| var
 | |
|   i: LongInt;
 | |
| begin
 | |
|   i:=Count;
 | |
|   while NextDownIndex(i) do
 | |
|     TKeyEvent(Items[i])(Sender, Key, Shift);
 | |
| end;
 | |
| 
 | |
| { TSynStatusChangedHandlerList }
 | |
| procedure TSynStatusChangedHandlerList.Add(AHandler: TStatusChangeEvent;
 | |
|   Changes: TSynStatusChanges);
 | |
| begin
 | |
|   AddBitFilter(TMethod(AHandler), LongInt(Changes));
 | |
| end;
 | |
| 
 | |
| procedure TSynStatusChangedHandlerList.Remove(AHandler: TStatusChangeEvent);
 | |
| begin
 | |
|   inherited Remove(TMethod(AHandler));
 | |
| end;
 | |
| 
 | |
| procedure TSynStatusChangedHandlerList.CallStatusChangedHandlers(Sender: TObject;
 | |
|   Changes: TSynStatusChanges);
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   i:=Count;
 | |
|   while NextDownIndexBitFilter(i, LongInt(Changes)) do
 | |
|     TStatusChangeEvent(FItems[i].FHandler)(Sender, Changes);
 | |
| end;
 | |
| 
 | |
| { TSynEditMarkListInternal }
 | |
| 
 | |
| function TSynEditMarkListInternal.GetLinesView: TSynEditStrings;
 | |
| begin
 | |
|   Result := FLines;
 | |
| end;
 | |
| 
 | |
| procedure TSynEditMarkListInternal.SetLinesView(const AValue: TSynEditStrings);
 | |
| begin
 | |
|   FLines := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TSynEditMarkListInternal.AddOwnerEdit(AEdit: TSynEditBase);
 | |
| begin
 | |
|   FOwnerList.Add(AEdit);
 | |
| end;
 | |
| 
 | |
| procedure TSynEditMarkListInternal.RemoveOwnerEdit(AEdit: TSynEditBase);
 | |
| begin
 | |
|   FOwnerList.Remove(AEdit);
 | |
| end;
 | |
| 
 | |
| initialization
 | |
|   InitSynDefaultFont;
 | |
|   Register;
 | |
| 
 | |
| end.
 | 
