{ /*************************************************************************** SourceEditor.pp ------------------- ***************************************************************************/ *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** } { This unit builds the TSourceNotebook that the editors are held on. It also has a class that controls the editors (TSourceEditor) } unit SourceEditor; {$mode objfpc} {$H+} interface {$I ide.inc} { $DEFINE VerboseIDECompletionBox} uses {$IFDEF IDE_MEM_CHECK} MemCheck, {$ENDIF} SynEditMouseCmds, // RTL + FCL Classes, SysUtils, StrUtils, Types, Contnrs, Math, RegExpr, Laz_AVL_Tree, // LCL Controls, Forms, ComCtrls, StdCtrls, Graphics, Dialogs, Extctrls, Menus, LCLProc, LCLType, LCLIntf, ClipBrd, HelpIntfs, Messages, LMessages, // LazControls ExtendedNotebook, // LazUtils LConvEncoding, FileUtil, LazFileUtils, LazFileCache, LazUTF8, LazMethodList, LazLoggerBase, LazLogger, Translations, LazUtilities, LazTracer, LazStringUtils, // codetools BasicCodeTools, CodeBeautifier, CodeToolManager, CodeCache, SourceLog, LinkScanner, CodeTree, SourceChanger, IdentCompletionTool, // synedit SynEditLines, SynEditStrConst, SynEditTypes, SynEdit, SynEditHighlighter, SynEditAutoComplete, SynEditKeyCmds, SynCompletion, SynEditMiscClasses, SynEditMarkupHighAll, SynEditMarks, SynBeautifier, SynPluginMultiCaret, SynPluginSyncronizedEditBase, SourceSynEditor, SynExportHTML, SynHighlighterPas, SynEditMarkup, SynEditMarkupIfDef, SynBeautifierPascal, // IdeIntf SrcEditorIntf, MenuIntf, LazIDEIntf, PackageIntf, IDEHelpIntf, IDEImagesIntf, IDEWindowIntf, ProjectIntf, MacroDefIntf, ToolBarIntf, IDEDialogs, IDECommands, EditorSyntaxHighlighterDef, // DebuggerIntf DbgIntfDebuggerBase, // IDE units IDECmdLine, LazarusIDEStrConsts, EditorOptions, EnvironmentOpts, WordCompletion, FindReplaceDialog, IDEProcs, IDEOptionDefs, IDEHelpManager, MacroPromptDlg, TransferMacros, CodeContextForm, SrcEditHintFrm, etMessagesWnd, etSrcEditMarks, CodeMacroPrompt, CodeTemplatesDlg, CodeToolsOptions, editor_general_options, SortSelectionDlg, EncloseSelectionDlg, EncloseIfDef, InvertAssignTool, SourceEditProcs, SourceMarks, CharacterMapDlg, SearchFrm, MultiPasteDlg, EditorMacroListViewer, EditorToolbarStatic, editortoolbar_options, InputhistoryWithSearchOpt, FPDocHints, MainIntf, GotoFrm, BaseDebugManager, Debugger; type TSourceNotebook = class; TSourceEditorManager = class; TSourceEditor = class; TNotifyFileEvent = procedure(Sender: TObject; Filename : AnsiString) of object; TOnProcessUserCommand = procedure(Sender: TObject; Command: word; var Handled: boolean) of object; TOnUserCommandProcessed = procedure(Sender: TObject; Command: word; var Handled: boolean) of object; TPackageForSourceEditorEvent = function(out APackage: TIDEPackage; ASrcEdit: TObject): TLazPackageFile of object; TPlaceBookMarkEvent = procedure(Sender: TObject; var Mark: TSynEditMark) of object; TPlaceBookMarkIdEvent = procedure(Sender: TObject; ID: Integer) of object; TBookMarkActionEvent = procedure(Sender: TObject; ID: Integer; Toggle: Boolean) of object; TUpdateProjectFileEvent = procedure(Sender: TObject; AnUpdates: TSrcEditProjectUpdatesNeeded) of object; TCharSet = set of Char; // for TSourcEditor.CenterCursorHoriz TSourceEditHCenterMode = ( hcmCenter, // Center X-Caret to exact middle of Screen hcmCenterKeepEOL, // Center X-Caret to middle of Screen, but keep EOL at right border hcmSoft, // Soft Center (distance to screen edge) Caret hcmSoftKeepEOL // Soft Center (distance to screen edge) Caret, but keep EOL at right border ); TSourceEditCompletionForm = class(TSynCompletionForm) private FTextHighLightColor: TColor; public property TextHighLightColor: TColor read FTextHighLightColor write FTextHighLightColor; end; { TSourceEditCompletion } TSourceEditCompletion=class(TSynCompletion) private FIdentCompletionJumpToError: boolean; ccSelection: String; // colors for the completion form (popup form, e.g. word completion) FActiveEditBackgroundColor: TColor; FActiveEditBackgroundSelectedColor: TColor; FActiveEditBorderColor: TColor; FActiveEditTextColor: TColor; FActiveEditTextSelectedColor: TColor; FActiveEditTextHighLightColor: TColor; procedure ccExecute(Sender: TObject); procedure ccCancel(Sender: TObject); procedure ccComplete(var Value: string; SourceValue: string; var SourceStart, SourceEnd: TPoint; KeyChar: TUTF8Char; Shift: TShiftState); function OnSynCompletionPaintItem(const AKey: string; ACanvas: TCanvas; X, Y: integer; ItemSelected: boolean; Index: integer): boolean; function OnSynCompletionMeasureItem(const AKey: string; ACanvas: TCanvas; ItemSelected: boolean; Index: integer): TPoint; procedure OnSynCompletionSearchPosition(var APosition: integer); procedure OnSynCompletionCompletePrefix(Sender: TObject); procedure OnSynCompletionNextChar(Sender: TObject); procedure OnSynCompletionPrevChar(Sender: TObject); procedure OnSynCompletionKeyPress(Sender: TObject; var Key: Char); procedure OnSynCompletionUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char); procedure OnSynCompletionPositionChanged(Sender: TObject); function InitIdentCompletionValues(S: TStrings): boolean; procedure StartShowCodeHelp; procedure CompletionFormResized(Sender: TObject); protected CurrentCompletionType: TCompletionType; function Manager: TSourceEditorManager; function GetCompletionFormClass: TSynBaseCompletionFormClass; override; public constructor Create(AOwner: TComponent); override; property IdentCompletionJumpToError: Boolean read FIdentCompletionJumpToError write FIdentCompletionJumpToError; end; { TSourceEditorSharedValues } TSourceEditorSharedValues = class(TSourceEditorSharedValuesBase) private FSharedEditorList: TFPList; // list of TSourceEditor sharing one TSynEdit function GetOtherSharedEditors(Caller: TSourceEditor; Index: Integer): TSourceEditor; function GetSharedEditors(Index: Integer): TSourceEditor; function SynEditor: TIDESynEditor; protected function GetSharedEditorsBase(Index: Integer): TSourceEditorBase; override; public procedure AddSharedEditor(AnEditor: TSourceEditor); procedure RemoveSharedEditor(AnEditor: TSourceEditor); procedure SetActiveSharedEditor(AnEditor: TSourceEditor); function SharedEditorCount: Integer; override; function OtherSharedEditorCount: Integer; property SharedEditors[Index: Integer]: TSourceEditor read GetSharedEditors; property OtherSharedEditors[Caller: TSourceEditor; Index: Integer]: TSourceEditor read GetOtherSharedEditors; private FExecutionMark: TSourceMark; FMarksRequested: Boolean; FMarklingsValid: boolean; FMarksRequestedForFile: String; function GetExecutionLine: Integer; public UpdatingExecutionMark: Integer; procedure CreateExecutionMark; property ExecutionLine: Integer read GetExecutionLine;// write FExecutionLine; property ExecutionMark: TSourceMark read FExecutionMark write FExecutionMark; procedure SetExecutionLine(NewLine: integer); property MarksRequested: Boolean read FMarksRequested write FMarksRequested; property MarksRequestedForFile: String read FMarksRequestedForFile write FMarksRequestedForFile; private FInGlobalUpdate: Integer; FModified: boolean; FIgnoreCodeBufferLock: integer; FEditorStampCommitedToCodetools: int64; FCodeBuffer: TCodeBuffer; FLinkScanners: TFPList; // list of TLinkScanner FMainLinkScanner: TLinkScanner; FLastWarnedMainLinkFilename: string; function GetModified: Boolean; procedure SetCodeBuffer(const AValue: TCodeBuffer); procedure SetModified(const AValue: Boolean); procedure OnCodeBufferChanged(Sender: TSourceLog; SrcLogEntry: TSourceLogEntry); public procedure BeginGlobalUpdate; procedure EndGlobalUpdate; property Modified: Boolean read GetModified write SetModified; property IgnoreCodeBufferLock: Integer read FIgnoreCodeBufferLock; procedure IncreaseIgnoreCodeBufferLock; procedure DecreaseIgnoreCodeBufferLock; function NeedsUpdateCodeBuffer: boolean; procedure UpdateCodeBuffer; property CodeBuffer: TCodeBuffer read FCodeBuffer write SetCodeBuffer; // IfDef nodes procedure ConnectScanner(Scanner: TLinkScanner); procedure DisconnectScanner(Scanner: TLinkScanner); function GetMainLinkScanner(Scan: boolean): TLinkScanner; public constructor Create; destructor Destroy; override; function Filename: string; override; end; { TSourceEditor --- TSourceEditor is the class that controls access for a single source editor, which is part of TSourceNotebook. } TSourceEditor = class(TSourceEditorBase) private //FAOwner is normally a TSourceNotebook. This is set in the Create constructor. FAOwner: TComponent; FIsLocked: Boolean; FProjectFileUpdatesNeeded: TSrcEditProjectUpdatesNeeded; FSharedValues: TSourceEditorSharedValues; FEditor: TIDESynEditor; FTempCaret: TPoint; FTempTopLine: Integer; FEditPlugin: TETSynPlugin; // used to update the "Messages Window" // when text is inserted/deleted FOnIfdefNodeStateRequest: TSynMarkupIfdefStateRequest; FLastIfDefNodeScannerStep: integer; FCodeCompletionState: record State: (ccsReady, ccsCancelled, ccsDot, ccsOnTyping, ccsOnTypingScheduled); LastTokenStartPos: TPoint; end; FSyncroLockCount: Integer; FPageName: string; FPopUpMenu: TPopupMenu; FMouseActionPopUpMenu: TPopupMenu; FSyntaxHighlighterType: TLazSyntaxHighlighter; FErrorLine: integer; FErrorColumn: integer; FLineInfoNotification: TIDELineInfoNotification; FInEditorChangedUpdating: Boolean; FOnEditorChange: TStatusChangeEvent; FVisible: Boolean; FOnMouseMove: TMouseMoveEvent; FOnMouseDown: TMouseEvent; FOnMouseWheel : TMouseWheelEvent; FOnKeyDown: TKeyEvent; FOnKeyUp: TKeyEvent; FSourceNoteBook: TSourceNotebook; procedure EditorMouseMoved(Sender: TObject; Shift: TShiftState; X,Y:Integer); procedure EditorMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,Y: Integer); procedure EditorMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure EditorMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); procedure EditorKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure EditorKeyUp({%H-}Sender: TObject; var {%H-}Key: Word; {%H-}Shift: TShiftState); procedure EditorStatusChanged(Sender: TObject; {%H-}Changes: TSynStatusChanges); procedure EditorPaste(Sender: TObject; var AText: String; var AMode: TSynSelectionMode; ALogStartPos: TPoint; var AnAction: TSynCopyPasteAction); procedure EditorPlaceBookmark(Sender: TObject; var Mark: TSynEditMark); procedure EditorClearBookmark(Sender: TObject; var Mark: TSynEditMark); procedure EditorEnter(Sender: TObject); procedure EditorActivateSyncro(Sender: TObject); procedure EditorDeactivateSyncro(Sender: TObject); procedure EditorChangeUpdating({%H-}ASender: TObject; AnUpdating: Boolean); function EditorHandleMouseAction(AnAction: TSynEditMouseAction; var {%H-}AnInfo: TSynEditMouseActionInfo): Boolean; function GetCodeBuffer: TCodeBuffer; function GetExecutionLine: integer; function GetHasExecutionMarks: Boolean; function GetSharedEditors(Index: Integer): TSourceEditor; procedure SetCodeBuffer(NewCodeBuffer: TCodeBuffer); function GetSource: TStrings; procedure SetIsLocked(const AValue: Boolean); procedure UpdateExecutionSourceMark; procedure UpdatePageName; procedure SetSource(Value: TStrings); function GetCurrentCursorXLine: Integer; procedure SetCurrentCursorXLine(num : Integer); function GetCurrentCursorYLine: Integer; procedure SetCurrentCursorYLine(num: Integer); Function GetInsertMode: Boolean; procedure SetPopupMenu(NewPopupMenu: TPopupMenu); function GotoLine(Value: Integer): Integer; procedure CreateEditor(AOwner: TComponent; AParent: TWinControl); procedure UpdateNoteBook(const ANewNoteBook: TSourceNotebook; ANewPage: TTabSheet); procedure SetVisible(Value: boolean); procedure UnbindEditor; procedure UpdateIfDefNodeStates(Force: Boolean = False); protected function GetPageCaption: string; override; function GetPageName: string; override; procedure SetPageName(const AValue: string); protected procedure DoMultiCaretBeforeCommand(Sender: TObject; ACommand: TSynEditorCommand; var AnAction: TSynMultiCaretCommandAction; var {%H-}AFlags: TSynMultiCaretCommandFlags); procedure ProcessCommand(Sender: TObject; var Command: TSynEditorCommand; var AChar: TUTF8Char; {%H-}Data: pointer); procedure ProcessUserCommand(Sender: TObject; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer); procedure UserCommandProcessed(Sender: TObject; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer); function AutoCompleteChar(Char: TUTF8Char; var AddChar: boolean; Category: TAutoCompleteOption): boolean; function AutoBlockCompleteChar({%H-}Char: TUTF8Char; var {%H-}AddChar: boolean; Category: TAutoCompleteOption; aTextPos: TPoint; Line: string): boolean; function AutoBlockCompleteChar({%H-}Char: TUTF8Char): boolean; procedure AutoCompleteBlock; procedure FocusEditor;// called by TSourceNotebook when the Notebook page // changes so the editor is focused procedure OnGutterClick(Sender: TObject; {%H-}X, {%H-}Y, Line: integer; {%H-}Mark: TSynEditMark); procedure OnEditorSpecialLineColor(Sender: TObject; Line: integer; var Special: boolean; Markup: TSynSelectedColor); function RefreshEditorSettings: Boolean; function GetModified: Boolean; override; procedure SetModified(const NewValue: Boolean); override; procedure SetSyntaxHighlighterType(AHighlighterType: TLazSyntaxHighlighter); procedure SetErrorLine(NewLine: integer); procedure SetExecutionLine(NewLine: integer); procedure StartIdentCompletionBox(JumpToError, CanAutoComplete: boolean); procedure StartWordCompletionBox; function IsFirstShared(Sender: TObject): boolean; function GetFilename: string; override; function GetEditorControl: TWinControl; override; function GetCodeToolsBuffer: TObject; override; Function GetReadOnly: Boolean; override; procedure SetReadOnly(const NewValue: boolean); override; function Manager: TSourceEditorManager; property Visible: Boolean read FVisible write SetVisible default False; function GetSharedValues: TSourceEditorSharedValuesBase; override; function IsSharedWith(AnOtherEditor: TSourceEditor): Boolean; procedure BeforeCodeBufferReplace; procedure AfterCodeBufferReplace; function Close: Boolean; public constructor Create(AOwner: TComponent; AParent: TWinControl; ASharedEditor: TSourceEditor = nil); destructor Destroy; override; // codebuffer procedure BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF}; override; procedure EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF}; override; procedure BeginUpdate; override; procedure EndUpdate; override; procedure BeginGlobalUpdate; procedure EndGlobalUpdate; procedure IncreaseIgnoreCodeBufferLock; override; procedure DecreaseIgnoreCodeBufferLock; override; procedure UpdateCodeBuffer; override;// copy the source from EditorComponent function NeedsUpdateCodeBuffer: boolean; override; procedure ConnectScanner(Scanner: TLinkScanner); // find procedure StartFindAndReplace(Replace:boolean); procedure AskReplace(Sender: TObject; const ASearch, AReplace: string; Line, Column: integer; out Action: TSrcEditReplaceAction); override; procedure OnReplace(Sender: TObject; const ASearch, AReplace: string; {%H-}Line, {%H-}Column: integer; var Action: TSynReplaceAction); function DoFindAndReplace(aFindText, aReplaceText: String; anOptions: TSynSearchOptions): Integer; procedure FindNextUTF8; procedure FindPrevious; procedure FindNextWordOccurrence(DirectionForward: boolean); procedure ShowGotoLineDialog; // dialogs procedure GetDialogPosition(Width, Height: integer; out Left, Top: integer); procedure ActivateHint(const ClientPos: TPoint; const ABaseURL, AHint: string; AAutoShown: Boolean = True); overload; procedure ActivateHint(ClientRect: TRect; const ABaseURL, AHint: string; AAutoShown: Boolean; AMouseOffset: Boolean = True); overload; // selections function SelectionAvailable: boolean; override; function GetText(OnlySelection: boolean): string; override; procedure SelectText(const StartPos, EndPos: TPoint); override; procedure InsertLine(StartLine: Integer; const NewText: String; aKeepMarks: Boolean = False); override; procedure ReplaceLines(StartLine, EndLine: integer; const NewText: string; aKeepMarks: Boolean = False); override; procedure EncloseSelection; procedure UpperCaseSelection; procedure LowerCaseSelection; procedure SwapCaseSelection; procedure TabsToSpacesInSelection; procedure CommentSelection; procedure UncommentSelection; procedure ToggleCommentSelection; procedure UpdateCommentSelection(CommentOn, Toggle: Boolean); procedure ConditionalSelection; procedure SortSelection; procedure BreakLinesInSelection; procedure InvertAssignment; procedure SelectToBrace; procedure SelectWord; procedure SelectLine; procedure SelectParagraph; function CommentText(const Txt: string; CommentType: TCommentType): string; procedure InsertCharacterFromMap; procedure InsertLicenseNotice(const Notice: string; CommentType: TCommentType); procedure InsertGPLNotice(CommentType: TCommentType; Translated: boolean); procedure InsertLGPLNotice(CommentType: TCommentType; Translated: boolean); procedure InsertModifiedLGPLNotice(CommentType: TCommentType; Translated: boolean); procedure InsertMITNotice(CommentType: TCommentType; Translated: boolean); procedure InsertUsername; procedure InsertDateTime; procedure InsertChangeLogEntry; procedure InsertCVSKeyword(const AKeyWord: string); procedure InsertGUID; procedure InsertFilename; function GetSelEnd: Integer; override; function GetSelStart: Integer; override; procedure SetSelEnd(const AValue: Integer); override; procedure SetSelStart(const AValue: Integer); override; function GetSelection: string; override; procedure SetSelection(const AValue: string); override; procedure CopyToClipboard; override; procedure CutToClipboard; override; function GetBookMark(BookMark: Integer; out X, Y: Integer): Boolean; override; procedure SetBookMark(BookMark: Integer; X, Y: Integer); override; procedure ExportAsHtml(AFileName: String); // context help procedure FindHelpForSourceAtCursor; //Smart hint procedure ShowSmartHintForSourceAtCursor; // editor commands procedure DoEditorExecuteCommand(EditorCommand: word); override; procedure MoveToWindow(AWindowIndex: Integer); override; procedure CopyToWindow(AWindowIndex: Integer); override; function GetCodeAttributeName(LogXY: TPoint): String; // used to get the word at the mouse cursor function CurrentWordLogStartOrCaret: TPoint; function GetWordFromCaret(const ACaretPos: TPoint): String; function GetWordAtCurrentCaret: String; function GetOperandFromCaret(const ACaretPos: TPoint): String; function GetOperandAtCurrentCaret: String; function CaretInSelection(const ACaretPos: TPoint): Boolean; // cursor procedure CenterCursor(SoftCenter: Boolean = False); // vertical procedure CenterCursorHoriz(HCMode: TSourceEditHCenterMode); // horiz function TextToScreenPosition(const Position: TPoint): TPoint; override; function ScreenToTextPosition(const Position: TPoint): TPoint; override; function ScreenToPixelPosition(const Position: TPoint): TPoint; override; function GetCursorScreenXY: TPoint; override; function GetCursorTextXY: TPoint; override; procedure SetCursorScreenXY(const AValue: TPoint); override; procedure SetCursorTextXY(const AValue: TPoint); override; function GetBlockBegin: TPoint; override; function GetBlockEnd: TPoint; override; procedure SetBlockBegin(const AValue: TPoint); override; procedure SetBlockEnd(const AValue: TPoint); override; function GetLinesInWindow: Integer; override; function GetTopLine: Integer; override; procedure SetTopLine(const AValue: Integer); override; function CursorInPixel: TPoint; override; function IsCaretOnScreen(ACaret: TPoint; UseSoftCenter: Boolean = False): Boolean; // text procedure MultiPasteText; function SearchReplace(const ASearch, AReplace: string; SearchOptions: TSrcEditSearchOptions): integer; override; function GetSourceText: string; override; procedure SetSourceText(const AValue: string); override; function LineCount: Integer; override; function WidthInChars: Integer; override; function HeightInLines: Integer; override; function CharWidth: integer; override; function GetLineText: string; override; procedure SetLineText(const AValue: string); override; function GetLines: TStrings; override; procedure SetLines(const AValue: TStrings); override; // context function GetProjectFile: TLazProjectFile; override; procedure UpdateProjectFile(AnUpdates: TSrcEditProjectUpdatesNeeded = []); override; function GetDesigner(LoadForm: boolean): TIDesigner; override; // notebook procedure Activate; function PageIndex: integer; function IsActiveOnNoteBook: boolean; procedure CheckActiveWindow; // debugging procedure DoRequestExecutionMarks({%H-}Data: PtrInt); procedure FillExecutionMarks; procedure ClearExecutionMarks; procedure LineInfoNotificationChange(const {%H-}ASender: TObject; const ASource: String); function SourceToDebugLine(aLinePos: Integer): Integer; function DebugToSourceLine(aLinePos: Integer): Integer; procedure InvalidateAllIfdefNodes; procedure SetIfdefNodeState(ALinePos, AstartPos: Integer; AState: TSynMarkupIfdefNodeState); property OnIfdefNodeStateRequest: TSynMarkupIfdefStateRequest read FOnIfdefNodeStateRequest write FOnIfdefNodeStateRequest; public // properties property CodeBuffer: TCodeBuffer read GetCodeBuffer write SetCodeBuffer; property CurrentCursorXLine: Integer read GetCurrentCursorXLine write SetCurrentCursorXLine; property CurrentCursorYLine: Integer read GetCurrentCursorYLine write SetCurrentCursorYLine; property EditorComponent: TIDESynEditor read FEditor; property ErrorLine: integer read FErrorLine write SetErrorLine; property ExecutionLine: integer read GetExecutionLine write SetExecutionLine; property HasExecutionMarks: Boolean read GetHasExecutionMarks; property InsertMode: Boolean read GetInsertmode; property OnEditorChange: TStatusChangeEvent read FOnEditorChange write FOnEditorChange; property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove; property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown; property OnMouseWheel: TMouseWheelEvent read FOnMouseWheel write FOnMouseWheel; property OnKeyDown: TKeyEvent read FOnKeyDown write FOnKeyDown; property OnKeyUp: TKeyEvent read FOnKeyUp write FOnKeyUp; property Owner: TComponent read FAOwner; property PageName: string read GetPageName write SetPageName; property PopupMenu: TPopupMenu read FPopUpMenu write SetPopUpMenu; property ReadOnly: Boolean read GetReadOnly write SetReadOnly; property Source: TStrings read GetSource write SetSource; property SourceNotebook: TSourceNotebook read FSourceNoteBook; property SyntaxHighlighterType: TLazSyntaxHighlighter read fSyntaxHighlighterType write SetSyntaxHighlighterType; property SyncroLockCount: Integer read FSyncroLockCount; function SharedEditorCount: Integer; property SharedEditors[Index: Integer]: TSourceEditor read GetSharedEditors; property SharedValues: TSourceEditorSharedValues read FSharedValues; property IsLocked: Boolean read FIsLocked write SetIsLocked; end; //============================================================================ { TSourceNotebook } TJumpHistoryAction = (jhaBack, jhaForward, jhaViewWindow); TCloseSrcEditorOption = (ceoCloseOthers, ceoCloseOthersOnRightSide); TCloseSrcEditorOptions = set of TCloseSrcEditorOption; TOnJumpToHistoryPoint = procedure(out NewCaretXY: TPoint; out NewTopLine: integer; out DestEditor: TSourceEditor; Action: TJumpHistoryAction) of object; TOnAddJumpPoint = procedure(ACaretXY: TPoint; ATopLine: integer; AEditor: TSourceEditor; DeleteForwardHistory: boolean) of object; TOnMovingPage = procedure(Sender: TObject; OldPageIndex, NewPageIndex: integer) of object; TOnCloseSrcEditor = procedure(Sender: TObject; ACloseOptions: TCloseSrcEditorOptions) of object; TOnShowHintForSource = procedure(SrcEdit: TSourceEditor; CaretPos: TPoint; AutoShown: Boolean) of object; TOnInitIdentCompletion = procedure(Sender: TObject; JumpToError: boolean; out Handled, Abort: boolean) of object; TSrcEditPopupMenuEvent = procedure(const AddMenuItemProc: TAddMenuItemProc ) of object; TOnShowCodeContext = procedure(JumpToError: boolean; out Abort: boolean) of object; TOnGetIndentEvent = function(Sender: TObject; Editor: TSourceEditor; LogCaret, OldLogCaret: TPoint; FirstLinePos, LinesCount: Integer; Reason: TSynEditorCommand; SetIndentProc: TSynBeautifierSetIndentProc ): boolean of object; TSourceNotebookState = ( snIncrementalFind, snWarnedFont, snUpdateStatusBarNeeded, snNotebookPageChangedNeeded ); TSourceNotebookStates = set of TSourceNotebookState; TSourceNotebookUpdateFlag = ( ufPageNames, ufTabsAndPage, ufStatusBar, ufProjectFiles, ufFocusEditor, ufActiveEditorChanged, ufPageIndexChanged ); TSourceNotebookUpdateFlags = set of TSourceNotebookUpdateFlag; TBrowseEditorTabHistoryDialog = class; { TSourceNotebook } TSourceNotebook = class(TSourceEditorWindowInterface) GoToLineMenuItem: TMenuItem; OpenFolderMenuItem: TMenuItem; StatusPopUpMenu: TPopupMenu; StatusBar: TStatusBar; procedure CompleteCodeMenuItemClick(Sender: TObject); procedure DbgPopUpMenuPopup(Sender: TObject); procedure EditorLockClicked(Sender: TObject); procedure EncodingClicked(Sender: TObject); procedure ExtractProcMenuItemClick(Sender: TObject); procedure FindOverloadsMenuItemClick(Sender: TObject); procedure FormMouseUp(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer); procedure GoToLineMenuItemClick(Sender: TObject); procedure HighlighterClicked(Sender: TObject); procedure InsertCharacter(const C: TUTF8Char); procedure InvertAssignmentMenuItemClick(Sender: TObject); procedure LineEndingClicked(Sender: TObject); procedure MakeResourceStringMenuItemClick(Sender: TObject); procedure NotebookPageChanged(Sender: TObject); procedure NotebookShowTabHint(Sender: TObject; HintInfo: PHintInfo); procedure OnPopupMenuOpenFile(Sender: TObject); procedure OnPopupOpenPackageFile(Sender: TObject); procedure OnPopupOpenProjectInsp(Sender: TObject); procedure OpenAtCursorClicked(Sender: TObject); procedure OpenFolderMenuItemClick(Sender: TObject); procedure RenameIdentifierMenuItemClick(Sender: TObject); procedure ShowAbstractMethodsMenuItemClick(Sender: TObject); procedure ShowEmptyMethodsMenuItemClick(Sender: TObject); procedure ShowUnusedUnitsMenuItemClick(Sender: TObject); procedure SourceNotebookDropFiles(Sender: TObject; const FileNames: array of String); procedure SrcEditMenuCopyToExistingWindowClicked(Sender: TObject); procedure SrcEditMenuFindInWindowClicked(Sender: TObject); procedure SrcEditMenuMoveToExistingWindowClicked(Sender: TObject); procedure SrcPopUpMenuPopup(Sender: TObject); procedure StatusBarClick(Sender: TObject); procedure StatusBarDblClick(Sender: TObject); procedure StatusBarContextPopup(Sender: TObject; MousePos: TPoint; var {%H-}Handled: Boolean); procedure StatusBarDrawPanel({%H-}AStatusBar: TStatusBar; APanel: TStatusPanel; const ARect: TRect); procedure TabPopUpMenuPopup(Sender: TObject); private FNotebook: TExtendedNotebook; FBaseCaption: String; FIsClosing: Boolean; FSrcEditsSortedForFilenames: TAvlTree; // TSourceEditorInterface sorted for Filename TabPopUpMenu, SrcPopUpMenu, DbgPopUpMenu: TPopupMenu; procedure ApplyPageIndex; procedure ExecuteEditorItemClick(Sender: TObject); public procedure DeleteBreakpointClicked(Sender: TObject); procedure ToggleBreakpointClicked(Sender: TObject); procedure ToggleBreakpointEnabledClicked(Sender: TObject); private FManager: TSourceEditorManager; FUpdateLock, FFocusLock, fAutoFocusLock: Integer; FUpdateFlags: TSourceNotebookUpdateFlags; FPageIndex: Integer; FIncrementalSearchPos: TPoint; // last set position fIncrementalSearchStartPos: TPoint; // position where to start searching FIncrementalSearchStr, FIncrementalFoundStr: string; FIncrementalSearchBackwards : Boolean; FIncrementalSearchEditor: TSourceEditor; // editor with active search (MWE:shouldnt all FIncrementalSearch vars go to that editor ?) FLastCodeBuffer: TCodeBuffer; FProcessingCommand: boolean; FSourceEditorList: TFPList; // list of TSourceEditor FHistoryList: TFPList; // list of TSourceEditor page order for when a window closes FHistoryDlg: TBrowseEditorTabHistoryDialog; FStopBtnIdx: Integer; FOnEditorPageCaptionUpdate: TMethodList; private FUpdateTabAndPageTimer: TTimer; FWindowID: Integer; // PopupMenu procedure BuildPopupMenu; //forwarders to FNoteBook function GetNoteBookPage(Index: Integer): TTabSheet; function GetNotebookPages: TStrings; function GetPageCount: Integer; function GetPageIndex: Integer; procedure SetPageIndex(AValue: Integer); procedure UpdateHighlightMenuItems(SrcEdit: TSourceEditor); procedure UpdateLineEndingMenuItems(SrcEdit: TSourceEditor); procedure UpdateEncodingMenuItems(SrcEdit: TSourceEditor); procedure RemoveUserDefinedMenuItems; function AddUserDefinedPopupMenuItem(const NewCaption: string; const NewEnabled: boolean; const NewOnClick: TNotifyEvent): TIDEMenuItem; procedure RemoveContextMenuItems; function AddContextPopupMenuItem(const NewCaption: string; const NewEnabled: boolean; const NewOnClick: TNotifyEvent): TIDEMenuItem; // Incremental Search procedure UpdateActiveEditColors(AEditor: TSynEdit); procedure SetIncrementalSearchStr(const AValue: string); procedure IncrementalSearch(ANext, ABackward: Boolean); procedure UpdatePageNames; procedure UpdateProjectFiles(ACurrentEditor: TSourceEditor = nil); property NoteBookPage[Index: Integer]: TTabSheet read GetNoteBookPage; procedure NoteBookInsertPage(Index: Integer; const S: string); procedure NoteBookDeletePage(APageIndex: Integer); procedure UpdateTabsAndPageTitle; procedure UpdateTabsAndPageTimeReached(Sender: TObject); procedure CallOnEditorPageCaptionUpdate(Sender: TObject); protected function NoteBookIndexOfPage(APage: TTabSheet): Integer; procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); override; procedure DragCanceled; override; procedure DoActiveEditorChanged; protected States: TSourceNotebookStates; procedure Activate; override; procedure CreateNotebook; function NewSE(Pagenum: Integer; NewPagenum: Integer = -1; ASharedEditor: TSourceEditor = nil; ATabCaption: String = ''): TSourceEditor; procedure AcceptEditor(AnEditor: TSourceEditor; SendEvent: Boolean = False); procedure ReleaseEditor(AnEditor: TSourceEditor; SendEvent: Boolean = False); procedure EditorChanged(Sender: TObject; Changes: TSynStatusChanges); procedure DoClose(var CloseAction: TCloseAction); override; procedure DoShow; override; procedure DoHide; override; function GetWindowID: Integer; override; protected function GetActiveCompletionPlugin: TSourceEditorCompletionPlugin; override; function GetBaseCaption: String; override; function GetCompletionPlugins(Index: integer): TSourceEditorCompletionPlugin; override; procedure EditorMouseMove(Sender: TObject; {%H-}Shift: TShiftstate; {%H-}X,{%H-}Y: Integer); procedure EditorMouseDown(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftstate; {%H-}X,{%H-}Y: Integer); function EditorGetIndent(Sender: TObject; Editor: TObject; LogCaret, OldLogCaret: TPoint; FirstLinePos, LastLinePos: Integer; Reason: TSynEditorCommand; SetIndentProc: TSynBeautifierSetIndentProc): Boolean; procedure EditorKeyDown(Sender: TObject; var {%H-}Key: Word; {%H-}Shift: TShiftState); procedure EditorKeyUp(Sender: TObject; var {%H-}Key: Word; {%H-}Shift: TShiftState); procedure EditorMouseWheel(Sender: TObject; {%H-}Shift: TShiftState; {%H-}WheelDelta: Integer; {%H-}MousePos: TPoint; var {%H-}Handled: Boolean); procedure NotebookMouseDown(Sender: TObject; Button: TMouseButton; {%H-}Shift: TShiftState; X,Y: Integer); procedure NotebookMouseUp(Sender: TObject; Button: TMouseButton; {%H-}Shift: TShiftState; X,Y: Integer); procedure NotebookDragDropEx(Sender, Source: TObject; OldIndex, NewIndex: Integer; CopyDrag: Boolean; var Done: Boolean); procedure NotebookDragOverEx(Sender, Source: TObject; OldIndex, NewIndex: Integer; CopyDrag: Boolean; var Accept: Boolean); procedure NotebookDragOver(Sender, Source: TObject; {%H-}X,{%H-}Y: Integer; State: TDragState; var Accept: Boolean); procedure NotebookEndDrag(Sender, {%H-}Target: TObject; {%H-}X,{%H-}Y: Integer); procedure OnApplicationDeactivate(Sender: TObject); procedure ShowSynEditHint(const MousePos: TPoint); procedure NextEditor; procedure PrevEditor; procedure MoveEditorLeft(CurrentPageIndex: integer); procedure MoveEditorRight(CurrentPageIndex: integer); procedure MoveActivePageLeft; procedure MoveActivePageRight; procedure MoveEditorFirst(CurrentPageIndex: integer); procedure MoveEditorLast(CurrentPageIndex: integer); procedure MoveActivePageFirst; procedure MoveActivePageLast; procedure GotoNextWindow(Backward: Boolean = False); procedure GotoNextSharedEditor(Backward: Boolean = False); procedure MoveEditorNextWindow(Backward: Boolean = False; Copy: Boolean = False); procedure CopyEditor(OldPageIndex, NewWindowIndex, NewPageIndex: integer; Focus: Boolean = False); function GetActiveEditor: TSourceEditorInterface; override; procedure SetActiveEditor(const AValue: TSourceEditorInterface); override; procedure SetBaseCaption(AValue: String); override; function GetItems(Index: integer): TSourceEditorInterface; override; function GetEditors(Index:integer): TSourceEditor; property Manager: TSourceEditorManager read FManager; procedure BeginAutoFocusLock; procedure EndAutoFocusLock; protected procedure CloseTabClicked(Sender: TObject); procedure CloseClicked(Sender: TObject; CloseOptions: TCloseSrcEditorOptions = []); procedure ToggleFormUnitClicked(Sender: TObject); procedure ToggleObjectInspClicked(Sender: TObject); procedure IncUpdateLockInternal; procedure DecUpdateLockInternal; // editor page history procedure HistorySetMostRecent(APage: TTabSheet); procedure HistoryRemove(APage: TTabSheet); function HistoryGetTopPageIndex: Integer; // incremental find procedure BeginIncrementalFind; procedure EndIncrementalFind; property IncrementalSearchStr: string read FIncrementalSearchStr write SetIncrementalSearchStr; procedure StartShowCodeContext(JumpToError: boolean); // paste and copy procedure CutClicked(Sender: TObject); procedure CopyClicked(Sender: TObject); procedure PasteClicked(Sender: TObject); procedure ReloadEditorOptions; procedure CheckFont; public procedure AddUpdateEditorPageCaptionHandler(AEvent: TNotifyEvent; const AsLast: Boolean = True); override; procedure RemoveUpdateEditorPageCaptionHandler(AEvent: TNotifyEvent); override; procedure ProcessParentCommand(Sender: TObject; var Command: TSynEditorCommand; var {%H-}AChar: TUTF8Char; {%H-}Data: pointer; var Handled: boolean); procedure ParentCommandProcessed(Sender: TObject; var Command: TSynEditorCommand; var {%H-}AChar: TUTF8Char; {%H-}Data: pointer; var Handled: boolean); public constructor Create(AOwner: TComponent); override; overload; constructor Create(AOwner: TComponent; AWindowID: Integer); overload; destructor Destroy; override; function EditorCount: integer; function IndexOfEditor(aEditor: TSourceEditorInterface): integer; function Count: integer; override; function SourceEditorIntfWithFilename(const Filename: string ): TSourceEditorInterface; override; function FindSourceEditorWithPageIndex(APageIndex:integer):TSourceEditor; function FindPageWithEditor(ASourceEditor: TSourceEditor):integer; function FindSourceEditorWithEditorComponent(EditorComp: TComponent): TSourceEditor; function GetActiveSE: TSourceEditor; { $note deprecate and use SetActiveEditor} procedure CheckCurrentCodeBufferChanged; function IndexOfEditorInShareWith(AnOtherEditor: TSourceEditorInterface): Integer; override; procedure MoveEditor(OldPageIndex, NewPageIndex: integer); procedure MoveEditor(OldPageIndex, NewWindowIndex, NewPageIndex: integer); procedure UpdateStatusBar; procedure ClearExecutionLines; procedure ClearExecutionMarks; // new, close, focus function NewFile(const NewShortName: String; ASource: TCodeBuffer; FocusIt: boolean; AShareEditor: TSourceEditor = nil): TSourceEditor; procedure CloseFile(APageIndex:integer); procedure FocusEditor; function GetCapabilities: TCTabControlCapabilities; procedure IncUpdateLock; override; procedure DecUpdateLock; override; public property Editors[Index:integer]:TSourceEditor read GetEditors; // !!! not ordered for PageIndex // forwarders to the FNotebook property PageIndex: Integer read GetPageIndex write SetPageIndex; property PageCount: Integer read GetPageCount; property NotebookPages: TStrings read GetNotebookPages; end; { TBrowseEditorTabHistoryDialog } TBrowseEditorTabHistoryDialog = class(TForm) private FNotebook: TSourceNotebook; FEditorList: TListBox; procedure MoveInList(aForward: Boolean); protected procedure KeyDown(var Key: Word; Shift: TShiftState); override; procedure KeyUp(var Key: Word; Shift: TShiftState); override; procedure DoCreate; override; public procedure Show(aForward: Boolean); reintroduce; end; TSrcEditMangerHandlerType = ( semhtCopyPaste ); TSrcEditManagerUpdateFlag = ( ufMgrActiveEditorChanged, ufShowWindowOnTop, ufShowWindowOnTopFocus); TSrcEditManagerUpdateFlags = set of TSrcEditManagerUpdateFlag; { TSourceEditorManagerBase } (* Implement all Methods with the Interface types *) TSourceEditorManagerBase = class(TSourceEditorManagerInterface) private FActiveWindow: TSourceNotebook; FSourceWindowList: TFPList; FSourceWindowByFocusList: TFPList; FUpdateLock: Integer; FActiveEditorLock: Integer; FAutoFocusLock: Integer; FUpdateFlags: TSrcEditManagerUpdateFlags; FShowTabs: Boolean; procedure FreeSourceWindows; function GetActiveSourceWindowIndex: integer; function GetSourceWindowByLastFocused(Index: Integer): TSourceEditorWindowInterface; procedure SetActiveSourceWindowIndex(const AValue: integer); protected fProducers: TFPList; // list of TSourceMarklingProducer FChangeNotifyLists: Array [TsemChangeReason] of TMethodList; FHandlers: array[TSrcEditMangerHandlerType] of TMethodList; FChangesQueuedForMsgWnd: TETMultiSrcChanges;// source editor changes waiting to be applied to the Messages window function GetActiveSourceWindow: TSourceEditorWindowInterface; override; procedure SetActiveSourceWindow(const AValue: TSourceEditorWindowInterface); override; function GetSourceWindows(Index: integer): TSourceEditorWindowInterface; override; procedure DoWindowFocused({%H-}AWindow: TSourceNotebook); // Includes Focus to ChildControl (aka Activated) function GetActiveEditor: TSourceEditorInterface; override; procedure SetActiveEditor(const AValue: TSourceEditorInterface); override; procedure DoActiveEditorChanged; procedure DoEditorStatusChanged(AEditor: TSourceEditor); function GetSourceEditors(Index: integer): TSourceEditorInterface; override; function GetUniqueSourceEditors(Index: integer): TSourceEditorInterface; override; function GetMarklingProducers(Index: integer): TSourceMarklingProducer; override; procedure SyncMessageWnd(Sender: TObject); procedure DoWindowShow(AWindow: TSourceNotebook); procedure DoWindowHide(AWindow: TSourceNotebook); function GetShowTabs: Boolean; override; procedure SetShowTabs(const AShowTabs: Boolean); override; public procedure BeginAutoFocusLock; procedure EndAutoFocusLock; function HasAutoFocusLock: Boolean; // Windows function SourceWindowWithEditor(const AEditor: TSourceEditorInterface): TSourceEditorWindowInterface; override; function SourceWindowCount: integer; override; function IndexOfSourceWindow(AWindow: TSourceEditorWindowInterface): integer; property ActiveSourceWindowIndex: integer read GetActiveSourceWindowIndex write SetActiveSourceWindowIndex; function IndexOfSourceWindowByLastFocused(AWindow: TSourceEditorWindowInterface): integer; property SourceWindowByLastFocused[Index: Integer]: TSourceEditorWindowInterface read GetSourceWindowByLastFocused; // Editors function SourceEditorIntfWithFilename(const Filename: string): TSourceEditorInterface; override; function SourceEditorCount: integer; override; function UniqueSourceEditorCount: integer; override; // Settings function GetEditorControlSettings(EditControl: TControl): boolean; override; function GetHighlighterSettings(Highlighter: TObject): boolean; override; private // Completion Plugins FCompletionPlugins: TFPList; FDefaultCompletionForm: TSourceEditCompletion; FActiveCompletionPlugin: TSourceEditorCompletionPlugin; function GetDefaultCompletionForm: TSourceEditCompletion; procedure FreeCompletionPlugins; function GetScreenRectForToken(AnEditor: TCustomSynEdit; PhysColumn, PhysRow, EndColumn: Integer): TRect; protected CodeToolsToSrcEditTimer: TTimer; function GetActiveCompletionPlugin: TSourceEditorCompletionPlugin; override; function GetCompletionBoxPosition: integer; override; function GetCompletionPlugins(Index: integer): TSourceEditorCompletionPlugin; override; function GetDefaultSynCompletionForm: TCustomForm; override; function GetSynCompletionLinesInWindow: integer; override; procedure SetSynCompletionLinesInWindow(LineCnt: integer); override; function FindIdentCompletionPlugin(SrcEdit: TSourceEditor; JumpToError: boolean; var s: string; var BoxX, BoxY: integer; var UseWordCompletion: boolean): boolean; property DefaultCompletionForm: TSourceEditCompletion read GetDefaultCompletionForm; public // Completion Plugins function CompletionPluginCount: integer; override; procedure DeactivateCompletionForm; override; procedure RegisterCompletionPlugin(Plugin: TSourceEditorCompletionPlugin); override; procedure UnregisterCompletionPlugin(Plugin: TSourceEditorCompletionPlugin); override; protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure IncUpdateLockInternal; procedure DecUpdateLockInternal; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure RegisterChangeEvent(AReason: TsemChangeReason; AHandler: TNotifyEvent); override; procedure UnRegisterChangeEvent(AReason: TsemChangeReason; AHandler: TNotifyEvent); override; procedure RegisterCopyPasteEvent(AHandler: TSemCopyPasteEvent); override; procedure UnRegisterCopyPasteEvent(AHandler: TSemCopyPasteEvent); override; // producers function MarklingProducerCount: integer; override; procedure RegisterMarklingProducer(aProducer: TSourceMarklingProducer); override; procedure UnregisterMarklingProducer(aProducer: TSourceMarklingProducer); override; procedure InvalidateMarklingsOfAllFiles(aProducer: TSourceMarklingProducer); override; procedure InvalidateMarklings(aProducer: TSourceMarklingProducer; aFilename: string); override; public procedure IncUpdateLock; procedure DecUpdateLock; procedure ShowActiveWindowOnTop(Focus: Boolean = False); override; private FMacroRecorder: TIdeEditorMacro; FOnCurrentCodeBufferChanged: TNotifyEvent; procedure DoMacroRecorderState(Sender: TObject); public // codetools property OnCurrentCodeBufferChanged: TNotifyEvent read FOnCurrentCodeBufferChanged write FOnCurrentCodeBufferChanged; end; TJumpToSectionType = ( jmpInterface, jmpInterfaceUses, jmpImplementation, jmpImplementationUses, jmpInitialization); TJumpToProcedureType = (jmpHeader, jmpBegin); TSourceEditorHintWindowManager = class(TIDEHintWindowManager) private FManager: TSourceEditorManager; FAutoShown: Boolean; FAutoHintMousePos: TPoint; FAutoHintTimer: TIdleTimer; FAutoHideHintTimer: TTimer; FLastHint: string; FScreenRect: TRect; procedure HintTimer(Sender: TObject); procedure HideHintTimer(Sender: TObject); public procedure ActivateHint(const ScreenRect: TRect; const ABaseURL, AHint: string; AAutoShown: Boolean = True; AMouseOffset: Boolean = True); overload; procedure ActivateHint(const ScreenPos: TPoint; const ABaseURL, AHint: string; AAutoShown: Boolean = True; AMouseOffset: Boolean = True); overload; procedure HideAutoHintAfterMouseMoved; procedure HideAutoHint; procedure UpdateHintTimer; public constructor Create(AManager: TSourceEditorManager); destructor Destroy; override; public property AutoHintTimer: TIdleTimer read FAutoHintTimer; end; { TSourceEditorManager } (* Reintroduce all Methods with the final types *) TSourceEditorManager = class(TSourceEditorManagerBase) private procedure DoConfigureEditorToolbar(Sender: TObject); function GetActiveSourceNotebook: TSourceNotebook; function GetActiveSrcEditor: TSourceEditor; function GetSourceEditorsByPage(WindowIndex, PageIndex: integer): TSourceEditor; function GetSourceNbByLastFocused(Index: Integer): TSourceNotebook; function GetSrcEditors(Index: integer): TSourceEditor; procedure SetActiveSourceNotebook(const AValue: TSourceNotebook); function GetSourceNotebook(Index: integer): TSourceNotebook; procedure SetActiveSrcEditor(const AValue: TSourceEditor); procedure SrcEditMenuProcedureJumpGetCaption(Sender: TObject; var ACaption, {%H-}AHint: string); public // Windows function SourceWindowWithEditor(const AEditor: TSourceEditorInterface): TSourceNotebook; reintroduce; property SourceWindows[Index: integer]: TSourceNotebook read GetSourceNotebook; // reintroduce property ActiveSourceWindow: TSourceNotebook read GetActiveSourceNotebook write SetActiveSourceNotebook; // reintroduce function ActiveOrNewSourceWindow: TSourceNotebook; function NewSourceWindow: TSourceNotebook; procedure CreateSourceWindow(Sender: TObject; aFormName: string; var AForm: TCustomForm; DoDisableAutoSizing: boolean); procedure GetDefaultLayout(Sender: TObject; aFormName: string; out aBounds: TRect; out DockSibling: string; out DockAlign: TAlign); function SourceWindowWithPage(const APage: TTabSheet): TSourceNotebook; property SourceWindowByLastFocused[Index: Integer]: TSourceNotebook read GetSourceNbByLastFocused; function IndexOfSourceWindowWithID(const AnID: Integer): Integer; override; function SourceWindowWithID(const AnID: Integer): TSourceNotebook; // Editors function SourceEditorCount: integer; override; function GetActiveSE: TSourceEditor; { $note deprecate and use ActiveEditor} property ActiveEditor: TSourceEditor read GetActiveSrcEditor write SetActiveSrcEditor; // reintroduced property SourceEditors[Index: integer]: TSourceEditor read GetSrcEditors; // reintroduced property SourceEditorsByPage[WindowIndex, PageIndex: integer]: TSourceEditor read GetSourceEditorsByPage; procedure SetWindowByIDAndPage(AWindowID, APageIndex: integer); function SourceEditorIntfWithFilename(const Filename: string): TSourceEditor; reintroduce; function FindSourceEditorWithEditorComponent(EditorComp: TComponent): TSourceEditor; // With SynEdit protected procedure NewEditorCreated(AEditor: TSourceEditor); procedure EditorRemoved(AEditor: TSourceEditor); procedure SendEditorCreated(AEditor: TSourceEditor); procedure SendEditorDestroyed(AEditor: TSourceEditor); procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure RemoveWindow(AWindow: TSourceNotebook); public // Forward to all windows procedure ClearErrorLines; override; procedure ClearExecutionLines; procedure ClearExecutionMarks; procedure FillExecutionMarks; procedure ReloadEditorOptions; function Beautify(const Src: string; const Flags: TSemBeautyFlags = []): string; override; // find / replace text procedure FindClicked(Sender: TObject); procedure FindNextClicked(Sender: TObject); procedure FindPreviousClicked(Sender: TObject); procedure ReplaceClicked(Sender: TObject); procedure IncrementalFindClicked(Sender: TObject); procedure GotoLineClicked(Sender: TObject); procedure JumpBackClicked(Sender: TObject); procedure JumpForwardClicked(Sender: TObject); procedure JumpToNextErrorClicked(Sender: TObject); procedure JumpToPrevErrorClicked(Sender: TObject); procedure AddJumpPointClicked(Sender: TObject); procedure AddCustomJumpPoint(ACaretXY: TPoint; ATopLine: integer; AEditor: TSourceEditor; DeleteForwardHistory: boolean); procedure DeleteLastJumpPointClicked(Sender: TObject); procedure ViewJumpHistoryClicked(Sender: TObject); protected // Bookmarks procedure BookMarkToggleClicked(Sender: TObject); procedure BookMarkGotoClicked(Sender: TObject); public procedure BookMarkNextClicked(Sender: TObject); procedure BookMarkPrevClicked(Sender: TObject); procedure JumpToPos(FileName: string; Pos: TCodeXYPosition; TopLine: Integer); procedure JumpToPos(FileName: string; Pos: TCodeXYPosition; TopLine, BlockTopLine, BlockBottomLine: Integer); procedure JumpToSection(JumpType: TJumpToSectionType); procedure JumpToInterfaceClicked(Sender: TObject); procedure JumpToInterfaceUsesClicked(Sender: TObject); procedure JumpToImplementationClicked(Sender: TObject); procedure JumpToImplementationUsesClicked(Sender: TObject); procedure JumpToInitializationClicked(Sender: TObject); procedure JumpToProcedure(const JumpType: TJumpToProcedureType); procedure JumpToProcedureHeaderClicked(Sender: TObject); procedure JumpToProcedureBeginClicked(Sender: TObject); protected // macros function MacroFuncCol(const {%H-}s:string; const {%H-}Data: PtrInt; var {%H-}Abort: boolean): string; function MacroFuncRow(const {%H-}s:string; const {%H-}Data: PtrInt; var {%H-}Abort: boolean): string; function MacroFuncEdFile(const {%H-}s:string; const {%H-}Data: PtrInt; var {%H-}Abort: boolean): string; function MacroFuncCurToken(const {%H-}s:string; const {%H-}Data: PtrInt; var {%H-}Abort: boolean): string; function MacroFuncConfirm(const s:string; const {%H-}Data: PtrInt; var Abort: boolean): string; function MacroFuncPrompt(const s:string; const {%H-}Data: PtrInt; var Abort: boolean): string; function MacroFuncSave(const {%H-}s:string; const {%H-}Data: PtrInt; var Abort: boolean): string; function MacroFuncSaveAll(const {%H-}s:string; const {%H-}Data: PtrInt; var Abort: boolean): string; public procedure InitMacros(AMacroList: TTransferMacroList); procedure SetupShortCuts; function FindUniquePageName(FileName:string; IgnoreEditor: TSourceEditor):string; function SomethingModified(Verbose: boolean = false): boolean; procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean); procedure OnUserInput(Sender: TObject; Msg: Cardinal); procedure LockAllEditorsInSourceChangeCache; procedure UnlockAllEditorsInSourceChangeCache; procedure BeginGlobalUpdate; procedure EndGlobalUpdate; procedure CloseFile(AEditor: TSourceEditorInterface); procedure HideHint; // history jumping procedure HistoryJump(Sender: TObject; JumpAction: TJumpHistoryAction); private // Hints FHints: TSourceEditorHintWindowManager; procedure ActivateHint(const ScreenRect: TRect; const BaseURL, TheHint: string; AutoShown: Boolean = True; AMouseOffset: Boolean = True); overload; procedure ActivateHint(const ScreenPos: TPoint; const BaseURL, TheHint: string; AutoShown: Boolean = True; AMouseOffset: Boolean = True); overload; private FCodeTemplateModul: TSynEditAutoComplete; FGotoDialog: TfrmGoto; procedure OnCodeTemplateTokenNotFound(Sender: TObject; AToken: string; AnEditor: TCustomSynEdit; var Index:integer); procedure OnCodeTemplateExecuteCompletion( ASynAutoComplete: TCustomSynAutoComplete; Index: integer); protected procedure CodeToolsToSrcEditTimerTimer(Sender: TObject); procedure OnSourceCompletionTimer(Sender: TObject); // marks procedure OnSourceMarksAction(AMark: TSourceMark; {%H-}AAction: TMarksAction); procedure OnSourceMarksGetSynEdit(Sender: TObject; aFilename: string; var aSynEdit: TSynEdit); // goto dialog function GotoDialog: TfrmGoto; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function CreateNewWindow(Activate: Boolean= False; DoDisableAutoSizing: boolean = False; AnID: Integer = -1 ): TSourceNotebook; function SenderToEditor(Sender: TObject): TSourceEditor; property CodeTemplateModul: TSynEditAutoComplete read FCodeTemplateModul write FCodeTemplateModul; private // Context-Menu procedure CloseOtherPagesClicked(Sender: TObject); procedure CloseOtherPagesClickedAsync(Sender: PtrInt); procedure CloseRightPagesClicked(Sender: TObject); procedure CloseRightPagesClickedAsync(Sender: PtrInt); procedure ReadOnlyClicked(Sender: TObject); procedure ToggleLineNumbersClicked(Sender: TObject); procedure ToggleI18NForLFMClicked(Sender: TObject); procedure ShowUnitInfo(Sender: TObject); procedure CopyFilenameClicked(Sender: TObject); procedure EditorPropertiesClicked(Sender: TObject); private FOnAddJumpPoint: TOnAddJumpPoint; FOnClearBookmark: TPlaceBookMarkEvent; FOnClearBookmarkId: TPlaceBookMarkIdEvent; FOnClickLink: TMouseEvent; FOnCloseClicked: TOnCloseSrcEditor; FOnDeleteLastJumpPoint: TNotifyEvent; FOnUpdateProjectFile: TUpdateProjectFileEvent; FOnFindDeclarationClicked: TNotifyEvent; FOnGetIndent: TOnGetIndentEvent; FOnGotoBookmark: TBookMarkActionEvent; FOnInitIdentCompletion: TOnInitIdentCompletion; FOnJumpToHistoryPoint: TOnJumpToHistoryPoint; FOnMouseLink: TSynMouseLinkEvent; FOnNoteBookCloseQuery: TCloseEvent; FOnOpenFileAtCursorClicked: TNotifyEvent; FOnPackageForSourceEditor: TPackageForSourceEditorEvent; FOnPlaceMark: TPlaceBookMarkEvent; FOnPopupMenu: TSrcEditPopupMenuEvent; FOnProcessUserCommand: TOnProcessUserCommand; fOnReadOnlyChanged: TNotifyEvent; FOnSetBookmark: TBookMarkActionEvent; FOnShowCodeContext: TOnShowCodeContext; FOnShowHintForSource: TOnShowHintForSource; FOnShowUnitInfo: TNotifyEvent; FOnToggleFormUnitClicked: TNotifyEvent; FOnToggleObjectInspClicked: TNotifyEvent; FOnUserCommandProcessed: TOnUserCommandProcessed; FOnViewJumpHistory: TNotifyEvent; public property OnAddJumpPoint: TOnAddJumpPoint read FOnAddJumpPoint write FOnAddJumpPoint; property OnCloseClicked: TOnCloseSrcEditor read FOnCloseClicked write FOnCloseClicked; property OnClickLink: TMouseEvent read FOnClickLink write FOnClickLink; property OnMouseLink: TSynMouseLinkEvent read FOnMouseLink write FOnMouseLink; property OnGetIndent: TOnGetIndentEvent read FOnGetIndent write FOnGetIndent; property OnDeleteLastJumpPoint: TNotifyEvent read FOnDeleteLastJumpPoint write FOnDeleteLastJumpPoint; property OnUpdateProjectFile: TUpdateProjectFileEvent read FOnUpdateProjectFile write FOnUpdateProjectFile; property OnFindDeclarationClicked: TNotifyEvent read FOnFindDeclarationClicked write FOnFindDeclarationClicked; property OnInitIdentCompletion: TOnInitIdentCompletion read FOnInitIdentCompletion write FOnInitIdentCompletion; property OnShowCodeContext: TOnShowCodeContext read FOnShowCodeContext write FOnShowCodeContext; property OnJumpToHistoryPoint: TOnJumpToHistoryPoint read FOnJumpToHistoryPoint write FOnJumpToHistoryPoint; property OnPlaceBookmark: TPlaceBookMarkEvent // Bookmark was placed by SynEdit read FOnPlaceMark write FOnPlaceMark; property OnClearBookmark: TPlaceBookMarkEvent // Bookmark was cleared by SynEdit read FOnClearBookmark write FOnClearBookmark; property OnClearBookmarkId: TPlaceBookMarkIdEvent read FOnClearBookmarkId write FOnClearBookmarkId; property OnSetBookmark: TBookMarkActionEvent // request to set a Bookmark read FOnSetBookmark write FOnSetBookmark; property OnGotoBookmark: TBookMarkActionEvent // request to go to a Bookmark read FOnGotoBookmark write FOnGotoBookmark; property OnOpenFileAtCursorClicked: TNotifyEvent read FOnOpenFileAtCursorClicked write FOnOpenFileAtCursorClicked; property OnProcessUserCommand: TOnProcessUserCommand read FOnProcessUserCommand write FOnProcessUserCommand; property OnUserCommandProcessed: TOnUserCommandProcessed read FOnUserCommandProcessed write FOnUserCommandProcessed; property OnReadOnlyChanged: TNotifyEvent read fOnReadOnlyChanged write fOnReadOnlyChanged; property OnShowHintForSource: TOnShowHintForSource read FOnShowHintForSource write FOnShowHintForSource; property OnShowUnitInfo: TNotifyEvent read FOnShowUnitInfo write FOnShowUnitInfo; property OnToggleFormUnitClicked: TNotifyEvent read FOnToggleFormUnitClicked write FOnToggleFormUnitClicked; property OnToggleObjectInspClicked: TNotifyEvent read FOnToggleObjectInspClicked write FOnToggleObjectInspClicked; property OnViewJumpHistory: TNotifyEvent read FOnViewJumpHistory write FOnViewJumpHistory; property OnPopupMenu: TSrcEditPopupMenuEvent read FOnPopupMenu write FOnPopupMenu; property OnNoteBookCloseQuery: TCloseEvent read FOnNoteBookCloseQuery write FOnNoteBookCloseQuery; property OnPackageForSourceEditor: TPackageForSourceEditorEvent read FOnPackageForSourceEditor write FOnPackageForSourceEditor; end; TSourceEditorWordCompletion = class(TWordCompletion) private type TSourceListItem = class Source: TStrings; IgnoreWordPos: TPoint; end; private FIncludeWords: TIdentComplIncludeWords; FSourceList: TObjectList; procedure ReloadSourceList; protected procedure DoGetSource(var Source: TStrings; var {%H-}TopLine, {%H-}BottomLine: Integer; var IgnoreWordPos: TPoint; SourceIndex: integer); override; public constructor Create; destructor Destroy; override; public property IncludeWords: TIdentComplIncludeWords read FIncludeWords write FIncludeWords; end; function SourceEditorManager: TSourceEditorManager; inline; //============================================================================= const SourceTabMenuRootName = 'SourceTab'; SourceEditorMenuRootName = 'SourceEditor'; var // Clipboard SrcEditMenuCut: TIDEMenuCommand; SrcEditMenuCopy: TIDEMenuCommand; SrcEditMenuPaste: TIDEMenuCommand; SrcEditMenuMultiPaste: TIDEMenuCommand; SrcEditMenuCopyFilename: TIDEMenuCommand; SrcEditMenuFindDeclaration: TIDEMenuCommand; SrcEditMenuSelectAll: TIDEMenuCommand; // finding / jumping SrcEditMenuProcedureJump: TIDEMenuCommand; SrcEditMenuFindNextWordOccurrence: TIDEMenuCommand; SrcEditMenuFindPrevWordOccurrence: TIDEMenuCommand; SrcEditMenuFindinFiles: TIDEMenuCommand; SrcEditMenuFindIdentifierReferences: TIDEMenuCommand; SrcEditMenuFindUsedUnitReferences: TIDEMenuCommand; // open file SrcEditMenuOpenFileAtCursor: TIDEMenuCommand; SrcEditMenuClosePage: TIDEMenuCommand; SrcEditMenuCloseOtherPages: TIDEMenuCommand; SrcEditMenuCloseOtherPagesToRight: TIDEMenuCommand; // bookmarks SrcEditMenuNextBookmark: TIDEMenuCommand; SrcEditMenuPrevBookmark: TIDEMenuCommand; SrcEditMenuSetFreeBookmark: TIDEMenuCommand; SrcEditMenuClearFileBookmark: TIDEMenuCommand; SrcEditMenuClearAllBookmark: TIDEMenuCommand; SrcEditMenuGotoBookmark: array [TBookmarkNumRange] of TIDEMenuCommand; SrcEditMenuToggleBookmark: array [TBookmarkNumRange] of TIDEMenuCommand; // debugging SrcEditMenuToggleBreakpoint: TIDEMenuCommand; SrcEditMenuStepToCursor: TIDEMenuCommand; SrcEditMenuRunToCursor: TIDEMenuCommand; SrcEditMenuEvaluateModify: TIDEMenuCommand; SrcEditMenuAddWatchAtCursor: TIDEMenuCommand; SrcEditMenuAddWatchPointAtCursor: TIDEMenuCommand; SrcEditMenuInspect: TIDEMenuCommand; SrcEditMenuViewCallStack: TIDEMenuCommand; // source SrcEditMenuEncloseSelection: TIDEMenuCommand; SrcEditMenuEncloseInIFDEF: TIDEMenuCommand; SrcEditMenuCompleteCode: TIDEMenuCommand; SrcEditMenuUseUnit: TIDEMenuCommand; SrcEditMenuShowUnitInfo: TIDEMenuCommand; // refactoring SrcEditMenuRenameIdentifier: TIDEMenuCommand; SrcEditMenuExtractProc: TIDEMenuCommand; SrcEditMenuInvertAssignment: TIDEMenuCommand; SrcEditMenuShowAbstractMethods: TIDEMenuCommand; SrcEditMenuShowEmptyMethods: TIDEMenuCommand; SrcEditMenuShowUnusedUnits: TIDEMenuCommand; SrcEditMenuFindOverloads: TIDEMenuCommand; SrcEditMenuMakeResourceString: TIDEMenuCommand; SrcEditMenuMoveEditorLeft: TIDEMenuCommand; SrcEditMenuMoveEditorRight: TIDEMenuCommand; SrcEditMenuMoveEditorFirst: TIDEMenuCommand; SrcEditMenuMoveEditorLast: TIDEMenuCommand; SrcEditMenuReadOnly: TIDEMenuCommand; SrcEditMenuShowLineNumbers: TIDEMenuCommand; SrcEditMenuDisableI18NForLFM: TIDEMenuCommand; SrcEditMenuEditorProperties: TIDEMenuCommand; {$IFnDEF SingleSrcWindow} // Multi Window SrcEditMenuMoveToNewWindow: TIDEMenuCommand; SrcEditMenuMoveToOtherWindow: TIDEMenuSection; SrcEditMenuMoveToOtherWindowNew: TIDEMenuCommand; SrcEditMenuMoveToOtherWindowList: TIDEMenuSection; SrcEditMenuCopyToNewWindow: TIDEMenuCommand; SrcEditMenuCopyToOtherWindow: TIDEMenuSection; SrcEditMenuCopyToOtherWindowNew: TIDEMenuCommand; SrcEditMenuCopyToOtherWindowList: TIDEMenuSection; SrcEditMenuFindInOtherWindow: TIDEMenuSection; SrcEditMenuFindInOtherWindowList: TIDEMenuSection; // EditorLocks SrcEditMenuEditorLock: TIDEMenuCommand; {$ENDIF} function GetIdeCmdAndToolBtn(ACommand: word; out ToolButton: TIDEButtonCommand): TIDECommand; function GetIdeCmdRegToolBtn(ACommand: word): TIDECommand; procedure RegisterStandardSourceTabMenuItems; procedure RegisterStandardSourceEditorMenuItems; function dbgSourceNoteBook(snb: TSourceNotebook): string; function CompareSrcEditIntfWithFilename(SrcEdit1, SrcEdit2: Pointer): integer; function CompareFilenameWithSrcEditIntf(FilenameStr, SrcEdit: Pointer): integer; var Highlighters: array[TLazSyntaxHighlighter] of TSynCustomHighlighter; EnglishGPLNotice: string; EnglishLGPLNotice: string; EnglishModifiedLGPLNotice: string; EnglishMITNotice: string; type { TToolButton_GotoBookmarks } TToolButton_GotoBookmarks = class(TIDEToolButton_ButtonDrop) protected procedure RefreshMenu; override; public class procedure ShowAloneMenu(Sender: TObject); static; end; { TToolButton_ToggleBookmarks } TToolButton_ToggleBookmarks = class(TIDEToolButton_ButtonDrop) protected procedure RefreshMenu; override; public class procedure ShowAloneMenu(Sender: TObject); static; end; implementation {$R *.lfm} {$R ../images/bookmark.res} var AWordCompletion: TWordCompletion = nil; var SRCED_LOCK, SRCED_OPEN, SRCED_CLOSE, SRCED_PAGES: PLazLoggerLogGroup; const (* SoftCenter are the visible Lines in the Editor where the caret can be located, without CenterCursor adjusting the topline. SoftCenter is defined by the amount of lines on the top/bottom of the editor, which are *not* part of it. *) SoftCenterFactor = 5; // One fifth of the "LinesInWindow"on each side (top/bottom) SoftCenterMinimum = 1; SoftCenterMaximum = 8; var AutoStartCompletionBoxTimer: TIdleTimer = nil; SourceCompletionCaretXY: TPoint; PasBeautifier: TSynBeautifierPascal; function dbgs(AFlag: TSourceNotebookUpdateFlag): string; overload; begin WriteStr(Result, AFlag); end; function dbgs(AFlags: TSourceNotebookUpdateFlags): string; overload; var i: TSourceNotebookUpdateFlag; begin Result := ''; for i := low(TSourceNotebookUpdateFlags) to high(TSourceNotebookUpdateFlags) do if i in AFlags then begin if Result <> '' then Result := Result + ','; Result := Result + dbgs(i); end; Result := '['+ Result + ']'; end; function GetIdeCmdAndToolBtn(ACommand: word; out ToolButton: TIDEButtonCommand): TIDECommand; // Find and return IDECommand. // Register IDEButtonCommand for it, also returned in out param. begin Result:=IDECommandList.FindIDECommand(ACommand); if Result<>nil then ToolButton := RegisterIDEButtonCommand(Result) else ToolButton := nil; end; function GetIdeCmdRegToolBtn(ACommand: word): TIDECommand; // Find and return IDECommand. Register IDEButtonCommand for it. begin Result:=IDECommandList.FindIDECommand(ACommand); if Result<>nil then RegisterIDEButtonCommand(Result); end; function SourceEditorManager: TSourceEditorManager; begin Result := TSourceEditorManager(SourceEditorManagerIntf); end; procedure ExecuteIdeMenuClick(Sender: TObject); var ActEdit: TSourceEditor; r: Boolean; begin if SourceEditorManager = nil then exit; if not (Sender is TIDESpecialCommand) then exit; if TIDESpecialCommand(Sender).Command = nil then exit; ActEdit := SourceEditorManager.ActiveEditor; if ActEdit = nil then begin TIDESpecialCommand(Sender).Command.Execute(Sender); exit; end; r := TIDESpecialCommand(Sender).Command.OnExecuteProc = @ExecuteIdeMenuClick; if r then TIDESpecialCommand(Sender).Command.OnExecuteProc := nil; // Commands may not work without focusing when anchordocking is installed ActEdit.FocusEditor; ActEdit.DoEditorExecuteCommand(TIDESpecialCommand(Sender).Command.Command); if r then TIDESpecialCommand(Sender).Command.OnExecuteProc := @ExecuteIdeMenuClick; end; procedure RegisterStandardSourceTabMenuItems; var AParent: TIDEMenuSection; begin SourceTabMenuRoot:=RegisterIDEMenuRoot(SourceTabMenuRootName); {%region *** Pages section ***} SrcEditMenuSectionPages:=RegisterIDEMenuSection(SourceTabMenuRoot, 'Pages'); AParent:=SrcEditMenuSectionPages; SrcEditMenuClosePage := RegisterIDEMenuCommand(AParent, 'Close Page', uemClosePage, nil, @ExecuteIdeMenuClick, nil, 'menu_close'); SrcEditMenuCloseOtherPages := RegisterIDEMenuCommand(AParent, 'Close All Other Pages',uemCloseOtherPages, nil, @ExecuteIdeMenuClick); SrcEditMenuCloseOtherPagesToRight := RegisterIDEMenuCommand(AParent, 'Close Pages To the Right',uemCloseOtherPagesRight, nil, @ExecuteIdeMenuClick); {$IFnDEF SingleSrcWindow} // Lock Editor SrcEditMenuEditorLock := RegisterIDEMenuCommand(AParent, 'LockEditor', uemLockPage, nil, @ExecuteIdeMenuClick); SrcEditMenuEditorLock.ShowAlwaysCheckable := True; // Move to other Window SrcEditMenuMoveToNewWindow := RegisterIDEMenuCommand(AParent, 'MoveToNewWindow', uemMoveToNewWindow, nil, @ExecuteIdeMenuClick); {%region * Move To Other *} SrcEditMenuMoveToOtherWindow := RegisterIDESubMenu(AParent, 'MoveToOtherWindow', uemMoveToOtherWindow); SrcEditMenuMoveToOtherWindowNew := RegisterIDEMenuCommand(SrcEditMenuMoveToOtherWindow, 'MoveToOtherWindowNew', uemMoveToOtherWindowNew, nil, @ExecuteIdeMenuClick); // Section for dynamically created targets SrcEditMenuMoveToOtherWindowList := RegisterIDEMenuSection(SrcEditMenuMoveToOtherWindow, 'MoveToOtherWindowList Section'); {%endregion} SrcEditMenuCopyToNewWindow := RegisterIDEMenuCommand(AParent, 'CopyToNewWindow', uemCopyToNewWindow, nil, @ExecuteIdeMenuClick); {%region * Copy To Other *} SrcEditMenuCopyToOtherWindow := RegisterIDESubMenu(AParent, 'CopyToOtherWindow', uemCopyToOtherWindow); SrcEditMenuCopyToOtherWindowNew := RegisterIDEMenuCommand(SrcEditMenuCopyToOtherWindow, 'CopyToOtherWindowNew', uemCopyToOtherWindowNew, nil, @ExecuteIdeMenuClick); // Section for dynamically created targets SrcEditMenuCopyToOtherWindowList := RegisterIDEMenuSection(SrcEditMenuCopyToOtherWindow, 'CopyToOtherWindowList Section'); {%endregion} SrcEditMenuFindInOtherWindow := RegisterIDESubMenu(AParent, 'FindInOtherWindow', uemFindInOtherWindow); // Section for dynamically created targets SrcEditMenuFindInOtherWindowList := RegisterIDEMenuSection(SrcEditMenuFindInOtherWindow, 'FindInOtherWindowList Section'); {$ENDIF} {%region * Move Page (left/right) *} SrcEditSubMenuMovePage:=RegisterIDESubMenu(AParent, 'Move Page', lisMovePage); AParent:=SrcEditSubMenuMovePage; SrcEditMenuMoveEditorLeft := RegisterIDEMenuCommand(AParent, 'MoveEditorLeft', uemMovePageLeft, nil, @ExecuteIdeMenuClick); SrcEditMenuMoveEditorRight := RegisterIDEMenuCommand(AParent, 'MoveEditorRight', uemMovePageRight, nil, @ExecuteIdeMenuClick); SrcEditMenuMoveEditorFirst := RegisterIDEMenuCommand(AParent, 'MoveEditorLeftmost', uemMovePageLeftmost, nil, @ExecuteIdeMenuClick); SrcEditMenuMoveEditorLast := RegisterIDEMenuCommand(AParent, 'MoveEditorRightmost', uemMovePageRightmost, nil, @ExecuteIdeMenuClick); {%endregion} {%endregion} {%region *** Editors section ***} SrcEditMenuSectionEditors:=RegisterIDEMenuSection(SourceTabMenuRoot, 'Editors'); {%endregion} end; procedure RegisterStandardSourceEditorMenuItems; var AParent: TIDEMenuSection; I: Integer; begin SourceEditorMenuRoot:=RegisterIDEMenuRoot(SourceEditorMenuRootName); AParent:=SourceEditorMenuRoot; // register the first dynamic section for often used context sensitive stuff SrcEditMenuSectionFirstDynamic:=RegisterIDEMenuSection(SourceEditorMenuRoot, 'First dynamic section'); // Felipe: Please keep "Find Declaration" as the first item {%region *** first static section *** } SrcEditMenuSectionFirstStatic:=RegisterIDEMenuSection(SourceEditorMenuRoot, 'First static section'); AParent:=SrcEditMenuSectionFirstStatic; SrcEditMenuFindDeclaration := RegisterIDEMenuCommand (AParent, 'Find Declaration', uemFindDeclaration, nil, @ExecuteIdeMenuClick); {%region *** Submenu: Find Section *** } SrcEditSubMenuFind := RegisterIDESubMenu(AParent, 'Find section', lisMenuFind, nil, nil, 'menu_search_find'); AParent:=SrcEditSubMenuFind; SrcEditMenuProcedureJump := RegisterIDEMenuCommand(AParent, 'Procedure Jump', uemProcedureJump, nil, @ExecuteIdeMenuClick); SrcEditMenuFindNextWordOccurrence := RegisterIDEMenuCommand(AParent, 'Find next word occurrence', srkmecFindNextWordOccurrence, nil, @ExecuteIdeMenuClick, nil, 'next_word'); SrcEditMenuFindPrevWordOccurrence := RegisterIDEMenuCommand(AParent, 'Find previous word occurrence', srkmecFindPrevWordOccurrence, nil, @ExecuteIdeMenuClick, nil, 'previous_word'); SrcEditMenuFindInFiles := RegisterIDEMenuCommand(AParent, 'Find in files', srkmecFindInFiles + ' ...', nil, @ExecuteIdeMenuClick, nil, 'menu_search_files'); SrcEditMenuFindIdentifierReferences := RegisterIDEMenuCommand(AParent, 'FindIdentifierReferences',lisMenuFindIdentifierRefs, nil, @ExecuteIdeMenuClick); SrcEditMenuFindUsedUnitReferences := RegisterIDEMenuCommand(AParent, 'FindUsedUnitReferences', lisMenuFindReferencesOfUsedUnit, nil, @ExecuteIdeMenuClick); {%endregion} {%endregion} {%region *** Clipboard section ***} SrcEditMenuSectionClipboard:=RegisterIDEMenuSection(SourceEditorMenuRoot, 'Clipboard'); AParent:=SrcEditMenuSectionClipboard; SrcEditMenuCut:=RegisterIDEMenuCommand(AParent,'Cut',lisCut, nil, nil, nil, 'laz_cut'); SrcEditMenuCopy:=RegisterIDEMenuCommand(AParent,'Copy',lisCopy, nil, nil, nil, 'laz_copy'); SrcEditMenuPaste:=RegisterIDEMenuCommand(AParent,'Paste',lisPaste, nil, nil, nil, 'laz_paste'); SrcEditMenuMultiPaste:=RegisterIDEMenuCommand(AParent,'MultiPaste',lisMenuMultiPaste); SrcEditMenuSelectAll:=RegisterIDEMenuCommand(AParent,'SelectAll',lisMenuSelectAll); SrcEditMenuCopyFilename:=RegisterIDEMenuCommand(AParent,'Copy filename', uemCopyFilename); {%endregion} {%region *** Files section ***} SrcEditMenuSectionFiles:=RegisterIDEMenuSection(SourceEditorMenuRoot, 'Files'); {%region * sub menu Open File *} SrcEditSubMenuOpenFile:=RegisterIDESubMenu(SrcEditMenuSectionFiles, 'Open File',lisOpenFile, nil, nil, 'laz_open'); AParent:=SrcEditSubMenuOpenFile; SrcEditMenuOpenFileAtCursor:=RegisterIDEMenuCommand(AParent, 'Open File At Cursor', uemOpenFileAtCursor, nil, @ExecuteIdeMenuClick, nil, 'menu_search_openfile_atcursor'); // register the File Specific dynamic section SrcEditMenuSectionFileDynamic:=RegisterIDEMenuSection(AParent, 'File dynamic section'); {%endregion} {%region * sub menu Flags section *} SrcEditSubMenuFlags:=RegisterIDESubMenu(SrcEditMenuSectionFiles,'Flags section',lisFileSettings); AParent:=SrcEditSubMenuFlags; SrcEditMenuReadOnly := RegisterIDEMenuCommand(AParent,'ReadOnly',uemReadOnly); SrcEditMenuReadOnly.ShowAlwaysCheckable:=true; SrcEditMenuShowLineNumbers := RegisterIDEMenuCommand(AParent, 'ShowLineNumbers',uemShowLineNumbers); SrcEditMenuShowLineNumbers.ShowAlwaysCheckable:=true; SrcEditMenuDisableI18NForLFM := RegisterIDEMenuCommand(AParent, 'DisableI18NForLFM',lisDisableI18NForLFM); SrcEditSubMenuHighlighter := RegisterIDESubMenu(AParent,'Highlighter', uemHighlighter); SrcEditSubMenuEncoding := RegisterIDESubMenu(AParent,'Encoding', uemEncoding); SrcEditSubMenuLineEnding := RegisterIDESubMenu(AParent,'LineEnding', uemLineEnding); {%endregion} {%endregion} {%region *** Goto Marks section ***} SrcEditMenuSectionMarks:=RegisterIDEMenuSection(SourceEditorMenuRoot, 'Marks section'); AParent:=SrcEditMenuSectionMarks; // register the Goto Bookmarks Submenu SrcEditSubMenuGotoBookmarks:=RegisterIDESubMenu(AParent, 'Goto bookmarks', uemGotoBookmark, nil, TNotifyProcedure(@TToolButton_GotoBookmarks.ShowAloneMenu), 'menu_goto_bookmarks'); AParent:=SrcEditSubMenuGotoBookmarks; for I in TBookmarkNumRange do SrcEditMenuGotoBookmark[I]:=RegisterIDEMenuCommand(AParent, 'GotoBookmark'+IntToStr(I), Format(uemBookmarkNUnSet, [IntToStr(I)]), nil, @ExecuteIdeMenuClick, nil, 'menu_goto_bookmark'+IntToStr(I)); AParent:=RegisterIDEMenuSection(AParent, 'Next/Prev Bookmark section'); SrcEditMenuNextBookmark:=RegisterIDEMenuCommand(AParent, 'Goto next Bookmark',uemNextBookmark, nil, @ExecuteIdeMenuClick, nil, 'menu_search_next_bookmark'); SrcEditMenuPrevBookmark:=RegisterIDEMenuCommand(AParent, 'Goto previous Bookmark',uemPrevBookmark, nil, @ExecuteIdeMenuClick, nil, 'menu_search_previous_bookmark'); {%endregion} {%region *** Toggle Bookmarks Submenu ***} AParent:=SrcEditMenuSectionMarks; SrcEditSubMenuToggleBookmarks:=RegisterIDESubMenu(AParent, 'Toggle bookmarks', uemToggleBookmark, nil, TNotifyProcedure(@TToolButton_ToggleBookmarks.ShowAloneMenu), 'menu_toggle_bookmarks'); AParent:=SrcEditSubMenuToggleBookmarks; for I in TBookmarkNumRange do SrcEditMenuToggleBookmark[I]:=RegisterIDEMenuCommand(AParent, 'ToggleBookmark'+IntToStr(I), Format(uemToggleBookmarkNUnset, [IntToStr(I)]), nil, @ExecuteIdeMenuClick, nil, 'menu_toggle_bookmark'+IntToStr(I)); AParent:=RegisterIDEMenuSection(AParent, 'Set Free Bookmark section'); SrcEditMenuSetFreeBookmark:=RegisterIDEMenuCommand(AParent, 'Set a free Bookmark',uemSetFreeBookmark, nil, @ExecuteIdeMenuClick, nil, 'menu_set_free_bookmark'); AParent:=RegisterIDEMenuSection(AParent, 'Clear Bookmarks section'); SrcEditMenuClearFileBookmark:=RegisterIDEMenuCommand(AParent, 'Clear Bookmark for current file',srkmecClearBookmarkForFile, nil, @ExecuteIdeMenuClick, nil, 'menu_clear_file_bookmarks'); SrcEditMenuClearAllBookmark:=RegisterIDEMenuCommand(AParent, 'Clear all Bookmark',srkmecClearAllBookmark, nil, @ExecuteIdeMenuClick, nil, 'menu_clear_all_bookmarks'); {%endregion} {%region *** Debug Section ***} // Commands will be assigned by DebugManager SrcEditMenuSectionDebug:=RegisterIDEMenuSection(SourceEditorMenuRoot, 'Debug section'); // register the Debug submenu SrcEditSubMenuDebug:=RegisterIDESubMenu(SrcEditMenuSectionDebug, 'Debug', uemDebugWord, nil, nil, 'debugger'); AParent:=SrcEditSubMenuDebug; // register the Debug submenu items SrcEditMenuToggleBreakpoint:=RegisterIDEMenuCommand(AParent, 'Toggle Breakpoint', uemToggleBreakpoint, nil, @ExecuteIdeMenuClick); SrcEditMenuEvaluateModify:=RegisterIDEMenuCommand(AParent, 'Evaluate/Modify...', uemEvaluateModify, nil, nil, nil, 'debugger_modify'); SrcEditMenuEvaluateModify.Enabled:=False; SrcEditMenuAddWatchAtCursor:=RegisterIDEMenuCommand(AParent, 'Add Watch at Cursor', uemAddWatchAtCursor); SrcEditMenuAddWatchPointAtCursor:=RegisterIDEMenuCommand(AParent, 'Add Watch at Cursor', uemAddWatchPointAtCursor); SrcEditMenuInspect:=RegisterIDEMenuCommand(AParent, 'Inspect...', uemInspect, nil, nil, nil, 'debugger_inspect'); SrcEditMenuInspect.Enabled:=False; SrcEditMenuStepToCursor:=RegisterIDEMenuCommand(AParent, 'Run to cursor', lisMenuStepToCursor, nil, nil, nil, 'menu_step_cursor'); SrcEditMenuRunToCursor:=RegisterIDEMenuCommand(AParent, 'Run to cursor', lisMenuRunToCursor, nil, nil, nil, 'menu_run_cursor'); SrcEditMenuViewCallStack:=RegisterIDEMenuCommand(AParent, 'View Call Stack', uemViewCallStack, nil, @ExecuteIdeMenuClick, nil, 'debugger_call_stack'); {%endregion} {%region *** Source Section ***} SrcEditSubMenuSource := RegisterIDESubMenu(SourceEditorMenuRoot, 'Source', uemSource, nil, nil, 'item_unit'); AParent:=SrcEditSubMenuSource; SrcEditMenuEncloseSelection := RegisterIDEMenuCommand(AParent, 'EncloseSelection', lisMenuEncloseSelection); SrcEditMenuEncloseInIFDEF := RegisterIDEMenuCommand(AParent, 'itmSourceEncloseInIFDEF', lisMenuEncloseInIFDEF); SrcEditMenuCompleteCode := RegisterIDEMenuCommand(AParent, 'CompleteCode', lisMenuCompleteCode, nil, @ExecuteIdeMenuClick); SrcEditMenuInvertAssignment := RegisterIDEMenuCommand(AParent, 'InvertAssignment', uemInvertAssignment, nil, @ExecuteIdeMenuClick); SrcEditMenuUseUnit := RegisterIDEMenuCommand(AParent, 'UseUnit', lisMenuUseUnit, nil, @ExecuteIdeMenuClick); SrcEditMenuShowUnitInfo := RegisterIDEMenuCommand(AParent, 'ShowUnitInfo', lisMenuViewUnitInfo , nil, nil, nil, 'menu_view_unit_info'); {%endregion} {%region *** Refactoring Section ***} SrcEditSubMenuRefactor:=RegisterIDESubMenu(SourceEditorMenuRoot, 'Refactoring',uemRefactor); AParent:=SrcEditSubMenuRefactor; SrcEditMenuRenameIdentifier := RegisterIDEMenuCommand(AParent, 'RenameIdentifier', lisMenuRenameIdentifier, nil, @ExecuteIdeMenuClick); SrcEditMenuExtractProc := RegisterIDEMenuCommand(AParent, 'ExtractProc', lisMenuExtractProc, nil, @ExecuteIdeMenuClick); SrcEditMenuShowAbstractMethods := RegisterIDEMenuCommand(AParent, 'ShowAbstractMethods', srkmecAbstractMethods, nil, @ExecuteIdeMenuClick); SrcEditMenuShowEmptyMethods := RegisterIDEMenuCommand(AParent, 'ShowEmptyMethods', srkmecEmptyMethods, nil, @ExecuteIdeMenuClick); SrcEditMenuShowUnusedUnits := RegisterIDEMenuCommand(AParent, 'ShowUnusedUnits', srkmecUnusedUnits, nil, @ExecuteIdeMenuClick); SrcEditMenuFindOverloads := RegisterIDEMenuCommand(AParent, 'FindOverloads', srkmecFindOverloadsCapt, nil, @ExecuteIdeMenuClick); {$IFnDEF EnableFindOverloads} SrcEditMenuFindOverloads.Visible := false; {$ENDIF} SrcEditMenuMakeResourceString := RegisterIDEMenuCommand(AParent, 'MakeResourceString', lisMenuMakeResourceString, nil, @ExecuteIdeMenuClick); {%endregion} SrcEditMenuEditorProperties:=RegisterIDEMenuCommand(SourceEditorMenuRoot, 'EditorProperties', lisMenuGeneralOptions, nil, nil, nil, 'menu_environment_options'); end; function dbgSourceNoteBook(snb: TSourceNotebook): string; var i: Integer; begin Result:=''; if snb=nil then begin Result:='nil'; end else if snb.Count=0 then begin Result:='empty'; end else begin for i:=0 to 4 do begin if i>=snb.Count then break; Result+='"'+ExtractFilename(snb.Items[i].FileName)+'",'; end; end; if RightStr(Result,1)=',' then Result:=LeftStr(Result,length(Result)-1); Result:='['+Result+']'; end; function CompareSrcEditIntfWithFilename(SrcEdit1, SrcEdit2: Pointer): integer; var SE1: TSourceEditorInterface absolute SrcEdit1; SE2: TSourceEditorInterface absolute SrcEdit2; begin Result:=CompareFilenames(SE1.FileName,SE2.FileName); end; function CompareFilenameWithSrcEditIntf(FilenameStr, SrcEdit: Pointer): integer; var SE1: TSourceEditorInterface absolute SrcEdit; begin Result:=CompareFilenames(AnsiString(FileNameStr),SE1.FileName); end; { TToolButton_GotoBookmarks } procedure TToolButton_GotoBookmarks.RefreshMenu; var cmd: TIDEMenuCommand; begin for cmd in SrcEditMenuGotoBookmark do if cmd <> nil then begin cmd.CreateNewMenuItem; DropdownMenu.Items.Add(cmd.MenuItem); end; DropdownMenu.Items.AddSeparator; SrcEditMenuPrevBookmark.CreateNewMenuItem; SrcEditMenuNextBookmark.CreateNewMenuItem; DropdownMenu.Items.Add([ SrcEditMenuPrevBookmark.MenuItem, SrcEditMenuNextBookmark.MenuItem]); end; class procedure TToolButton_GotoBookmarks.ShowAloneMenu(Sender: TObject); // on shortcuts only const Btn: TToolButton_GotoBookmarks=nil; // static var begin if Btn = nil then Btn := TToolButton_GotoBookmarks.Create(Application); Btn.PopUpAloneMenu(nil); // Btn should not be destroyed immediately after PopUp end; { TToolButton_ToggleBookmarks } procedure TToolButton_ToggleBookmarks.RefreshMenu; var cmd: TIDEMenuCommand; begin for cmd in SrcEditMenuToggleBookmark do if cmd <> nil then begin cmd.CreateNewMenuItem; DropdownMenu.Items.Add(cmd.MenuItem); end; DropdownMenu.Items.AddSeparator; SrcEditMenuSetFreeBookmark.CreateNewMenuItem; DropdownMenu.Items.Add(SrcEditMenuSetFreeBookmark.MenuItem); DropdownMenu.Items.AddSeparator; SrcEditMenuClearFileBookmark.CreateNewMenuItem; SrcEditMenuClearAllBookmark.CreateNewMenuItem; DropdownMenu.Items.Add([ SrcEditMenuClearFileBookmark.MenuItem, SrcEditMenuClearAllBookmark.MenuItem]); end; class procedure TToolButton_ToggleBookmarks.ShowAloneMenu(Sender: TObject); // on shortcuts only const Btn: TToolButton_ToggleBookmarks=nil; // static var begin if Btn = nil then Btn := TToolButton_ToggleBookmarks.Create(Application); Btn.PopUpAloneMenu(nil); // Btn should not be destroyed immediately after PopUp end; { TSourceEditorWordCompletion } constructor TSourceEditorWordCompletion.Create; begin inherited Create; FSourceList := TObjectList.Create; FIncludeWords := icwIncludeFromAllUnits; end; destructor TSourceEditorWordCompletion.Destroy; begin FSourceList.Free; inherited Destroy; end; procedure TSourceEditorWordCompletion.DoGetSource(var Source: TStrings; var TopLine, BottomLine: Integer; var IgnoreWordPos: TPoint; SourceIndex: integer); var SourceItem: TSourceListItem; begin if SourceIndex=0 then ReloadSourceList; if SourceIndex>=FSourceList.Count then Exit; SourceItem := FSourceList[SourceIndex] as TSourceListItem; Source := SourceItem.Source; IgnoreWordPos := SourceItem.IgnoreWordPos; end; procedure TSourceEditorWordCompletion.ReloadSourceList; var CurEditor, TempEditor: TSourceEditor; I: integer; Mng: TSourceEditorManager; New: TSourceListItem; AddedFileNames: TStringListUTF8Fast; begin FSourceList.Clear; if FIncludeWords=icwDontInclude then Exit; Mng := SourceEditorManager; CurEditor:=Mng.GetActiveSE; if (CurEditor<>nil) then begin New := TSourceListItem.Create; New.Source:=CurEditor.EditorComponent.Lines; New.IgnoreWordPos:=CurEditor.EditorComponent.LogicalCaretXY; Dec(New.IgnoreWordPos.Y); // LogicalCaretXY starts with 1 as top line FSourceList.Add(New); end; if FIncludeWords=icwIncludeFromAllUnits then begin AddedFileNames := TStringListUTF8Fast.Create; try AddedFileNames.Sorted := True; AddedFileNames.Duplicates := dupIgnore; for I := 0 to Mng.SourceEditorCount-1 do begin TempEditor := Mng.SourceEditors[I]; if ( TempEditor<>CurEditor) and (TempEditor.FileName<>CurEditor.FileName) and (AddedFileNames.IndexOf(TempEditor.FileName)=-1) then begin New := TSourceListItem.Create; New.Source:=TempEditor.EditorComponent.Lines; New.IgnoreWordPos:=Point(-1,-1); FSourceList.Add(New); AddedFileNames.Add(TempEditor.FileName); end; end; finally AddedFileNames.Free; end; end; end; { TBrowseEditorTabHistoryDialog } procedure TBrowseEditorTabHistoryDialog.DoCreate; begin inherited DoCreate; Assert(Owner is TSourceNotebook); FNotebook := TSourceNotebook(Owner); Caption := lisCoolbarSourceTab; BorderIcons:=[]; BorderStyle:=bsSizeable; KeyPreview := True; FEditorList := TListBox.Create(Self); FEditorList.Parent := Self; FEditorList.Align := alClient; {$IFDEF MSWINDOWS}//bsNone seems to work on Windows only FEditorList.BorderStyle := bsNone; {$ENDIF} end; procedure TBrowseEditorTabHistoryDialog.KeyDown(var Key: Word; Shift: TShiftState); function CommandUsed(Command: TIDECommand): Boolean; begin Result := ((Command.ShortcutA.Key1 = Key) and (Command.ShortcutA.Shift1 = Shift)) or ((Command.ShortcutA.Key2 = Key) and (Command.ShortcutA.Shift2 = Shift)); end; var xPrev, xNext: TIDECommand; begin xPrev := IDECommandList.FindIDECommand(ecPrevEditorInHistory); if CommandUsed(xPrev) then begin MoveInList(True); Key := 0; end else begin xNext := IDECommandList.FindIDECommand(ecNextEditorInHistory); if CommandUsed(xNext) then begin MoveInList(False); Key := 0; end; end; inherited KeyDown(Key, Shift); end; procedure TBrowseEditorTabHistoryDialog.MoveInList(aForward: Boolean); begin if aForward then begin if FEditorList.ItemIndex < FEditorList.Items.Count-1 then FEditorList.ItemIndex := FEditorList.ItemIndex + 1 else FEditorList.ItemIndex := 0; end else begin if FEditorList.ItemIndex > 0 then FEditorList.ItemIndex := FEditorList.ItemIndex - 1 else FEditorList.ItemIndex := FEditorList.Items.Count - 1; end; end; procedure TBrowseEditorTabHistoryDialog.KeyUp(var Key: Word; Shift: TShiftState); begin if Key = VK_CONTROL then begin Close; if FEditorList.ItemIndex >= 0 then FNotebook.PageIndex := TCustomPage(FEditorList.Items.Objects[FEditorList.ItemIndex]).PageIndex; if (FNotebook.ActiveEditor<>nil) and FNotebook.ActiveEditor.EditorControl.CanSetFocus then FNotebook.ActiveEditor.EditorControl.SetFocus; Key := 0; end; inherited KeyUp(Key, Shift); end; procedure TBrowseEditorTabHistoryDialog.Show(aForward: Boolean); procedure PlaceMe; var xWidth, xHeight: Integer; xTopLeft, xRightBottom: TPoint; begin xWidth := Canvas.TextWidth('m')*20; if FEditorList.ItemHeight>0 then//ItemHeight can be 0 the first time xHeight := Min(10, FEditorList.Items.Count)*FEditorList.ItemHeight else xHeight := 200; {$IFNDEF MSWINDOWS} xHeight := xHeight + GetSystemMetrics(SM_CYBORDER)*2; {$ENDIF} xTopLeft := FNotebook.ClientToScreen(Point(0, 0)); xRightBottom := FNotebook.ClientToScreen(FNotebook.ClientRect.BottomRight); SetBounds( (xTopLeft.x+xRightBottom.x-xWidth) div 2, (xTopLeft.y+xRightBottom.y-xHeight) div 2, xWidth, xHeight); end; var I: Integer; xPage: TCustomPage; xIndex: TAvlTree; begin if FNotebook.PageCount <= 1 then Exit; FEditorList.Items.BeginUpdate; xIndex := TAvlTree.Create; try FEditorList.Items.Clear; for I := 0 to FNotebook.FHistoryList.Count-1 do begin xPage := TCustomPage(FNotebook.FHistoryList[I]); FEditorList.Items.AddObject(xPage.Caption, xPage); xIndex.Add(xPage); end; //add pages not in history to the right of the active page for I := FNotebook.PageIndex+1 to FNotebook.PageCount-1 do begin xPage := FNotebook.NoteBookPage[I]; if xIndex.Find(xPage)=nil then FEditorList.Items.AddObject(xPage.Caption, xPage); end; //add pages not in history to the left of the active page for I := 0 to FNotebook.PageIndex-1 do begin xPage := FNotebook.NoteBookPage[I]; if xIndex.Find(xPage)=nil then FEditorList.Items.AddObject(xPage.Caption, xPage); end; finally xIndex.Free; FEditorList.Items.EndUpdate; end; if aForward then FEditorList.ItemIndex := 1 else FEditorList.ItemIndex := FEditorList.Count-1; PlaceMe; Visible := True; PlaceMe; BringToFront; end; { TSourceEditorHintWindowManager } procedure TSourceEditorHintWindowManager.ActivateHint(const ScreenPos: TPoint; const ABaseURL, AHint: string; AAutoShown: Boolean; AMouseOffset: Boolean); begin ActivateHint(Rect(ScreenPos.x, ScreenPos.y, ScreenPos.x, ScreenPos.y), ABaseURL, AHint, AAutoShown, AMouseOffset); end; procedure TSourceEditorHintWindowManager.ActivateHint(const ScreenRect: TRect; const ABaseURL, AHint: string; AAutoShown: Boolean; AMouseOffset: Boolean); begin FAutoShown := AAutoShown; BaseURL := ABaseURL; FLastHint := AHint; FScreenRect := ScreenRect; if AAutoShown then begin FAutoHintMousePos := Mouse.CursorPos; if not(HintIsVisible and (FLastHint = AHint)) then ShowHint(Point(ScreenRect.Left, ScreenRect.Bottom),AHint,AMouseOffset); end else begin if HintIsVisible and (FLastHint = AHint) then HideIfVisible else ShowHint(Point(ScreenRect.Left, ScreenRect.Bottom),AHint,AMouseOffset); end; FAutoHideHintTimer.Enabled := AAutoShown; end; constructor TSourceEditorHintWindowManager.Create(AManager: TSourceEditorManager); begin inherited Create; FManager := AManager; // HintTimer FAutoHintTimer := TIdleTimer.Create(nil); with FAutoHintTimer do begin Interval := EditorOpts.AutoHintDelayInMSec; Enabled := False; AutoEnabled := False; OnTimer := @HintTimer; end; // Track mouse movements outside the IDE, if hint is visible FAutoHideHintTimer := TTimer.Create(nil); with FAutoHideHintTimer do begin Interval := 500; Enabled := False; OnTimer := @HideHintTimer; end; end; destructor TSourceEditorHintWindowManager.Destroy; begin FAutoHintTimer.Free; FAutoHideHintTimer.Free; inherited Destroy; end; procedure TSourceEditorHintWindowManager.HideAutoHint; begin if FAutoHintTimer<>nil then begin FAutoHintTimer.AutoEnabled := false; FAutoHintTimer.Enabled:=false; end; if FAutoHideHintTimer <> nil then FAutoHideHintTimer.Enabled := False; if AutoStartCompletionBoxTimer<>nil then AutoStartCompletionBoxTimer.Enabled:=false; if (FManager.ActiveEditor <> nil) and (FManager.ActiveEditor.FCodeCompletionState.State in [ccsDot, ccsOnTyping]) then FManager.ActiveEditor.FCodeCompletionState.State := ccsReady; if FAutoShown then HideHint; end; procedure TSourceEditorHintWindowManager.HideHintTimer(Sender: TObject); begin if HintIsVisible and FAutoShown then begin if ComparePoints(FAutoHintMousePos, Mouse.CursorPos) <> 0 then begin // TODO: introduce property, to indicate if hint is interactive if HintIsComplex or not IsRectEmpty(FScreenRect) then HideAutoHintAfterMouseMoved else HideAutoHint; end; end else FAutoHideHintTimer.Enabled := false; end; procedure TSourceEditorHintWindowManager.HintTimer(Sender: TObject); var MousePos: TPoint; AControl: TControl; begin FAutoHintTimer.Enabled := False; FAutoHintTimer.AutoEnabled := False; if not FManager.ActiveSourceWindow.IsVisible then exit; MousePos := Mouse.CursorPos; AControl:=FindLCLControl(MousePos); if (AControl=nil) or (not FManager.ActiveSourceWindow.ContainsControl(AControl)) then exit; if AControl is TSynEdit then FManager.ActiveSourceWindow.ShowSynEditHint(MousePos); end; procedure TSourceEditorHintWindowManager.HideAutoHintAfterMouseMoved; const MaxJitter = 3; var Cur: TPoint; OkX, OkY: Boolean; hw: THintWindow; begin if HintIsVisible and not FAutoShown then Exit; FAutoHideHintTimer.Enabled := False; if HintIsVisible then begin Cur := Mouse.CursorPos; // Desktop coordinates if (not IsRectEmpty(FScreenRect)) then begin // Do not close, if mouse still over the same word, that triggered the hint if PtInRect(FScreenRect, Cur) then Exit; end else begin // Fallback if FScreenRect is empty (for legacy calls) hw := CurHintWindow; OkX := ( (FAutoHintMousePos.x <= hw.Left) and (Cur.x > FAutoHintMousePos.x) and (Cur.x <= hw.Left + hw.Width) ) or ( (FAutoHintMousePos.x >= hw.Left + hw.Width) and (Cur.x < FAutoHintMousePos.x) and (Cur.x >= hw.Left) ) or ( (Cur.x >= hw.Left) and (Cur.x <= hw.Left + hw.Width) ); OkY := ( (FAutoHintMousePos.y <= hw.Top) and (Cur.y > FAutoHintMousePos.y) and (Cur.y <= hw.Top + hw.Height) ) or ( (FAutoHintMousePos.y >= hw.Top + hw.Height) and (Cur.y < FAutoHintMousePos.y) and (Cur.y >= hw.Top) ) or ( (Cur.y >= hw.Top) and (Cur.y <= hw.Top + hw.Height) ); if OkX then FAutoHintMousePos.x := Cur.x; if OkY then FAutoHintMousePos.y := Cur.y; OkX := OkX or ( (FAutoHintMousePos.x <= hw.Left + MaxJitter) and (Cur.x > FAutoHintMousePos.x - MaxJitter) and (Cur.x <= hw.Left + hw.Width + MaxJitter) ) or ( (FAutoHintMousePos.x >= hw.Left + hw.Width - MaxJitter) and (Cur.x < FAutoHintMousePos.x + MaxJitter) and (Cur.x >= hw.Left - MaxJitter) ); OkY := OkY or ( (FAutoHintMousePos.y <= hw.Top + MaxJitter) and (Cur.y > FAutoHintMousePos.y - MaxJitter) and (Cur.y <= hw.Top + hw.Height + MaxJitter) ) or ( (FAutoHintMousePos.y >= hw.Top + hw.Height - MaxJitter) and (Cur.y < FAutoHintMousePos.y + MaxJitter) and (Cur.y >= hw.Top - MaxJitter) ); if (OkX and OkY) then begin FAutoHideHintTimer.Enabled := True; exit; end; end; end; HideHint; end; procedure TSourceEditorHintWindowManager.UpdateHintTimer; begin with EditorOpts do if (MainIDEInterface.ToolStatus=itDebugger) then FAutoHintTimer.AutoEnabled := AutoToolTipExprEval or AutoToolTipSymbTools else FAutoHintTimer.AutoEnabled := AutoToolTipSymbTools; end; { TSourceEditCompletion } procedure TSourceEditCompletion.CompletionFormResized(Sender: TObject); begin EnvironmentOptions.Desktop.CompletionWindowWidth := TheForm.Width; EnvironmentOptions.Desktop.CompletionWindowHeight := TheForm.NbLinesInWindow; end; function TSourceEditCompletion.GetCompletionFormClass: TSynBaseCompletionFormClass; begin Result := TSourceEditCompletionForm; end; procedure TSourceEditCompletion.ccExecute(Sender: TObject); // init completion form // called by OnExecute just before showing var SL: TStrings; Prefix: String; I: Integer; NewStr: String; SynEditor: TIDESynEditor; Begin {$IFDEF VerboseIDECompletionBox} debugln(['TSourceEditCompletion.ccExecute START']); {$ENDIF} TheForm.Font := Editor.Font; FActiveEditTextColor := Editor.Font.Color; FActiveEditBorderColor := RGBToColor(200, 200, 200); FActiveEditBackgroundColor := Editor.Color; FActiveEditTextSelectedColor := TSynEdit(Editor).SelectedColor.Foreground; FActiveEditBackgroundSelectedColor := TSynEdit(Editor).SelectedColor.Background; FActiveEditTextHighLightColor := clNone; if Editor.Highlighter<>nil then begin with Editor.Highlighter do begin if IdentifierAttribute<>nil then begin if IdentifierAttribute.ForeGround<>clNone then FActiveEditTextColor:=IdentifierAttribute.ForeGround; if IdentifierAttribute.BackGround<>clNone then FActiveEditBackgroundColor:=IdentifierAttribute.BackGround; end; end; end; if Editor is TIDESynEditor then begin SynEditor := TIDESynEditor(Editor); if SynEditor.MarkupIdentComplWindow.TextColor<>clNone then FActiveEditTextColor := SynEditor.MarkupIdentComplWindow.TextColor; if SynEditor.MarkupIdentComplWindow.BorderColor<>clNone then FActiveEditBorderColor := SynEditor.MarkupIdentComplWindow.BorderColor; if SynEditor.MarkupIdentComplWindow.WindowColor<>clNone then FActiveEditBackgroundColor := SynEditor.MarkupIdentComplWindow.WindowColor; if SynEditor.MarkupIdentComplWindow.TextSelectedColor<>clNone then FActiveEditTextSelectedColor := SynEditor.MarkupIdentComplWindow.TextSelectedColor; if SynEditor.MarkupIdentComplWindow.BackgroundSelectedColor<>clNone then FActiveEditBackgroundSelectedColor := SynEditor.MarkupIdentComplWindow.BackgroundSelectedColor; if SynEditor.MarkupIdentComplWindow.HighlightColor<>clNone then FActiveEditTextHighLightColor := SynEditor.MarkupIdentComplWindow.HighlightColor; end; SL := TStringList.Create; try Prefix := CurrentString; case CurrentCompletionType of ctIdentCompletion: if InitIdentCompletionValues(SL) then begin ToggleReplaceWhole:=not CodeToolsOpts.IdentComplReplaceIdentifier; end else begin ItemList.Clear; exit; end; ctWordCompletion: begin ToggleReplaceWhole:=not CodeToolsOpts.IdentComplReplaceIdentifier; ccSelection := ''; end; ctTemplateCompletion: begin ccSelection:=''; for I := 0 to Manager.CodeTemplateModul.Completions.Count-1 do begin NewStr := Manager.CodeTemplateModul.Completions[I]; if NewStr<>'' then begin NewStr:=#3'B'+NewStr+#3'b'; while length(NewStr)<10+4 do NewStr:=NewStr+' '; NewStr:=NewStr+' '+Manager.CodeTemplateModul.CompletionComments[I]; SL.Add(NewStr); end; end; end; end; ItemList := SL; finally SL.Free; end; CurrentString:=Prefix; // set colors if (Editor<>nil) and (TheForm<>nil) then begin with TheForm as TSourceEditCompletionForm do begin DrawBorderColor := FActiveEditBorderColor; BackgroundColor := FActiveEditBackgroundColor; clSelect := FActiveEditBackgroundSelectedColor; TextColor := FActiveEditTextColor; TextSelectedColor := FActiveEditTextSelectedColor; TextHighLightColor:= FActiveEditTextHighLightColor; //debugln('TSourceEditCompletion.ccExecute A Color=',DbgS(Color), // ' clSelect=',DbgS(clSelect), // ' TextColor=',DbgS(TextColor), // ' TextSelectedColor=',DbgS(TextSelectedColor), // ''); end; debugln(['TSourceEditCompletion.ccExecute ',DbgSName(SourceEditorManager.ActiveCompletionPlugin)]); if (CurrentCompletionType=ctIdentCompletion) and (SourceEditorManager.ActiveCompletionPlugin=nil) then StartShowCodeHelp else if SrcEditHintWindow<>nil then begin SrcEditHintWindow.HelpEnabled:=false; TheForm.LongLineHintType := EditorOpts.CompletionLongLineHintType; end; end; end; procedure TSourceEditCompletion.ccCancel(Sender: TObject); // user cancels completion form begin {$IFDEF VerboseIDECompletionBox} DebugLnEnter(['TSourceNotebook.ccCancel START']); try //debugln(GetStackTrace(true)); {$ENDIF} Manager.DeactivateCompletionForm; {$IFDEF VerboseIDECompletionBox} finally DebugLnExit(['TSourceNotebook.ccCancel END']); end; //debugln(GetStackTrace(true)); {$ENDIF} with Manager.ActiveEditor do begin FCodeCompletionState.LastTokenStartPos := CurrentWordLogStartOrCaret; FCodeCompletionState.State := ccsCancelled; end; end; procedure TSourceEditCompletion.ccComplete(var Value: string; SourceValue: string; var SourceStart, SourceEnd: TPoint; KeyChar: TUTF8Char; Shift: TShiftState); // completion selected -> deactivate completion form // Called when user has selected a completion item function CharBehindIdent(const Line: string; StartPos: integer): char; begin while (StartPos<=length(Line)) and (Line[StartPos] in ['_','A'..'Z','a'..'z']) do inc(StartPos); while (StartPos<=length(Line)) and (Line[StartPos] in [' ',#9]) do inc(StartPos); if StartPos<=length(Line) then Result:=Line[StartPos] else Result:=#0; end; function CharInFrontOfIdent(const Line: string; StartPos: integer): char; begin while (StartPos>=1) and (Line[StartPos] in ['_','A'..'Z','a'..'z']) do dec(StartPos); while (StartPos>=1) and (Line[StartPos] in [' ',#9]) do dec(StartPos); if StartPos>=1 then Result:=Line[StartPos] else Result:=#0; end; var p1, p2: integer; ValueType: TIdentComplValue; NewCaretXY: TPoint; CursorToLeft: integer; NewValue: String; OldCompletionType: TCompletionType; prototypeAdded: boolean; SourceNoteBook: TSourceNotebook; IdentItem: TIdentifierListItem; Begin {$IFDEF VerboseIDECompletionBox} DebugLnEnter(['TSourceNotebook.ccComplete START']); try {$ENDIF} prototypeAdded := false; OldCompletionType:=CurrentCompletionType; case CurrentCompletionType of ctIdentCompletion: begin if Manager.ActiveCompletionPlugin<>nil then begin Manager.ActiveCompletionPlugin.Complete(Value,SourceValue, SourceStart,SourceEnd,KeyChar,Shift); Manager.FActiveCompletionPlugin:=nil; end else begin IdentItem:=CodeToolBoss.IdentifierList.FilteredItems[Position]; // add to history CodeToolBoss.IdentifierHistory.Add(IdentItem); if IdentItem is TCodeTemplateIdentifierListItem then begin if IdentItem.Identifier<>'' then Manager.CodeTemplateModul.ExecuteCompletion(IdentItem.Identifier, Editor); end else begin // get value NewValue:=GetIdentCompletionValue(Self, KeyChar, ValueType, CursorToLeft); if ValueType=icvIdentifier then ; // insert value plus special chars like brackets, semicolons, ... if ValueType <> icvNone then Editor.TextBetweenPointsEx[SourceStart, SourceEnd, scamEnd] := NewValue; if ValueType in [icvProcWithParams,icvIndexedProp] then prototypeAdded := true; if CursorToLeft>0 then begin NewCaretXY:=Editor.CaretXY; dec(NewCaretXY.X,CursorToLeft); Editor.CaretXY:=NewCaretXY; end; end; ccSelection := ''; Value:=''; SourceEnd := SourceStart; end; end; ctTemplateCompletion: begin // the completion is the bold text between #3'B' and #3'b' p1:=Pos(#3,Value); if p1>=0 then begin p2:=p1+2; while (p2<=length(Value)) and (Value[p2]<>#3) do inc(p2); Value:=copy(Value,p1+2,p2-p1-2); // keep parent identifier (in front of '.') p1:=length(ccSelection); while (p1>=1) and (ccSelection[p1]<>'.') do dec(p1); if p1>=1 then Value:=copy(ccSelection,1,p1)+Value; end; ccSelection := ''; if Value<>'' then Manager.CodeTemplateModul.ExecuteCompletion(Value, Editor); SourceEnd := SourceStart; Value:=''; end; ctWordCompletion: // the completion is already in Value begin ccSelection := ''; if Value<>'' then AWordCompletion.AddWord(Value); end; else begin Value:=''; end; end; Manager.DeactivateCompletionForm; //DebugLn(['TSourceNotebook.ccComplete ',KeyChar,' ',OldCompletionType=ctIdentCompletion]); Manager.ActiveEditor.FCodeCompletionState.State := ccsReady; if (KeyChar='.') and (OldCompletionType=ctIdentCompletion) then begin SourceCompletionCaretXY:=Editor.LogicalCaretXY; AutoStartCompletionBoxTimer.AutoEnabled:=true; Manager.ActiveEditor.FCodeCompletionState.State := ccsDot; end else if prototypeAdded and EditorOpts.AutoDisplayFunctionPrototypes then begin if Editor.Owner is TSourceNoteBook then begin SourceNoteBook := Editor.Owner as TSourceNoteBook; SourceNotebook.StartShowCodeContext(CodeToolsOpts.IdentComplJumpToError); end; end; {$IFDEF VerboseIDECompletionBox} finally DebugLnExit(['TSourceNotebook.ccComplete END']); end; {$ENDIF} end; function TSourceEditCompletion.OnSynCompletionPaintItem(const AKey: string; ACanvas: TCanvas; X, Y: integer; ItemSelected: boolean; Index: integer): boolean; var MaxX: Integer; t: TCompletionType; hl: TSynCustomHighlighter; Colors: TPaintCompletionItemColors; begin try with ACanvas do begin if (Editor<>nil) then Font := Editor.Font else begin Font.Size:=EditorOpts.EditorFontSize; // set Size before name for XLFD ! Font.Name:=EditorOpts.EditorFont; end; Font.Style:=[]; end; Colors.BackgroundColor := FActiveEditBackgroundColor; Colors.BackgroundSelectedColor := FActiveEditBackgroundSelectedColor; Colors.TextColor := FActiveEditTextColor; Colors.TextSelectedColor := FActiveEditTextSelectedColor; Colors.TextHilightColor := FActiveEditTextHighLightColor; MaxX:=TheForm.ClientWidth; t:=CurrentCompletionType; if Manager.ActiveCompletionPlugin<>nil then begin if Manager.ActiveCompletionPlugin.HasCustomPaint then begin Manager.ActiveCompletionPlugin.PaintItem(AKey,ACanvas,X,Y,ItemSelected,Index); end else begin t:=ctWordCompletion; end; end; hl := nil; if Editor <> nil then hl := Editor.Highlighter; PaintCompletionItem(AKey, ACanvas, X, Y, MaxX, ItemSelected, Index, self, t, hl, @Colors); Result:=true; except DebugLn('OnSynCompletionPaintItem failed'); DumpStack; Result := false; end; end; function TSourceEditCompletion.OnSynCompletionMeasureItem(const AKey: string; ACanvas: TCanvas; ItemSelected: boolean; Index: integer): TPoint; var MaxX: Integer; t: TCompletionType; begin try with ACanvas do begin if (Editor<>nil) then Font:=Editor.Font else begin Font.Size:=EditorOpts.EditorFontSize; // set Size before name of XLFD ! Font.Name:=EditorOpts.EditorFont; end; Font.Style:=[]; end; MaxX := Screen.Width-20; t:=CurrentCompletionType; if Manager.ActiveCompletionPlugin<>nil then begin if Manager.ActiveCompletionPlugin.HasCustomPaint then begin Manager.ActiveCompletionPlugin.MeasureItem(AKey,ACanvas,ItemSelected,Index); end else begin t:=ctWordCompletion; end; end; Result := PaintCompletionItem(AKey,ACanvas,0,0,MaxX,ItemSelected,Index, self,t,nil,nil,True); Result.Y:=FontHeight; except DebugLn('OnSynCompletionMeasureItem failed'); DumpStack; Result := Point(FontHeight * length(AKey), FontHeight); end; end; procedure TSourceEditCompletion.OnSynCompletionSearchPosition( var APosition: integer); // prefix changed -> filter list var i,x:integer; CurStr,s:Ansistring; SL: TStrings; ItemCnt: Integer; begin case CurrentCompletionType of ctIdentCompletion: if Manager.ActiveCompletionPlugin<>nil then begin // let plugin rebuild completion list SL:=TStringList.Create; try Manager.ActiveCompletionPlugin.PrefixChanged(CurrentString,APosition,sl); ItemList:=SL; finally SL.Free; end; end else begin // rebuild completion list APosition:=0; CurStr:=CurrentString; CodeToolBoss.IdentifierList.ContainsFilter := CodeToolsOpts.IdentComplUseContainsFilter; CodeToolBoss.IdentifierList.Prefix:=CurStr; ItemCnt:=CodeToolBoss.IdentifierList.GetFilteredCount; SL:=TStringList.Create; try sl.Capacity:=ItemCnt; for i:=0 to ItemCnt-1 do SL.Add('Dummy'); // these entries are not shown ItemList:=SL; finally SL.Free; end; end; ctTemplateCompletion: begin // search CurrentString in bold words (words after #3'B') CurStr:=CurrentString; i:=0; while i#3) do inc(x); if xnil then SrcEditHintWindow.UpdateHints; end; procedure TSourceEditCompletion.OnSynCompletionCompletePrefix(Sender: TObject); var OldPrefix: String; NewPrefix: String; SL: TStringList; AddPrefix: String; begin OldPrefix:=CurrentString; NewPrefix:=OldPrefix; case CurrentCompletionType of ctIdentCompletion: if Manager.ActiveCompletionPlugin<>nil then begin Manager.ActiveCompletionPlugin.CompletePrefix(NewPrefix); end else begin NewPrefix:=CodeToolBoss.IdentifierList.CompletePrefix(OldPrefix); end; ctWordCompletion: begin aWordCompletion.CompletePrefix(OldPrefix,NewPrefix,false); end; end; if NewPrefix<>OldPrefix then begin AddPrefix:=copy(NewPrefix,length(OldPrefix)+1,length(NewPrefix)); Editor.InsertTextAtCaret(AddPrefix); if CurrentCompletionType=ctWordCompletion then begin SL:=TStringList.Create; try aWordCompletion.GetWordList(SL, NewPrefix, CodeToolBoss.IdentifierList.ContainsFilter, false, 100); ItemList:=SL; finally SL.Free; end; end; CurrentString:=NewPrefix; end; end; procedure TSourceEditCompletion.OnSynCompletionNextChar(Sender: TObject); var NewPrefix: String; Line: String; LogCaret: TPoint; CharLen: LongInt; AddPrefix: String; begin if Editor=nil then exit; LogCaret:=Editor.LogicalCaretXY; if LogCaret.Y>Editor.Lines.Count then exit; //* LogCaret.Y One Based Line:=Editor.Lines[LogCaret.Y-1]; if LogCaret.X>length(Line) then exit; CharLen:=UTF8CodepointSize(@Line[LogCaret.X]); AddPrefix:=copy(Line,LogCaret.X,CharLen); if not Editor.IsIdentChar(AddPrefix) then exit; NewPrefix:=CurrentString+AddPrefix; //debugln('TSourceNotebook.OnSynCompletionNextChar NewPrefix="',NewPrefix,'" LogCaret.X=',dbgs(LogCaret.X)); inc(LogCaret.X); Editor.LogicalCaretXY:=LogCaret; CurrentString:=NewPrefix; end; procedure TSourceEditCompletion.OnSynCompletionPrevChar(Sender: TObject); var NewPrefix: String; NewLen: LongInt; begin NewPrefix:=CurrentString; if NewPrefix='' then exit; if Editor=nil then exit; Editor.CaretX:=Editor.CaretX-1; NewLen:=UTF8FindNearestCharStart(PChar(NewPrefix),length(NewPrefix), length(NewPrefix)-1); NewPrefix:=copy(NewPrefix,1,NewLen); CurrentString:=NewPrefix; end; procedure TSourceEditCompletion.OnSynCompletionKeyPress(Sender: TObject; var Key: Char); begin if (System.Pos(Key,EndOfTokenChr)>0) then begin // identifier completed //debugln('TSourceNotebook.OnSynCompletionKeyPress A'); TheForm.OnValidate(Sender,Key,[]); //debugln('TSourceNotebook.OnSynCompletionKeyPress B'); Key:=#0; end; end; procedure TSourceEditCompletion.OnSynCompletionUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char); begin if (length(UTF8Key)=1) and (System.Pos(UTF8Key[1],EndOfTokenChr)>0) then begin // identifier completed //debugln('TSourceNotebook.OnSynCompletionUTF8KeyPress A'); TheForm.OnValidate(Sender,UTF8Key,[]); //debugln('TSourceNotebook.OnSynCompletionKeyPress B'); UTF8Key:=''; end; //debugln('TSourceNotebook.OnSynCompletionKeyPress B UTF8Key=',dbgstr(UTF8Key)); end; procedure TSourceEditCompletion.OnSynCompletionPositionChanged(Sender: TObject); begin if Manager.ActiveCompletionPlugin<>nil then Manager.ActiveCompletionPlugin.IndexChanged(Position); if SrcEditHintWindow<>nil then SrcEditHintWindow.UpdateHints; end; function TSourceEditCompletion.InitIdentCompletionValues(S: TStrings): boolean; var i: integer; Handled: boolean; Abort: boolean; Prefix: string; ItemCnt: Integer; begin Result:=false; Prefix := CurrentString; if Manager.ActiveCompletionPlugin<>nil then begin Result := Manager.ActiveCompletionPlugin.Collect(S); end else if Assigned(Manager.OnInitIdentCompletion) then begin Manager.OnInitIdentCompletion(Self, FIdentCompletionJumpToError, Handled, Abort); if Handled then begin if Abort then exit; // add one entry per item CodeToolBoss.IdentifierList.Prefix:=Prefix; ItemCnt:=CodeToolBoss.IdentifierList.GetFilteredCount; //DebugLn('InitIdentCompletion B Prefix=',Prefix,' ItemCnt=',IntToStr(ItemCnt)); Position:=0; for i:=0 to ItemCnt-1 do s.Add('Dummy'); Result:=true; exit; end; end; end; procedure TSourceEditCompletion.StartShowCodeHelp; begin if SrcEditHintWindow = nil then begin SrcEditHintWindow := TSrcEditHintWindow.Create(Manager); SrcEditHintWindow.Name:='TSourceNotebook_SrcEditHintWindow'; SrcEditHintWindow.Provider:=TFPDocHintProvider.Create(SrcEditHintWindow); end; SrcEditHintWindow.AnchorForm := TheForm; //debugln(['TSourceEditCompletion.StartShowCodeHelp ',CodeToolsOpts.IdentComplShowHelp]); if CodeToolsOpts.IdentComplShowHelp then begin TheForm.LongLineHintType:=sclpNone; SrcEditHintWindow.HelpEnabled:=true; end else begin TheForm.LongLineHintType:=EditorOpts.CompletionLongLineHintType; SrcEditHintWindow.HelpEnabled:=false; end; end; function TSourceEditCompletion.Manager: TSourceEditorManager; begin Result := SourceEditorManager; end; constructor TSourceEditCompletion.Create(AOwner: TComponent); begin inherited; EndOfTokenChr:='()[].,;:-+=^*<>/'; Width:=400; OnExecute := @ccExecute; OnCancel := @ccCancel; OnCodeCompletion := @ccComplete; OnPaintItem:=@OnSynCompletionPaintItem; OnMeasureItem := @OnSynCompletionMeasureItem; OnSearchPosition:=@OnSynCompletionSearchPosition; OnKeyCompletePrefix:=@OnSynCompletionCompletePrefix; OnKeyNextChar:=@OnSynCompletionNextChar; OnKeyPrevChar:=@OnSynCompletionPrevChar; OnKeyPress:=@OnSynCompletionKeyPress; OnUTF8KeyPress:=@OnSynCompletionUTF8KeyPress; OnPositionChanged:=@OnSynCompletionPositionChanged; ShortCut:=Menus.ShortCut(VK_UNKNOWN,[]); TheForm.ShowSizeDrag := True; TheForm.Width := Max(50, EnvironmentOptions.Desktop.CompletionWindowWidth); TheForm.NbLinesInWindow := Max(3, EnvironmentOptions.Desktop.CompletionWindowHeight); TheForm.OnDragResized := @CompletionFormResized; end; { TSourceEditorSharedValues } function TSourceEditorSharedValues.GetSharedEditors(Index: Integer): TSourceEditor; begin Result := TSourceEditor(FSharedEditorList[Index]); end; function TSourceEditorSharedValues.GetOtherSharedEditors(Caller: TSourceEditor; Index: Integer): TSourceEditor; begin if Index >= FSharedEditorList.IndexOf(Caller) then inc(Index); Result := TSourceEditor(FSharedEditorList[Index]); end; function TSourceEditorSharedValues.SynEditor: TIDESynEditor; begin Result := SharedEditors[0].FEditor; end; function TSourceEditorSharedValues.GetSharedEditorsBase(Index: Integer): TSourceEditorBase; begin Result := TSourceEditorBase(FSharedEditorList[Index]); end; procedure TSourceEditorSharedValues.SetCodeBuffer(const AValue: TCodeBuffer); var i: Integer; SrcEdit: TSourceEditor; SharedEdit: TSourceEditor; ETChanges: TETSingleSrcChanges; begin if FCodeBuffer = AValue then exit; if FCodeBuffer<>nil then begin for i := 0 to FSharedEditorList.Count - 1 do begin SharedEdit := SharedEditors[i]; if SharedEdit.FEditPlugin<>nil then SharedEdit.FEditPlugin.Changes:=nil; end; FCodeBuffer.RemoveChangeHook(@OnCodeBufferChanged); if FCodeBuffer.Scanner<>nil then DisconnectScanner(FCodeBuffer.Scanner); if FMainLinkScanner<>nil then begin DisconnectScanner(FMainLinkScanner); FMainLinkScanner:=nil; end; end; for i := 0 to FSharedEditorList.Count - 1 do begin SrcEdit:=SharedEditors[i]; SrcEdit.SourceNotebook.FSrcEditsSortedForFilenames.RemovePointer(SrcEdit); end; FCodeBuffer := AValue; for i := 0 to FSharedEditorList.Count - 1 do begin SrcEdit:=SharedEditors[i]; SrcEdit.SourceNotebook.FSrcEditsSortedForFilenames.Add(SrcEdit); end; if FCodeBuffer <> nil then begin DebugBoss.LockCommandProcessing; try for i := 0 to FSharedEditorList.Count - 1 do begin // HasExecutionMarks is shared through synedit => this is only needed once // but HasExecutionMarks must be called on each synedit, so each synedit is notified SharedEditors[i].ClearExecutionMarks; end; FCodeBuffer.AddChangeHook(@OnCodeBufferChanged); if FCodeBuffer.Scanner<>nil then ConnectScanner(FCodeBuffer.Scanner); ETChanges := SourceEditorManager.FChangesQueuedForMsgWnd.GetChanges( FCodeBuffer.Filename,true); for i := 0 to FSharedEditorList.Count - 1 do begin SharedEdit:=SharedEditors[i]; if assigned(SharedEdit.FEditPlugin) then SharedEdit.FEditPlugin.Changes := ETChanges; end; if MessagesView<>nil then MessagesView.MessagesFrame1.CreateMarksForFile(SynEditor,FCodeBuffer.Filename,true); if (FIgnoreCodeBufferLock <= 0) and (not FCodeBuffer.IsEqual(SynEditor.Lines)) then begin {$IFDEF IDE_DEBUG} debugln(' *** WARNING *** : TSourceEditor.SetCodeBuffer - loosing marks: ',FCodeBuffer.Filename); {$ENDIF} for i := 0 to FSharedEditorList.Count - 1 do begin SharedEdit:=SharedEditors[i]; if assigned(SharedEdit.FEditPlugin) then SharedEdit.FEditPlugin.Enabled := False; end; SynEditor.BeginUpdate; SynEditor.InvalidateAllIfdefNodes; FCodeBuffer.AssignTo(SynEditor.Lines, false); FEditorStampCommitedToCodetools:=(SynEditor.Lines as TSynEditLines).TextChangeStamp; SynEditor.EndUpdate; for i := 0 to FSharedEditorList.Count - 1 do begin SharedEdit:=SharedEditors[i]; if assigned(SharedEdit.FEditPlugin) then SharedEdit.FEditPlugin.Enabled := True; if SharedEdit.Visible then SharedEdit.UpdateIfDefNodeStates(True); end; end; for i := 0 to FSharedEditorList.Count - 1 do begin SharedEdit:=SharedEditors[i]; if SharedEdit.IsActiveOnNoteBook then SharedEdit.SourceNotebook.UpdateStatusBar; // HasExecutionMarks is shared through synedit => this is only needed once // but HasExecutionMarks must be called on each synedit, so each synedit is notified if (DebugBoss.State in [dsPause, dsRun]) and not SharedEdit.HasExecutionMarks and (FCodeBuffer.FileName <> '') then SharedEdit.FillExecutionMarks; end; finally DebugBoss.UnLockCommandProcessing; end; end; end; function TSourceEditorSharedValues.GetModified: Boolean; begin Result := FModified or SynEditor.Modified; end; procedure TSourceEditorSharedValues.SetModified(const AValue: Boolean); var OldModified: Boolean; i: Integer; begin OldModified := Modified; // Include SynEdit FModified := AValue; if not FModified then begin SynEditor.Modified := False; // All shared SynEdits share this value FEditorStampCommitedToCodetools := TSynEditLines(SynEditor.Lines).TextChangeStamp; for i := 0 to FSharedEditorList.Count - 1 do SharedEditors[i].FEditor.MarkTextAsSaved; // Todo: centralize in SynEdit end; if OldModified <> Modified then for i := 0 to FSharedEditorList.Count - 1 do begin SharedEditors[i].UpdatePageName; SharedEditors[i].SourceNotebook.UpdateStatusBar; end; end; procedure TSourceEditorSharedValues.OnCodeBufferChanged(Sender: TSourceLog; SrcLogEntry: TSourceLogEntry); procedure MoveTxt(const StartPos, EndPos, MoveToPos: TPoint; DirectionForward: boolean); var Txt: string; begin if DirectionForward then begin SynEditor.TextBetweenPointsEx[MoveToPos, MoveToPos, scamAdjust] := SynEditor.TextBetweenPoints[StartPos, EndPos]; SynEditor.TextBetweenPointsEx[StartPos, EndPos, scamAdjust] := ''; end else begin Txt := SynEditor.TextBetweenPoints[StartPos, EndPos]; SynEditor.TextBetweenPointsEx[StartPos, EndPos, scamAdjust] := ''; SynEditor.TextBetweenPointsEx[MoveToPos, MoveToPos, scamAdjust] := Txt;; end; end; var StartPos, EndPos, MoveToPos: TPoint; CodeToolsInSync: Boolean; i: Integer; begin {$IFDEF IDE_DEBUG} debugln(['[TSourceEditor.OnCodeBufferChanged] A ',FIgnoreCodeBufferLock,' ',SrcLogEntry<>nil]); {$ENDIF} if FIgnoreCodeBufferLock>0 then exit; DebugBoss.LockCommandProcessing; SynEditor.BeginUpdate; try CodeToolsInSync:=not NeedsUpdateCodeBuffer; if SrcLogEntry<>nil then begin SynEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditorSharedValues.OnCodeBufferChanged'){$ENDIF}; SynEditor.BeginUpdate; SynEditor.TemplateEdit.IncExternalEditLock; SynEditor.SyncroEdit.IncExternalEditLock; try case SrcLogEntry.Operation of sleoInsert: begin Sender.AbsoluteToLineCol(SrcLogEntry.Position,StartPos.Y,StartPos.X); if StartPos.Y>=1 then SynEditor.TextBetweenPointsEx[StartPos, StartPos, scamAdjust] := SrcLogEntry.Txt; end; sleoDelete: begin Sender.AbsoluteToLineCol(SrcLogEntry.Position,StartPos.Y,StartPos.X); Sender.AbsoluteToLineCol(SrcLogEntry.Position+SrcLogEntry.Len, EndPos.Y,EndPos.X); if (StartPos.Y>=1) and (EndPos.Y>=1) then SynEditor.TextBetweenPointsEx[StartPos, EndPos, scamAdjust] := ''; end; sleoMove: begin Sender.AbsoluteToLineCol(SrcLogEntry.Position,StartPos.Y,StartPos.X); Sender.AbsoluteToLineCol(SrcLogEntry.Position+SrcLogEntry.Len, EndPos.Y,EndPos.X); Sender.AbsoluteToLineCol(SrcLogEntry.MoveTo,MoveToPos.Y,MoveToPos.X); if (StartPos.Y>=1) and (EndPos.Y>=1) and (MoveToPos.Y>=1) then MoveTxt(StartPos, EndPos, MoveToPos, SrcLogEntry.Position this is only needed once // but HasExecutionMarks must be called on each synedit, so each synedit is notified for i := 0 to FSharedEditorList.Count - 1 do SharedEditors[i].ClearExecutionMarks; for i := 0 to SharedEditorCount-1 do SharedEditors[i].BeforeCodeBufferReplace; SynEditor.InvalidateAllIfdefNodes; Sender.AssignTo(SynEditor.Lines,false); for i := 0 to SharedEditorCount-1 do SharedEditors[i].AfterCodeBufferReplace; // HasExecutionMarks is shared through synedit => this is only needed once // but HasExecutionMarks must be called on each synedit, so each synedit is notified for i := 0 to FSharedEditorList.Count - 1 do begin SharedEditors[i].FillExecutionMarks; if SharedEditors[i].Visible then SharedEditors[i].UpdateIfDefNodeStates(True); end; end; if CodeToolsInSync then begin // synedit and codetools were in sync -> mark as still in sync FEditorStampCommitedToCodetools:=TSynEditLines(SynEditor.Lines).TextChangeStamp; end; finally SynEditor.EndUpdate; DebugBoss.UnLockCommandProcessing; end; end; procedure TSourceEditorSharedValues.BeginGlobalUpdate; begin inc(FInGlobalUpdate); if FInGlobalUpdate > 1 then exit; SynEditor.BeginUpdate; // locks all shared SynEdits too SynEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditorSharedValues.BeginGlobalUpdate'){$ENDIF}; end; procedure TSourceEditorSharedValues.EndGlobalUpdate; begin dec(FInGlobalUpdate); if FInGlobalUpdate > 0 then exit; SynEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditorSharedValues.EndGlobalUpdate'){$ENDIF}; SynEditor.EndUpdate; end; procedure TSourceEditorSharedValues.AddSharedEditor(AnEditor: TSourceEditor); begin if FSharedEditorList.IndexOf(AnEditor) < 0 then FSharedEditorList.Add(AnEditor); end; procedure TSourceEditorSharedValues.RemoveSharedEditor(AnEditor: TSourceEditor); begin FSharedEditorList.Remove(AnEditor); end; procedure TSourceEditorSharedValues.SetActiveSharedEditor(AnEditor: TSourceEditor); begin if FInGlobalUpdate > 0 then exit; // Move to the front, for UpdateCodetools (get undo-caret from correct synedit) FSharedEditorList.Remove(AnEditor); FSharedEditorList.Insert(0, AnEditor); end; function TSourceEditorSharedValues.SharedEditorCount: Integer; begin Result := FSharedEditorList.Count; end; function TSourceEditorSharedValues.OtherSharedEditorCount: Integer; begin Result := FSharedEditorList.Count - 1; end; function TSourceEditorSharedValues.GetExecutionLine: Integer; begin if (FExecutionMark = nil) or (not FExecutionMark.Visible) then Result := -1 else Result := FExecutionMark.Line; end; procedure TSourceEditorSharedValues.CreateExecutionMark; begin FExecutionMark := TSourceMark.Create(SharedEditors[0], nil); SourceEditorMarks.Add(FExecutionMark); FExecutionMark.LineColorAttrib := ahaExecutionPoint; FExecutionMark.Priority := 1; end; procedure TSourceEditorSharedValues.SetExecutionLine(NewLine: integer); var BrkMark: TSourceMark; CurELine: Integer; begin CurELine := ExecutionLine; if CurELine = NewLine then exit; inc(UpdatingExecutionMark); try if CurELine >= 0 then begin BrkMark := SourceEditorMarks.FindBreakPointMark(SharedEditors[0], CurELine); if BrkMark <> nil then BrkMark.Visible := True; end; if (FExecutionMark = nil) then CreateExecutionMark; FExecutionMark.Visible := NewLine <> -1; if NewLine >= 0 then begin BrkMark := SourceEditorMarks.FindBreakPointMark(SharedEditors[0], NewLine); if BrkMark <> nil then BrkMark.Visible := False; end; FExecutionMark.Line := NewLine; finally dec(UpdatingExecutionMark); end; end; procedure TSourceEditorSharedValues.IncreaseIgnoreCodeBufferLock; begin inc(FIgnoreCodeBufferLock); end; procedure TSourceEditorSharedValues.DecreaseIgnoreCodeBufferLock; begin if FIgnoreCodeBufferLock<=0 then raise Exception.Create('unbalanced calls'); dec(FIgnoreCodeBufferLock); end; function TSourceEditorSharedValues.NeedsUpdateCodeBuffer: boolean; begin Result := TSynEditLines(SharedEditors[0].FEditor.Lines).TextChangeStamp <> FEditorStampCommitedToCodetools; end; procedure TSourceEditorSharedValues.UpdateCodeBuffer; begin if not NeedsUpdateCodeBuffer then exit; {$IFDEF IDE_DEBUG} if FCodeBuffer=nil then begin debugln('*********** Oh, no: UpdateCodeBuffer ************ '); end; {$ENDIF} if FCodeBuffer=nil then exit; //DebugLn(['TSourceEditor.UpdateCodeBuffer ',FCodeBuffer.FileName]); IncreaseIgnoreCodeBufferLock; SynEditor.BeginUpdate(False); try FCodeBuffer.Assign(SynEditor.Lines); FEditorStampCommitedToCodetools:=(SynEditor.Lines as TSynEditLines).TextChangeStamp; finally SynEditor.EndUpdate; DecreaseIgnoreCodeBufferLock; end; end; function TSourceEditorSharedValues.Filename: string; begin Result:=FCodeBuffer.Filename; end; procedure TSourceEditorSharedValues.ConnectScanner(Scanner: TLinkScanner); // If this is an include file, several scanners might use this file // all of them should store directives begin if Scanner=nil then exit; if FLinkScanners.IndexOf(Scanner)>=0 then exit; //debugln(['TSourceEditorSharedValues.ConnectScanner ',Filename,' ',Scanner.MainFilename]); FLinkScanners.Add(Scanner); Scanner.DemandStoreDirectives; end; procedure TSourceEditorSharedValues.DisconnectScanner(Scanner: TLinkScanner); var i: Integer; begin if Scanner=nil then exit; i:=FLinkScanners.IndexOf(Scanner); if i<0 then exit; FLinkScanners.Delete(i); Scanner.ReleaseStoreDirectives; if Scanner=FMainLinkScanner then FMainLinkScanner:=nil; end; function TSourceEditorSharedValues.GetMainLinkScanner(Scan: boolean): TLinkScanner; // Note: if this is an include file, the main scanner may change var SrcEdit: TIDESynEditor; begin Result:=FMainLinkScanner; if Result=nil then begin // create main scanner //debugln(['TSourceEditorSharedValues.GetMainLinkScanner fetching unit codebuffer ...']); if CodeBuffer=nil then begin // file is currently creating //debugln(['TSourceEditorSharedValues.GetMainLinkScanner CodeBuffer=nil']); exit; end; if SharedEditorCount=0 then exit; SrcEdit:=SharedEditors[0].EditorComponent; if SrcEdit=nil then exit; if not (SrcEdit.Highlighter is TSynPasSyn) then begin if Filename<>FLastWarnedMainLinkFilename then begin if FilenameIsPascalSource(Filename) then if ConsoleVerbosity>1 then debugln(['TSourceEditorSharedValues.GetMainLinkScanner not Pascal highlighted: ',Filename,' Highligther=',DbgSName(SrcEdit.Highlighter)]); end; FLastWarnedMainLinkFilename:=Filename; exit; end; if not CodeToolBoss.InitCurCodeTool(CodeBuffer) then begin if Filename<>FLastWarnedMainLinkFilename then debugln(['TSourceEditorSharedValues.GetMainLinkScanner failed to find the unit of ',Filename]); FLastWarnedMainLinkFilename:=Filename; exit; end; Result:=CodeToolBoss.CurCodeTool.Scanner; ConnectScanner(Result); FMainLinkScanner:=Result; end; if Scan and (FMainLinkScanner<>nil) then begin try FMainLinkScanner.Scan(lsrEnd,false); except on E: Exception do begin //CodeToolBoss.HandleException(e); end; end; end; end; constructor TSourceEditorSharedValues.Create; begin FSharedEditorList := TFPList.Create; FExecutionMark := nil; FMarksRequested := False; FInGlobalUpdate := 0; FLinkScanners := TFPList.Create; end; destructor TSourceEditorSharedValues.Destroy; var i: integer; begin SourceEditorMarks.DeleteAllForEditorID(Self); CodeBuffer := nil; FreeAndNil(FSharedEditorList); if FLinkScanners<>nil then begin for i:=0 to FLinkScanners.Count-1 do TLinkScanner(FLinkScanners[i]).ReleaseStoreDirectives; FreeAndNil(FLinkScanners); end; // no need to care about ExecutionMark, it is removed with all other marks, // if the last SynEdit is destroyed (TSynEditMark.Destroy will free the SourceMark) inherited Destroy; end; { TSourceEditor } { The constructor for @link(TSourceEditor). AOwner is the @link(TSourceNotebook) and the AParent is usually a page of a @link(TPageControl) } constructor TSourceEditor.Create(AOwner: TComponent; AParent: TWinControl; ASharedEditor: TSourceEditor = nil); Begin FInEditorChangedUpdating := False; if ASharedEditor = nil then FSharedValues := TSourceEditorSharedValues.Create else FSharedValues := ASharedEditor.FSharedValues; FSharedValues.AddSharedEditor(Self); inherited Create; FAOwner := AOwner; if FAOwner is TSourceNotebook then FSourceNoteBook:=TSourceNotebook(FAOwner) else FSourceNoteBook:=nil; FSyntaxHighlighterType:=lshNone; FErrorLine:=-1; FErrorColumn:=-1; FSyncroLockCount := 0; FLineInfoNotification := TIDELineInfoNotification.Create; FLineInfoNotification.AddReference; FLineInfoNotification.OnChange := @LineInfoNotificationChange; CreateEditor(AOwner,AParent); FProjectFileUpdatesNeeded := []; if ASharedEditor <> nil then begin PageName := ASharedEditor.PageName; FEditor.ShareTextBufferFrom(ASharedEditor.EditorComponent); FEditor.Highlighter := ASharedEditor.EditorComponent.Highlighter; if ASharedEditor.EditorComponent.Beautifier is TSynBeautifierPascal then FEditor.Beautifier := ASharedEditor.EditorComponent.Beautifier; end; FEditPlugin := TETSynPlugin.Create(FEditor); FEditPlugin.OnIsEnabled:=@IsFirstShared; end; destructor TSourceEditor.Destroy; begin DebugLnEnter(SRCED_CLOSE, ['TSourceEditor.Destroy ']); Application.RemoveAsyncCalls(Self); if FInEditorChangedUpdating then begin debugln(['***** TSourceEditor.Destroy: FInEditorChangedUpdating was true']); DebugBoss.UnLockCommandProcessing; FInEditorChangedUpdating := False; end; PopupMenu := nil; if (FAOwner<>nil) and (FEditor<>nil) then begin UnbindEditor; FEditor.Visible:=false; FEditor.Parent:=nil; TSourceNotebook(FAOwner).ReleaseEditor(self, True); // free the synedit control after processing the events EditorComponent.Owner.RemoveComponent(EditorComponent); Application.ReleaseComponent(FEditor); end; FEditor:=nil; if (DebugBoss <> nil) and (DebugBoss.LineInfo <> nil) then DebugBoss.LineInfo.RemoveNotification(FLineInfoNotification); FLineInfoNotification.ReleaseReference; inherited Destroy; FSharedValues.RemoveSharedEditor(Self); if FSharedValues.SharedEditorCount = 0 then begin if FSharedValues.MarksRequested and (FSharedValues.MarksRequestedForFile <> '') then DebugBoss.LineInfo.Cancel(FSharedValues.MarksRequestedForFile); FreeAndNil(FSharedValues); end; DebugLnExit(SRCED_CLOSE, ['TSourceEditor.Destroy ']); end; {------------------------------G O T O L I N E -----------------------------} function TSourceEditor.GotoLine(Value: Integer): Integer; Var P: TPoint; NewTopLine: integer; Begin Manager.AddJumpPointClicked(Self); P.X := 1; P.Y := Value; NewTopLine := P.Y - (FEditor.LinesInWindow div 2); if NewTopLine < 1 then NewTopLine:=1; FEditor.CaretXY := P; FEditor.TopLine := NewTopLine; Result:=FEditor.CaretY; end; procedure TSourceEditor.ShowGotoLineDialog; var NewLeft: integer; NewTop: integer; dlg: TfrmGoto; begin dlg := Manager.GotoDialog; dlg.Edit1.Text:=''; GetDialogPosition(dlg.Width, dlg.Height, NewLeft, NewTop); dlg.SetBounds(NewLeft, NewTop, dlg.Width, dlg.Height); if (dlg.ShowModal = mrOK) then GotoLine(StrToIntDef(dlg.Edit1.Text,1)); Self.FocusEditor; end; procedure TSourceEditor.ShowSmartHintForSourceAtCursor; begin if Assigned(Manager) and Assigned(Manager.OnShowHintForSource) then Manager.OnShowHintForSource(Self,FEditor.LogicalCaretXY, False); end; procedure TSourceEditor.GetDialogPosition(Width, Height: integer; out Left, Top: integer); var P: TPoint; ABounds: TRect; begin with EditorComponent do P := ClientToScreen(Point(CaretXPix, CaretYPix)); ABounds := Screen.MonitorFromPoint(P).WorkareaRect; Left := EditorComponent.ClientOrigin.X + (EditorComponent.Width - Width) div 2; Top := P.Y - Height - 3 * EditorComponent.LineHeight; if Top < ABounds.Top + 10 then Top := P.Y + 2 * EditorComponent.LineHeight; if Top + Height > ABounds.Bottom then Top := (ABounds.Bottom + ABounds.Top - Height) div 2; if Top < ABounds.Top then Top := ABounds.Top; end; procedure TSourceEditor.ActivateHint(ClientRect: TRect; const ABaseURL, AHint: string; AAutoShown: Boolean; AMouseOffset: Boolean); var ScreenRect: TRect; begin if SourceNotebook=nil then exit; ScreenRect.TopLeft:=EditorComponent.ClientToScreen(ClientRect.TopLeft); ScreenRect.BottomRight:=EditorComponent.ClientToScreen(ClientRect.BottomRight); Manager.ActivateHint(ScreenRect,ABaseURL,AHint,AAutoShown,AMouseOffset); end; {------------------------------S T A R T F I N D-----------------------------} procedure TSourceEditor.StartFindAndReplace(Replace:boolean); var NewOptions: TSynSearchOptions; ALeft,ATop:integer; DlgResult: TModalResult; AState: TLazFindReplaceState; begin LazFindReplaceDialog.SaveState(AState); LazFindReplaceDialog.ResetUserHistory; //debugln('TSourceEditor.StartFindAndReplace A LazFindReplaceDialog.FindText="',dbgstr(LazFindReplaceDialog.FindText),'"'); if ReadOnly then Replace := False; NewOptions:=LazFindReplaceDialog.Options; if Replace then NewOptions := NewOptions + [ssoReplace, ssoReplaceAll] else NewOptions := NewOptions - [ssoReplace, ssoReplaceAll]; NewOptions:=NewOptions-InputHistoriesSO.SaveOptions +InputHistoriesSO.FindOptions[EditorComponent.SelAvail]; // Fill in history items LazFindReplaceDialog.TextToFindComboBox.Items.Assign(InputHistoriesSO.FindHistory); LazFindReplaceDialog.ReplaceTextComboBox.Items.Assign(InputHistoriesSO.ReplaceHistory); with EditorComponent do begin if EditorOpts.FindTextAtCursor then begin if SelAvail and (BlockBegin.Y = BlockEnd.Y) and ( ((ComparePoints(BlockBegin, LogicalCaretXY) <= 0) and (ComparePoints(BlockEnd, LogicalCaretXY) >= 0)) or ((ComparePoints(BlockBegin, LogicalCaretXY) >= 0) and (ComparePoints(BlockEnd, LogicalCaretXY) <= 0)) ) then begin //debugln('TSourceEditor.StartFindAndReplace B FindTextAtCursor SelAvail'); LazFindReplaceDialog.FindText := SelText; end else begin //debugln('TSourceEditor.StartFindAndReplace B FindTextAtCursor not SelAvail'); LazFindReplaceDialog.FindText := GetWordAtRowCol(LogicalCaretXY); end; end else begin //debugln('TSourceEditor.StartFindAndReplace B not FindTextAtCursor'); LazFindReplaceDialog.FindText:=''; end; end; LazFindReplaceDialog.EnableAutoComplete:=InputHistoriesSO.FindAutoComplete; // if there is no FindText, use the most recently used FindText if (LazFindReplaceDialog.FindText='') and (InputHistoriesSO.FindHistory.Count > 0) then LazFindReplaceDialog.FindText:=InputHistoriesSO.FindHistory[0]; GetDialogPosition(LazFindReplaceDialog.Width,LazFindReplaceDialog.Height,ALeft,ATop); LazFindReplaceDialog.Left:=ALeft; LazFindReplaceDialog.Top:=ATop; LazFindReplaceDialog.Options := NewOptions; DlgResult:=LazFindReplaceDialog.ShowModal; InputHistoriesSO.FindOptions[EditorComponent.SelAvail]:=LazFindReplaceDialog.Options; InputHistoriesSO.FindAutoComplete:=LazFindReplaceDialog.EnableAutoComplete; if DlgResult = mrCancel then begin LazFindReplaceDialog.RestoreState(AState); exit; end; //debugln('TSourceEditor.StartFindAndReplace B LazFindReplaceDialog.FindText="',dbgstr(LazFindReplaceDialog.FindText),'"'); Replace:=ssoReplace in LazFindReplaceDialog.Options; if Replace then InputHistoriesSO.AddToReplaceHistory(LazFindReplaceDialog.ReplaceText); InputHistoriesSO.AddToFindHistory(LazFindReplaceDialog.FindText); InputHistoriesSO.Save; DoFindAndReplace(LazFindReplaceDialog.FindText, LazFindReplaceDialog.ReplaceText, LazFindReplaceDialog.Options); end; procedure TSourceEditor.AskReplace(Sender: TObject; const ASearch, AReplace: string; Line, Column: integer; out Action: TSrcEditReplaceAction); var SynAction: TSynReplaceAction; begin SynAction:=raCancel; SourceNotebook.BringToFront; OnReplace(Sender, ASearch, AReplace, Line, Column, SynAction); case SynAction of raSkip: Action:=seraSkip; raReplaceAll: Action:=seraReplaceAll; raReplace: Action:=seraReplace; raCancel: Action:=seraCancel; else RaiseGDBException('TSourceEditor.AskReplace: inconsistency'); end; end; {------------------------------F I N D A G A I N ----------------------------} procedure TSourceEditor.FindNextUTF8; begin if snIncrementalFind in FSourceNoteBook.States then begin FSourceNoteBook.IncrementalSearch(True, False); end else if LazFindReplaceDialog.FindText = '' then begin StartFindAndReplace(False) end else begin DoFindAndReplace(LazFindReplaceDialog.FindText, LazFindReplaceDialog.ReplaceText, LazFindReplaceDialog.Options - [ssoEntireScope, ssoSelectedOnly] + [ssoFindContinue]); end; End; {---------------------------F I N D P R E V I O U S ------------------------} procedure TSourceEditor.FindPrevious; var SrchOptions: TSynSearchOptions; begin if snIncrementalFind in FSourceNoteBook.States then begin FSourceNoteBook.IncrementalSearch(True, True); end else if LazFindReplaceDialog.FindText = '' then begin // TODO: maybe start with default set to backwards direction? But StartFindAndReplace replaces it with input-history StartFindAndReplace(False); end else begin SrchOptions:=LazFindReplaceDialog.Options - [ssoEntireScope, ssoSelectedOnly] + [ssoFindContinue]; if ssoBackwards in SrchOptions then SrchOptions := SrchOptions - [ssoBackwards] else SrchOptions := SrchOptions + [ssoBackwards]; DoFindAndReplace(LazFindReplaceDialog.FindText, LazFindReplaceDialog.ReplaceText, SrchOptions); end; end; procedure TSourceEditor.FindNextWordOccurrence(DirectionForward: boolean); var StartX, EndX: Integer; Flags: TSynSearchOptions; LogCaret: TPoint; begin LogCaret:=EditorComponent.LogicalCaretXY; EditorComponent.GetWordBoundsAtRowCol(LogCaret,StartX,EndX); if EndX<=StartX then exit; Flags:=[ssoWholeWord]; if DirectionForward then begin LogCaret.X:=EndX; end else begin LogCaret.X:=StartX; Include(Flags,ssoBackwards); end; EditorComponent.BeginUpdate(False); try EditorComponent.LogicalCaretXY:=LogCaret; EditorComponent.SearchReplace(EditorComponent.GetWordAtRowCol(LogCaret), '',Flags); CenterCursor(True); finally EditorComponent.EndUpdate; end; end; function TSourceEditor.DoFindAndReplace(aFindText, aReplaceText: String; anOptions: TSynSearchOptions): Integer; var AText, ACaption: String; OldEntireScope, Again: Boolean; begin Result:=0; if (ssoReplace in anOptions) and ReadOnly then begin DebugLn(['TSourceEditor.DoFindAndReplace Read only']); exit; end; if SourceNotebook<>nil then Manager.AddJumpPointClicked(Self); OldEntireScope := ssoEntireScope in anOptions; //do not show lisUESearchStringContinueBeg/lisUESearchStringContinueEnd if the caret is in the beginning/end if ssoBackwards in anOptions then Again := ((FEditor.CaretY >= FEditor.Lines.Count) and (FEditor.CaretX > Length(FEditor.LineText)))//caret in the last line and last character else Again := ((FEditor.CaretY = 1) and (FEditor.CaretX = 1));//caret at the top/left repeat try Result:=EditorComponent.SearchReplace(aFindText, aReplaceText, anOptions); except on E: ERegExpr do begin IDEMessageDialog(lisUEErrorInRegularExpression, E.Message,mtError,[mbCancel]); exit; end; end; if (Result = 0) and not (ssoReplaceAll in anOptions) then begin ACaption:=lisUENotFound; AText:=Format(lisUESearchStringNotFound, [Utf8EscapeControlChars(aFindText, emPascal)]); if not (Again or OldEntireScope) then begin if ssoBackwards in anOptions then AText:=AText+' '+lisUESearchStringContinueEnd else AText:=AText+' '+lisUESearchStringContinueBeg; Again:=MessageDlg(ACaption, AText, mtConfirmation, [mbYes,mbNo], 0) = mrYes; anOptions:=anOptions + [ssoEntireScope]; end else begin Again := False; IDEMessageDialog(ACaption, AText, mtInformation, [mbOK]); end; if not Again then Manager.DeleteLastJumpPointClicked(Self); end else begin Again := False; CenterCursor(True); end; until not Again; end; procedure TSourceEditor.OnReplace(Sender: TObject; const ASearch, AReplace: string; Line, Column: integer; var Action: TSynReplaceAction); function Shorten(const s: string): string; const MAX_LEN=300; begin Result:=s; if Length(Result)>MAX_LEN then Result:=LeftStr(Result, MAX_LEN)+'...'; end; var a,x,y:integer; AText:AnsiString; begin if FAOwner<>nil then TSourceNotebook(FAOwner).UpdateStatusBar; CenterCursor(True); CenterCursorHoriz(hcmSoftKeepEOL); AText:=Format(lisUEReplaceThisOccurrenceOfWith,[Shorten(ASearch),LineEnding,Shorten(AReplace)]); GetDialogPosition(FEditor.Scale96ToFont(300), FEditor.Scale96ToFont(150), X, Y); a:=MessageDlgPos(AText,mtconfirmation, [mbYes,mbYesToAll,mbNo,mbCancel],0,X,Y); case a of mrYes:Action:=raReplace; mrNo :Action:=raSkip; mrAll,mrYesToAll:Action:=raReplaceAll; else Action:=raCancel; end; end; //----------------------------------------------------------------------------- procedure TSourceEditor.FocusEditor; Begin DebugLnEnter(SRCED_PAGES, ['>> TSourceEditor.FocusEditor A ',PageName,' ',FEditor.Name]); IDEWindowCreators.ShowForm(SourceNotebook, true, vmOnlyMoveOffScreenToVisible); if FEditor.IsVisible then begin FEditor.SetFocus; // TODO: will cal EditorEnter, which does self.Activate => maybe lock, and do here? FSharedValues.SetActiveSharedEditor(Self); end else begin debugln(SRCED_PAGES, ['TSourceEditor.FocusEditor not IsVisible: ',PageName,' ',FEditor.Name]); end; //DebugLn('TSourceEditor.FocusEditor ',dbgsName(FindOwnerControl(GetFocus)),' ',dbgs(GetFocus)); DebugLnExit(SRCED_PAGES, ['<< TSourceEditor.FocusEditor END ',PageName,' ',FEditor.Name]); end; function TSourceEditor.GetReadOnly: Boolean; Begin Result:=FEditor.ReadOnly; End; procedure TSourceEditor.SetReadOnly(const NewValue: boolean); begin FEditor.ReadOnly:=NewValue; end; function TSourceEditor.Manager: TSourceEditorManager; begin if FSourceNoteBook <> nil then Result := FSourceNoteBook.Manager else Result := nil; end; procedure TSourceEditor.MoveToWindow(AWindowIndex: Integer); begin SourceNotebook.MoveEditor(PageIndex, AWindowIndex, -1) end; function TSourceEditor.GetSharedValues: TSourceEditorSharedValuesBase; begin Result := FSharedValues; end; function TSourceEditor.IsSharedWith(AnOtherEditor: TSourceEditor): Boolean; begin Result := (AnOtherEditor <> nil) and (AnOtherEditor.FSharedValues = FSharedValues); end; procedure TSourceEditor.BeforeCodeBufferReplace; begin FTempTopLine := FEditor.TopLine; FTempCaret := FEditor.CaretXY; end; procedure TSourceEditor.AfterCodeBufferReplace; begin if (FTempTopLine > FEditor.Lines.Count) or(FTempCaret.Y > FEditor.Lines.Count) then exit; FEditor.TopLine := FTempTopLine; FEditor.CaretXY := FTempCaret; end; procedure TSourceEditor.DoMultiCaretBeforeCommand(Sender: TObject; ACommand: TSynEditorCommand; var AnAction: TSynMultiCaretCommandAction; var AFlags: TSynMultiCaretCommandFlags); begin if (FSourceNoteBook<>nil) and (snIncrementalFind in FSourceNoteBook.States) then begin AnAction := ccaClearCarets; end; case ACommand of ecToggleComment: if FEditor.SelAvail then AnAction := ccaAdjustCarets else AnAction := ccaRepeatCommandPerLine; // one per line ecInsertUserName, ecInsertDateTime, ecInsertChangeLogEntry, ecInsertCVSAuthor, ecInsertCVSDate, ecInsertCVSHeader, ecInsertCVSID, ecInsertCVSLog, ecInsertCVSName, ecInsertCVSRevision, ecInsertCVSSource, ecInsertGUID, ecInsertFilename: AnAction := ccaRepeatCommand; end; end; procedure TSourceEditor.ProcessCommand(Sender: TObject; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer); // these are normal commands for synedit (lower than ecUserFirst), // define extra actions here // for non synedit keys (bigger than ecUserFirst) use ProcessUserCommand var AddChar, IsIdent, ok: Boolean; s, AttrName: String; i, WordStart, WordEnd: Integer; p: TPoint; begin //DebugLn('TSourceEditor.ProcessCommand Command=',dbgs(Command)); FSharedValues.SetActiveSharedEditor(Self); AutoStartCompletionBoxTimer.AutoEnabled:=false; if FCodeCompletionState.State in [ccsDot, ccsOnTyping] then FCodeCompletionState.State := ccsReady; if (Command=ecChar) and (AChar=#27) then begin // close hint windows if (CodeContextFrm<>nil) then CodeContextFrm.Hide; if (SrcEditHintWindow<>nil) then SrcEditHintWindow.Hide; end; Manager.HideHint; if (FSourceNoteBook<>nil) and (snIncrementalFind in FSourceNoteBook.States) then begin case Command of ecChar: begin if AChar=#27 then begin if (CodeContextFrm<>nil) then CodeContextFrm.Hide; FSourceNoteBook.IncrementalSearchStr:=''; end else FSourceNoteBook.IncrementalSearchStr:= FSourceNoteBook.IncrementalSearchStr+AChar; Command:=ecNone; end; ecDeleteLastChar: begin i := length(FSourceNoteBook.IncrementalSearchStr); i := UTF8FindNearestCharStart(PChar(FSourceNoteBook.IncrementalSearchStr), i, i-1); FSourceNoteBook.IncrementalSearchStr:= LeftStr(FSourceNoteBook.IncrementalSearchStr, i); Command:=ecNone; end; ecLineBreak: begin FSourceNoteBook.EndIncrementalFind; Command:=ecNone; end; ecPaste: begin s:=Clipboard.AsText; s:=copy(s,1,EditorOpts.RightMargin); FSourceNoteBook.IncrementalSearchStr:= FSourceNoteBook.IncrementalSearchStr+s; Command:=ecNone; end; ecScrollUp, ecScrollDown, ecScrollLeft, ecScrollRight: ; // ignore else FSourceNoteBook.EndIncrementalFind; end; end; case Command of ecSelEditorTop, ecSelEditorBottom, ecEditorTop, ecEditorBottom: begin if (FaOwner<>nil) and (not FEditor.IsInMultiCaretRepeatExecution) then Manager.AddJumpPointClicked(Self); end; ecCopy,ecCut,ecCopyAdd,ecCutAdd: begin if (not FEditor.SelAvail) then begin // nothing selected if EditorOpts.CopyWordAtCursorOnCopyNone then begin FEditor.SelectWord; end; end; end; ecTab: begin AddChar:=true; if AutoCompleteChar(aChar,AddChar,acoTab) then begin // completed end; if not AddChar then Command:=ecNone; end; ecChar: begin AddChar:=true; IsIdent:=FEditor.IsIdentChar(aChar); //debugln(['TSourceEditor.ProcessCommand AChar="',AChar,'" AutoIdentifierCompletion=',dbgs(EditorOpts.AutoIdentifierCompletion),' Interval=',AutoStartCompletionBoxTimer.Interval,' ',Dbgs(FEditor.CaretXY),' ',FEditor.IsIdentChar(aChar)]); if (aChar=' ') and AutoCompleteChar(aChar,AddChar,acoSpace) then begin // completed end else if (not IsIdent) and AutoCompleteChar(aChar,AddChar,acoWordEnd) then begin // completed end else if CodeToolsOpts.IdentComplAutoInvokeOnType and ( IsIdent or (AChar='.') ) and (ActiveEditorMacro = nil) then begin // store caret position to detect caret changes // add the char p := FEditor.LogicalCaretXY; SourceCompletionCaretXY:=p; inc(SourceCompletionCaretXY.x,length(AChar)); AttrName := GetCodeAttributeName(p); ok := (FCodeCompletionState.State <> ccsCancelled) and (FEditor.MultiCaret.CaretsCount = 0) and (AttrName <> SYNS_XML_AttrComment) and (AttrName <> SYNS_XML_AttrDirective) and (AttrName <> SYNS_XML_AttrString); if ok then begin if AChar = '.' then begin ok := CodeToolsOpts.IdentComplAutoStartAfterPoint; end else if (CodeToolsOpts.IdentComplOnTypeMinLength > 1) or CodeToolsOpts.IdentComplOnTypeOnlyWordEnd then begin FEditor.GetWordBoundsAtRowCol(p, WordStart, WordEnd); ok := (p.x <= WordEnd) and // inside word ((not CodeToolsOpts.IdentComplOnTypeOnlyWordEnd) or (p.x = WordEnd)) and // at word end? ((WordEnd-WordStart+1) >= CodeToolsOpts.IdentComplOnTypeMinLength); end; end; if ok then begin if CodeToolsOpts.IdentComplOnTypeUseTimer then begin AutoStartCompletionBoxTimer.AutoEnabled:=true; FCodeCompletionState.State := ccsOnTyping; end else begin FCodeCompletionState.State := ccsOnTypingScheduled; end; end; end else if CodeToolsOpts.IdentComplAutoStartAfterPoint and (AChar='.') and (ActiveEditorMacro = nil) then begin // store caret position to detect caret changes // add the char SourceCompletionCaretXY:=FEditor.LogicalCaretXY; inc(SourceCompletionCaretXY.x,length(AChar)); AutoStartCompletionBoxTimer.AutoEnabled:=true; FCodeCompletionState.State := ccsDot; end; //DebugLn(['TSourceEditor.ProcessCommand ecChar AddChar=',AddChar]); if not AddChar then Command:=ecNone; end; ecLineBreak: begin AddChar:=true; if AutoCompleteChar(aChar,AddChar,acoLineBreak) then ; //DebugLn(['TSourceEditor.ProcessCommand ecLineBreak AddChar=',AddChar,' EditorOpts.AutoBlockCompletion=',EditorOpts.AutoBlockCompletion]); if not AddChar then Command:=ecNone; if EditorOpts.AutoBlockCompletion then AutoCompleteBlock; end; ecPrevBookmark: // Note: book mark commands lower than ecUserFirst must be handled here if Assigned(Manager.OnGotoBookmark) then Manager.OnGotoBookmark(Self, -1, True); ecNextBookmark: if Assigned(Manager.OnGotoBookmark) then Manager.OnGotoBookmark(Self, -1, False); ecGotoMarker0..ecGotoMarker9: if Assigned(Manager.OnGotoBookmark) then Manager.OnGotoBookmark(Self, Command - ecGotoMarker0, False); ecSetMarker0..ecSetMarker9: if Assigned(Manager.OnSetBookmark) then Manager.OnSetBookmark(Self, Command - ecSetMarker0, False); ecToggleMarker0..ecToggleMarker9: if Assigned(Manager.OnSetBookmark) then Manager.OnSetBookmark(Self, Command - ecToggleMarker0, True); ecSelectAll: Manager.AddJumpPointClicked(Self); end; //debugln('TSourceEditor.ProcessCommand B IdentCompletionTimer.AutoEnabled=',dbgs(AutoStartCompletionBoxTimer.AutoEnabled)); end; procedure TSourceEditor.ProcessUserCommand(Sender: TObject; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer); // these are the keys above ecUserFirst // define all extra keys here, that should not be handled by synedit var Handled: boolean; i,x,y: Integer; Begin //debugln('TSourceEditor.ProcessUserCommand A ',dbgs(Command)); FSharedValues.SetActiveSharedEditor(Self); Handled:=true; CheckActiveWindow; case Command of ecMultiPaste: MultiPasteText; ecContextHelp: FindHelpForSourceAtCursor; ecSmartHint: ShowSmartHintForSourceAtCursor; ecIdentCompletion: StartIdentCompletionBox(CodeToolsOpts.IdentComplJumpToError,true); ecShowCodeContext: SourceNotebook.StartShowCodeContext(CodeToolsOpts.IdentComplJumpToError); ecWordCompletion: StartWordCompletionBox; ecFind: StartFindAndReplace(false); ecFindNext: FindNextUTF8; ecFindPrevious: FindPrevious; ecIncrementalFind: if FSourceNoteBook<>nil then FSourceNoteBook.BeginIncrementalFind; ecReplace: StartFindAndReplace(true); ecGotoLineNumber: ShowGotoLineDialog; ecFindNextWordOccurrence: FindNextWordOccurrence(true); ecFindPrevWordOccurrence: FindNextWordOccurrence(false); ecSelectionEnclose: EncloseSelection; ecSelectionUpperCase: UpperCaseSelection; ecSelectionLowerCase: LowerCaseSelection; ecSelectionSwapCase: SwapCaseSelection; ecSelectionTabs2Spaces: TabsToSpacesInSelection; ecSelectionComment: CommentSelection; ecSelectionUnComment: UncommentSelection; ecToggleComment: ToggleCommentSelection; ecSelectionEncloseIFDEF: ConditionalSelection; ecSelectionSort: SortSelection; ecSelectionBreakLines: BreakLinesInSelection; ecInvertAssignment: InvertAssignment; ecSelectToBrace: SelectToBrace; ecSelectLine: SelectLine; ecSelectWord: SelectWord; ecSelectParagraph: SelectParagraph; ecInsertCharacter: InsertCharacterFromMap; ecInsertGPLNotice: InsertGPLNotice(comtDefault,false); ecInsertGPLNoticeTranslated: InsertGPLNotice(comtDefault,true); ecInsertLGPLNotice: InsertLGPLNotice(comtDefault,false); ecInsertLGPLNoticeTranslated:InsertLGPLNotice(comtDefault,true); ecInsertModifiedLGPLNotice: InsertModifiedLGPLNotice(comtDefault,false); ecInsertModifiedLGPLNoticeTranslated: InsertModifiedLGPLNotice(comtDefault,true); ecInsertMITNotice: InsertMITNotice(comtDefault,false); ecInsertMITNoticeTranslated: InsertMITNotice(comtDefault,true); ecInsertUserName: InsertUsername; ecInsertDateTime: InsertDateTime; ecInsertChangeLogEntry: InsertChangeLogEntry; ecInsertCVSAuthor: InsertCVSKeyword('Author'); ecInsertCVSDate: InsertCVSKeyword('Date'); ecInsertCVSHeader: InsertCVSKeyword('Header'); ecInsertCVSID: InsertCVSKeyword('ID'); ecInsertCVSLog: InsertCVSKeyword('Log'); ecInsertCVSName: InsertCVSKeyword('Name'); ecInsertCVSRevision: InsertCVSKeyword('Revision'); ecInsertCVSSource: InsertCVSKeyword('Source'); ecInsertGUID: InsertGUID; ecInsertFilename: InsertFilename; ecLockEditor: IsLocked := not IsLocked; ecSynMacroPlay: begin If ActiveEditorMacro = EditorMacroForRecording then begin if EditorMacroForRecording.State = emRecording then EditorMacroForRecording.Pause else EditorMacroForRecording.Resume; end else if (SelectedEditorMacro <> nil) and (SelectedEditorMacro.State = emStopped) then SelectedEditorMacro.PlaybackMacro(FEditor); end; ecSynMacroRecord: begin If ActiveEditorMacro = nil then EditorMacroForRecording.RecordMacro(FEditor) else If ActiveEditorMacro = EditorMacroForRecording then EditorMacroForRecording.Stop; end; ecClearBookmarkForFile: begin if Assigned(Manager) and Assigned(Manager.OnClearBookmarkId) then for i in TBookmarkNumRange do if EditorComponent.GetBookMark(i,x{%H-},y{%H-}) then Manager.OnClearBookmarkId(Self, i); end; ecCloseOtherTabs: Application.QueueAsyncCall(@Manager.CloseOtherPagesClickedAsync, PtrInt(SourceNotebook.GetNoteBookPage(SourceNotebook.FindPageWithEditor(Self)))); ecCloseRightTabs: Application.QueueAsyncCall(@Manager.CloseRightPagesClickedAsync, PtrInt(SourceNotebook.GetNoteBookPage(SourceNotebook.FindPageWithEditor(Self)))); else begin Handled:=false; if FaOwner<>nil then TSourceNotebook(FaOwner).ProcessParentCommand(self,Command,aChar,Data, Handled); end; end; //case if Handled then Command:=ecNone; end; procedure TSourceEditor.UserCommandProcessed(Sender: TObject; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer); // called after the source editor processed a key var Handled: boolean; begin Handled:=true; if (Command <> ecCompleteCode) and (Command <> ecCompleteCodeInteractive) and (FCodeCompletionState.State = ccsCancelled) then begin if CompareCaret(FCodeCompletionState.LastTokenStartPos, CurrentWordLogStartOrCaret) <> 0 then FCodeCompletionState.State := ccsReady; end; case Command of ecNone: ; ecChar: begin if AutoBlockCompleteChar(AChar) then Handled:=true; if EditorOpts.AutoDisplayFunctionPrototypes then if (aChar = '(') or (aChar = ',') then SourceNotebook.StartShowCodeContext(False); if FCodeCompletionState.State = ccsOnTypingScheduled then begin FCodeCompletionState.State := ccsOnTyping; StartIdentCompletionBox(false,false); end; end; else begin Handled:=false; if FaOwner<>nil then TSourceNotebook(FaOwner).ParentCommandProcessed(Self,Command,aChar,Data, Handled); end; end; if Handled then Command:=ecNone; end; procedure TSourceEditor.EditorStatusChanged(Sender: TObject; Changes: TSynStatusChanges); Begin If Assigned(OnEditorChange) then OnEditorChange(Sender, Changes); UpdatePageName; if Changes * [scCaretX, scCaretY, scSelection] <> [] then IDECommandList.PostponeUpdateEvents; end; function TSourceEditor.SelectionAvailable: boolean; begin Result := EditorComponent.SelAvail; end; function TSourceEditor.GetText(OnlySelection: boolean): string; begin if OnlySelection then Result:=EditorComponent.SelText else Result:=EditorComponent.Lines.Text; end; procedure TSourceEditor.UpperCaseSelection; // Turns current text selection uppercase. begin if ReadOnly then exit; if not EditorComponent.SelAvail then exit; FEditor.SetTextBetweenPoints(FEditor.BlockBegin, FEditor.BlockEnd, UTF8UpperCase(EditorComponent.SelText), [setSelect], scamIgnore, smaKeep, smCurrent ); end; procedure TSourceEditor.LowerCaseSelection; // Turns current text selection lowercase. begin if ReadOnly then exit; if not EditorComponent.SelAvail then exit; FEditor.SetTextBetweenPoints(FEditor.BlockBegin, FEditor.BlockEnd, UTF8LowerCase(EditorComponent.SelText), [setSelect], scamIgnore, smaKeep, smCurrent ); end; procedure TSourceEditor.SwapCaseSelection; begin if ReadOnly then exit; if not EditorComponent.SelAvail then exit; FEditor.SetTextBetweenPoints(FEditor.BlockBegin, FEditor.BlockEnd, UTF8SwapCase(EditorComponent.SelText), [setSelect], scamIgnore, smaKeep, smCurrent ); end; {------------------------------------------------------------------------------- method TSourceEditor.TabsToSpacesInSelection Convert all tabs into spaces in current text selection. -------------------------------------------------------------------------------} procedure TSourceEditor.TabsToSpacesInSelection; begin if ReadOnly then exit; if not EditorComponent.SelAvail then exit; FEditor.SetTextBetweenPoints(FEditor.BlockBegin, FEditor.BlockEnd, TabsToSpaces(EditorComponent.SelText, EditorComponent.TabWidth, FEditor.UseUTF8), [setSelect], scamAdjust, smaKeep, smCurrent ); end; procedure TSourceEditor.CommentSelection; begin UpdateCommentSelection(True, False); end; procedure TSourceEditor.UncommentSelection; begin UpdateCommentSelection(False, False); end; procedure TSourceEditor.ToggleCommentSelection; begin UpdateCommentSelection(False, True); end; procedure TSourceEditor.UpdateCommentSelection(CommentOn, Toggle: Boolean); var OldCaretPos, OldBlockStart, OldBlockEnd: TPoint; WasSelAvail: Boolean; WasSelMode: TSynSelectionMode; BlockBeginLine: Integer; BlockEndLine: Integer; CommonIndent: Integer; function FirstNonBlankPos(const Text: String; Start: Integer = 1): Integer; var i: Integer; begin for i := Start to Length(Text) do if (Text[i] <> #32) and (Text[i] <> #9) then exit(i); Result := -1; end; function MinCommonIndent: Integer; var i, j: Integer; begin If CommonIndent = 0 then begin CommonIndent := Max(FirstNonBlankPos(FEditor.Lines[BlockBeginLine - 1]), 1); for i := BlockBeginLine + 1 to BlockEndLine do begin j := FirstNonBlankPos(FEditor.Lines[i - 1]); if (j < CommonIndent) and (j > 0) then CommonIndent := j; end; end; Result := CommonIndent; end; function InsertPos(ALine: Integer): Integer; begin if not WasSelAvail then Result := MinCommonIndent else case WasSelMode of smColumn: // CommonIndent is not used otherwise begin if CommonIndent = 0 then CommonIndent := Min(FEditor.LogicalToPhysicalPos(OldBlockStart).X, FEditor.LogicalToPhysicalPos(OldBlockEnd).X); Result := FEditor.PhysicalToLogicalPos(Point(CommonIndent, ALine)).X; end; smNormal: begin Result := MinCommonIndent; end; else Result := 1; end; end; function DeletePos(ALine: Integer): Integer; var line: String; begin line := FEditor.Lines[ALine - 1]; Result := FirstNonBlankPos(line, InsertPos(ALine)); if (WasSelMode = smColumn) and((Result < 1) or (Result > length(line) - 1)) then Result := length(line) - 1; Result := Max(1, Result); if (Length(line) < Result +1) or (line[Result] <> '/') or (line[Result+1] <> '/') then Result := -1; end; var i: Integer; NonBlankStart: Integer; begin if ReadOnly then exit; OldCaretPos := FEditor.CaretXY; OldBlockStart := FEditor.BlockBegin; OldBlockEnd := FEditor.BlockEnd; WasSelAvail := FEditor.SelAvail; WasSelMode := FEditor.SelectionMode; CommonIndent := 0; BlockBeginLine := OldBlockStart.Y; BlockEndLine := OldBlockEnd.Y; if (OldBlockEnd.X = 1) and (BlockEndLine > BlockBeginLine) and (FEditor.SelectionMode <> smLine) then Dec(BlockEndLine); if Toggle then begin CommentOn := False; for i := BlockBeginLine to BlockEndLine do if DeletePos(i) < 0 then begin CommentOn := True; break; end; end; BeginUpdate; BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.UpdateCommentSelection'){$ENDIF}; FEditor.SelectionMode := smNormal; if CommentOn then begin for i := BlockEndLine downto BlockBeginLine do FEditor.TextBetweenPoints[Point(InsertPos(i), i), Point(InsertPos(i), i)] := '//'; if OldCaretPos.X > InsertPos(OldCaretPos.Y) then OldCaretPos.x := OldCaretPos.X + 2; if OldBlockStart.X > InsertPos(OldBlockStart.Y) then OldBlockStart.X := OldBlockStart.X + 2; if OldBlockEnd.X > InsertPos(OldBlockEnd.Y) then OldBlockEnd.X := OldBlockEnd.X + 2; end else begin for i := BlockEndLine downto BlockBeginLine do begin NonBlankStart := DeletePos(i); if NonBlankStart < 1 then continue; FEditor.TextBetweenPoints[Point(NonBlankStart, i), Point(NonBlankStart + 2, i)] := ''; if (OldCaretPos.Y = i) and (OldCaretPos.X > NonBlankStart) then OldCaretPos.x := Max(OldCaretPos.X - 2, NonBlankStart); if (OldBlockStart.Y = i) and (OldBlockStart.X > NonBlankStart) then OldBlockStart.X := Max(OldBlockStart.X - 2, NonBlankStart); if (OldBlockEnd.Y = i) and (OldBlockEnd.X > NonBlankStart) then OldBlockEnd.X := Max(OldBlockEnd.X - 2, NonBlankStart); end; end; EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.UpdateCommentSelection'){$ENDIF}; EndUpdate; FEditor.CaretXY := OldCaretPos; FEditor.BlockBegin := OldBlockStart; FEditor.BlockEnd := OldBlockEnd; FEditor.SelectionMode := WasSelMode; end; procedure TSourceEditor.ConditionalSelection; var IsPascal: Boolean; i: Integer; P: TPoint; begin if ReadOnly then exit; FEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.ConditionalSelection'){$ENDIF}; try if not EditorComponent.SelAvail then begin P.Y := FEditor.CaretY; P.X := 1; FEditor.BlockBegin := P; Inc(P.Y); FEditor.BlockEnd := P; end; // ToDo: replace step by step to keep bookmarks and breakpoints IsPascal := True; i:=EditorOpts.HighlighterList.FindByHighlighter(FEditor.Highlighter); if i>=0 then IsPascal := EditorOpts.HighlighterList[i].DefaultCommentType <> comtCPP; // will show modal dialog - must not be in Editor.BeginUpdate block, or painting will not work FEditor.SelText:=EncloseInsideIFDEF(EditorComponent.SelText,IsPascal); finally FEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.ConditionalSelection'){$ENDIF}; end; end; procedure TSourceEditor.SortSelection; var OldSelText, NewSortedText: string; begin if ReadOnly then exit; OldSelText:=EditorComponent.SelText; if OldSelText='' then exit; if ShowSortSelectionDialog(OldSelText,EditorComponent.Highlighter, NewSortedText)=mrOk then EditorComponent.SelText:=NewSortedText; end; procedure TSourceEditor.BreakLinesInSelection; var OldSelection: String; begin if ReadOnly then exit; if not EditorComponent.SelAvail then exit; FEditor.BeginUpdate; FEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.BreakLinesInSelection'){$ENDIF}; // ToDo: replace step by step to keep bookmarks and breakpoints try OldSelection:=EditorComponent.SelText; FEditor.SelText:=BreakLinesInText(OldSelection,FEditor.RightEdge); finally FEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.BreakLinesInSelection'){$ENDIF}; FEditor.EndUpdate; end; end; procedure TSourceEditor.InvertAssignment; begin if ReadOnly then exit; if not EditorComponent.SelAvail then exit; FEditor.BeginUpdate; FEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.InvertAssignment'){$ENDIF}; try // ToDo: replace step by step to keep bookmarks and breakpoints FEditor.SelText := InvertAssignTool.InvertAssignment(FEditor.SelText); finally FEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.InvertAssignment'){$ENDIF}; FEditor.EndUpdate; end; end; procedure TSourceEditor.SelectToBrace; begin EditorComponent.SelectToBrace; end; procedure TSourceEditor.SelectWord; begin EditorComponent.SelectWord; end; procedure TSourceEditor.SelectLine; begin EditorComponent.SelectLine; end; procedure TSourceEditor.SelectParagraph; begin EditorComponent.SelectParagraph; end; function TSourceEditor.CommentText(const Txt: string; CommentType: TCommentType ): string; var i: integer; begin Result:=Txt; case CommentType of comtNone: exit; comtDefault: begin i:=EditorOpts.HighlighterList.FindByHighlighter(FEditor.Highlighter); if i>=0 then CommentType:=EditorOpts.HighlighterList[i].DefaultCommentType; end; end; Result:=LazStringUtils.CommentText(Txt,CommentType); end; procedure TSourceEditor.InsertCharacterFromMap; begin ShowCharacterMap(@SourceNotebook.InsertCharacter); end; procedure TSourceEditor.InsertLicenseNotice(const Notice: string; CommentType: TCommentType); var Txt: string; begin if ReadOnly then Exit; Txt:=CommentText(BreakString(Notice, FEditor.RightEdge-2,0),CommentType); FEditor.InsertTextAtCaret(Txt); end; procedure TSourceEditor.InsertGPLNotice(CommentType: TCommentType; Translated: boolean); var s: String; begin if Translated then s:=lisGPLNotice else s:=EnglishGPLNotice; InsertLicenseNotice(s, CommentType); end; procedure TSourceEditor.InsertLGPLNotice(CommentType: TCommentType; Translated: boolean); var s: String; begin if Translated then s:=lisLGPLNotice else s:=EnglishLGPLNotice; InsertLicenseNotice(s, CommentType); end; procedure TSourceEditor.InsertModifiedLGPLNotice(CommentType: TCommentType; Translated: boolean); var s: String; begin if Translated then s:=lisModifiedLGPLNotice else s:=EnglishModifiedLGPLNotice; InsertLicenseNotice(s, CommentType); end; procedure TSourceEditor.InsertMITNotice(CommentType: TCommentType; Translated: boolean); var s: String; begin if Translated then s:=lisMITNotice else s:=EnglishMITNotice; InsertLicenseNotice(s, CommentType); end; procedure TSourceEditor.InsertUsername; begin if ReadOnly then Exit; FEditor.InsertTextAtCaret(GetCurrentUserName); end; procedure TSourceEditor.InsertDateTime; begin if ReadOnly then Exit; FEditor.InsertTextAtCaret(DateTimeToStr(now)); end; procedure TSourceEditor.InsertChangeLogEntry; var s: string; begin if ReadOnly then Exit; s:=DateToStr(now)+' '+GetCurrentUserName+' '+GetCurrentChangeLog; FEditor.InsertTextAtCaret(s); end; procedure TSourceEditor.InsertCVSKeyword(const AKeyWord: string); begin if ReadOnly then Exit; FEditor.InsertTextAtCaret('$'+AKeyWord+'$'+LineEnding); end; procedure TSourceEditor.InsertGUID; const cGUID = '[''%s'']'; // The format of the GUID used for Interfaces var lGUID: TGUID; begin if ReadOnly then Exit; CreateGUID(lGUID); FEditor.InsertTextAtCaret(Format(cGUID, [GUIDToString(lGUID)])); end; procedure TSourceEditor.InsertFilename; var Dlg: TIDEOpenDialog; begin if ReadOnly then Exit; Dlg:=IDEOpenDialogClass.Create(nil); try InitIDEFileDialog(Dlg); Dlg.Title:=lisSelectFile; if not Dlg.Execute then exit; FEditor.InsertTextAtCaret(Dlg.FileName); finally Dlg.Free; end; end; function TSourceEditor.GetSelEnd: Integer; begin Result:=FEditor.SelEnd; end; function TSourceEditor.GetSelStart: Integer; begin Result:=FEditor.SelStart; end; procedure TSourceEditor.SetSelEnd(const AValue: Integer); begin FEditor.SelEnd:=AValue; end; procedure TSourceEditor.SetSelStart(const AValue: Integer); begin FEditor.SelStart:=AValue; end; function TSourceEditor.GetSelection: string; begin Result:=FEditor.SelText; end; procedure TSourceEditor.SetSelection(const AValue: string); begin FEditor.SelText:=AValue; end; procedure TSourceEditor.CopyToClipboard; begin FEditor.CopyToClipboard; end; procedure TSourceEditor.CutToClipboard; begin FEditor.CutToClipboard; end; function TSourceEditor.GetBookMark(BookMark: Integer; out X, Y: Integer): Boolean; begin X := 0; Y := 0; Result := FEditor.GetBookMark(BookMark, X, Y); end; procedure TSourceEditor.SetBookMark(BookMark: Integer; X, Y: Integer); begin FEditor.SetBookMark(BookMark, X, Y); end; procedure TSourceEditor.ExportAsHtml(AFileName: String); var Html: TSynExporterHTML; begin Html := TSynExporterHTML.Create(nil); try Html.Clear; Html.ExportAsText := True; Html.Highlighter := FEditor.Highlighter; Html.Title := PageName; Html.ExportAll(FEditor.Lines); Html.SaveToFile(AFileName); finally Html.Free; end; end; procedure TSourceEditor.FindHelpForSourceAtCursor; begin //DebugLn('TSourceEditor.FindHelpForSourceAtCursor A'); ShowHelpOrErrorForSourcePosition(Filename,FEditor.LogicalCaretXY); end; procedure TSourceEditor.OnGutterClick(Sender: TObject; X, Y, Line: integer; Mark: TSynEditMark); var Marks: PSourceMark; i, MarkCount: Integer; BreakFound: Boolean; Ctrl: Boolean; ABrkPoint: TIDEBreakPoint; Mrk: TSourceMark; begin // create or delete breakpoint // find breakpoint Mark at line Marks := nil; Ctrl := SYNEDIT_LINK_MODIFIER in GetKeyShiftState; try SourceEditorMarks.GetMarksForLine(Self, Line, Marks, MarkCount); BreakFound := False; for i := 0 to MarkCount - 1 do begin Mrk := Marks[i]; if Mrk.IsBreakPoint and (Mrk.Data <> nil) and (Mrk.Data is TIDEBreakPoint) then begin BreakFound := True; if Ctrl then TIDEBreakPoint(Mrk.Data).Enabled := not TIDEBreakPoint(Mrk.Data).Enabled else DebugBoss.DoDeleteBreakPointAtMark(Mrk) end; end; finally FreeMem(Marks); end; if not BreakFound then begin DebugBoss.LockCommandProcessing; try DebugBoss.DoCreateBreakPoint(Filename, Line, True, ABrkPoint, True); if Ctrl and (ABrkPoint <> nil) then ABrkPoint.Enabled := False; finally if ABrkPoint <> nil then ABrkPoint.EndUpdate; DebugBoss.UnLockCommandProcessing; end; end; end; procedure TSourceEditor.OnEditorSpecialLineColor(Sender: TObject; Line: integer; var Special: boolean; Markup: TSynSelectedColor); var i:integer; aha: TAdditionalHilightAttribute; CurMarks: PSourceMark; CurMarkCount: integer; CurFG: TColor; CurBG: TColor; begin aha := ahaNone; Special := False; if ErrorLine = Line then begin aha := ahaErrorLine end else begin SourceEditorMarks.GetMarksForLine(Self, Line, CurMarks, CurMarkCount); if CurMarkCount > 0 then begin for i := 0 to CurMarkCount - 1 do begin if not CurMarks[i].Visible then Continue; // check highlight attribute aha := CurMarks[i].LineColorAttrib; if aha <> ahaNone then Break; // check custom colors CurFG := CurMarks[i].LineColorForeGround; CurBG := CurMarks[i].LineColorBackGround; if (CurFG <> clNone) or (CurBG <> clNone) then begin Markup.Foreground := CurFG; Markup.Background := CurBG; Special := True; break; end; end; // clean up FreeMem(CurMarks); end; end; if aha <> ahaNone then begin Special := True; EditorOpts.SetMarkupColor(TCustomSynEdit(Sender).Highlighter, aha, Markup); end; end; procedure TSourceEditor.SetSyntaxHighlighterType(AHighlighterType: TLazSyntaxHighlighter); var HlIsPas, OldHlIsPas: Boolean; begin if (AHighlighterType=fSyntaxHighlighterType) and ((FEditor.Highlighter<>nil) = EditorOpts.UseSyntaxHighlight) then exit; OldHlIsPas := FEditor.Highlighter is TSynPasSyn; HlIsPas := False; if EditorOpts.UseSyntaxHighlight then begin if Highlighters[AHighlighterType]=nil then Highlighters[AHighlighterType]:=EditorOpts.CreateSyn(AHighlighterType); FEditor.Highlighter:=Highlighters[AHighlighterType]; HlIsPas := FEditor.Highlighter is TSynPasSyn; end else FEditor.Highlighter:=nil; if (OldHlIsPas <> HlIsPas) then begin if HlIsPas then FEditor.Beautifier := PasBeautifier else FEditor.Beautifier := nil; // use default EditorOpts.GetSynEditSettings(FEditor, nil); end; FSyntaxHighlighterType:=AHighlighterType; SourceNotebook.UpdateActiveEditColors(FEditor); end; procedure TSourceEditor.SetErrorLine(NewLine: integer); begin if fErrorLine=NewLine then exit; fErrorLine:=NewLine; fErrorColumn:=EditorComponent.CaretX; EditorComponent.Invalidate; end; procedure TSourceEditor.UpdateExecutionSourceMark; var BreakPoint: TIDEBreakPoint; ExecutionMark: TSourceMark; BrkMark: TSourceMark; begin if FSharedValues.UpdatingExecutionMark > 0 then exit; ExecutionMark := FSharedValues.ExecutionMark; if ExecutionMark = nil then exit; inc(FSharedValues.UpdatingExecutionMark); try if ExecutionMark.Visible then begin BrkMark := SourceEditorMarks.FindBreakPointMark(Self, ExecutionLine); if BrkMark <> nil then begin BrkMark.Visible := False; BreakPoint := DebugBoss.BreakPoints.Find(Self.FileName, ExecutionLine); if (BreakPoint <> nil) and (not BreakPoint.Enabled) then ExecutionMark.ImageIndex := SourceEditorMarks.CurrentLineDisabledBreakPointImg else ExecutionMark.ImageIndex := SourceEditorMarks.CurrentLineBreakPointImg; end else ExecutionMark.ImageIndex := SourceEditorMarks.CurrentLineImg; end; finally dec(FSharedValues.UpdatingExecutionMark); end; end; procedure TSourceEditor.SetExecutionLine(NewLine: integer); begin if ExecutionLine=NewLine then exit; FSharedValues.SetExecutionLine(NewLine); UpdateExecutionSourceMark; end; function TSourceEditor.RefreshEditorSettings: Boolean; var SimilarEditor: TSynEdit; Begin Result:=true; SetSyntaxHighlighterType(fSyntaxHighlighterType); // try to copy settings from an editor to the left SimilarEditor:=nil; if (SourceNotebook.EditorCount>0) and (SourceNotebook.Editors[0]<>Self) then SimilarEditor:=SourceNotebook.Editors[0].EditorComponent; EditorOpts.GetSynEditSettings(FEditor,SimilarEditor); SourceNotebook.UpdateActiveEditColors(FEditor); if Visible then UpdateIfDefNodeStates(True); end; function TSourceEditor.AutoCompleteChar(Char: TUTF8Char; var AddChar: boolean; Category: TAutoCompleteOption): boolean; // returns true if handled var AToken: String; i, x1, x2: Integer; p: TPoint; Line: String; CatName: String; SrcToken: String; IdChars: TSynIdentChars; WordToken: String; begin Result:=false; Line:=GetLineText; p:=GetCursorTextXY; if (p.x>length(Line)+1) or (Line='') then exit; CatName:=AutoCompleteOptionNames[Category]; FEditor.GetWordBoundsAtRowCol(p, x1, x2); // use the token left of the caret x2 := Min(x2, p.x); WordToken := copy(Line, x1, x2-x1); IdChars := FEditor.IdentChars; for i:=0 to Manager.CodeTemplateModul.Completions.Count-1 do begin AToken:=Manager.CodeTemplateModul.Completions[i]; if AToken='' then continue; if AToken[1] in IdChars then SrcToken:=WordToken else SrcToken:=copy(Line,length(Line)-length(AToken)+1,length(AToken)); //DebugLn(['TSourceEditor.AutoCompleteChar ',AToken,' SrcToken=',SrcToken,' CatName=',CatName,' Index=',Manager.CodeTemplateModul.CompletionAttributes[i].IndexOfName(CatName)]); if (UTF8CompareLatinTextFast(AToken,SrcToken)=0) and (Manager.CodeTemplateModul.CompletionAttributes[i].IndexOfName(CatName)>=0) and ( (not FEditor.SelAvail) or (Manager.CodeTemplateModul.CompletionAttributes[i].IndexOfName( AutoCompleteOptionNames[acoIgnoreForSelection]) < 0) ) then begin Result:=true; //DebugLn(['TSourceEditor.AutoCompleteChar ',AToken,' SrcToken=',SrcToken,' CatName=',CatName,' Index=',Manager.CodeTemplateModul.CompletionAttributes[i].IndexOfName(CatName)]); Manager.CodeTemplateModul.ExecuteCompletion(AToken,FEditor); AddChar:=not Manager.CodeTemplateModul.CompletionAttributes[i].IndexOfName( AutoCompleteOptionNames[acoRemoveChar])>=0; exit; end; end; if EditorOpts.AutoBlockCompletion and (SyntaxHighlighterType in [lshFreePascal,lshDelphi]) then Result:=AutoBlockCompleteChar(Char,AddChar,Category,p,Line); end; function TSourceEditor.AutoBlockCompleteChar(Char: TUTF8Char; var AddChar: boolean; Category: TAutoCompleteOption; aTextPos: TPoint; Line: string): boolean; // returns true if handled var x1: integer; x2: integer; WordToken: String; p: LongInt; StartPos: integer; s: String; begin Result:=false; if (not EditorOpts.AutoBlockCompletion) or (not (SyntaxHighlighterType in [lshFreePascal,lshDelphi])) then exit; FEditor.GetWordBoundsAtRowCol(aTextPos, x1, x2); // use the token left of the caret x2 := Min(x2, aTextPos.x); WordToken := copy(Line, x1, x2-x1); if (Category in [acoSpace]) and ((SysUtils.CompareText(WordToken,'if')=0) or (SysUtils.CompareText(WordToken,'while')=0) or (SysUtils.CompareText(WordToken,'for')=0) ) then begin p:=x2; ReadRawNextPascalAtom(Line,p,StartPos); if SysUtils.CompareText(copy(Line,StartPos,p-StartPos),'begin')=0 then begin // 'if begin' => insert 'then' // 'while begin' => insert 'do' // 'for begin' => insert 'do' Result:=true; if (SysUtils.CompareText(WordToken,'if')=0) then s:='then' else s:='do'; s:=' '+CodeToolBoss.SourceChangeCache.BeautifyCodeOptions.BeautifyKeyWord(s); if not (Line[x2] in [' ',#9]) then s:=s+' '; FEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.AutoBlockCompleteChar'){$ENDIF}; try FEditor.InsertTextAtCaret(s); FEditor.LogicalCaretXY:=aTextPos; finally FEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.AutoBlockCompleteChar'){$ENDIF}; end; end; end; end; function TSourceEditor.AutoBlockCompleteChar(Char: TUTF8Char): boolean; var p: TPoint; x1: integer; x2: integer; Line: String; WordToken: String; begin Result:=false; if (not EditorOpts.AutoBlockCompletion) or (not (SyntaxHighlighterType in [lshFreePascal,lshDelphi])) then exit; p:=GetCursorTextXY; FEditor.GetWordBoundsAtRowCol(p, x1, x2); Line:=GetLineText; WordToken := copy(Line, x1, x2-x1); if (SysUtils.CompareText(WordToken,'begin')=0) then begin debugln(['TSourceEditor.AutoBlockCompleteChar ']); // user typed 'begin' LazarusIDE.SaveSourceEditorChangesToCodeCache(self); FEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.AutoBlockCompleteChar (2)'){$ENDIF}; FEditor.BeginUpdate; try if not CodeToolBoss.CompleteBlock(CodeBuffer,p.X,p.Y,true) then exit; finally FEditor.EndUpdate; FEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.AutoBlockCompleteChar (2)'){$ENDIF}; end; end; end; procedure TSourceEditor.AutoCompleteBlock; var XY: TPoint; NewCode: TCodeBuffer; NewX, NewY, NewTopLine: integer; begin LazarusIDE.SaveSourceEditorChangesToCodeCache(Self); XY:=FEditor.LogicalCaretXY; FEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.AutoCompleteBlock'){$ENDIF}; FEditor.BeginUpdate; try if not CodeToolBoss.CompleteBlock(CodeBuffer,XY.X,XY.Y,false, NewCode,NewX,NewY,NewTopLine) then exit; XY:=FEditor.LogicalCaretXY; //DebugLn(['TSourceEditor.AutoCompleteBlock XY=',dbgs(XY),' NewX=',NewX,' NewY=',NewY]); if (NewCode<>CodeBuffer) or (NewX<>XY.X) or (NewY<>XY.Y) or (NewTopLine>0) then begin XY.X:=NewX; XY.Y:=NewY; FEditor.LogicalCaretXY:=XY; end; finally FEditor.EndUpdate; FEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.AutoCompleteBlock'){$ENDIF}; end; end; procedure TSourceEditor.UpdateNoteBook(const ANewNoteBook: TSourceNotebook; ANewPage: TTabSheet); begin if FSourceNoteBook = ANewNoteBook then exit; FSourceNoteBook := ANewNoteBook; FAOwner := ANewNoteBook; FPageName := ANewNoteBook.NoteBookPages[ANewNoteBook.NoteBookIndexOfPage(ANewPage)]; EditorComponent.Parent := nil; // Change the Owner of the SynEdit EditorComponent.Owner.RemoveComponent(EditorComponent); FSourceNoteBook.InsertComponent(EditorComponent); // And the Parent EditorComponent.Parent := ANewPage; end; { AOwner is the TSourceNotebook AParent is a page of the TPageControl } procedure TSourceEditor.CreateEditor(AOwner: TComponent; AParent: TWinControl); var NewName: string; i: integer; bmp: TCustomBitmap; Begin {$IFDEF IDE_DEBUG} debugln('TSourceEditor.CreateEditor A '); {$ENDIF} if not assigned(FEditor) then Begin FVisible := False; i:=0; repeat inc(i); NewName:='SynEdit'+IntToStr(i); until (AOwner.FindComponent(NewName)=nil); FEditor := TIDESynEditor.Create(AOwner); FEditor.BeginUpdate; with FEditor do begin Name:=NewName; Text:=''; Align := alClient; Visible := False; BookMarkOptions.EnableKeys := false; BookMarkOptions.LeftMargin:=1; BookMarkOptions.BookmarkImages := SourceEditorMarks.ImgList; Gutter.MarksPart.DebugMarksImageIndex := SourceEditorMarks.SourceLineImg; WantTabs := true; ScrollBars := ssAutoBoth; // IMPORTANT: when you change below, don't forget updating UnbindEditor OnStatusChange := @EditorStatusChanged; OnProcessCommand := @ProcessCommand; OnProcessUserCommand := @ProcessUserCommand; OnCommandProcessed := @UserCommandProcessed; OnReplaceText := @OnReplace; OnGutterClick := @Self.OnGutterClick; OnSpecialLineMarkup := @OnEditorSpecialLineColor; OnMouseMove := @EditorMouseMoved; OnMouseWheel := @EditorMouseWheel; OnMouseDown := @EditorMouseDown; OnMouseUp := @EditorMouseUp; OnClickLink := Manager.OnClickLink; OnMouseLink := Manager.OnMouseLink; OnKeyDown := @EditorKeyDown; OnKeyUp := @EditorKeyUp; OnPaste:=@EditorPaste; OnEnter:=@EditorEnter; OnPlaceBookmark := @EditorPlaceBookmark; OnClearBookmark := @EditorClearBookmark; OnChangeUpdating := @EditorChangeUpdating; OnMultiCaretBeforeCommand := @DoMultiCaretBeforeCommand; RegisterMouseActionExecHandler(@EditorHandleMouseAction); // IMPORTANT: when you change above, don't forget updating UnbindEditor Parent := AParent; if AParent.Font.PixelsPerInch<>96 then AutoAdjustLayout(lapAutoAdjustForDPI, 96, AParent.Font.PixelsPerInch, 0, 0); end; Manager.CodeTemplateModul.AddEditor(FEditor); Manager.FMacroRecorder.AddEditor(FEditor); Manager.NewEditorCreated(self); FEditor.TemplateEdit.OnActivate := @EditorActivateSyncro; FEditor.TemplateEdit.OnDeactivate := @EditorDeactivateSyncro; bmp := CreateBitmapFromResourceName(HInstance, 'tsynsyncroedit'); FEditor.SyncroEdit.GutterGlyph.Assign(bmp); bmp.Free; FEditor.SyncroEdit.OnBeginEdit := @EditorActivateSyncro; FEditor.SyncroEdit.OnEndEdit := @EditorDeactivateSyncro; RefreshEditorSettings; FEditor.EndUpdate; end else begin FEditor.Parent:=AParent; end; end; procedure TSourceEditor.SetCodeBuffer(NewCodeBuffer: TCodeBuffer); begin FSharedValues.CodeBuffer := NewCodeBuffer; end; procedure TSourceEditor.StartIdentCompletionBox(JumpToError, CanAutoComplete: boolean); var I: Integer; TextS, TextS2: String; LogCaret: TPoint; UseWordCompletion: Boolean; Completion: TSourceEditCompletion; CompletionRect: TRect; begin {$IFDEF VerboseIDECompletionBox} debugln(['TSourceEditor.StartIdentCompletionBox JumpToError: ',JumpToError]); {$ENDIF} if (FEditor.ReadOnly) then exit; Completion := Manager.DefaultCompletionForm; if (Completion.CurrentCompletionType<>ctNone) then exit; Completion.IdentCompletionJumpToError := JumpToError; Completion.CurrentCompletionType:=ctIdentCompletion; TextS := FEditor.LineText; LogCaret:=FEditor.LogicalCaretXY; Completion.Editor:=FEditor; i := LogCaret.X - 1; if i > length(TextS) then TextS2 := '' else begin while (i > 0) and (TextS[i] in ['a'..'z','A'..'Z','0'..'9','_']) do dec(i); TextS2 := Trim(copy(TextS, i + 1, LogCaret.X - i - 1)); end; UseWordCompletion:=false; CompletionRect := Manager.GetScreenRectForToken(FEditor, FEditor.CaretX-length(TextS2), FEditor.CaretY, FEditor.CaretX-1); if not Manager.FindIdentCompletionPlugin (Self, JumpToError, TextS2, CompletionRect.Top, CompletionRect.Left, UseWordCompletion) then exit; if UseWordCompletion then Completion.CurrentCompletionType:=ctWordCompletion; Completion.AutoUseSingleIdent := CanAutoComplete and (FCodeCompletionState.State = ccsDot) and CodeToolsOpts.IdentComplAutoUseSingleIdent; Completion.Execute(TextS2, CompletionRect); {$IFDEF VerboseIDECompletionBox} debugln(['TSourceEditor.StartIdentCompletionBox END Completion.TheForm.Visible=',Completion.TheForm.Visible]); {$ENDIF} end; procedure TSourceEditor.StartWordCompletionBox; var TextS: String; LogCaret: TPoint; i: Integer; TextS2: String; Completion: TSourceEditCompletion; begin if (FEditor.ReadOnly) then exit; Completion := Manager.DefaultCompletionForm; if (Completion.CurrentCompletionType<>ctNone) then exit; Completion.CurrentCompletionType:=ctWordCompletion; TextS := FEditor.LineText; LogCaret:=FEditor.LogicalCaretXY; Completion.Editor:=FEditor; i := LogCaret.X - 1; if i > length(TextS) then TextS2 := '' else begin while (i > 0) and (TextS[i] in ['a'..'z','A'..'Z','0'..'9','_']) do dec(i); TextS2 := Trim(copy(TextS, i + 1, LogCaret.X - i - 1)); end; Completion.Execute (TextS2, Manager.GetScreenRectForToken(FEditor, FEditor.CaretX-length(TextS2), FEditor.CaretY, FEditor.CaretX-1)); end; procedure TSourceEditor.IncreaseIgnoreCodeBufferLock; begin FSharedValues.IncreaseIgnoreCodeBufferLock; end; procedure TSourceEditor.DecreaseIgnoreCodeBufferLock; begin FSharedValues.DecreaseIgnoreCodeBufferLock; end; procedure TSourceEditor.UpdateCodeBuffer; // copy the source from EditorComponent to codetools begin FSharedValues.UpdateCodeBuffer; end; function TSourceEditor.NeedsUpdateCodeBuffer: boolean; begin Result := FSharedValues.NeedsUpdateCodeBuffer; end; procedure TSourceEditor.ConnectScanner(Scanner: TLinkScanner); begin FSharedValues.ConnectScanner(Scanner); end; function TSourceEditor.GetSource: TStrings; Begin //return synedit's source. Result := FEditor.Lines; end; procedure TSourceEditor.SetIsLocked(const AValue: Boolean); begin if FIsLocked = AValue then exit; FIsLocked := AValue; UpdatePageName; SourceNotebook.UpdateStatusBar; UpdateProjectFile; end; procedure TSourceEditor.SetPageName(const AValue: string); begin if FPageName=AValue then exit; FPageName:=AValue; UpdatePageName; end; procedure TSourceEditor.UpdatePageName; var p: Integer; NewPageCaption: String; begin if SourceNotebook.FUpdateLock > 0 then begin include(SourceNotebook.FUpdateFlags, ufPageNames); exit; end; p:=SourceNotebook.FindPageWithEditor(Self); if EditorOpts.ShowTabNumbers and (p < 10) then // Number pages 1, ..., 9, 0 -- according to Alt+N hotkeys. NewPageCaption:=Format('%s:%d', [FPageName, (p+1) mod 10]) else NewPageCaption:=FPageName; if IsLocked then NewPageCaption:='#'+NewPageCaption; if Modified then NewPageCaption:='*'+NewPageCaption; if SourceNotebook.NoteBookPages[p] <> NewPageCaption then begin SourceNotebook.NoteBookPages[p] := NewPageCaption; SourceNotebook.UpdateTabsAndPageTitle; SourceNotebook.CallOnEditorPageCaptionUpdate(Self); end; end; procedure TSourceEditor.SetSource(Value: TStrings); Begin FEditor.Lines.Assign(Value); end; function TSourceEditor.GetCurrentCursorXLine: Integer; Begin Result := FEditor.CaretX end; procedure TSourceEditor.SetCurrentCursorXLine(num: Integer); Begin FEditor.CaretX := Num; end; function TSourceEditor.GetCurrentCursorYLine: Integer; Begin Result := FEditor.CaretY; end; procedure TSourceEditor.SetCurrentCursorYLine(num: Integer); Begin FEditor.CaretY := Num; end; procedure TSourceEditor.SelectText(const StartPos, EndPos: TPoint); Begin FEditor.BlockBegin := StartPos; FEditor.BlockEnd := EndPos; end; procedure TSourceEditor.InsertLine(StartLine: Integer; const NewText: String; aKeepMarks: Boolean); const MarksMode: array[Boolean] of TSynMarksAdjustMode = (smaMoveUp, smaKeep); var Pt: TPoint; begin if not ReadOnly then begin if StartLine > 1 then Pt := Point(Length(FEditor.Lines[StartLine - 2]) + 1, StartLine - 1) else Pt := Point(1, 1); FEditor.SetTextBetweenPoints(Pt, Pt, LineEnding + NewText, [], scamEnd, MarksMode[aKeepMarks]); end; end; procedure TSourceEditor.ReplaceLines(StartLine, EndLine: integer; const NewText: string; aKeepMarks: Boolean = False); const MarksMode: array[Boolean] of TSynMarksAdjustMode = (smaMoveUp, smaKeep); begin if not ReadOnly then FEditor.SetTextBetweenPoints( Point(1, StartLine), Point(Length(FEditor.Lines[Endline - 1]) + 1, EndLine), NewText, [], scamEnd, MarksMode[aKeepMarks]); end; procedure TSourceEditor.EncloseSelection; var EncloseType: TEncloseSelectionType; EncloseTemplate: string; NewSelection: string; NewCaretXY: TPoint; begin if ReadOnly then exit; if not FEditor.SelAvail then exit; if ShowEncloseSelectionDialog(EncloseType)<>mrOk then exit; GetEncloseSelectionParams(EncloseType,EncloseTemplate); EncloseTextSelection(EncloseTemplate,FEditor.Lines, FEditor.BlockBegin,FEditor.BlockEnd, NewSelection,NewCaretXY); //debugln(['TSourceEditor.EncloseSelection A NewCaretXY=',NewCaretXY.X,',',NewCaretXY.Y,' "',NewSelection,'"']); FEditor.SelText:=NewSelection; FEditor.LogicalCaretXY:=NewCaretXY; end; function TSourceEditor.GetModified: Boolean; Begin Result := FSharedValues.Modified; end; procedure TSourceEditor.SetModified(const NewValue: Boolean); begin FSharedValues.SetModified(NewValue); end; function TSourceEditor.GetInsertMode: Boolean; Begin Result := FEditor.Insertmode; end; function TSourceEditor.Close: Boolean; Begin DebugLnEnter(SRCED_CLOSE, ['TSourceEditor.Close ShareCount=', FSharedValues.SharedEditorCount]); Result := True; Visible := False; Manager.EditorRemoved(Self); UnbindEditor; FEditor.Parent:=nil; if FSharedValues.SharedEditorCount = 1 then CodeBuffer := nil; DebugLnExit(SRCED_CLOSE, ['TSourceEditor.Close ']); end; procedure TSourceEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF}; begin FEditor.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.BeginUndoBlock ' + ACaller){$ENDIF}; end; procedure TSourceEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}(ACaller: String = ''){$ENDIF}; begin FEditor.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('TSourceEditor.EndUndoBlock ' + ACaller){$ENDIF}; end; procedure TSourceEditor.BeginUpdate; begin FEditor.BeginUpdate; end; procedure TSourceEditor.EndUpdate; begin FEditor.EndUpdate; end; procedure TSourceEditor.BeginGlobalUpdate; begin FSharedValues.BeginGlobalUpdate; end; procedure TSourceEditor.EndGlobalUpdate; begin FSharedValues.EndGlobalUpdate; end; procedure TSourceEditor.SetPopupMenu(NewPopupMenu: TPopupMenu); begin if NewPopupMenu<>FPopupMenu then begin FPopupMenu:=NewPopupMenu; if FEditor<>nil then begin if FEditor.PopupMenu <> nil then // Todo: why? FEditor.PopupMenu.RemoveFreeNotification(FEditor); FEditor.PopupMenu:=NewPopupMenu; end; end; end; function TSourceEditor.GetFilename: string; begin if CodeBuffer <> nil then Result := CodeBuffer.Filename else Result := ''; end; function TSourceEditor.GetEditorControl: TWinControl; begin Result:=FEditor; end; function TSourceEditor.GetCodeToolsBuffer: TObject; begin Result:=CodeBuffer; end; procedure TSourceEditor.EditorPaste(Sender: TObject; var AText: String; var AMode: TSynSelectionMode; ALogStartPos: TPoint; var AnAction: TSynCopyPasteAction); var p: integer; NestedComments: Boolean; NewIndent: TFABIndentationPolicy; Indent: LongInt; NewSrc: string; i: Integer; SemMode: TSemSelectionMode; SemAction: TSemCopyPasteAction; begin if Assigned(Manager) then begin // call handlers i:=Manager.FHandlers[semhtCopyPaste].Count; while Manager.FHandlers[semhtCopyPaste].NextDownIndex(i) do begin SemMode:=TSemSelectionMode(AMode); SemAction:=TSemCopyPasteAction(AnAction); TSemCopyPasteEvent(Manager.FHandlers[semhtCopyPaste][i])(Self,AText, SemMode,ALogStartPos,SemAction); AMode:=TSynSelectionMode(SemMode); AnAction:=TSynCopyPasteAction(SemAction); if AnAction=scaAbort then exit; end; end; if AMode<>smNormal then exit; if SyncroLockCount > 0 then exit; if not CodeToolsOpts.IndentOnPaste then exit; if not (SyntaxHighlighterType in [lshFreePascal, lshDelphi]) then exit; {$IFDEF VerboseIndenter} debugln(['TSourceEditor.EditorPaste LogCaret=',dbgs(ALogStartPos)]); {$ENDIF} if ALogStartPos.X>1 then exit; UpdateCodeBuffer; CodeBuffer.LineColToPosition(ALogStartPos.Y,ALogStartPos.X,p); if p<1 then exit; {$IFDEF VerboseIndenter} if ALogStartPos.Y>1 then DebugLn(['TSourceEditor.EditorPaste Y-1=',Lines[ALogStartPos.Y-2]]); DebugLn(['TSourceEditor.EditorPaste Y+0=',Lines[ALogStartPos.Y-1]]); if ALogStartPos.Y 0) or (FSourceNoteBook.FFocusLock <> 0) then exit; if (FSourceNoteBook.PageIndex = PageIndex) then Activate else begin SrcEdit:=SourceNotebook.GetActiveSE; if SrcEdit<>nil then SrcEdit.FocusEditor; // Navigating with mousebuttons between editors (eg jump history on btn 4/5) // can trigger the old editor to be refocused (while not visible) end; end; procedure TSourceEditor.EditorActivateSyncro(Sender: TObject); begin inc(FSyncroLockCount); end; procedure TSourceEditor.EditorDeactivateSyncro(Sender: TObject); begin dec(FSyncroLockCount); end; function TSourceEditor.GetCodeBuffer: TCodeBuffer; begin Result := FSharedValues.CodeBuffer; end; function TSourceEditor.GetExecutionLine: integer; begin Result := FSharedValues.ExecutionLine; end; function TSourceEditor.GetHasExecutionMarks: Boolean; begin Result := EditorComponent.IDEGutterMarks.HasDebugMarks; end; function TSourceEditor.GetSharedEditors(Index: Integer): TSourceEditor; begin Result := FSharedValues.SharedEditors[Index]; end; procedure TSourceEditor.EditorChangeUpdating(ASender: TObject; AnUpdating: Boolean); begin // Calls may be unbalanced, because the event handler may not be assigned to the event on the first BeginUpdate If AnUpdating then begin //if FInEditorChangedUpdating then // debugln(['***** TSourceEditor.EditorChangeUpdating: Updating=True, but FInEditorChangedUpdating was true already']); if not FInEditorChangedUpdating then begin FInEditorChangedUpdating := True; DebugBoss.LockCommandProcessing; end; FMouseActionPopUpMenu := nil; end else begin //if not FInEditorChangedUpdating then // debugln(['***** TSourceEditor.EditorChangeUpdating: Updating=False, but FInEditorChangedUpdating was false already']); if FInEditorChangedUpdating then begin FInEditorChangedUpdating := False; // set before unlocking DebugBoss.UnLockCommandProcessing; // may lead to recursion end; //FMouseActionPopUpMenu := if (FMouseActionPopUpMenu <> nil) then begin FMouseActionPopUpMenu.PopupComponent := FEditor; FMouseActionPopUpMenu.PopUp; FMouseActionPopUpMenu := nil; end; end; end; function TSourceEditor.EditorHandleMouseAction(AnAction: TSynEditMouseAction; var AnInfo: TSynEditMouseActionInfo): Boolean; begin Result := AnAction.Command = emcContextMenu; if not Result then exit; case AnAction.Option2 of 1: FMouseActionPopUpMenu := SourceNotebook.DbgPopUpMenu; 2: FMouseActionPopUpMenu := SourceNotebook.TabPopUpMenu; else FMouseActionPopUpMenu := PopupMenu; end; if (not FInEditorChangedUpdating) and (FMouseActionPopUpMenu <> nil) then begin FMouseActionPopUpMenu.PopupComponent := FEditor; FMouseActionPopUpMenu.PopUp; FMouseActionPopUpMenu := nil; end; end; procedure TSourceEditor.EditorMouseMoved(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin // debugln('MouseMove in Editor',X,',',Y); if Assigned(OnMouseMove) then OnMouseMove(Self,Shift,X,Y); end; procedure TSourceEditor.EditorMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); begin // debugln('MouseWheel in Editor'); if Assigned(OnMouseWheel) then OnMouseWheel(Self, Shift, WheelDelta, MousePos, Handled) end; procedure TSourceEditor.EditorMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin CheckActiveWindow; if Assigned(OnMouseDown) then OnMouseDown(Sender, Button, Shift, X,Y); if (Manager <> nil) then Manager.FChangeNotifyLists[semEditorMouseDown].CallNotifyEvents(Self); end; procedure TSourceEditor.EditorMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if (Manager <> nil) then Manager.FChangeNotifyLists[semEditorMouseUp].CallNotifyEvents(Self); end; procedure TSourceEditor.EditorKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin //DebugLn(['TSourceEditor.EditorKeyDown A ',dbgsName(Sender),' Key=',IntToStr(Key),' File=',ExtractFileName(Filename),' Wnd=',dbgSourceNoteBook(SourceNotebook)]); CheckActiveWindow; if Assigned(OnKeyDown) then OnKeyDown(Sender, Key, Shift); IDECommandList.PostponeUpdateEvents; end; procedure TSourceEditor.EditorKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin CheckActiveWindow; if Assigned(OnKeyUp) then OnKeyUp(Sender, Key, Shift); IDECommandList.PostponeUpdateEvents; end; {------------------------------------------------------------------------------- method TSourceEditor.CenterCursor Params: none Result: none Center the current cursor line in editor. -------------------------------------------------------------------------------} procedure TSourceEditor.CenterCursor(SoftCenter: Boolean = False); var Y, CurTopLine, LinesInWin, MinLines, NewTopLine: Integer; begin LinesInWin := EditorComponent.LinesInWindow; CurTopLine := EditorComponent.TopView; Y := EditorComponent.TextIndexToViewPos(EditorComponent.CaretY); if SoftCenter then begin MinLines := Min( Min( Max(LinesInWin div SoftCenterFactor, SoftCenterMinimum), SoftCenterMaximum), Max(LinesInWin div 2 - 1, 0) // make sure there is at least one line in the soft center ); if (Y <= CurTopLine) or (Y >= CurTopLine + LinesInWin) then // Caret not yet visible => hard-center NewTopLine := Max(1, Y - (LinesInWin div 2)) else if Y < CurTopLine + MinLines then NewTopLine := Max(1, Y - MinLines) else if Y > CurTopLine + LinesInWin - MinLines then NewTopLine := Max(1, Y - LinesInWin + MinLines) else NewTopLine := CurTopLine; end else // not using SoftCenter NewTopLine := Max(1, Y - (LinesInWin div 2)); if NewTopLine < 1 then NewTopLine := 1; EditorComponent.TopView := NewTopLine; end; procedure TSourceEditor.CenterCursorHoriz(HCMode: TSourceEditHCenterMode); var i: Integer; begin case HCMode of hcmCenter: with EditorComponent do begin LeftChar:=Max(LogicalCaretXY.X - (CharsInWindow div 2), 1); end; hcmCenterKeepEOL: with EditorComponent do begin i := LogicalToPhysicalPos(Point(Length(Lines[CaretY-1]) + 1, CaretY)).X; LeftChar:=Max(Min(LogicalCaretXY.X - (CharsInWindow div 2), i - CharsInWindow ), 1); end; hcmSoft: // TODO: offset on left side with EditorComponent do begin LeftChar:=Max(LogicalCaretXY.X - (CharsInWindow * 4 div 5), 1); end; hcmSoftKeepEOL: // TODO: offset on left side with EditorComponent do begin i := LogicalToPhysicalPos(Point(Length(Lines[CaretY-1]) + 1, CaretY)).X; LeftChar:=Max(Min(LogicalCaretXY.X - (CharsInWindow * 4 div 5), i - CharsInWindow ), 1); end; end; end; function TSourceEditor.TextToScreenPosition(const Position: TPoint): TPoint; begin Result:=FEditor.LogicalToPhysicalPos(Position); end; function TSourceEditor.ScreenToTextPosition(const Position: TPoint): TPoint; begin Result:=FEditor.PhysicalToLogicalPos(Position); end; function TSourceEditor.ScreenToPixelPosition(const Position: TPoint): TPoint; begin Result:=FEditor.ScreenXYToPixels(FEditor.TextXYToScreenXY(Position)); end; function TSourceEditor.LineCount: Integer; begin Result:=FEditor.Lines.Count; end; function TSourceEditor.WidthInChars: Integer; begin Result:=FEditor.CharsInWindow; end; function TSourceEditor.HeightInLines: Integer; begin Result:=FEditor.LinesInWindow; end; function TSourceEditor.CharWidth: integer; begin Result:=FEditor.CharWidth; end; function TSourceEditor.GetLineText: string; begin Result:=FEditor.LineText; end; procedure TSourceEditor.SetLineText(const AValue: string); begin FEditor.LineText:=AValue; end; function TSourceEditor.GetLines: TStrings; begin Result:=FEditor.Lines; end; function TSourceEditor.GetLinesInWindow: Integer; begin Result := FEditor.LinesInWindow; end; procedure TSourceEditor.SetLines(const AValue: TStrings); begin FEditor.Lines:=AValue; end; function TSourceEditor.CurrentWordLogStartOrCaret: TPoint; var StartX, EndX: integer; begin Result := FEditor.LogicalCaretXY; FEditor.GetWordBoundsAtRowCol(Result, StartX, EndX); if (Result.x >= StartX) and (Result.x <= EndX) then Result.x := StartX; end; function TSourceEditor.GetProjectFile: TLazProjectFile; begin Result:=LazarusIDE.GetProjectFileForProjectEditor(Self); end; procedure TSourceEditor.UpdateProjectFile(AnUpdates: TSrcEditProjectUpdatesNeeded); begin AnUpdates := AnUpdates + FProjectFileUpdatesNeeded; FProjectFileUpdatesNeeded := []; if Assigned(Manager) and Assigned(Manager.OnUpdateProjectFile) then Manager.OnUpdateProjectFile(self, AnUpdates); end; function TSourceEditor.GetDesigner(LoadForm: boolean): TIDesigner; begin Result:=LazarusIDE.GetDesignerForProjectEditor(Self, LoadForm) end; function TSourceEditor.GetCursorScreenXY: TPoint; begin Result:=FEditor.CaretXY; end; function TSourceEditor.GetCursorTextXY: TPoint; begin Result:=FEditor.LogicalCaretXY; end; procedure TSourceEditor.SetCursorScreenXY(const AValue: TPoint); begin FEditor.CaretXY:=AValue; end; procedure TSourceEditor.SetCursorTextXY(const AValue: TPoint); begin FEditor.LogicalCaretXY:=AValue; end; function TSourceEditor.GetBlockBegin: TPoint; begin Result:=FEditor.BlockBegin; end; function TSourceEditor.GetBlockEnd: TPoint; begin Result:=FEditor.BlockEnd; end; procedure TSourceEditor.SetBlockBegin(const AValue: TPoint); begin FEditor.BlockBegin:=AValue; end; procedure TSourceEditor.SetBlockEnd(const AValue: TPoint); begin FEditor.BlockEnd:=AValue; end; function TSourceEditor.GetTopLine: Integer; begin Result:=FEditor.TopLine; end; procedure TSourceEditor.SetTopLine(const AValue: Integer); begin FEditor.TopLine:=AValue; end; function TSourceEditor.CursorInPixel: TPoint; begin Result:=Point(FEditor.CaretXPix,FEditor.CaretYPix); end; function TSourceEditor.IsCaretOnScreen(ACaret: TPoint; UseSoftCenter: Boolean = False): Boolean; var LinesInWin, MinLines, CurTopLine, Y: Integer; begin LinesInWin := EditorComponent.LinesInWindow; CurTopLine := EditorComponent.TopView; Y := EditorComponent.TextIndexToViewPos(ACaret.Y); if UsesoftCenter then begin MinLines := Min( Min( Max(LinesInWin div SoftCenterFactor, SoftCenterMinimum), SoftCenterMaximum), Max(LinesInWin div 2 - 1, 0) // make sure there is at least one line in the soft center ); end else MinLines := 0; Result := (Y >= CurTopLine + MinLines) and (Y <= CurTopLine + LinesInWin - MinLines) and (ACaret.X >= FEditor.LeftChar) and (ACaret.X <= FEditor.LeftChar + FEditor.CharsInWindow); end; procedure TSourceEditor.MultiPasteText; var I, CaretX: Integer; Content: TStringList; Dialog: TMultiPasteDialog; begin if ReadOnly then Exit; Dialog := TMultiPasteDialog.Create(nil); try if Dialog.ShowModal <> mrOK then Exit; CaretX := FEditor.CaretX; if CaretX > 1 then begin Content := TStringList.Create; try Content.Text := Dialog.Content.Text; for I := 0 to Pred(Content.Count) do if I > 0 then Content[I] := Concat(DupeString(' ', Pred(CaretX)), Content[I]); FEditor.InsertTextAtCaret(Content.Text); finally Content.Free; end; end else FEditor.InsertTextAtCaret(Dialog.Content.Text); finally Dialog.Free; end; end; function TSourceEditor.SearchReplace(const ASearch, AReplace: string; SearchOptions: TSrcEditSearchOptions): integer; const SrcEdit2SynEditSearchOption: array[TSrcEditSearchOption] of TSynSearchOption =( ssoMatchCase, ssoWholeWord, ssoBackwards, ssoEntireScope, ssoSelectedOnly, ssoReplace, ssoReplaceAll, ssoPrompt, ssoRegExpr, ssoRegExprMultiLine ); var NewOptions: TSynSearchOptions; o: TSrcEditSearchOption; begin NewOptions:=[]; for o:=Low(TSrcEditSearchOption) to High(TSrcEditSearchOption) do if o in SearchOptions then Include(NewOptions,SrcEdit2SynEditSearchOption[o]); Result:=DoFindAndReplace(ASearch, AReplace, NewOptions); end; function TSourceEditor.GetSourceText: string; begin Result:=FEditor.Text; end; procedure TSourceEditor.SetSourceText(const AValue: string); begin FEditor.Text:=AValue; end; procedure TSourceEditor.Activate; begin { $note: avoid this if FSourceNoteBook.FUpdateLock > 0 / e.g. debugger calls ProcessMessages, and the internall Index is lost/undone} if (FSourceNoteBook=nil) then exit; if (FSourceNoteBook.FUpdateLock = 0) then FSourceNoteBook.ActiveEditor := Self; end; procedure TSourceEditor.ActivateHint(const ClientPos: TPoint; const ABaseURL, AHint: string; AAutoShown: Boolean); var ScreenPos: TPoint; begin if SourceNotebook=nil then exit; ScreenPos:=EditorComponent.ClientToScreen(ClientPos); Manager.ActivateHint(ScreenPos,ABaseURL,AHint,AAutoShown); end; function TSourceEditor.PageIndex: integer; begin if FSourceNoteBook<>nil then Result:=FSourceNoteBook.FindPageWithEditor(Self) else Result:=-1; end; function TSourceEditor.CaretInSelection(const ACaretPos: TPoint): Boolean; begin Result := (CompareCaret(EditorComponent.BlockBegin, ACaretpos) >= 0) and (CompareCaret(ACaretPos, EditorComponent.BlockEnd) >= 0); end; function TSourceEditor.IsActiveOnNoteBook: boolean; begin if FSourceNoteBook<>nil then Result:=(FSourceNoteBook.GetActiveSE=Self) else Result:=false; end; procedure TSourceEditor.CheckActiveWindow; begin if Manager.ActiveSourceWindow = SourceNotebook then exit; debugln('Warning: ActiveSourceWindow is set incorrectly Active=',dbgSourceNoteBook(Manager.ActiveSourceWindow),' Me=',dbgSourceNoteBook(SourceNotebook)); Manager.ActiveSourceWindow := SourceNotebook; end; procedure TSourceEditor.DoRequestExecutionMarks(Data: PtrInt); begin DebugBoss.LineInfo.Request(FSharedValues.MarksRequestedForFile); end; procedure TSourceEditor.FillExecutionMarks; var ASource: String; i, idx: integer; HasAddr: Boolean; j: Integer; begin if EditorComponent.IDEGutterMarks.HasDebugMarks then Exit; ASource := FileName; idx := DebugBoss.LineInfo.IndexOf(ASource); if (idx = -1) then begin if not FSharedValues.MarksRequested then begin FSharedValues.MarksRequested := True; FSharedValues.MarksRequestedForFile := ASource; DebugBoss.LineInfo.AddNotification(FLineInfoNotification); Application.QueueAsyncCall(@DoRequestExecutionMarks, 0); end; Exit; end; FSharedValues.MarksRequestedForFile := ''; j := -1; EditorComponent.IDEGutterMarks.BeginSetDebugMarks; try for i := 1 to EditorComponent.Lines.Count do begin HasAddr := DebugBoss.LineInfo.HasAddress(idx, i); if (HasAddr) and (j < 0) then j := i; if (not HasAddr) and (j >= 0) then begin EditorComponent.IDEGutterMarks.SetDebugMarks(j, i-1); j := -1; end; end; if (HasAddr) and (j >= 0) then EditorComponent.IDEGutterMarks.SetDebugMarks(j, EditorComponent.Lines.Count); finally EditorComponent.IDEGutterMarks.EndSetDebugMarks; end; // TODO: move to SourceSyneditor for i := 0 to SharedEditorCount - 1 do SharedEditors[i].EditorComponent.IDEGutterMarks.HasDebugMarks; // update all shared editors end; procedure TSourceEditor.ClearExecutionMarks; var i: Integer; begin if FSharedValues.MarksRequested and (FSharedValues.MarksRequestedForFile <> '') then DebugBoss.LineInfo.Cancel(FSharedValues.MarksRequestedForFile); FSharedValues.MarksRequestedForFile := ''; EditorComponent.IDEGutterMarks.ClearDebugMarks; FSharedValues.MarksRequested := False; for i := 0 to SharedEditorCount - 1 do SharedEditors[i].EditorComponent.IDEGutterMarks.ClearDebugMarks; // update all shared editors if (FLineInfoNotification <> nil) and (DebugBoss <> nil) and (DebugBoss.LineInfo <> nil) then DebugBoss.LineInfo.RemoveNotification(FLineInfoNotification); end; procedure TSourceEditor.CopyToWindow(AWindowIndex: Integer); begin SourceNotebook.CopyEditor(PageIndex, AWindowIndex, -1) end; function TSourceEditor.GetCodeAttributeName(LogXY: TPoint): String; var Token: string; Attri: TSynHighlighterAttributes; begin Result := ''; if EditorComponent.GetHighlighterAttriAtRowCol(LogXY,Token,Attri) and (Attri<>nil) then begin Result := Attri.StoredName; end; end; procedure TSourceEditor.LineInfoNotificationChange(const ASender: TObject; const ASource: String); begin if ASource = FileName then begin Application.RemoveAsyncCalls(Self); FillExecutionMarks; end; end; function TSourceEditor.SourceToDebugLine(aLinePos: Integer): Integer; begin Result := FEditor.IDEGutterMarks.SourceLineToDebugLine(aLinePos, True); end; function TSourceEditor.DebugToSourceLine(aLinePos: Integer): Integer; begin Result := FEditor.IDEGutterMarks.DebugLineToSourceLine(aLinePos); end; procedure TSourceEditor.InvalidateAllIfdefNodes; begin FEditor.InvalidateAllIfdefNodes; end; procedure TSourceEditor.SetIfdefNodeState(ALinePos, AstartPos: Integer; AState: TSynMarkupIfdefNodeState); begin FEditor.SetIfdefNodeState(ALinePos, AstartPos, AState); end; procedure TSourceEditor.UpdateIfDefNodeStates(Force: Boolean = False); {off $DEFINE VerboseUpdateIfDefNodeStates} {$IFDEF VerboseUpdateIfDefNodeStates} const VFilePattern='blaunit'; VMinY=1; VMaxY=70; {$ENDIF} var Scanner: TLinkScanner; i: Integer; aDirective: PLSDirective; Code: TCodeBuffer; Y: integer; X: integer; SynState: TSynMarkupIfdefNodeStateEx; SrcPos: Integer; ActiveCnt: Integer; InactiveCnt: Integer; SkippedCnt: Integer; begin //debugln(['TSourceEditor.UpdateIfDefNodeStates START ',Filename]); if not EditorComponent.IsIfdefMarkupActive then exit; //debugln(['TSourceEditor.UpdateIfDefNodeStates CHECK ',Filename]); UpdateCodeBuffer; Scanner:=SharedValues.GetMainLinkScanner(true); if Scanner=nil then exit; if (Scanner.ChangeStep=FLastIfDefNodeScannerStep) and (not Force) then exit; //debugln(['TSourceEditor.UpdateIfDefNodeStates UPDATING ',Filename]); FLastIfDefNodeScannerStep:=Scanner.ChangeStep; EditorComponent.BeginUpdate; try //EditorComponent.InvalidateAllIfdefNodes; Code:=CodeBuffer; i:=0; while i0) then // debugln(['TSourceEditor.UpdateIfDefNodeStates ',i+1,'/',Scanner.DirectiveCount,' ',dbgs(aDirective^.Kind)]); inc(i); if TCodeBuffer(aDirective^.Code)<>Code then continue; if not (aDirective^.Kind in (lsdkAllIf+lsdkAllElse)) then continue; Code.AbsoluteToLineCol(aDirective^.SrcPos,Y,X); if Y<1 then continue; SynState:=idnInvalid; // a directive can be scanned multiple times (multi included include files) // => show it enabled if it was active at least once {$IFDEF VerboseUpdateIfDefNodeStates} if (Pos(VFilePattern,Code.Filename)>0) and (Y>=VMinY) and (Y<=VMaxY) then debugln(['TSourceEditor.UpdateIfDefNodeStates ',i,'/',Scanner.DirectiveCount,' ',dbgs(Pointer(Code)),' ',Code.Filename,' X=',X,' Y=',Y,' SrcPos=',aDirective^.SrcPos,' State=',dbgs(aDirective^.State)]); {$ENDIF} SrcPos:=aDirective^.SrcPos; ActiveCnt:=0; InactiveCnt:=0; SkippedCnt:=0; repeat case aDirective^.State of lsdsActive: inc(ActiveCnt); lsdsInactive: inc(InactiveCnt); lsdsSkipped: inc(SkippedCnt); end; if i < Scanner.DirectiveCount then begin ADirective:=Scanner.DirectivesSorted[i]; {$IFDEF VerboseUpdateIfDefNodeStates} if (Pos(VFilePattern,Code.Filename)>0) and (Y>=VMinY) and (Y<=VMaxY) and (ADirective^.SrcPos=SrcPos) then debugln(['TSourceEditor.UpdateIfDefNodeStates ',i,'/',Scanner.DirectiveCount,' MERGING ',dbgs(ADirective^.Code),' ',Code.Filename,' X=',X,' Y=',Y,' SrcPos=',aDirective^.SrcPos,' State=',dbgs(aDirective^.State)]); {$ENDIF} end; inc(i); until (ADirective^.SrcPos<>SrcPos) or (TCodeBuffer(ADirective^.Code)<>Code) or (i > Scanner.DirectiveCount); dec(i); if (ActiveCnt>0) and (InactiveCnt=0) and (SkippedCnt=0) then SynState:=idnEnabled else if (ActiveCnt=0) and (InactiveCnt+SkippedCnt>0) then SynState:=idnDisabled else if (ActiveCnt>0) then SynState:=idnTempEnabled else SynState:=idnInvalid; {$IFDEF VerboseUpdateIfDefNodeStates} if (Pos(VFilePattern,Code.Filename)>0) and (Y>=VMinY) and (Y<=VMaxY) then debugln(['TSourceEditor.UpdateIfDefNodeStates y=',y,' x=',x,' Counts:Inactive=',InactiveCnt,' Active=',ActiveCnt,' Skipped=',SkippedCnt,' SET SynState=',dbgs(SynState)]); {$ENDIF} EditorComponent.SetIfdefNodeState(Y,X,SynState); end; finally EditorComponent.EndUpdate; end; end; function TSourceEditor.SharedEditorCount: Integer; begin Result := FSharedValues.SharedEditorCount; if Result = 1 then Result := 0; // not a sharing editor end; function TSourceEditor.GetWordAtCurrentCaret: String; var CaretPos: TPoint; begin CaretPos.Y := CurrentCursorYLine; CaretPos.X := CurrentCursorXLine; Result := GetWordFromCaret(ScreenToTextPosition(CaretPos)); end; function TSourceEditor.GetOperandFromCaret(const ACaretPos: TPoint): String; begin UpdateCodeBuffer; if not CodeToolBoss.GetExpandedOperand(CodeBuffer, ACaretPos.X, ACaretPos.Y, Result, False) then if not CodeToolBoss.ExtractOperand(CodeBuffer, ACaretPos.X, ACaretPos.Y, Result, False, False, true) then Result := GetWordFromCaret(ACaretPos); end; function TSourceEditor.GetPageCaption: string; var I: Integer; begin I := SourceNotebook.FindPageWithEditor(Self); if I >= 0 then Result := SourceNotebook.NoteBookPages[I] else Result := FPageName; end; function TSourceEditor.GetPageName: string; begin Result := FPageName; end; function TSourceEditor.GetOperandAtCurrentCaret: String; var CaretPos: TPoint; begin CaretPos.Y := CurrentCursorYLine; CaretPos.X := CurrentCursorXLine; Result := GetOperandFromCaret(ScreenToTextPosition(CaretPos)); end; function TSourceEditor.GetWordFromCaret(const ACaretPos: TPoint): String; begin Result := FEditor.GetWordAtRowCol(ACaretPos); end; function TSourceEditor.IsFirstShared(Sender: TObject): boolean; begin Result:=SharedEditors[0]=Self; end; procedure TSourceEditor.SetVisible(Value: boolean); begin if FVisible=Value then exit; if FEditor<>nil then FEditor.Visible:=Value; FVisible:=Value; end; procedure TSourceEditor.UnbindEditor; // disconnect all events var i: Integer; begin with EditorComponent do begin OnStatusChange := nil; OnProcessCommand := nil; OnProcessUserCommand := nil; OnCommandProcessed := nil; OnReplaceText := nil; OnGutterClick := nil; OnSpecialLineMarkup := nil; OnMouseMove := nil; OnMouseWheel := nil; OnMouseDown := nil; OnClickLink := nil; OnMouseLink := nil; OnKeyDown := nil; OnKeyUp := nil; OnEnter := nil; OnPlaceBookmark := nil; OnClearBookmark := nil; OnChangeUpdating := nil; OnIfdefNodeStateRequest := nil; UnregisterMouseActionExecHandler(@EditorHandleMouseAction); end; for i := 0 to EditorComponent.PluginCount - 1 do if EditorComponent.Plugin[i] is TSynPluginSyncronizedEditBase then begin TSynPluginSyncronizedEditBase(EditorComponent.Plugin[i]).OnActivate := nil; TSynPluginSyncronizedEditBase(EditorComponent.Plugin[i]).OnDeactivate := nil; end; if FEditPlugin<>nil then begin FEditPlugin.Enabled:=false; end; end; procedure TSourceEditor.DoEditorExecuteCommand(EditorCommand: word); begin EditorComponent.CommandProcessor(TSynEditorCommand(EditorCommand),' ',nil); end; {------------------------------------------------------------------------} { TSourceNotebook } constructor TSourceNotebook.Create(AOwner: TComponent); var i: Integer; n: TComponent; begin FPageIndex := -1; i := 1; n := AOwner.FindComponent(NonModalIDEWindowNames[nmiwSourceNoteBook]); while (n <> nil) do begin inc(i); n := AOwner.FindComponent(NonModalIDEWindowNames[nmiwSourceNoteBook]+IntToStr(i)); end; Create(AOwner, i-1); end; constructor TSourceNotebook.Create(AOwner: TComponent; AWindowID: Integer); begin inherited Create(AOwner); FManager := TSourceEditorManager(AOwner); FUpdateLock := 0; FFocusLock := 0; Visible := false; FIsClosing := False; FWindowID := AWindowID; if AWindowID > 0 then Name := NonModalIDEWindowNames[nmiwSourceNoteBook] + IntToStr(AWindowID+1) else Name := NonModalIDEWindowNames[nmiwSourceNoteBook]; if AWindowID > 0 then BaseCaption := locWndSrcEditor + ' (' + IntToStr(AWindowID+1) + ')' else BaseCaption := locWndSrcEditor; Caption := BaseCaption; KeyPreview := true; FProcessingCommand := false; FSourceEditorList := TFPList.Create; FHistoryList := TFPList.Create; FSrcEditsSortedForFilenames := TAvlTree.Create(@CompareSrcEditIntfWithFilename); FHistoryDlg := TBrowseEditorTabHistoryDialog.CreateNew(Self); FOnEditorPageCaptionUpdate := TMethodList.Create; OnDropFiles := @SourceNotebookDropFiles; AllowDropFiles:=true; // popup menu BuildPopupMenu; FUpdateTabAndPageTimer := TTimer.Create(Self); FUpdateTabAndPageTimer.Interval := 500; FUpdateTabAndPageTimer.OnTimer := @UpdateTabsAndPageTimeReached; CreateNotebook; Application.AddOnDeactivateHandler(@OnApplicationDeactivate); Application.AddOnMinimizeHandler(@OnApplicationDeactivate); FStopBtnIdx := IDEImages.LoadImage('menu_stop'); {$IFNDEF LCLGtk2} try Icon.LoadFromResourceName(HInstance, 'WIN_SOURCEEDITOR'); except end; {$ENDIF} end; destructor TSourceNotebook.Destroy; var i: integer; begin DebugLnEnter(SRCED_CLOSE, ['TSourceNotebook.Destroy ']); if assigned(Manager) then Manager.RemoveWindow(Self); DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TSourceNotebook.Destroy'){$ENDIF}; FProcessingCommand:=false; for i:=FSourceEditorList.Count-1 downto 0 do Editors[i].Free; FreeAndNil(FSourceEditorList); FreeAndNil(FHistoryList); FreeAndNil(FOnEditorPageCaptionUpdate); FreeAndNil(FSrcEditsSortedForFilenames); Application.RemoveOnDeactivateHandler(@OnApplicationDeactivate); Application.RemoveOnMinimizeHandler(@OnApplicationDeactivate); FreeAndNil(FNotebook); inherited Destroy; DebugLnExit(SRCED_CLOSE, ['TSourceNotebook.Destroy ']); end; procedure TSourceNotebook.CreateNotebook; var APage: TTabSheet; Begin {$IFDEF IDE_DEBUG} debugln('[TSourceNotebook.CreateNotebook] START'); {$ENDIF} {$IFDEF IDE_MEM_CHECK} CheckHeapWrtMemCnt('[TSourceNotebook.CreateNotebook] A '); {$ENDIF} FNotebook := TExtendedNotebook.Create(self); {$IFDEF IDE_DEBUG} debugln('[TSourceNotebook.CreateNotebook] B'); {$ENDIF} {$IFDEF IDE_MEM_CHECK} CheckHeapWrtMemCnt('[TSourceNotebook.CreateNotebook] B '); {$ENDIF} FPageIndex := -1; with FNotebook do Begin Name:='SrcEditNotebook'; Parent := Self; {$IFDEF IDE_DEBUG} debugln('[TSourceNotebook.CreateNotebook] C'); {$ENDIF} Align := alClient; APage:=TTabSheet.Create(FNotebook); APage.Caption:='unit1'; APage.Parent:=FNotebook; PageIndex := 0; // Set it to the first page Options:=Options+[nboHidePageListPopup]; // hide default popup menu, we show custom in mouse up if EditorOpts.ShowTabCloseButtons then Options:=Options+[nboShowCloseButtons] else Options:=Options-[nboShowCloseButtons]; MultiLine := EditorOpts.MultiLineTab; if Manager<>nil then ShowTabs := Manager.ShowTabs else ShowTabs := True; if ShowTabs then TabPosition := EditorOpts.TabPosition; OnChange := @NotebookPageChanged; OnCloseTabClicked := @CloseTabClicked; OnMouseDown:=@NotebookMouseDown; OnMouseUp:=@NotebookMouseUp; TabDragMode := dmAutomatic; OnTabDragOverEx := @NotebookDragOverEx; OnTabDragDropEx := @NotebookDragDropEx; OnTabDragOver := @NotebookDragOver; OnTabEndDrag := @NotebookEndDrag; ShowHint:=true; OnShowHint:=@NotebookShowTabHint; {$IFDEF IDE_DEBUG} debugln('[TSourceNotebook.CreateNotebook] D'); {$ENDIF} Visible := False; end; //with {$IFDEF IDE_DEBUG} debugln('[TSourceNotebook.CreateNotebook] END'); {$ENDIF} {$IFDEF IDE_MEM_CHECK} CheckHeapWrtMemCnt('[TSourceNotebook.CreateNotebook] END '); {$ENDIF} End; type TLineEnding = (leLF, leCR, leCRLF); const LE_Strs : array [TLineEnding] of String = (#10, #13, #13#10); procedure TSourceNotebook.LineEndingClicked(Sender: TObject); var IDEMenuItem: TIDEMenuItem; SrcEdit: TSourceEditor; NewLineEnding: String; OldLineEnding: String; begin SrcEdit:=GetActiveSE; if SrcEdit=nil then exit; if not (Sender is TIDEMenuItem) then exit; if SrcEdit.CodeBuffer=nil then exit; IDEMenuItem:=TIDEMenuItem(Sender); NewLineEnding:=LE_Strs[TLineEnding(IDEMenuItem.Tag)]; DebugLn(['TSourceNotebook.LineEndingClicked NewLineEnding=',NewLineEnding]); OldLineEnding:=SrcEdit.CodeBuffer.DiskLineEnding; if OldLineEnding='' then OldLineEnding:=LineEnding; if NewLineEnding<>SrcEdit.CodeBuffer.DiskLineEnding then begin DebugLn(['TSourceNotebook.LineEndingClicked Old=',dbgstr(OldLineEnding),' New=',dbgstr(NewLineEnding)]); // change file SrcEdit.CodeBuffer.DiskLineEnding:=NewLineEnding; SrcEdit.CodeBuffer.Source:= ChangeLineEndings(SrcEdit.CodeBuffer.Source,NewLineEnding); SrcEdit.CodeBuffer.Modified:=true; SrcEdit.Modified:=true; end; end; procedure TSourceNotebook.EncodingClicked(Sender: TObject); var IDEMenuItem: TIDEMenuItem; SrcEdit: TSourceEditor; NewEncoding: String; OldEncoding: String; CurResult: TModalResult; begin SrcEdit:=GetActiveSE; if SrcEdit=nil then exit; if Sender is TIDEMenuItem then begin IDEMenuItem:=TIDEMenuItem(Sender); NewEncoding:=IDEMenuItem.Caption; if SysUtils.CompareText(copy(NewEncoding,1,length(EncodingAnsi)+2),EncodingAnsi+' (')=0 then begin // the ansi encoding is shown as 'ansi (system encoding)' -> cut NewEncoding:=EncodingAnsi; end else if NewEncoding=lisUtf8WithBOM then begin NewEncoding:=EncodingUTF8BOM; end; DebugLn(['Hint: (lazarus) TSourceNotebook.EncodingClicked NewEncoding=',NewEncoding]); if SrcEdit.CodeBuffer<>nil then begin OldEncoding:=NormalizeEncoding(SrcEdit.CodeBuffer.DiskEncoding); if OldEncoding='' then OldEncoding:=GetDefaultTextEncoding; if NewEncoding<>SrcEdit.CodeBuffer.DiskEncoding then begin DebugLn(['Hint: (lazarus) TSourceNotebook.EncodingClicked Old=',OldEncoding,' New=',NewEncoding]); if SrcEdit.ReadOnly then begin if SrcEdit.CodeBuffer.IsVirtual then CurResult:=mrCancel else CurResult:=IDEQuestionDialog(lisChangeEncoding, Format(lisEncodingOfFileOnDiskIsNewEncodingIs, [SrcEdit.CodeBuffer.Filename, LineEnding, OldEncoding, NewEncoding]), mtConfirmation, [mrOk, lisReopenWithAnotherEncoding, mrCancel]); end else begin if SrcEdit.CodeBuffer.IsVirtual then CurResult:=IDEQuestionDialog(lisChangeEncoding, Format(lisEncodingOfFileOnDiskIsNewEncodingIs, [SrcEdit.CodeBuffer.Filename, LineEnding, OldEncoding, NewEncoding]), mtConfirmation, [mrYes, lisChangeFile, mrCancel]) else CurResult:=IDEQuestionDialog(lisChangeEncoding, Format(lisEncodingOfFileOnDiskIsNewEncodingIs, [SrcEdit.CodeBuffer.Filename, LineEnding, OldEncoding, NewEncoding]), mtConfirmation, [mrYes,lisChangeFile,mrOk,lisReopenWithAnotherEncoding,mrCancel]); end; if CurResult=mrYes then begin // change file SrcEdit.CodeBuffer.DiskEncoding:=NewEncoding; SrcEdit.CodeBuffer.Modified:=true; // set override InputHistoriesSO.FileEncodings[SrcEdit.CodeBuffer.Filename]:=NewEncoding; DebugLn(['Hint: (lazarus) TSourceNotebook.EncodingClicked Change file to ',SrcEdit.CodeBuffer.DiskEncoding]); if (not SrcEdit.CodeBuffer.IsVirtual) and (LazarusIDE.DoSaveEditorFile(SrcEdit, []) <> mrOk) then begin DebugLn(['Hint: (lazarus) TSourceNotebook.EncodingClicked LazarusIDE.DoSaveEditorFile failed']); end; end else if CurResult=mrOK then begin // reopen with another encoding if SrcEdit.Modified then begin if IDEQuestionDialog(lisAbandonChanges, Format(lisAllYourModificationsToWillBeLostAndTheFileReopened, [SrcEdit.CodeBuffer.Filename, LineEnding]), mtConfirmation,[mbOk,mbAbort],'')<>mrOk then begin exit; end; end; // set override InputHistoriesSO.FileEncodings[SrcEdit.CodeBuffer.Filename]:=NewEncoding; if not SrcEdit.CodeBuffer.Revert then begin IDEMessageDialog(lisCodeToolsDefsReadError, Format(lisUnableToRead, [SrcEdit.CodeBuffer.Filename]), mtError,[mbCancel],''); exit; end; SrcEdit.EditorComponent.BeginUpdate; SrcEdit.CodeBuffer.AssignTo(SrcEdit.EditorComponent.Lines,False); SrcEdit.EditorComponent.EndUpdate; end; end; end; end; end; procedure TSourceNotebook.HighlighterClicked(Sender: TObject); var IDEMenuItem: TIDEMenuItem; i: LongInt; SrcEdit: TSourceEditor; h: TLazSyntaxHighlighter; begin SrcEdit:=GetActiveSE; if SrcEdit=nil then exit; if Sender is TIDEMenuItem then begin IDEMenuItem:=TIDEMenuItem(Sender); i:=IDEMenuItem.SectionIndex; if (i>=ord(Low(TLazSyntaxHighlighter))) and (i<=ord(High(TLazSyntaxHighlighter))) then begin h:=TLazSyntaxHighlighter(i); SrcEdit.SyntaxHighlighterType:=h; SrcEdit.UpdateProjectFile([sepuChangedHighlighter]); end; end; end; procedure TSourceNotebook.TabPopUpMenuPopup(Sender: TObject); var ASrcEdit: TSourceEditor; {$IFnDEF SingleSrcWindow} function ToWindow(ASection: TIDEMenuSection; const OpName: string; const OnClickMethod: TNotifyEvent; WinForFind: Boolean = False): Boolean; var i, ThisWin, SharedEditor: Integer; nb: TSourceNotebook; begin Result := False; ASection.Clear; ThisWin := Manager.IndexOfSourceWindow(self); for i := 0 to Manager.SourceWindowCount - 1 do begin nb:=Manager.SourceWindows[i]; SharedEditor:=nb.IndexOfEditorInShareWith(ASrcEdit); if (i <> ThisWin) and ((SharedEditor < 0) <> WinForFind) then begin Result := True; with RegisterIDEMenuCommand(ASection,OpName+IntToStr(i),nb.Caption,OnClickMethod) do Tag := i; end; end; end; {$ENDIF} procedure AddEditorToMenuSection(AEditor: TSourceEditor; AMenu: TIDEMenuSection; AIndex: Integer); var S: String; begin S := AEditor.PageName; if AEditor.Modified then S := '*'+S; RegisterIDEMenuCommand(AMenu, 'File'+IntToStr(AIndex), S, @ExecuteEditorItemClick, nil, nil, '', PtrUInt(AEditor)); end; var PageCtrl: TPageControl; PopM: TPopupMenu; PageI: integer; i: Integer; S: String; EditorCur: TSourceEditor; P: TIDEPackage; RecMenu, ProjMenu, M: TIDEMenuSection; EdList: TStringListUTF8Fast; begin PopM:=TPopupMenu(Sender); SourceTabMenuRoot.MenuItem:=PopM.Items; // Get the tab that was clicked if PopM.PopupComponent is TPageControl then begin PageCtrl:=TPageControl(PopM.PopupComponent); PageI:=PageCtrl.IndexOfPageAt(PageCtrl.ScreenToClient(PopM.PopupPoint)); if (PageI>=0) and (PageI nil then begin EdList := TStringListUTF8Fast.Create; EdList.OwnsObjects := False; for i := 0 to EditorCount - 1 do EdList.AddObject(Editors[i].PageName+' '+Editors[i].FileName, Editors[i]); EdList.Sorted := True; RecMenu := RegisterIDESubMenu(SrcEditMenuSectionEditors, lisRecentTabs, lisRecentTabs); RecMenu.Visible := False; ProjMenu := RegisterIDESubMenu(SrcEditMenuSectionEditors, dlgEnvProject, dlgEnvProject); ProjMenu.Visible := False; RegisterIDESubMenu(SrcEditMenuSectionEditors, lisMEOther, lisMEOther).Visible := False; //first add all pages in the correct order since the editor order can be different from the tab order for i := 0 to EdList.Count - 1 do begin EditorCur := TSourceEditor(EdList.Objects[i]); s := lisMEOther; P := nil; if (EditorCur.GetProjectFile <> nil) and (EditorCur.GetProjectFile.IsPartOfProject) then s := dlgEnvProject else begin Manager.OnPackageForSourceEditor(P, EditorCur); if P <> nil then s := Format(lisTabsFor, [p.Name]); end; if SrcEditMenuSectionEditors.FindByName(S) is TIDEMenuSection then begin M := TIDEMenuSection(SrcEditMenuSectionEditors.FindByName(S)) end else begin M := RegisterIDESubMenu(SrcEditMenuSectionEditors, S, S); M.UserTag := PtrUInt(P); end; M.Visible := True; AddEditorToMenuSection(EditorCur, M, i); // use tag to count modified if EditorCur.Modified then M.Tag := m.Tag + 1; end; EdList.Free; // add recent tabs. skip 0 since that is the active tab for i := 1 to Min(10, FHistoryList.Count-1) do begin EditorCur := FindSourceEditorWithPageIndex(FNotebook.IndexOf(TCustomPage(FHistoryList[i]))); if (EditorCur = nil) or (not EditorCur.FEditor.HandleAllocated) then continue; // show only if it was visited AddEditorToMenuSection(EditorCur, RecMenu, i); RecMenu.Visible := True; end; for i := 0 to SrcEditMenuSectionEditors.Count - 1 do begin if SrcEditMenuSectionEditors.Items[i] is TIDEMenuSection then begin M := SrcEditMenuSectionEditors.Items[i] as TIDEMenuSection; if M.Tag = 0 then M.Caption := M.Caption + Format(' (%d)', [M.Count]) else M.Caption := M.Caption + Format(' (*%d/%d)', [M.Tag, M.Count]); if M.UserTag <> 0 then RegisterIDEMenuCommand( RegisterIDEMenuSection(M as TIDEMenuSection, 'Open lpk sect '+TIDEPackage(M.UserTag).Filename), 'Open lpk '+TIDEPackage(M.UserTag).Filename, lisCompPalOpenPackage, @OnPopupOpenPackageFile, nil, nil, '', M.UserTag); end; end; if ProjMenu.Visible then begin RegisterIDEMenuCommand( RegisterIDEMenuSection(ProjMenu, 'Open proj sect '), 'Open proj', lisOpenProject2, @OnPopupOpenProjectInsp); end; end; end; procedure TSourceNotebook.SrcPopUpMenuPopup(Sender: TObject); var ASrcEdit: TSourceEditor; CurFilename: String; function MaybeAddPopup(const ASuffix: String; ANewOnClick: TNotifyEvent = nil; Filename: string = ''): TIDEMenuItem; begin Result:=nil; if ANewOnClick=nil then ANewOnClick:=@OnPopupMenuOpenFile; if Filename='' then Filename:=CurFilename; Filename:=ChangeFileExt(Filename,ASuffix); if FileExistsCached(Filename) then begin Filename:=CreateRelativePath(Filename,ExtractFilePath(ASrcEdit.FileName)); Result:=AddContextPopupMenuItem(Format(lisOpenLfm,[Filename]), true, ANewOnClick); end; end; var FPDocSrc, ShortFileName: String; EditorComp: TSynEdit; MainCodeBuf: TCodeBuffer; AnOwner: TObject; Marks: PSourceMark; i, MarkCount: integer; EditorPopupPoint, EditorCaret: TPoint; begin IDECommandList.ExecuteUpdateEvents; SourceEditorMenuRoot.MenuItem:=SrcPopupMenu.Items; RemoveUserDefinedMenuItems; RemoveContextMenuItems; ASrcEdit:=FindSourceEditorWithEditorComponent(TPopupMenu(Sender).PopupComponent); Assert(Assigned(ASrcEdit), 'TSourceNotebook.SrcPopUpMenuPopup: ASrcEdit=nil'); Assert((ASrcEdit=GetActiveSE), 'TSourceNotebook.SrcPopUpMenuPopup: ASrcEdit<>GetActiveSE'); EditorComp:=ASrcEdit.EditorComponent; SrcEditMenuReadOnly.Checked:=ASrcEdit.ReadOnly; SrcEditMenuShowLineNumbers.Checked := ASrcEdit.EditorComponent.Gutter.LineNumberPart.Visible; SrcEditMenuDisableI18NForLFM.Visible:=false; UpdateHighlightMenuItems(ASrcEdit); UpdateEncodingMenuItems(ASrcEdit); UpdateLineEndingMenuItems(ASrcEdit); // ask Codetools CurFilename:=ASrcEdit.FileName; ShortFileName:=ExtractFileName(CurFilename); MainCodeBuf:=nil; if FilenameHasPascalExt(ShortFileName) or FilenameExtIs(ShortFileName,'inc') then MainCodeBuf:=CodeToolBoss.GetMainCode(ASrcEdit.CodeBuffer) else if FilenameIsPascalSource(ShortFileName) then MainCodeBuf:=ASrcEdit.CodeBuffer; if (FilenameIsAbsolute(CurFilename)) then begin if (MainCodeBuf<>nil) and (MainCodeBuf<>ASrcEdit.CodeBuffer) and (not MainCodeBuf.IsVirtual) then begin // this is an include file => add link to open unit CurFilename:=MainCodeBuf.Filename; ShortFileName:=ExtractFileName(CurFilename); AddContextPopupMenuItem( Format(lisOpenLfm, [CreateRelativePath(CurFilename,ExtractFilePath(ASrcEdit.Filename))]), true,@OnPopupMenuOpenFile); end; if FilenameHasPascalExt(ShortFileName) then begin MaybeAddPopup('.lfm'); MaybeAddPopup('.dfm'); MaybeAddPopup('.lrs'); MaybeAddPopup('.s'); end; // ToDo: unit resources if FilenameExtIs(ShortFileName,'lfm',true) or FilenameExtIs(ShortFileName,'dfm') then begin MaybeAddPopup('.pas'); MaybeAddPopup('.pp'); MaybeAddPopup('.p'); end; if FilenameExtIn(ShortFileName, ['lpi','lpk'], true) then begin AddContextPopupMenuItem(Format(lisOpenLfm,[ShortFileName]),true,@OnPopupMenuOpenFile); end; FPDocSrc:=LazarusHelp.GetFPDocFilenameForSource(CurFilename,false,AnOwner); if FPDocSrc<>'' then AddContextPopupMenuItem( Format(lisOpenLfm, [CreateRelativePath(FPDocSrc,ExtractFilePath(CurFilename))]), true,@OnPopupMenuOpenFile); end; EditorPopupPoint:=EditorComp.ScreenToClient(SrcPopUpMenu.PopupPoint); if EditorPopupPoint.X<=EditorComp.Gutter.Width then begin EditorCaret := EditorComp.PhysicalToLogicalPos(EditorComp.PixelsToRowColumn(EditorPopupPoint)); // user clicked on gutter SourceEditorMarks.GetMarksForLine(ASrcEdit, EditorCaret.y, Marks, MarkCount); if Marks <> nil then begin for i := 0 to MarkCount-1 do Marks[i].CreatePopupMenuItems(@AddUserDefinedPopupMenuItem); FreeMem(Marks); end; if (EditorCaret.Y<=EditorComp.Lines.Count) and (MessagesView<>nil) then MessagesView.SourceEditorPopup(EditorComp.Marks.Line[EditorCaret.Y], EditorComp.LogicalCaretXY); end; if Assigned(Manager.OnPopupMenu) then Manager.OnPopupMenu(@AddContextPopupMenuItem); SourceEditorMenuRoot.NotifySubSectionOnShow(Self); //SrcPopupMenu.Items.WriteDebugReport('TSourceNotebook.SrcPopUpMenuPopup() '); end; procedure TSourceNotebook.DbgPopUpMenuPopup(Sender: TObject); begin SrcEditSubMenuDebug.MenuItem:=DbgPopUpMenu.Items; end; procedure TSourceNotebook.NotebookShowTabHint(Sender: TObject; HintInfo: PHintInfo); var Tabindex: integer; ASrcEdit: TSourceEditor; begin if (PageCount=0) or (HintInfo=nil) then exit; TabIndex:=FNoteBook.IndexOfPageAt(FNotebook.ScreenToClient(Mouse.CursorPos)); if TabIndex<0 then exit; ASrcEdit:=FindSourceEditorWithPageIndex(TabIndex); if ASrcEdit=nil then exit; if ASrcEdit.CodeBuffer<>nil then begin HintInfo^.HintStr:=ASrcEdit.CodeBuffer.Filename; end; end; function TSourceNotebook.GetItems(Index: integer): TSourceEditorInterface; begin Result:=TSourceEditorInterface(FSourceEditorList[Index]); end; procedure TSourceNotebook.BuildPopupMenu; begin //debugln('TSourceNotebook.BuildPopupMenu'); TabPopUpMenu := TPopupMenu.Create(Self); with TabPopupMenu do begin AutoPopup := True; OnPopup :=@TabPopupMenuPopup; Images := IDEImages.Images_16; end; SrcPopupMenu := TPopupMenu.Create(Self); with SrcPopupMenu do begin AutoPopup := True; OnPopup :=@SrcPopupMenuPopup; Images := IDEImages.Images_16; end; DbgPopUpMenu := TPopupMenu.Create(Self); with DbgPopupMenu do begin AutoPopup := True; OnPopup :=@DbgPopupMenuPopup; Images := IDEImages.Images_16; end; GoToLineMenuItem.Caption := lisMenuGotoLine; OpenFolderMenuItem.Caption := lisMenuOpenFolder; {$IFDEF VerboseMenuIntf} SrcPopupMenu.Items.WriteDebugReport('TSourceNotebook.BuildPopupMenu '); SourceTabMenuRoot.ConsistencyCheck; SourceEditorMenuRoot.ConsistencyCheck; {$ENDIF} end; procedure TSourceNotebook.CallOnEditorPageCaptionUpdate(Sender: TObject); begin FOnEditorPageCaptionUpdate.CallNotifyEvents(Sender); end; function TSourceNotebook.GetNoteBookPage(Index: Integer): TTabSheet; begin if FNotebook.Visible then Result := FNotebook.Pages[Index] else Result := nil; end; function TSourceNotebook.GetNotebookPages: TStrings; begin if FNotebook.Visible then Result := TCustomTabControl(FNotebook).Pages else Result := nil; end; function TSourceNotebook.GetPageCount: Integer; begin If FNotebook.Visible then Result := FNotebook.PageCount else Result := 0; end; function TSourceNotebook.GetPageIndex: Integer; begin if FUpdateLock > 0 then Result := FPageIndex else if FNotebook.Visible then Result := FNotebook.PageIndex else Result := -1 end; function TSourceNotebook.GetWindowID: Integer; begin Result := FWindowID; end; procedure TSourceNotebook.SetPageIndex(AValue: Integer); begin if (fPageIndex = AValue) and (FNotebook.PageIndex = AValue) then begin //debugln(['>> TSourceNotebook.SetPageIndex PageIndex=', PageIndex, ' FPageIndex=', FPageIndex, ' Value=', AValue, ' FUpdateLock=', FUpdateLock]); //DumpStack; exit; end; DebugLnEnter(SRCED_PAGES, ['>> TSourceNotebook.SetPageIndex Cur-PgIdx=', PageIndex, ' FPageIndex=', FPageIndex, ' Value=', AValue, ' FUpdateLock=', FUpdateLock]); //debugln(['>> TSourceNotebook.SetPageIndex CHANGE PageIndex=', PageIndex, ' FPageIndex=', FPageIndex, ' Value=', AValue, ' FUpdateLock=', FUpdateLock]); FPageIndex := AValue; if FUpdateLock = 0 then ApplyPageIndex else Include(FUpdateFlags,ufPageIndexChanged); DebugLnExit(SRCED_PAGES, ['<< TSourceNotebook.SetPageIndex ']); end; procedure TSourceNotebook.UpdateHighlightMenuItems(SrcEdit: TSourceEditor); var h: TLazSyntaxHighlighter; i: Integer; CurName: String; CurCaption: String; IDEMenuItem: TIDEMenuItem; begin SrcEditSubMenuHighlighter.ChildrenAsSubMenu:=true; i:=0; for h:=Low(TLazSyntaxHighlighter) to High(TLazSyntaxHighlighter) do begin CurName:='Highlighter'+IntToStr(i); CurCaption:=GetSyntaxHighlighterCaption(h); if SrcEditSubMenuHighlighter.Count=i then begin // add new item IDEMenuItem:=RegisterIDEMenuCommand(SrcEditSubMenuHighlighter, CurName,CurCaption,@HighlighterClicked); end else begin IDEMenuItem:=SrcEditSubMenuHighlighter[i]; IDEMenuItem.Caption:=CurCaption; IDEMenuItem.OnClick:=@HighlighterClicked; end; if IDEMenuItem is TIDEMenuCommand then TIDEMenuCommand(IDEMenuItem).Checked:=(SrcEdit<>nil) and (SrcEdit.SyntaxHighlighterType=h); inc(i); end; end; procedure TSourceNotebook.UpdateLineEndingMenuItems(SrcEdit: TSourceEditor); var le: TLineEnding; FileEndings: String; IDEMenuItem: TIDEMenuCommand; const LE_Names : array [TLineEnding] of String =( 'LF (Unix, Linux)', 'CR (Classic Mac)', 'CRLF (Win, DOS)' ); begin SrcEditSubMenuLineEnding.ChildrenAsSubMenu:=true; if (SrcEdit<>nil) and (SrcEdit.CodeBuffer<>nil) then FileEndings:=SrcEdit.CodeBuffer.DiskLineEnding else FileEndings:=LineEnding; //DebugLn(['TSourceNotebook.UpdateEncodingMenuItems ',Encoding]); for le:=low(TLineEnding) to High(TLineEnding) do begin if SrcEditSubMenuLineEnding.Count=Ord(le) then begin // add new item IDEMenuItem:=RegisterIDEMenuCommand(SrcEditSubMenuLineEnding, 'LineEnding'+IntToStr(Ord(le)),LE_Names[le],@LineEndingClicked); end else begin IDEMenuItem:=SrcEditSubMenuLineEnding[Ord(le)] as TIDEMenuCommand; IDEMenuItem.Caption:=LE_Names[le]; IDEMenuItem.OnClick:=@LineEndingClicked; end; IDEMenuItem.Tag:=Ord(le); IDEMenuItem.Checked:=(FileEndings=LE_Strs[le]); end; end; procedure TSourceNotebook.UpdatePageNames; var i: Integer; begin if FUpdateLock > 0 then begin include(FUpdateFlags, ufPageNames); exit; end; for i := 0 to EditorCount - 1 do Editors[i].UpdatePageName; UpdateTabsAndPageTitle; end; procedure TSourceNotebook.UpdateProjectFiles(ACurrentEditor: TSourceEditor = nil); var i: Integer; begin if FUpdateLock > 0 then begin if ACurrentEditor <> nil then ACurrentEditor.UpdateProjectFile; include(FUpdateFlags, ufProjectFiles); exit; end; for i := 0 to EditorCount - 1 do Editors[i].UpdateProjectFile; end; procedure TSourceNotebook.UpdateEncodingMenuItems(SrcEdit: TSourceEditor); var List: TStringList; i: Integer; Encoding: String; CurEncoding: string; CurName: String; CurCaption: String; IDEMenuItem: TIDEMenuItem; SysEncoding: String; begin SrcEditSubMenuEncoding.ChildrenAsSubMenu:=true; Encoding:=''; if SrcEdit<>nil then begin if SrcEdit.CodeBuffer<>nil then Encoding:=NormalizeEncoding(SrcEdit.CodeBuffer.DiskEncoding); end; if Encoding='' then Encoding:=GetDefaultTextEncoding; //DebugLn(['TSourceNotebook.UpdateEncodingMenuItems ',Encoding]); List:=TStringList.Create; GetSupportedEncodings(List); for i:=0 to List.Count-1 do begin CurName:='Encoding'+IntToStr(i); CurEncoding:=List[i]; CurCaption:=CurEncoding; if SysUtils.CompareText(CurEncoding,EncodingAnsi)=0 then begin SysEncoding:=GetDefaultTextEncoding; if (SysEncoding<>'') and (SysUtils.CompareText(SysEncoding,EncodingAnsi)<>0) then CurCaption:=CurCaption+' ('+GetDefaultTextEncoding+')'; end; if CurEncoding='UTF-8BOM' then begin CurCaption:=lisUtf8WithBOM; end; if SrcEditSubMenuEncoding.Count=i then begin // add new item IDEMenuItem:=RegisterIDEMenuCommand(SrcEditSubMenuEncoding, CurName,CurCaption,@EncodingClicked); end else begin IDEMenuItem:=SrcEditSubMenuEncoding[i]; IDEMenuItem.Caption:=CurCaption; IDEMenuItem.OnClick:=@EncodingClicked; end; if IDEMenuItem is TIDEMenuCommand then TIDEMenuCommand(IDEMenuItem).Checked:= Encoding=NormalizeEncoding(CurEncoding); end; List.Free; end; procedure TSourceNotebook.RemoveUserDefinedMenuItems; begin SrcEditMenuSectionFirstDynamic.Clear; end; function TSourceNotebook.AddUserDefinedPopupMenuItem(const NewCaption: string; const NewEnabled: boolean; const NewOnClick: TNotifyEvent): TIDEMenuItem; begin Result:=RegisterIDEMenuCommand(SrcEditMenuSectionFirstDynamic.GetPath, 'Dynamic',NewCaption,NewOnClick); Result.Enabled:=NewEnabled; end; procedure TSourceNotebook.RemoveContextMenuItems; begin SrcEditMenuSectionFileDynamic.Clear; {$IFDEF VerboseMenuIntf} SrcEditMenuSectionFileDynamic.WriteDebugReport('TSourceNotebook.RemoveContextMenuItems ', true); {$ENDIF} end; procedure TSourceNotebook.RemoveUpdateEditorPageCaptionHandler( AEvent: TNotifyEvent); begin FOnEditorPageCaptionUpdate.Remove(TMethod(AEvent)); end; function TSourceNotebook.AddContextPopupMenuItem(const NewCaption: string; const NewEnabled: boolean; const NewOnClick: TNotifyEvent): TIDEMenuItem; begin Result:=RegisterIDEMenuCommand(SrcEditMenuSectionFileDynamic.GetPath, 'FileDynamic',NewCaption,NewOnClick); Result.Enabled:=NewEnabled; end; procedure TSourceNotebook.AddUpdateEditorPageCaptionHandler( AEvent: TNotifyEvent; const AsLast: Boolean); begin FOnEditorPageCaptionUpdate.Add(TMethod(AEvent), AsLast); end; {------------------------------------------------------------------------------- Procedure TSourceNotebook.EditorChanged Params: Sender: TObject Result: none Called whenever an editor status changes. Sender is normally a TSynEdit. -------------------------------------------------------------------------------} procedure TSourceNotebook.EditorChanged(Sender: TObject; Changes: TSynStatusChanges); var SenderDeleted: boolean; Begin SenderDeleted:=(Sender as TControl).Parent=nil; if SenderDeleted then exit; UpdateStatusBar; if Assigned(Manager) then begin if not(Changes=[scFocus]) then // has to be here because of issue #29726 Manager.FHints.HideIfVisible; Manager.DoEditorStatusChanged(FindSourceEditorWithEditorComponent(TSynEdit(Sender))); end; End; procedure TSourceNotebook.DoClose(var CloseAction: TCloseAction); var Layout: TSimpleWindowLayout; begin DebugLnEnter(SRCED_CLOSE, ['TSourceNotebook.DoClose ', DbgSName(self)]); inherited DoClose(CloseAction); CloseAction := caHide; {$IFnDEF SingleSrcWindow} if (PageCount = 0) and (Parent=nil) then begin { $NOTE maybe keep the last one} // Make the name unique, because it may not immediately be released // => disconnect first Layout:=IDEWindowCreators.SimpleLayoutStorage.ItemByForm(Self); if Layout<>nil then Layout.Form:=nil; Name := Name + '___' + IntToStr({%H-}PtrUInt(Pointer(Self))); CloseAction := caFree; end else begin FIsClosing := True; try if Assigned(Manager) and Assigned(Manager.OnNoteBookCloseQuery) then Manager.OnNoteBookCloseQuery(Self, CloseAction); finally FIsClosing := False; end; end; {$ENDIF} DebugLnExit(SRCED_CLOSE, ['TSourceNotebook.DoClose ']); end; procedure TSourceNotebook.DoShow; begin inherited DoShow; // statusbar was not updated when visible=false, update now if snUpdateStatusBarNeeded in States then UpdateStatusBar; if Assigned(Manager) and (Parent <> nil) then Manager.DoWindowShow(Self); end; procedure TSourceNotebook.DoHide; begin inherited DoHide; if Assigned(Manager) and (Parent <> nil) then Manager.DoWindowHide(Self); end; function TSourceNotebook.IndexOfEditorInShareWith( AnOtherEditor: TSourceEditorInterface): Integer; var i: Integer; begin for i := 0 to EditorCount - 1 do if Editors[i].IsSharedWith(AnOtherEditor as TSourceEditor) then exit(i); Result := -1; end; function TSourceNotebook.GetActiveCompletionPlugin: TSourceEditorCompletionPlugin; begin Result := Manager.ActiveCompletionPlugin; end; function TSourceNotebook.GetBaseCaption: String; begin Result := FBaseCaption; end; function TSourceNotebook.GetCompletionPlugins(Index: integer ): TSourceEditorCompletionPlugin; begin Result := SourceEditorManager.CompletionPlugins[Index]; end; function TSourceNotebook.NewSE(Pagenum: Integer; NewPagenum: Integer; ASharedEditor: TSourceEditor; ATabCaption: String): TSourceEditor; begin {$IFDEF IDE_DEBUG} debugln('TSourceNotebook.NewSE A '); {$ENDIF} if Pagenum < 0 then begin // add a new page right to the current if NewPageNum >= 0 then PageNum := NewPageNum else Pagenum := PageIndex+1; Pagenum := Max(0,Min(PageNum, PageCount)); if ATabCaption = '' then ATabCaption := Manager.FindUniquePageName('', nil); NoteBookInsertPage(PageNum, ATabCaption); NotebookPage[PageNum].ReAlign; end; {$IFDEF IDE_DEBUG} debugln(['TSourceNotebook.NewSE B ', PageIndex,',',PageCount]); {$ENDIF} Result := TSourceEditor.Create(Self, NotebookPage[PageNum], ASharedEditor); Result.FPageName := NoteBookPages[Pagenum]; AcceptEditor(Result); PageIndex := Pagenum; {$IFDEF IDE_DEBUG} debugln('TSourceNotebook.NewSE end '); {$ENDIF} end; procedure TSourceNotebook.AcceptEditor(AnEditor: TSourceEditor; SendEvent: Boolean); begin FSourceEditorList.Add(AnEditor); FSrcEditsSortedForFilenames.Add(AnEditor); AnEditor.EditorComponent.BeginUpdate; AnEditor.PopupMenu := SrcPopupMenu; AnEditor.OnEditorChange := @EditorChanged; AnEditor.OnMouseMove := @EditorMouseMove; AnEditor.OnMouseDown := @EditorMouseDown; AnEditor.OnMouseWheel := @EditorMouseWheel; AnEditor.OnKeyDown := @EditorKeyDown; AnEditor.OnKeyUp := @EditorKeyUp; AnEditor.EditorComponent.Beautifier.OnGetDesiredIndent := @EditorGetIndent; AnEditor.EditorComponent.EndUpdate; if SendEvent then Manager.SendEditorCreated(AnEditor); end; procedure TSourceNotebook.ReleaseEditor(AnEditor: TSourceEditor; SendEvent: Boolean); begin FSourceEditorList.Remove(AnEditor); FSrcEditsSortedForFilenames.RemovePointer(AnEditor); if SendEvent then Manager.SendEditorDestroyed(AnEditor); end; function TSourceNotebook.FindSourceEditorWithPageIndex(APageIndex: integer): TSourceEditor; var I: integer; TempEditor: TControl; function FindSynEdit(AControl: TWinControl): TControl; var I: Integer; begin Result := nil; with AControl do for I := 0 to ControlCount - 1 do begin if Controls[I] is TIDESynEditor then Exit(Controls[I]) else if Controls[I] is TWinControl then begin Result := FindSynEdit(TWinControl(Controls[I])); if Result <> nil then Exit; end; end; end; begin Result := nil; if (FSourceEditorList=nil) or (APageIndex < 0) or (APageIndex >= PageCount) then exit; TempEditor := FindSynEdit(NotebookPage[APageIndex]); { TempEditor:=nil; with NotebookPage[APageIndex] do for I := 0 to ControlCount-1 do if Controls[I] is TSynEdit then Begin TempEditor := Controls[I]; Break; end; } if TempEditor=nil then exit; I := FSourceEditorList.Count-1; while (I>=0) and (TSourceEditor(FSourceEditorList[I]).EditorComponent <> TempEditor) do dec(i); if i<0 then exit; Result := TSourceEditor(FSourceEditorList[i]); end; function TSourceNotebook.GetActiveSE: TSourceEditor; Begin Result := nil; if (FSourceEditorList=nil) or (FSourceEditorList.Count=0) or (PageIndex<0) then exit; Result:=FindSourceEditorWithPageIndex(PageIndex); end; function TSourceNotebook.GetActiveEditor: TSourceEditorInterface; begin Result:=GetActiveSE; end; procedure TSourceNotebook.SetActiveEditor(const AValue: TSourceEditorInterface); var i: integer; begin i := FindPageWithEditor(AValue as TSourceEditor); inc(FFocusLock); if i>= 0 then PageIndex := i; dec(FFocusLock); SourceEditorManager.ActiveSourceWindow := Self; if EditorOpts.ShowFileNameInCaption then begin if ActiveEditor<>nil then Caption := BaseCaption+' - '+ActiveEditor.FileName else Caption := BaseCaption; end; end; procedure TSourceNotebook.SetBaseCaption(AValue: String); begin FBaseCaption := AValue; end; procedure TSourceNotebook.CheckCurrentCodeBufferChanged; var SrcEdit: TSourceEditor; begin // Todo: Move to manager, include window changes SrcEdit:=GetActiveSE; if SrcEdit <> nil then begin if FLastCodeBuffer=SrcEdit.CodeBuffer then exit; FLastCodeBuffer:=SrcEdit.CodeBuffer; end else if FLastCodeBuffer=nil then exit; if assigned(Manager) and Assigned(Manager.OnCurrentCodeBufferChanged) then Manager.OnCurrentCodeBufferChanged(Self); end; function TSourceNotebook.GetCapabilities: TCTabControlCapabilities; begin Result := FNotebook.GetCapabilities end; procedure TSourceNotebook.IncUpdateLockInternal; begin if FUpdateLock = 0 then begin FUpdateFlags := []; DebugLn(SRCED_LOCK, ['TSourceNotebook.IncUpdateLockInternal']); FPageIndex := PageIndex; end; inc(FUpdateLock); end; procedure TSourceNotebook.DecUpdateLockInternal; begin dec(FUpdateLock); if FUpdateLock = 0 then begin DebugLnEnter(SRCED_LOCK, ['>> TSourceNotebook.DecUpdateLockInternal UpdateFlags=', dbgs(FUpdateFlags), ' PageIndex=', FPageIndex]); if (ufPageIndexChanged in FUpdateFlags) or (PageIndex<>FPageIndex) then ApplyPageIndex; if (ufPageNames in FUpdateFlags) then UpdatePageNames; if (ufTabsAndPage in FUpdateFlags) then UpdateTabsAndPageTitle; if (ufStatusBar in FUpdateFlags) then UpdateStatusBar; if (ufProjectFiles in FUpdateFlags) then UpdateProjectFiles; if (ufFocusEditor in FUpdateFlags) then FocusEditor; if (ufActiveEditorChanged in FUpdateFlags) then DoActiveEditorChanged; FUpdateFlags := []; DebugLnExit(SRCED_LOCK, ['<< TSourceNotebook.DecUpdateLockInternal']); end; end; procedure TSourceNotebook.IncUpdateLock; begin Manager.IncUpdateLockInternal; // ensure mgr holds ActiveEditorChanged notificationback // TODO: make sure they are hlod in SourceNotebook instead, including SetAciveEditor.... IncUpdateLockInternal; end; procedure TSourceNotebook.DecUpdateLock; begin try DecUpdateLockInternal; finally Manager.DecUpdateLockInternal; end; end; procedure TSourceNotebook.NoteBookInsertPage(Index: Integer; const S: string); begin if FNotebook.Visible then NotebookPages.Insert(Index, S) else begin if Index<>0 then RaiseGDBException(''); IDEWindowCreators.ShowForm(Self,false); FNotebook.Visible := True; NotebookPages[Index] := S; FPageIndex := -1; end; UpdateTabsAndPageTitle; end; procedure TSourceNotebook.NoteBookDeletePage(APageIndex: Integer); begin DebugLnEnter(SRCED_PAGES, ['TSourceNotebook.NoteBookDeletePage ', APageIndex]); HistoryRemove(FNotebook.Pages[APageIndex]); //debugln(['TSourceNotebook.NoteBookDeletePage APageIndex=',APageIndex,' PageIndex=',PageIndex,' PageCount=',PageCount]); if PageCount > 1 then begin // make sure to select another page in the NoteBook, otherwise the // widgetset will choose one and will send a message // if this is the current page, switch to right APageIndex (if possible) if PageIndex = APageIndex then begin if EditorOpts.UseTabHistory then FPageIndex := HistoryGetTopPageIndex else FPageIndex := -1; // default if not in history or not using history if FPageIndex = -1 then if APageIndex < PageCount - 1 then FPageIndex := APageIndex + 1 else FPageIndex := APageIndex - 1; if FUpdateLock = 0 then ApplyPageIndex else Include(FUpdateFlags,ufPageIndexChanged); end; NotebookPages.Delete(APageIndex); end else begin FPageIndex := -1; FNotebook.Visible := False; end; //debugln(['TSourceNotebook.NoteBookDeletePage END PageIndex=',PageIndex,' FPageIndex=',FPageIndex]); UpdateTabsAndPageTitle; DebugLnExit(SRCED_PAGES, ['TSourceNotebook.NoteBookDeletePage ']); end; procedure TSourceNotebook.UpdateTabsAndPageTitle; begin if FUpdateLock > 0 then begin include(FUpdateFlags, ufTabsAndPage); exit; end; if (PageCount = 1) and (EditorOpts.HideSingleTabInWindow) then begin if not EditorOpts.ShowFileNameInCaption then Caption := BaseCaption + ': ' + NotebookPages[0]; FNotebook.ShowTabs := False; end else begin if not EditorOpts.ShowFileNameInCaption then Caption := BaseCaption; FNotebook.ShowTabs := (Manager=nil) or Manager.ShowTabs; end; end; procedure TSourceNotebook.UpdateTabsAndPageTimeReached(Sender: TObject); begin FUpdateTabAndPageTimer.Enabled := False; UpdateTabsAndPageTitle; end; function TSourceNotebook.NoteBookIndexOfPage(APage: TTabSheet): Integer; begin Result := FNoteBook.IndexOf(APage); end; procedure TSourceNotebook.DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin FUpdateTabAndPageTimer.Enabled := False; inherited DragOver(Source, X, Y, State, Accept); if State = dsDragLeave then FUpdateTabAndPageTimer.Enabled := True else if Source is TExtendedNotebook then FNotebook.ShowTabs := (Manager=nil) or Manager.ShowTabs; end; procedure TSourceNotebook.DragCanceled; begin inherited DragCanceled; FUpdateTabAndPageTimer.Enabled := True; end; procedure TSourceNotebook.DoActiveEditorChanged; begin if FUpdateLock > 0 then begin DebugLn(SRCED_PAGES, ['TSourceNotebook.DoActiveEditorChanged LOCKED']); include(FUpdateFlags, ufActiveEditorChanged); exit; end; exclude(FUpdateFlags, ufActiveEditorChanged); DebugLnEnter(SRCED_PAGES, ['>> TSourceNotebook.DoActiveEditorChanged ']); Manager.DoActiveEditorChanged; DebugLnExit(SRCED_PAGES, ['<< TSourceNotebook.DoActiveEditorChanged ']); end; procedure TSourceNotebook.BeginIncrementalFind; var TempEditor: TSourceEditor; begin if (snIncrementalFind in States)AND not(FIncrementalSearchEditor = nil) then begin if (IncrementalSearchStr= '') then begin FIncrementalSearchStr := FIncrementalFoundStr; IncrementalSearch(False, FIncrementalSearchBackwards); end else IncrementalSearch(True, FIncrementalSearchBackwards); exit; end; TempEditor:=GetActiveSE; if TempEditor = nil then exit; Include(States, snIncrementalFind); fIncrementalSearchStartPos:=TempEditor.EditorComponent.LogicalCaretXY; FIncrementalSearchPos:=fIncrementalSearchStartPos; FIncrementalSearchEditor := TempEditor; if assigned(FIncrementalSearchEditor.EditorComponent) then with FIncrementalSearchEditor.EditorComponent do begin UseIncrementalColor:= true; if assigned(MarkupByClass[TSynEditMarkupHighlightAllCaret]) then MarkupByClass[TSynEditMarkupHighlightAllCaret].TempDisable; end; IncrementalSearchStr:=''; UpdateStatusBar; end; procedure TSourceNotebook.EndIncrementalFind; begin if not (snIncrementalFind in States) then exit; Exclude(States,snIncrementalFind); if FIncrementalSearchEditor <> nil then begin if assigned(FIncrementalSearchEditor.EditorComponent) then with FIncrementalSearchEditor.EditorComponent do begin UseIncrementalColor:= False; if assigned(MarkupByClass[TSynEditMarkupHighlightAllCaret]) then MarkupByClass[TSynEditMarkupHighlightAllCaret].TempEnable; end; FIncrementalSearchEditor.EditorComponent.SetHighlightSearch('', []); FIncrementalSearchEditor := nil; end; LazFindReplaceDialog.FindText:=fIncrementalSearchStr; LazFindReplaceDialog.Options:=[]; UpdateStatusBar; end; procedure TSourceNotebook.NextEditor; Begin if PageIndex < PageCount-1 then PageIndex := PageIndex+1 else PageIndex := 0; End; procedure TSourceNotebook.PrevEditor; Begin if PageIndex > 0 then PageIndex := PageIndex-1 else PageIndex := PageCount-1; End; procedure TSourceNotebook.MoveEditor(OldPageIndex, NewPageIndex: integer); begin if (PageCount<=1) or (OldPageIndex=NewPageIndex) or (OldPageIndex<0) or (OldPageIndex>=PageCount) or (NewPageIndex<0) or (NewPageIndex>=PageCount) then exit; NoteBookPages.Move(OldPageIndex,NewPageIndex); UpdatePageNames; UpdateProjectFiles; end; procedure TSourceNotebook.MoveEditorLeft(CurrentPageIndex: integer); begin if (PageCount<=1) then exit; if CurrentPageIndex>0 then MoveEditor(CurrentPageIndex, CurrentPageIndex-1) else MoveEditor(CurrentPageIndex, PageCount-1); end; procedure TSourceNotebook.MoveEditorRight(CurrentPageIndex: integer); begin if (PageCount<=1) then exit; if CurrentPageIndex < PageCount-1 then MoveEditor(CurrentPageIndex, CurrentPageIndex+1) else MoveEditor(CurrentPageIndex, 0); end; procedure TSourceNotebook.MoveEditorFirst(CurrentPageIndex: integer); begin if (PageCount<=1) then exit; MoveEditor(CurrentPageIndex, 0) end; procedure TSourceNotebook.MoveEditorLast(CurrentPageIndex: integer); begin if (PageCount<=1) then exit; MoveEditor(CurrentPageIndex, PageCount-1); end; procedure TSourceNotebook.MoveActivePageLeft; begin MoveEditorLeft(PageIndex); end; procedure TSourceNotebook.MoveActivePageRight; begin MoveEditorRight(PageIndex); end; procedure TSourceNotebook.MoveActivePageFirst; begin MoveEditorFirst(PageIndex); end; procedure TSourceNotebook.MoveActivePageLast; begin MoveEditorLast(PageIndex); end; procedure TSourceNotebook.GotoNextWindow(Backward: Boolean); begin if Backward then begin if Manager.IndexOfSourceWindow(Self) > 0 then Manager.ActiveSourceWindow := Manager.SourceWindows[Manager.IndexOfSourceWindow(Self)-1] else Manager.ActiveSourceWindow := Manager.SourceWindows[Manager.SourceWindowCount-1]; end else begin if Manager.IndexOfSourceWindow(Self) < Manager.SourceWindowCount - 1 then Manager.ActiveSourceWindow := Manager.SourceWindows[Manager.IndexOfSourceWindow(Self)+1] else Manager.ActiveSourceWindow := Manager.SourceWindows[0]; end; Manager.ShowActiveWindowOnTop(True); end; procedure TSourceNotebook.GotoNextSharedEditor(Backward: Boolean = False); var SrcEd: TSourceEditor; i, j: Integer; begin i := Manager.IndexOfSourceWindow(Self); SrcEd := GetActiveSE; repeat if Backward then dec(i) else inc(i); if i < 0 then i := Manager.SourceWindowCount - 1; if i = Manager.SourceWindowCount then i := 0; j := Manager.SourceWindows[i].IndexOfEditorInShareWith(SrcEd); if j >= 0 then begin Manager.ActiveEditor := Manager.SourceWindows[i].Editors[j]; Manager.ShowActiveWindowOnTop(True); exit; end; until Manager.SourceWindows[i] = Self; end; procedure TSourceNotebook.MoveEditorNextWindow(Backward: Boolean; Copy: Boolean); var SrcEd: TSourceEditor; i: Integer; begin i := Manager.IndexOfSourceWindow(Self); SrcEd := GetActiveSE; repeat if Backward then dec(i) else inc(i); if i < 0 then i := Manager.SourceWindowCount - 1; if i = Manager.SourceWindowCount then i := 0; if Manager.SourceWindows[i].IndexOfEditorInShareWith(SrcEd) < 0 then break; until Manager.SourceWindows[i] = Self; if Manager.SourceWindows[i] = Self then exit; if Copy then CopyEditor(FindPageWithEditor(GetActiveSE), i, -1) else MoveEditor(FindPageWithEditor(GetActiveSE), i, -1); Manager.ActiveSourceWindowIndex := i; Manager.ShowActiveWindowOnTop(True); end; procedure TSourceNotebook.MoveEditor(OldPageIndex, NewWindowIndex, NewPageIndex: integer); var DestWin: TSourceNotebook; Edit: TSourceEditor; begin if (NewWindowIndex < 0) or (NewWindowIndex >= Manager.SourceWindowCount) then exit; DestWin := Manager.SourceWindows[NewWindowIndex]; if DestWin = self then begin MoveEditor(OldPageIndex, NewPageIndex); exit end; if NewPageIndex < 0 then NewPageIndex := DestWin.PageCount; if (OldPageIndex<0) or (OldPageIndex>=PageCount) or (NewPageIndex<0) or (NewPageIndex>DestWin.PageCount) then exit; DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TSourceNotebook.MoveEditor'){$ENDIF}; IncUpdateLock; try DestWin.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TSourceNotebook.MoveEdito DestWinr'){$ENDIF}; DestWin.IncUpdateLock; try Edit := FindSourceEditorWithPageIndex(OldPageIndex); DestWin.NoteBookInsertPage(NewPageIndex, Edit.PageName); DestWin.PageIndex := NewPageIndex; ReleaseEditor(Edit); Edit.UpdateNoteBook(DestWin, DestWin.NoteBookPage[NewPageIndex]); DestWin.AcceptEditor(Edit); DestWin.NotebookPage[NewPageIndex].ReAlign; NoteBookDeletePage(OldPageIndex); UpdatePageNames; UpdateProjectFiles; DestWin.UpdatePageNames; DestWin.UpdateProjectFiles(Edit); DestWin.UpdateActiveEditColors(Edit.EditorComponent); DestWin.UpdateStatusBar; DestWin.NotebookPageChanged(nil); // make sure page SynEdit willl be visible finally DestWin.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TSourceNotebook.MoveEdito DestWinr'){$ENDIF}; DestWin.DecUpdateLock; end; finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TSourceNotebook.MoveEditor'){$ENDIF}; DecUpdateLock end; if (PageCount = 0) and (Parent=nil) and not FIsClosing then Close; DoActiveEditorChanged; Manager.ActiveEditor := Edit; end; procedure TSourceNotebook.CopyEditor(OldPageIndex, NewWindowIndex, NewPageIndex: integer; Focus: Boolean = False); var DestWin: TSourceNotebook; SrcEdit, NewEdit: TSourceEditor; i: Integer; begin if (NewWindowIndex < 0) or (NewWindowIndex >= Manager.SourceWindowCount) then exit; DestWin := Manager.SourceWindows[NewWindowIndex]; if DestWin = self then exit; if (OldPageIndex<0) or (OldPageIndex>=PageCount) or (NewPageIndex>DestWin.PageCount) then exit; SrcEdit := FindSourceEditorWithPageIndex(OldPageIndex); NewEdit := DestWin.NewSE(-1, NewPageIndex, SrcEdit, SrcEdit.PageName); Include(NewEdit.FProjectFileUpdatesNeeded, sepuNewShared); NewEdit.PageName := SrcEdit.PageName; NewEdit.SyntaxHighlighterType := SrcEdit.SyntaxHighlighterType; NewEdit.EditorComponent.TopLine := SrcEdit.EditorComponent.TopLine; NewEdit.EditorComponent.CaretXY := SrcEdit.EditorComponent.CaretXY; UpdatePageNames; UpdateProjectFiles; DestWin.UpdateProjectFiles(NewEdit); // Creating a shared edit invalidates the tree in SynMarkup. Force setting it for all editors for i := 0 to SrcEdit.SharedEditorCount - 1 do SrcEdit.SharedEditors[i].UpdateIfDefNodeStates(True); // Update IsVisibleTab; needs UnitEditorInfo created in DestWin.UpdateProjectFiles if Focus then begin Manager.ActiveEditor := NewEdit; Manager.ShowActiveWindowOnTop(True); end; DestWin.NotebookPageChanged(nil); // make sure page SynEdit willl be visible DoActiveEditorChanged; end; procedure TSourceNotebook.StartShowCodeContext(JumpToError: boolean); var Abort: boolean; begin if assigned(Manager) and (Manager.OnShowCodeContext<>nil) then begin Manager.OnShowCodeContext(JumpToError,Abort); if Abort then ; end; end; procedure TSourceNotebook.OnPopupMenuOpenFile(Sender: TObject); var ResStr: String; p: SizeInt; aFilename: TTranslateString; begin // open the filename of the caption // the caption was created by the resourcestring lisOpenLFM, with the // placeholder %s // => cut the surrounding caption to the get the filename aFilename:=(Sender as TIDEMenuItem).Caption; ResStr:=lisOpenLfm; p:=System.Pos('%s',ResStr); aFilename:=copy(aFilename,p,length(aFilename)-(length(ResStr)-2)); if not FilenameIsAbsolute(aFilename) then aFilename:=TrimFilename(ExtractFilePath(GetActiveSE.Filename)+aFilename); if FilenameExtIs(aFilename,'lpi',true) then MainIDEInterface.DoOpenProjectFile(aFilename,[ofOnlyIfExists,ofAddToRecent,ofUseCache]) else if FilenameExtIs(aFilename,'lpk',true) then PackageEditingInterface.DoOpenPackageFile(aFilename,[pofAddToRecent],false) else MainIDEInterface.DoOpenEditorFile(aFilename, PageIndex+1, Manager.IndexOfSourceWindow(self), [ofOnlyIfExists,ofAddToRecent,ofRegularFile,ofUseCache,ofDoNotLoadResource]); end; procedure TSourceNotebook.OnPopupOpenPackageFile(Sender: TObject); begin if (Sender as TIDEMenuItem).UserTag <> 0 then begin PackageEditingInterface.DoOpenPackageFile (TIDEPackage((Sender as TIDEMenuItem).UserTag).Filename,[pofAddToRecent],false) end; end; procedure TSourceNotebook.OnPopupOpenProjectInsp(Sender: TObject); begin MainIDEInterface.DoShowProjectInspector; end; procedure TSourceNotebook.OpenAtCursorClicked(Sender: TObject); begin if Assigned(Manager) and Assigned(Manager.OnOpenFileAtCursorClicked) then Manager.OnOpenFileAtCursorClicked(Sender); end; procedure TSourceNotebook.CutClicked(Sender: TObject); var ActSE: TSourceEditor; begin ActSE := GetActiveSE; if ActSE <> nil then ActSE.DoEditorExecuteCommand(ecCut); end; procedure TSourceNotebook.CopyClicked(Sender: TObject); var ActSE: TSourceEditor; begin ActSE := GetActiveSE; if ActSE <> nil then ActSE.DoEditorExecuteCommand(ecCopy); end; procedure TSourceNotebook.PasteClicked(Sender: TObject); var ActSE: TSourceEditor; begin ActSE := GetActiveSE; if ActSE <> nil then ActSE.DoEditorExecuteCommand(ecPaste); end; procedure TSourceNotebook.StatusBarClick(Sender: TObject); var P: TPoint; begin P := StatusBar.ScreenToClient(Mouse.CursorPos); if StatusBar.GetPanelIndexAt(P.X, P.Y) = 1 then EditorMacroForRecording.Stop; end; procedure TSourceNotebook.StatusBarDblClick(Sender: TObject); var P: TPoint; begin P := StatusBar.ScreenToClient(Mouse.CursorPos); case StatusBar.GetPanelIndexAt(P.X, P.Y) of // Based on panel: 0: GoToLineMenuItemClick(Nil); // Show "Goto Line" dialog. 4: OpenFolderMenuItemClick(Nil); // Show system file manager on file's folder. end; end; procedure TSourceNotebook.StatusBarContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean); var Pnl: Integer; begin Pnl := StatusBar.GetPanelIndexAt(MousePos.X, MousePos.Y); GoToLineMenuItem.Visible := Pnl=0; OpenFolderMenuItem.Visible := Pnl=4; if Pnl in [0, 4] then StatusPopUpMenu.PopUp; end; procedure TSourceNotebook.StatusBarDrawPanel(AStatusBar: TStatusBar; APanel: TStatusPanel; const ARect: TRect); begin if APanel = StatusBar.Panels[1] then begin IDEImages.Images_16.ResolutionForControl[16, AStatusBar] .Draw(StatusBar.Canvas, ARect.Left, ARect.Top, FStopBtnIdx); end; end; procedure TSourceNotebook.ToggleBreakpointClicked(Sender: TObject); var ASrcEdit: TSourceEditor; Line: LongInt; BreakPtMark: TSourceMark; begin ASrcEdit:=GetActiveSE; if ASrcEdit=nil then exit; // create or delete breakpoint // find breakpoint mark at line Line:=ASrcEdit.EditorComponent.CaretY; BreakPtMark := SourceEditorMarks.FindBreakPointMark(ASrcEdit, Line); if BreakPtMark = nil then DebugBoss.DoCreateBreakPoint(ASrcEdit.Filename,Line,true) else DebugBoss.DoDeleteBreakPointAtMark(BreakPtMark); end; procedure TSourceNotebook.ToggleBreakpointEnabledClicked(Sender: TObject); var ASrcEdit: TSourceEditor; Line: LongInt; BreakPtMark: TSourceMark; BreakPoint: TIDEBreakPoint; begin ASrcEdit:=GetActiveSE; if ASrcEdit=nil then exit; // create or delete breakpoint // find breakpoint mark at line Line:=ASrcEdit.EditorComponent.CaretY; BreakPtMark := SourceEditorMarks.FindBreakPointMark(ASrcEdit, Line); if BreakPtMark = nil then begin DebugBoss.DoCreateBreakPoint(ASrcEdit.Filename,Line,true, BreakPoint, True); if BreakPoint <> nil then begin BreakPoint.Enabled := False; BreakPoint.EndUpdate; end; end else begin BreakPoint := DebugBoss.BreakPoints.Find(ASrcEdit.FileName, Line); BreakPoint.Enabled := not BreakPoint.Enabled; end; end; procedure TSourceNotebook.CompleteCodeMenuItemClick(Sender: TObject); begin MainIDEInterface.DoCommand(ecCompleteCode); end; procedure TSourceNotebook.DeleteBreakpointClicked(Sender: TObject); var ASrcEdit: TSourceEditor; begin ASrcEdit:=GetActiveSE; if ASrcEdit=nil then exit; DebugBoss.DoDeleteBreakPoint(ASrcEdit.Filename, ASrcEdit.EditorComponent.CaretY); end; procedure TSourceNotebook.ExtractProcMenuItemClick(Sender: TObject); begin MainIDEInterface.DoCommand(ecExtractProc); end; procedure TSourceNotebook.InvertAssignmentMenuItemClick(Sender: TObject); var ASrcEdit: TSourceEditor; begin ASrcEdit:=GetActiveSE; if ASrcEdit=nil then exit; ASrcEdit.InvertAssignment; end; procedure TSourceNotebook.RenameIdentifierMenuItemClick(Sender: TObject); begin MainIDEInterface.DoCommand(ecRenameIdentifier); end; procedure TSourceNotebook.ShowAbstractMethodsMenuItemClick(Sender: TObject); begin MainIDEInterface.DoCommand(ecShowAbstractMethods); end; procedure TSourceNotebook.ShowEmptyMethodsMenuItemClick(Sender: TObject); begin MainIDEInterface.DoCommand(ecRemoveEmptyMethods); end; procedure TSourceNotebook.ShowUnusedUnitsMenuItemClick(Sender: TObject); begin MainIDEInterface.DoCommand(ecRemoveUnusedUnits); end; procedure TSourceNotebook.SourceNotebookDropFiles(Sender: TObject; const FileNames: array of String); begin FManager.ActiveSourceWindow := Self; LazarusIDE.DoDropFiles(Sender,Filenames,WindowID); end; procedure TSourceNotebook.FindOverloadsMenuItemClick(Sender: TObject); begin MainIDEInterface.DoCommand(ecFindOverloads); end; procedure TSourceNotebook.MakeResourceStringMenuItemClick(Sender: TObject); begin MainIDEInterface.DoCommand(ecMakeResourceString); end; function TSourceNotebook.NewFile(const NewShortName: String; ASource: TCodeBuffer; FocusIt: boolean; AShareEditor: TSourceEditor = nil): TSourceEditor; var s: String; Begin //create a new page debugln(SRCED_OPEN, '[TSourceNotebook.NewFile] A '); // Debugger cause ProcessMessages, which could lead to entering methods in unexpected order DebugBoss.LockCommandProcessing; try DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TSourceNotebook.NewFile'){$ENDIF}; try IDEWindowCreators.ShowForm(Self,false); s := Manager.FindUniquePageName(NewShortName, AShareEditor); Result := NewSE(-1, -1, AShareEditor, s); debugln(SRCED_OPEN, '[TSourceNotebook.NewFile] B '); Result.CodeBuffer:=ASource; debugln(SRCED_OPEN, '[TSourceNotebook.NewFile] D '); //debugln(['TSourceNotebook.NewFile ',NewShortName,' ',ASource.Filename]); Result.PageName:= s; UpdatePageNames; UpdateProjectFiles(Result); UpdateStatusBar; Manager.SendEditorCreated(Result); finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TSourceNotebook.NewFile'){$ENDIF}; end; if FocusIt then FocusEditor; finally DebugBoss.UnLockCommandProcessing; end; debugln(SRCED_OPEN, '[TSourceNotebook.NewFile] end'); CheckFont; end; procedure TSourceNotebook.CloseFile(APageIndex:integer); var TempEditor: TSourceEditor; WasSelected: Boolean; begin (* Do not use DisableAutoSizing in here, if a new Editor is focused it needs immediate autosize (during handle creation) *) // Inc/DecUpdateLockInternal does currently noth work, since a tab will be removed DebugLnEnter(SRCED_CLOSE, ['>> TSourceNotebook.CloseFile A APageIndex=',APageIndex, ' Cur-Page=', PageIndex]); DebugBoss.LockCommandProcessing; try TempEditor:=FindSourceEditorWithPageIndex(APageIndex); if TempEditor=nil then exit; WasSelected:=PageIndex=APageIndex; debugln(SRCED_CLOSE, ['TSourceNotebook.CloseFile ', DbgSName(TempEditor), ' ', TempEditor.FileName]); EndIncrementalFind; TempEditor.Close; NoteBookDeletePage(APageIndex); // delete page before sending notification senEditorDestroyed TempEditor.Free; // sends semEditorDestroy TempEditor:=nil; // delete the page UpdateProjectFiles; UpdatePageNames; if WasSelected then UpdateStatusBar; // set focus to new editor if (PageCount = 0) and (Parent=nil) then begin {$IFnDEF SingleSrcWindow} Manager.RemoveWindow(self); FManager := nil; {$ENDIF} if not FIsClosing then Close; end; // Move focus from Notebook-tabs to editor TempEditor:=FindSourceEditorWithPageIndex(PageIndex); if IsVisible and (TempEditor <> nil) and (FUpdateLock = 0) then // this line raises exception when editor is in other tab (for example - focused is designer) ;// TempEditor.EditorComponent.SetFocus; finally debugln(SRCED_CLOSE, ['TSourceNotebook.CloseFile UnLock']); DebugBoss.UnLockCommandProcessing; DebugLnExit(SRCED_CLOSE, ['<< TSourceNotebook.CloseFile']); end; end; procedure TSourceNotebook.FocusEditor; var SrcEdit: TSourceEditor; begin if FUpdateLock > 0 then begin include(FUpdateFlags, ufFocusEditor); exit; end; if (fAutoFocusLock>0) then exit; SrcEdit:=GetActiveSE; if SrcEdit=nil then exit; SrcEdit.FocusEditor; end; procedure TSourceNotebook.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Cursor:=crDefault; end; // Two Popup menu handlers: procedure TSourceNotebook.GoToLineMenuItemClick(Sender: TObject); begin if Assigned(Manager) then Manager.GotoLineClicked(nil); end; procedure TSourceNotebook.OpenFolderMenuItemClick(Sender: TObject); begin OpenDocument(ExtractFilePath(Statusbar.Panels[4].Text)); end; procedure TSourceNotebook.ExecuteEditorItemClick(Sender: TObject); var Editor: TSourceEditor; begin if SourceEditorManager = nil then exit; Editor := TSourceEditor((sender as TIDEMenuCommand).UserTag); SourceEditorManager.ActiveEditor :=Editor; SourceEditorManager.ShowActiveWindowOnTop(True); end; procedure TSourceNotebook.ApplyPageIndex; begin Exclude(FUpdateFlags,ufPageIndexChanged); DebugBoss.LockCommandProcessing; try //debugln(['TSourceNotebook.ApplyPageIndex FPageIndex=',FPageIndex]); FPageIndex := Max(0,Min(FPageIndex,FNotebook.PageCount-1)); if Assigned(Manager) and (FNotebook.PageIndex = FPageIndex) then DoActiveEditorChanged; // make sure the statusbar is updated Include(States, snNotebookPageChangedNeeded); FNotebook.PageIndex := FPageIndex; if snNotebookPageChangedNeeded in States then begin DebugLn(SRCED_PAGES, ['TSourceNotebook.ApplyPageIndex calling NotebookPageChanged']); NotebookPageChanged(nil); end; HistorySetMostRecent(FNotebook.Pages[FPageIndex]); finally DebugBoss.UnLockCommandProcessing; end; end; procedure TSourceNotebook.CloseTabClicked(Sender: TObject); begin if (GetKeyShiftState * [ssShift, ssCtrl, ssAlt] = EditorOpts.MiddleTabClickClosesOthersModifier) and (EditorOpts.MiddleTabClickClosesOthersModifier <> []) then CloseClicked(Sender, [ceoCloseOthers]) else if (GetKeyShiftState * [ssShift, ssCtrl, ssAlt] = EditorOpts.MiddleTabClickClosesToRightModifier) and (EditorOpts.MiddleTabClickClosesToRightModifier <> []) then CloseClicked(Sender, [ceoCloseOthersOnRightSide]) else CloseClicked(Sender, []); end; function TSourceNotebook.GetEditors(Index:integer):TSourceEditor; begin Result:=TSourceEditor(FSourceEditorList[Index]); end; function TSourceNotebook.EditorCount:integer; begin Result:=FSourceEditorList.Count; end; function TSourceNotebook.IndexOfEditor(aEditor: TSourceEditorInterface): integer; begin Result := FSourceEditorList.IndexOf(aEditor); end; function TSourceNotebook.Count: integer; begin Result:=FSourceEditorList.Count; end; function TSourceNotebook.SourceEditorIntfWithFilename(const Filename: string ): TSourceEditorInterface; var Node: TAvlTreeNode; begin Node:=FSrcEditsSortedForFilenames.FindKey(Pointer(Filename),@CompareFilenameWithSrcEditIntf); if Node<>nil then Result:=TSourceEditorInterface(Node.Data) else Result:=nil; end; procedure TSourceNotebook.CloseClicked(Sender: TObject; CloseOptions: TCloseSrcEditorOptions); Begin if assigned(Manager) and Assigned(Manager.OnCloseClicked) then Manager.OnCloseClicked(Sender, CloseOptions); end; procedure TSourceNotebook.ToggleFormUnitClicked(Sender: TObject); begin if assigned(Manager) and Assigned(Manager.OnToggleFormUnitClicked) then Manager.OnToggleFormUnitClicked(Sender); end; procedure TSourceNotebook.ToggleObjectInspClicked(Sender: TObject); begin if assigned(Manager) and Assigned(Manager.OnToggleObjectInspClicked) then Manager.OnToggleObjectInspClicked(Sender); end; procedure TSourceNotebook.HistorySetMostRecent(APage: TTabSheet); var Index: Integer; begin if APage = nil then Exit; Index := FHistoryList.IndexOf(APage); if Index <> -1 then FHistoryList.Delete(Index); FHistoryList.Insert(0, APage); end; procedure TSourceNotebook.HistoryRemove(APage: TTabSheet); var Index: Integer; begin Index := FHistoryList.IndexOf(APage); if Index <> -1 then FHistoryList.Delete(Index); end; function TSourceNotebook.HistoryGetTopPageIndex: Integer; begin Result := -1; if FHistoryList.Count = 0 then Exit; Result := FNotebook.IndexOf(TCustomPage(FHistoryList.Items[0])); end; procedure TSourceNotebook.InsertCharacter(const C: TUTF8Char); var FActiveEdit: TSourceEditor; begin FActiveEdit := GetActiveSE; if FActiveEdit <> nil then begin if FActiveEdit.ReadOnly then Exit; FActiveEdit.EditorComponent.InsertTextAtCaret(C); end; end; procedure TSourceNotebook.SrcEditMenuCopyToExistingWindowClicked(Sender: TObject); begin inc(FFocusLock); try CopyEditor(PageIndex, (Sender as TIDEMenuItem).Tag, -1); finally dec(FFocusLock); end; end; procedure TSourceNotebook.SrcEditMenuMoveToExistingWindowClicked(Sender: TObject); begin MoveEditor(PageIndex, (Sender as TIDEMenuItem).Tag, -1) end; procedure TSourceNotebook.SrcEditMenuFindInWindowClicked(Sender: TObject); var TargetIndex: Integer; DestWin: TSourceNotebook; Edit: TSourceEditor; SharedEditorIdx: Integer; begin TargetIndex := (Sender as TIDEMenuItem).Tag; if (TargetIndex < 0) or (TargetIndex >= Manager.SourceWindowCount) then exit; DestWin := Manager.SourceWindows[TargetIndex]; Edit := FindSourceEditorWithPageIndex(PageIndex); SharedEditorIdx := DestWin.IndexOfEditorInShareWith(Edit); If SharedEditorIdx < 0 then exit; Manager.ActiveEditor := DestWin.Editors[SharedEditorIdx]; Manager.ShowActiveWindowOnTop(True); end; procedure TSourceNotebook.EditorLockClicked(Sender: TObject); begin GetActiveSE.IsLocked := not GetActiveSE.IsLocked; end; procedure TSourceNotebook.UpdateStatusBar; var tempEditor: TSourceEditor; PanelFilename: String; PanelCharMode: string; PanelXY: string; PanelFileMode: string; CurEditor: TSynEdit; begin if FUpdateLock > 0 then begin include(FUpdateFlags, ufStatusBar); exit; end; if (not IsVisible) or (FUpdateLock > 0) then begin Include(States,snUpdateStatusBarNeeded); exit; end; Exclude(States,snUpdateStatusBarNeeded); TempEditor := GetActiveSE; if TempEditor = nil then Exit; CurEditor:=TempEditor.EditorComponent; //debugln(['TSourceNotebook.UpdateStatusBar ',tempEditor.FileName,' ',PageIndex]); if (snIncrementalFind in States) and (CompareCaret(CurEditor.LogicalCaretXY,FIncrementalSearchPos)<>0) then begin // some action has changed the cursor during incremental search // -> end incremental search EndIncrementalFind; // this called UpdateStatusBar -> exit exit; end; if (CurEditor.CaretY<>TempEditor.ErrorLine) or (CurEditor.CaretX<>TempEditor.fErrorColumn) then TempEditor.ErrorLine:=-1; Statusbar.BeginUpdate; if snIncrementalFind in States then begin Statusbar.SimplePanel:=true; Statusbar.SimpleText:=Format(lisUESearching, [IncrementalSearchStr]); end else begin Statusbar.SimplePanel:=false; PanelFilename:=TempEditor.Filename; If TempEditor.Modified then PanelFileMode := ueModified else PanelFileMode := ''; If TempEditor.ReadOnly then begin if PanelFileMode <> '' then PanelFileMode := PanelFileMode + lisUEModeSeparator; PanelFileMode := PanelFileMode + uepReadonly; end; if (EditorMacroForRecording.State = emRecording) and (EditorMacroForRecording.IsRecording(CurEditor)) then begin if PanelFileMode <> '' then PanelFileMode := PanelFileMode + lisUEModeSeparator; PanelFileMode := PanelFileMode + ueMacroRecording; end; if (EditorMacroForRecording.State = emRecPaused) and (EditorMacroForRecording.IsRecording(CurEditor)) then begin if PanelFileMode <> '' then PanelFileMode := PanelFileMode + lisUEModeSeparator; PanelFileMode := PanelFileMode + ueMacroRecordingPaused; end; If TempEditor.IsLocked then begin if PanelFileMode <> '' then PanelFileMode := PanelFileMode + lisUEModeSeparator; PanelFileMode := PanelFileMode + ueLocked; end; PanelXY := Format(' %6d:%4d', [TempEditor.CurrentCursorYLine,TempEditor.CurrentCursorXLine]); if GetActiveSE.InsertMode then PanelCharMode := uepIns else PanelCharMode := uepOvr; Statusbar.Panels[0].Text := PanelXY; StatusBar.Panels[2].Text := PanelFileMode; Statusbar.Panels[3].Text := PanelCharMode; Statusbar.Panels[4].Text := PanelFilename; if(EditorMacroForRecording.IsRecording(CurEditor)) then Statusbar.Panels[1].Width := IDEImages.ScaledSize(20) else Statusbar.Panels[1].Width := 0; end; Statusbar.EndUpdate; CheckCurrentCodeBufferChanged; End; function TSourceNotebook.FindPageWithEditor(ASourceEditor: TSourceEditor): integer; var LTabSheet, LParent: TWinControl; begin Result:=-1; LParent := ASourceEditor.EditorComponent.Parent; if LParent is TTabSheet then begin repeat LTabSheet := LParent; LParent := LParent.Parent; until (LParent = FNotebook) or (LParent = nil); if (LParent <> nil) and (LTabSheet is TTabSheet) then Result:=TTabSheet(LTabSheet).PageIndex; end; end; function TSourceNotebook.FindSourceEditorWithEditorComponent(EditorComp: TComponent): TSourceEditor; var i: integer; begin for i:=0 to EditorCount-1 do begin Result:=Editors[i]; if Result.EditorComponent=EditorComp then exit; end; Result:=nil; end; procedure TSourceNotebook.NotebookMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var TabIndex: Integer; begin if (Button = mbMiddle) then begin TabIndex:=FNotebook.IndexOfPageAt(X, Y); if TabIndex>=0 then begin if (GetKeyShiftState * [ssShift, ssCtrl, ssAlt] = EditorOpts.MiddleTabClickClosesOthersModifier) and (EditorOpts.MiddleTabClickClosesOthersModifier <> []) then CloseClicked(NoteBookPage[TabIndex], [ceoCloseOthers]) else if (GetKeyShiftState * [ssShift, ssCtrl, ssAlt] = EditorOpts.MiddleTabClickClosesToRightModifier) and (EditorOpts.MiddleTabClickClosesToRightModifier <> []) then CloseClicked(NoteBookPage[TabIndex], [ceoCloseOthersOnRightSide]) else CloseClicked(NoteBookPage[TabIndex], []); end; end else if (Button = mbRight) then begin //select on right click TabIndex:=FNotebook.IndexOfPageAt(X, Y); if TabIndex>=0 then begin FNotebook.ActivePageIndex := TabIndex; if Assigned(IDEDockMaster) then Manager.ActiveEditor:=GetActiveSE; end; end; end; procedure TSourceNotebook.NotebookMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var TabIndex: Integer; begin if (Button = mbRight) then begin TabIndex:=FNotebook.IndexOfPageAt(X, Y); if TabIndex>=0 then begin TabPopUpMenu.PopupComponent := FNotebook; TabPopUpMenu.PopUp; TabPopUpMenu.PopupComponent := nil; end; end; end; procedure TSourceNotebook.NotebookDragDropEx(Sender, Source: TObject; OldIndex, NewIndex: Integer; CopyDrag: Boolean; var Done: Boolean); function SourceIndex: Integer; begin Result := Manager.SourceWindowCount - 1; while Result >= 0 do begin if Manager.SourceWindows[Result].FNotebook = Source then break; dec(Result); end; end; begin {$IFnDEF SingleSrcWindow} If CopyDrag then begin Manager.SourceWindows[SourceIndex].CopyEditor (OldIndex, Manager.IndexOfSourceWindow(self), NewIndex); end else begin {$ENDIF} if (Source = FNotebook) then MoveEditor(OldIndex, NewIndex) else begin Manager.SourceWindows[SourceIndex].MoveEditor (OldIndex, Manager.IndexOfSourceWindow(self), NewIndex); end; {$IFnDEF SingleSrcWindow} end; {$ENDIF} Manager.ActiveSourceWindow := self; Manager.ShowActiveWindowOnTop(True); Done := True; end; procedure TSourceNotebook.NotebookDragOverEx(Sender, Source: TObject; OldIndex, NewIndex: Integer; CopyDrag: Boolean; var Accept: Boolean); function SourceIndex: Integer; begin Result := Manager.SourceWindowCount - 1; while Result >= 0 do begin if Manager.SourceWindows[Result].FNotebook = Source then break; dec(Result); end; end; var Src: TSourceNotebook; NBHasSharedEditor: Boolean; begin Src := Manager.SourceWindows[SourceIndex]; NBHasSharedEditor := IndexOfEditorInShareWith (Src.FindSourceEditorWithPageIndex(OldIndex)) >= 0; {$IFnDEF SingleSrcWindow} if CopyDrag then Accept := (NewIndex >= 0) and (Source <> Sender) and (not NBHasSharedEditor) else {$ENDIF} Accept := (NewIndex >= 0) and ((Source <> Sender) or (OldIndex <> NewIndex)) and ((Source = Sender) or (not NBHasSharedEditor)); end; procedure TSourceNotebook.NotebookDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin if Accept=true then ; // set by NotebookDragOverEx FUpdateTabAndPageTimer.Enabled := False; if State = dsDragLeave then FUpdateTabAndPageTimer.Enabled := True else if Source is TExtendedNotebook then FNotebook.ShowTabs := (Manager=nil) or Manager.ShowTabs; end; procedure TSourceNotebook.NotebookEndDrag(Sender, Target: TObject; X, Y: Integer); begin FUpdateTabAndPageTimer.Enabled := True; end; procedure TSourceNotebook.NotebookPageChanged(Sender: TObject); var SrcEdit:TSourceEditor; CaretXY: TPoint; TopLine: Integer; Begin if (Manager = nil) or (FUpdateLock > 0) Then begin Include(States, snNotebookPageChangedNeeded); exit; end; DebugLnEnter(SRCED_PAGES, ['>> TSourceNotebook.NotebookPageChanged PageIndex=', PageIndex, ' AutoFocusLock=', fAutoFocusLock, ' Sender=',DbgSName(Sender)]); DebugBoss.LockCommandProcessing; try Exclude(States, snNotebookPageChangedNeeded); SrcEdit:=GetActiveSE; Manager.FHints.HideIfVisible; if (CodeContextFrm<>nil) then CodeContextFrm.Hide; DebugLn(SRCED_PAGES, ['TSourceNotebook.NotebookPageChanged TempEdit=', DbgSName(SrcEdit), ' Vis=', dbgs(IsVisible), ' Hnd=', dbgs(HandleAllocated)]); if SrcEdit <> nil then begin if not SrcEdit.Visible then begin // As long as SynEdit had no Handle, it had kept all those Values untouched CaretXY := SrcEdit.EditorComponent.CaretXY; TopLine := SrcEdit.EditorComponent.TopLine; TSynEditMarkupManager(SrcEdit.EditorComponent.MarkupMgr).IncPaintLock; SrcEdit.BeginUpdate; SrcEdit.FEditor.HandleNeeded; // make sure we have a handle SrcEdit.Visible := True; SrcEdit.EndUpdate; // Restore the intial Positions, must be after lock SrcEdit.EditorComponent.LeftChar := 1; SrcEdit.EditorComponent.CaretXY := CaretXY; SrcEdit.EditorComponent.TopLine := TopLine; TSynEditMarkupManager(SrcEdit.EditorComponent.MarkupMgr).DecPaintLock; SrcEdit.UpdateIfDefNodeStates; // after editor is initialized end; if (fAutoFocusLock=0) and (Screen.ActiveCustomForm=GetParentForm(Self)) and not(Manager.HasAutoFocusLock) then begin DebugLnEnter(SRCED_PAGES, ['TSourceNotebook.NotebookPageChanged BEFORE SetFocus ', DbgSName(SrcEdit.EditorComponent),' Page=', FindPageWithEditor(SrcEdit), ' ', SrcEdit.FileName]); SrcEdit.FocusEditor; // recursively calls NotebookPageChanged, via EditorEnter DebugLnExit(SRCED_PAGES, ['TSourceNotebook.NotebookPageChanged AFTER SetFocus ', DbgSName(SrcEdit.EditorComponent),' Page=', FindPageWithEditor(SrcEdit)]); end; UpdateStatusBar; UpdateActiveEditColors(SrcEdit.EditorComponent); if (DebugBoss.State in [dsPause, dsRun]) and not SrcEdit.HasExecutionMarks and (SrcEdit.FileName <> '') then SrcEdit.FillExecutionMarks; DoActiveEditorChanged; end; CheckCurrentCodeBufferChanged; finally DebugBoss.UnLockCommandProcessing; end; DebugLnExit(SRCED_PAGES, ['<< TSourceNotebook.NotebookPageChanged ']); end; procedure TSourceNotebook.ProcessParentCommand(Sender: TObject; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; var Handled: boolean); var IDECmd: TIDECommand; r: Boolean; begin //DebugLn(['TSourceNotebook.ProcessParentCommand START ',dbgsName(Sender),' Command=',Command,' AChar=',AChar]); FProcessingCommand:=true; if Assigned(Manager.OnProcessUserCommand) then begin Handled:=false; IDECmd:=IDECommandList.FindIDECommand(Command); r := (IDECmd <> nil) and (IDECmd.OnExecuteProc = @ExecuteIdeMenuClick); if r then IDECmd.OnExecuteProc := nil; Manager.OnProcessUserCommand(Self,Command,Handled); if r then IDECmd.OnExecuteProc := @ExecuteIdeMenuClick; if Handled or (Command=ecNone) then begin FProcessingCommand:=false; Command:=ecNone; exit; end; end; //DebugLn(['TSourceNotebook.ProcessParentCommand after mainide: ',dbgsName(Sender),' Command=',Command,' AChar=',AChar]); Handled:=true; case Command of ecNextEditor: NextEditor; ecPrevEditor: PrevEditor; ecPrevEditorInHistory: FHistoryDlg.Show(True); ecNextEditorInHistory: FHistoryDlg.Show(False); ecMoveEditorLeft: MoveActivePageLeft; ecMoveEditorRight: MoveActivePageRight; ecMoveEditorLeftmost: MoveActivePageFirst; ecMoveEditorRightmost: MoveActivePageLast; ecNextSharedEditor: GotoNextSharedEditor(False); ecPrevSharedEditor: GotoNextSharedEditor(True); ecNextWindow: GotoNextWindow(False); ecPrevWindow: GotoNextWindow(True); ecMoveEditorNextWindow: MoveEditorNextWindow(False, False); ecMoveEditorPrevWindow: MoveEditorNextWindow(True, False); ecMoveEditorNewWindow: if EditorCount > 1 then MoveEditor(FindPageWithEditor(GetActiveSE), Manager.IndexOfSourceWindow(Manager.CreateNewWindow(True)), -1); ecCopyEditorNextWindow: MoveEditorNextWindow(False, True); ecCopyEditorPrevWindow: MoveEditorNextWindow(True, True); ecCopyEditorNewWindow: CopyEditor(FindPageWithEditor(GetActiveSE), Manager.IndexOfSourceWindow(Manager.CreateNewWindow(True)), -1, True); ecOpenFileAtCursor: OpenAtCursorClicked(self); ecGotoEditor1..ecGotoEditor9,ecGotoEditor0: if PageCount>Command-ecGotoEditor1 then PageIndex := Command-ecGotoEditor1; ecToggleFormUnit: ToggleFormUnitClicked(Self); ecToggleObjectInsp: ToggleObjectInspClicked(Self); ecSetFreeBookmark: if Assigned(Manager.OnSetBookmark) then Manager.OnSetBookmark(GetActiveSE, -1, False); ecClearAllBookmark: if Assigned(Manager.OnClearBookmarkId) then Manager.OnClearBookmarkId(Self, -1); ecJumpBack: Manager.HistoryJump(Self,jhaBack); ecJumpForward: Manager.HistoryJump(Self,jhaForward); ecAddJumpPoint: Manager.AddJumpPointClicked(Self); ecViewJumpHistory: Manager.ViewJumpHistoryClicked(Self); else Handled:=ExecuteIDECommand(Self,Command); DebugLn('TSourceNotebook.ProcessParentCommand Command=',dbgs(Command),' Handled=',dbgs(Handled)); end; //case if Handled then Command:=ecNone; FProcessingCommand:=false; end; procedure TSourceNotebook.ParentCommandProcessed(Sender: TObject; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; var Handled: boolean); begin Assert(Assigned(Manager), 'TSourceNotebook.ParentCommandProcessed: Manager=Nil.'); if Assigned(Manager.OnUserCommandProcessed) then begin Handled:=false; Manager.OnUserCommandProcessed(Self,Command,Handled); if Handled then exit; end; Handled:=(Command=ecClose); if Handled then Command:=ecNone; end; procedure TSourceNotebook.ReloadEditorOptions; var I: integer; Begin for i := 0 to EditorCount-1 do Editors[i].RefreshEditorSettings; if EditorOpts.ShowTabCloseButtons then FNoteBook.Options:=FNoteBook.Options+[nboShowCloseButtons] else FNoteBook.Options:=FNoteBook.Options-[nboShowCloseButtons]; FNoteBook.MultiLine := EditorOpts.MultiLineTab; if FNotebook.ShowTabs then FNotebook.TabPosition := EditorOpts.TabPosition else FNotebook.TabPosition := tpTop; Exclude(States,snWarnedFont); CheckFont; UpdatePageNames; end; procedure TSourceNotebook.CheckFont; var SrcEdit: TSourceEditor; DummyResult: TModalResult; CurFont: TFont; begin if (snWarnedFont in States) then exit; Include(States,snWarnedFont); SrcEdit:=GetActiveSE; if SrcEdit = nil then Exit; CurFont:=SrcEdit.EditorComponent.Font; if SystemCharSetIsUTF8 and ((EditorOpts.DoNotWarnForFont='') or (EditorOpts.DoNotWarnForFont<>CurFont.Name)) then begin {$IFDEF HasMonoSpaceFonts} DummyResult:=IDEQuestionDialog(lisUEFontWith, Format(lisUETheCurre, [LineEnding, LineEnding]), mtWarning, [mrIgnore, mrYesToAll, lisUEDoNotSho]); {$ELSE} DummyResult:=mrYesToAll; {$ENDIF} if DummyResult=mrYesToAll then begin if EditorOpts.DoNotWarnForFont<>CurFont.Name then begin EditorOpts.DoNotWarnForFont:=CurFont.Name; EditorOpts.Save; end; end; end; end; procedure TSourceNotebook.BeginAutoFocusLock; begin inc(fAutoFocusLock); end; procedure TSourceNotebook.EndAutoFocusLock; begin dec(fAutoFocusLock); end; procedure TSourceNotebook.EditorMouseMove(Sender: TObject; Shift: TShiftstate; X, Y: Integer); begin Manager.FHints.HideAutoHintAfterMouseMoved; if Visible then Manager.FHints.UpdateHintTimer; end; procedure TSourceNotebook.EditorMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); begin //handled:=true; //The scrolling is not done: it's not handled! See TWinControl.DoMouseWheel end; procedure TSourceNotebook.EditorMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftstate; X, Y: Integer); begin end; function TSourceNotebook.EditorGetIndent(Sender: TObject; Editor: TObject; LogCaret, OldLogCaret: TPoint; FirstLinePos, LastLinePos: Integer; Reason: TSynEditorCommand; SetIndentProc: TSynBeautifierSetIndentProc ): Boolean; var SrcEdit: TSourceEditor; p: LongInt; NestedComments: Boolean; NewIndent: TFABIndentationPolicy; Indent: LongInt; CodeBuf: TCodeBuffer; begin Result:=false; // SynBeautifier is shared arrcoss SynEdits, and may call the wrong SrcNoteBook if assigned(Manager) then SrcEdit := Manager.FindSourceEditorWithEditorComponent(TComponent(Editor)) else SrcEdit := FindSourceEditorWithEditorComponent(TComponent(Editor)); if SrcEdit = nil then exit; if assigned(Manager) and Assigned(Manager.OnGetIndent) then begin Result := Manager.OnGetIndent(Sender, SrcEdit, LogCaret, OldLogCaret, FirstLinePos, LastLinePos, Reason, SetIndentProc); if Result then exit; end; if (SrcEdit.SyncroLockCount > 0) then exit; if not (SrcEdit.SyntaxHighlighterType in [lshFreePascal, lshDelphi]) then exit; if Reason<>ecLineBreak then exit; if not CodeToolsOpts.IndentOnLineBreak then exit; {$IFDEF VerboseIndenter} debugln(['TSourceNotebook.EditorGetIndent LogCaret=',dbgs(LogCaret),' FirstLinePos=',FirstLinePos,' LastLinePos=',LastLinePos]); {$ENDIF} Result := True; SrcEdit.UpdateCodeBuffer; CodeBuf:=SrcEdit.CodeBuffer; CodeBuf.LineColToPosition(LogCaret.Y,LogCaret.X,p); if p<1 then exit; {$IFDEF VerboseIndenter} if FirstLinePos>0 then DebugLn(['TSourceNotebook.EditorGetIndent Firstline-1=',SrcEdit.Lines[FirstLinePos-2]]); DebugLn(['TSourceNotebook.EditorGetIndent Firstline+0=',SrcEdit.Lines[FirstLinePos-1]]); if FirstLinePosnil) then CodeContextFrm.Hide; if (Manager<>nil) and (Manager.FHints<>nil) then Manager.FHints.HideIfVisible; end; procedure TSourceNotebook.EditorKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin end; procedure TSourceNotebook.EditorKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin end; procedure TSourceNotebook.ShowSynEditHint(const MousePos: TPoint); var EditPos: TPoint; ASrcEdit: TSourceEditor; ASynEdit: TSynEdit; EditCaret: TPoint; AMark: TSourceMark; i: integer; HintStr: String; CurHint: String; MLine: TSynEditMarkLine; begin // hide other hints //debugln('TSourceNotebook.ShowSynEditHint A'); Application.HideHint; // ASrcEdit:=GetActiveSE; if ASrcEdit=nil then exit; ASynEdit:=ASrcEdit.EditorComponent; EditPos:=ASynEdit.ScreenToClient(MousePos); if not PtInRect(ASynEdit.ClientRect,EditPos) then exit; EditCaret:=ASynEdit.PhysicalToLogicalPos(ASynEdit.PixelsToRowColumn(EditPos)); if (EditCaret.Y<1) then exit; if EditPos.X nil then begin if ASynEdit.BookMarkOptions.DrawBookmarksFirst then MLine.Sort(smsoBookmarkFirst, smsoColumn) else MLine.Sort(smsoBookMarkLast, smsoColumn); for i := 0 to MLine.Count - 1 do begin if not (MLine[i] is TSourceMark) then continue; AMark := TSourceMark(MLine[i]); if AMark = nil then continue; CurHint:=AMark.GetHint; if CurHint='' then continue; if HintStr<>'' then HintStr:=HintStr+LineEnding; HintStr:=HintStr+CurHint; end; if (MessagesView<>nil) then MessagesView.SourceEditorHint(MLine,HintStr); end; if HintStr<>'' then Manager.ActivateHint(MousePos,'',HintStr); end; end else begin // hint for source if Assigned(Manager) and Assigned(Manager.OnShowHintForSource) then Manager.OnShowHintForSource(ASrcEdit,EditCaret, True); end; end; procedure TSourceNotebook.SetIncrementalSearchStr(const AValue: string); begin if FIncrementalSearchStr=AValue then exit; FIncrementalSearchStr:=AValue; IncrementalSearch(False, False); end; procedure TSourceNotebook.IncrementalSearch(ANext, ABackward: Boolean); const SEARCH_OPTS: array[Boolean] of TSynSearchOptions = ([], [ssoBackwards]); var CurEdit: TSynEdit; AStart : TPoint; begin if not (snIncrementalFind in States) then begin UpdateStatusBar; Exit; end; if FIncrementalSearchEditor = nil then Exit; // search string CurEdit := FIncrementalSearchEditor.EditorComponent; CurEdit.BeginUpdate; if FIncrementalSearchStr<>'' then begin // search from search start position when not searching for the next AStart := CurEdit.LogicalCaretXY; if not ANext then AStart := FIncrementalSearchStartPos else if ABackward then AStart := CurEdit.BlockBegin; FIncrementalSearchBackwards:=ABackward; CurEdit.SearchReplaceEx(FIncrementalSearchStr,'', SEARCH_OPTS[ABackward], AStart); // searching next resets incremental history if ANext then begin FIncrementalSearchStartPos := CurEdit.BlockBegin; end; // cut the not found FIncrementalSearchStr := CurEdit.SelText; CurEdit.SetHighlightSearch(FIncrementalSearchStr, []); if Length(FIncrementalSearchStr) > 0 then FIncrementalFoundStr := FIncrementalSearchStr; end else begin // go to start CurEdit.LogicalCaretXY:= FIncrementalSearchStartPos; CurEdit.BlockBegin:=CurEdit.LogicalCaretXY; CurEdit.BlockEnd:=CurEdit.BlockBegin; CurEdit.SetHighlightSearch('', []); end; FIncrementalSearchPos:=CurEdit.LogicalCaretXY; CurEdit.EndUpdate; UpdateStatusBar; end; procedure TSourceNotebook.Activate; begin inherited Activate; if assigned(Manager) then begin Manager.ActiveSourceWindow := self; Manager.DoWindowFocused(Self); end; end; procedure TSourceNotebook.UpdateActiveEditColors(AEditor: TSynEdit); begin if AEditor=nil then exit; EditorOpts.SetMarkupColors(AEditor); AEditor.UseIncrementalColor:= snIncrementalFind in States; end; procedure TSourceNotebook.ClearExecutionLines; var i: integer; begin for i := 0 to EditorCount - 1 do Editors[i].ExecutionLine := -1; end; procedure TSourceNotebook.ClearExecutionMarks; var i: integer; begin for i := 0 to EditorCount - 1 do Editors[i].ClearExecutionMarks; end; //----------------------------------------------------------------------------- procedure InternalInit; var h: TLazSyntaxHighlighter; begin // fetch the resourcestrings before they are translated EnglishGPLNotice:=lisGPLNotice; EnglishLGPLNotice:=lisLGPLNotice; EnglishModifiedLGPLNotice:=lisModifiedLGPLNotice; EnglishMITNotice:=lisMITNotice; for h:=Low(TLazSyntaxHighlighter) to High(TLazSyntaxHighlighter) do Highlighters[h]:=nil; IDESearchInText:=@SearchInText; PasBeautifier := TSynBeautifierPascal.Create(nil); SRCED_LOCK := DebugLogger.RegisterLogGroup('SRCED_LOCK' {$IFDEF SRCED_LOCK} , True {$ENDIF} ); SRCED_OPEN := DebugLogger.RegisterLogGroup('SRCED_OPEN' {$IFDEF SRCED_OPEN} , True {$ENDIF} ); SRCED_CLOSE := DebugLogger.RegisterLogGroup('SRCED_CLOSE' {$IFDEF SRCED_CLOSE} , True {$ENDIF} ); SRCED_PAGES := DebugLogger.RegisterLogGroup('SRCED_PAGES' {$IFDEF SRCED_PAGES} , True {$ENDIF} ); IDEWindowsGlobalOptions.Add(NonModalIDEWindowNames[nmiwSourceNoteBook], False); end; procedure InternalFinal; var h: TLazSyntaxHighlighter; begin for h:=Low(TLazSyntaxHighlighter) to High(TLazSyntaxHighlighter) do FreeThenNil(Highlighters[h]); FreeThenNil(aWordCompletion); FreeAndNil(PasBeautifier); end; { TSourceEditorManagerBase } procedure TSourceEditorManagerBase.DoMacroRecorderState(Sender: TObject); var i: Integer; begin For i := 0 to SourceWindowCount - 1 do TSourceNotebook(SourceWindows[i]).UpdateStatusBar; DoEditorMacroStateChanged; end; procedure TSourceEditorManagerBase.FreeSourceWindows; var s: TSourceEditorWindowInterface; begin PasBeautifier.OnGetDesiredIndent := nil; FSourceWindowByFocusList.Clear; while FSourceWindowList.Count > 0 do begin s := TSourceEditorWindowInterface(FSourceWindowList[0]); FSourceWindowList.Delete(0); s.Free; end; FSourceWindowList.Clear; end; function TSourceEditorManagerBase.GetActiveSourceWindowIndex: integer; begin Result := IndexOfSourceWindow(ActiveSourceWindow); end; function TSourceEditorManagerBase.GetSourceWindowByLastFocused(Index: Integer): TSourceEditorWindowInterface; begin Result := TSourceEditorWindowInterface(FSourceWindowByFocusList[Index]); end; procedure TSourceEditorManagerBase.SetActiveSourceWindowIndex(const AValue: integer); begin ActiveSourceWindow := SourceWindows[AValue]; end; procedure TSourceEditorManagerBase.SetShowTabs(const AShowTabs: Boolean); begin FShowTabs := AShowTabs; end; function TSourceEditorManagerBase.GetActiveSourceWindow: TSourceEditorWindowInterface; begin Result := FActiveWindow; end; procedure TSourceEditorManagerBase.SetActiveSourceWindow( const AValue: TSourceEditorWindowInterface); var NewWindow: TSourceNotebook; begin NewWindow:= AValue as TSourceNotebook; if NewWindow = FActiveWindow then exit; //debugln(['TSourceEditorManagerBase.SetActiveSourceWindow ',dbgSourceNoteBook(FActiveWindow),' ',dbgSourceNoteBook(NewWindow)]); if (FActiveWindow <> nil) and (NewWindow <> nil) and (FActiveWindow.Focused) then NewWindow.SetFocus; FActiveWindow := NewWindow; FSourceWindowByFocusList.Remove(NewWindow); FSourceWindowByFocusList.Insert(0, NewWindow); if Assigned(OnCurrentCodeBufferChanged) then OnCurrentCodeBufferChanged(nil); FChangeNotifyLists[semWindowActivate].CallNotifyEvents(FActiveWindow); DoActiveEditorChanged; end; function TSourceEditorManagerBase.GetSourceWindows(Index: integer ): TSourceEditorWindowInterface; begin Result := TSourceEditorWindowInterface(FSourceWindowList[Index]); end; procedure TSourceEditorManagerBase.DoWindowFocused(AWindow: TSourceNotebook); begin FChangeNotifyLists[semWindowFocused].CallNotifyEvents(FActiveWindow); end; function TSourceEditorManagerBase.GetActiveEditor: TSourceEditorInterface; begin If FActiveWindow <> nil then Result := FActiveWindow.ActiveEditor else Result := nil; end; procedure TSourceEditorManagerBase.SetActiveEditor(const AValue: TSourceEditorInterface); var Window: TSourceEditorWindowInterface; begin inc(FActiveEditorLock); try if (FActiveWindow <> nil) and (FActiveWindow.IndexOfEditor(AValue) >= 0) then Window := FActiveWindow else Window := SourceWindowWithEditor(AValue); if Window = nil then exit; ActiveSourceWindow := TSourceNotebook(Window); Window.ActiveEditor := AValue; finally dec(FActiveEditorLock); DoActiveEditorChanged; end; end; procedure TSourceEditorManagerBase.DoActiveEditorChanged; begin if FActiveEditorLock > 0 then exit; if FUpdateLock > 0 then begin include(FUpdateFlags, ufMgrActiveEditorChanged); exit; end; exclude(FUpdateFlags, ufMgrActiveEditorChanged); FChangeNotifyLists[semEditorActivate].CallNotifyEvents(ActiveEditor); end; procedure TSourceEditorManagerBase.DoEditorStatusChanged(AEditor: TSourceEditor); begin CodeToolsToSrcEditTimer.Enabled:=false; FChangeNotifyLists[semEditorStatus].CallNotifyEvents(AEditor); end; function TSourceEditorManagerBase.GetSourceEditors(Index: integer): TSourceEditorInterface; var i: Integer; begin i := 0; while (i < SourceWindowCount) and (Index >= SourceWindows[i].Count) do begin Index := Index - SourceWindows[i].Count; inc(i); end; if (i < SourceWindowCount) then Result := SourceWindows[i].Items[Index] else Result := nil; end; function TSourceEditorManagerBase.GetUniqueSourceEditors(Index: integer): TSourceEditorInterface; var i: Integer; begin for i := 0 to SourceEditorCount - 1 do begin Result := SourceEditors[i]; if (TSourceEditor(Result).SharedEditorCount = 0) or (TSourceEditor(Result).SharedEditors[0] = Result) then dec(Index); if Index < 0 then exit; end; Result := nil; end; function TSourceEditorManagerBase.SourceWindowWithEditor( const AEditor: TSourceEditorInterface): TSourceEditorWindowInterface; var i: Integer; begin Result := nil; for i := FSourceWindowList.Count-1 downto 0 do begin if TSourceNotebook(SourceWindows[i]).IndexOfEditor(AEditor) >= 0 then begin Result := SourceWindows[i]; break; end; end; end; function TSourceEditorManagerBase.SourceWindowCount: integer; begin if assigned(FSourceWindowList) then Result := FSourceWindowList.Count else Result := 0; end; function TSourceEditorManagerBase.IndexOfSourceWindow( AWindow: TSourceEditorWindowInterface): integer; begin Result := SourceWindowCount - 1; while Result >= 0 do Begin if SourceWindows[Result] = AWindow then exit; dec(Result); end; end; function TSourceEditorManagerBase.IndexOfSourceWindowByLastFocused(AWindow: TSourceEditorWindowInterface): integer; begin Result := FSourceWindowByFocusList.IndexOf(AWindow); end; function TSourceEditorManagerBase.SourceEditorIntfWithFilename( const Filename: string): TSourceEditorInterface; var i: Integer; begin for i:=0 to SourceWindowCount-1 do begin Result:=SourceWindows[i].SourceEditorIntfWithFilename(Filename); if Result<>nil then exit; end; Result:=nil; end; function TSourceEditorManagerBase.SourceEditorCount: integer; var i: Integer; begin Result := 0; for i := 0 to SourceWindowCount - 1 do Result := Result + SourceWindows[i].Count; end; function TSourceEditorManagerBase.UniqueSourceEditorCount: integer; var SrcEdit: TSourceEditor; i: Integer; begin Result := 0; for i := 0 to SourceEditorCount - 1 do begin SrcEdit := TSourceEditor(SourceEditors[i]); if (SrcEdit.SharedEditorCount = 0) or (SrcEdit.SharedEditors[0] = SrcEdit) then inc(Result); end; end; function TSourceEditorManagerBase.GetEditorControlSettings(EditControl: TControl): boolean; begin Result:=true; if EditControl is TSynEdit then begin EditorOpts.GetSynEditSettings(TSynEdit(EditControl)); Result:=true; end else begin Result:=false; end; end; function TSourceEditorManagerBase.GetHighlighterSettings(Highlighter: TObject): boolean; begin Result:=true; if Highlighter is TSynCustomHighlighter then begin EditorOpts.GetHighlighterSettings(TSynCustomHighlighter(Highlighter)); Result:=true; end else begin Result:=false; end; end; function TSourceEditorManagerBase.GetDefaultCompletionForm: TSourceEditCompletion; var i: Integer; begin Result := FDefaultCompletionForm; if Result <> nil then exit; FDefaultCompletionForm := TSourceEditCompletion.Create(Self); FDefaultCompletionForm.LongLineHintTime := EditorOpts.CompletionLongLineHintInMSec; FDefaultCompletionForm.LongLineHintType := EditorOpts.CompletionLongLineHintType; Result := FDefaultCompletionForm; for i:=0 to SourceEditorCount - 1 do FDefaultCompletionForm.AddEditor(TSourceEditor(SourceEditors[i]).EditorComponent); end; function TSourceEditorManagerBase.GetDefaultSynCompletionForm: TCustomForm; begin if Assigned(GetDefaultCompletionForm) then Result := GetDefaultCompletionForm.TheForm else Result := nil; end; function TSourceEditorManagerBase.GetSynCompletionLinesInWindow: integer; var ComplForm: TCustomForm; begin ComplForm := GetDefaultSynCompletionForm; if Assigned(ComplForm) then Result := TSynBaseCompletionForm(ComplForm).NbLinesInWindow else Result := -1; end; procedure TSourceEditorManagerBase.SetSynCompletionLinesInWindow(LineCnt: integer); var ComplForm: TCustomForm; begin ComplForm := GetDefaultSynCompletionForm; if Assigned(ComplForm) then TSynBaseCompletionForm(ComplForm).NbLinesInWindow := LineCnt; end; procedure TSourceEditorManagerBase.FreeCompletionPlugins; var p: TSourceEditorCompletionPlugin; begin while FCompletionPlugins.Count > 0 do begin p := TSourceEditorCompletionPlugin(FCompletionPlugins[0]); FCompletionPlugins.Delete(0); p.Free; end; FCompletionPlugins.Clear; end; function TSourceEditorManagerBase.GetScreenRectForToken(AnEditor: TCustomSynEdit; PhysColumn, PhysRow, EndColumn: Integer): TRect; begin Result.TopLeft := AnEditor.ClientToScreen(AnEditor.RowColumnToPixels(Point(PhysColumn, PhysRow))); Result.BottomRight := AnEditor.ClientToScreen(AnEditor.RowColumnToPixels(Point(EndColumn+1, PhysRow+1))); end; function TSourceEditorManagerBase.GetShowTabs: Boolean; begin Result := FShowTabs; end; function TSourceEditorManagerBase.GetMarklingProducers(Index: integer ): TSourceMarklingProducer; begin Result:=TSourceMarklingProducer(fProducers[Index]); end; procedure TSourceEditorManagerBase.DoWindowShow(AWindow: TSourceNotebook); begin FChangeNotifyLists[semWindowShow].CallNotifyEvents(AWindow); end; procedure TSourceEditorManagerBase.DoWindowHide(AWindow: TSourceNotebook); begin FChangeNotifyLists[semWindowHide].CallNotifyEvents(AWindow); end; procedure TSourceEditorManagerBase.SyncMessageWnd(Sender: TObject); begin MessagesView.MessagesFrame1.ApplyMultiSrcChanges(Sender as TETMultiSrcChanges); end; procedure TSourceEditorManagerBase.BeginAutoFocusLock; begin inc(FAutoFocusLock); end; procedure TSourceEditorManagerBase.EndAutoFocusLock; begin dec(FAutoFocusLock); end; function TSourceEditorManagerBase.HasAutoFocusLock: Boolean; begin Result := FAutoFocusLock > 0; end; function TSourceEditorManagerBase.GetActiveCompletionPlugin: TSourceEditorCompletionPlugin; begin Result := FActiveCompletionPlugin; end; function TSourceEditorManagerBase.GetCompletionBoxPosition: integer; begin Result:=-1; if (FDefaultCompletionForm<>nil) and FDefaultCompletionForm.IsActive then Result := FDefaultCompletionForm.Position; end; function TSourceEditorManagerBase.GetCompletionPlugins(Index: integer ): TSourceEditorCompletionPlugin; begin Result:=TSourceEditorCompletionPlugin(fCompletionPlugins[Index]); end; function TSourceEditorManagerBase.FindIdentCompletionPlugin( SrcEdit: TSourceEditor; JumpToError: boolean; var s: string; var BoxX, BoxY: integer; var UseWordCompletion: boolean): boolean; var i: Integer; Plugin: TSourceEditorCompletionPlugin; Handled: Boolean; Cancel: Boolean; begin for i:=0 to CompletionPluginCount-1 do begin Plugin := CompletionPlugins[i]; Handled:=false; Cancel:=false; Plugin.Init(SrcEdit,JumpToError,Handled,Cancel,s,BoxX,BoxY); if Cancel then begin DeactivateCompletionForm; exit(false); end; if Handled then begin FActiveCompletionPlugin:=Plugin; exit(true); end; end; if not (SrcEdit.SyntaxHighlighterType in [lshFreePascal, lshDelphi]) then UseWordCompletion:=true; Result:=true; end; function TSourceEditorManagerBase.CompletionPluginCount: integer; begin Result:=fCompletionPlugins.Count; end; procedure TSourceEditorManagerBase.DeactivateCompletionForm; var PluginFocused: Boolean; begin {$IFDEF VerboseIDECompletionBox} DebugLnEnter(['>> TSourceEditorManagerBase.DeactivateCompletionForm']); try {$ENDIF} if ActiveCompletionPlugin<>nil then begin ActiveCompletionPlugin.Cancel; FActiveCompletionPlugin:=nil; end; if (FDefaultCompletionForm=nil) or (FDefaultCompletionForm.CurrentCompletionType = ctNone) then exit; // Do not move focus, if it was moved by user PluginFocused := FDefaultCompletionForm.TheForm.Focused; {$IFDEF VerboseIDECompletionBox} DebugLn(['DeactivateCompletionForm PluginFocused=', dbgs(PluginFocused), ' ActiveEditor=', DbgSName(ActiveEditor)]); {$ENDIF} // clear the IdentifierList (otherwise it would try to update everytime // the codetools are used) CodeToolBoss.IdentifierList.Clear; FDefaultCompletionForm.CurrentCompletionType:=ctNone; // SetFocus and Deactivate will all trigger this proc to be reentered. // Setting "CurrentCompletionType:=ctNone" ensures an immediate exit // Due to a bug under XFCE we must move focus before we close the form // This is relevant if the form is closed by enter/escape key if PluginFocused and (ActiveEditor<>nil) then TSourceEditor(ActiveEditor).FocusEditor; // hide/close the form FDefaultCompletionForm.Deactivate; // Ensure focus *after* the form was closed. // This is the normal implementation (all but XFCE) if PluginFocused and (ActiveEditor<>nil) then TSourceEditor(ActiveEditor).FocusEditor; {$IFDEF VerboseIDECompletionBox} finally DebugLnExit(['<< TSourceEditorManagerBase.DeactivateCompletionForm']); end; {$ENDIF} end; procedure TSourceEditorManagerBase.RegisterCompletionPlugin( Plugin: TSourceEditorCompletionPlugin); begin fCompletionPlugins.Add(Plugin); Plugin.FreeNotification(Self); end; procedure TSourceEditorManagerBase.UnregisterCompletionPlugin( Plugin: TSourceEditorCompletionPlugin); begin Plugin.RemoveFreeNotification(Self); fCompletionPlugins.Remove(Plugin); end; procedure TSourceEditorManagerBase.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if Operation=opRemove then begin if Assigned(fCompletionPlugins) then fCompletionPlugins.Remove(AComponent); if ActiveCompletionPlugin = AComponent then DeactivateCompletionForm; if AComponent is TSourceMarklingProducer then fProducers.Remove(AComponent); end; end; constructor TSourceEditorManagerBase.Create(AOwner: TComponent); var i: TsemChangeReason; h: TSrcEditMangerHandlerType; begin FMacroRecorder := TIdeEditorMacro.Create(Self); FMacroRecorder.OnStateChange := @DoMacroRecorderState; OnEditorMacroStateChange := @DoMacroRecorderState; if EditorMacroForRecording = nil then EditorMacroForRecording := FMacroRecorder; FShowTabs := True; FUpdateFlags := []; FAutoFocusLock := 0; for i := low(TsemChangeReason) to high(TsemChangeReason) do FChangeNotifyLists[i] := TMethodList.Create; for h:=low(FHandlers) to high(FHandlers) do FHandlers[h] := TMethodList.Create; SrcEditorIntf.SourceEditorManagerIntf := Self; FSourceWindowList := TFPList.Create; FSourceWindowByFocusList := TFPList.Create; FCompletionPlugins := TFPList.Create; FUpdateLock := 0; FActiveEditorLock := 0; fProducers := TFPList.Create; FChangesQueuedForMsgWnd:=TETMultiSrcChanges.Create(Self); FChangesQueuedForMsgWnd.AutoSync:=true; FChangesQueuedForMsgWnd.OnSync:=@SyncMessageWnd; inherited; end; destructor TSourceEditorManagerBase.Destroy; var i: integer; cr: TsemChangeReason; h: TSrcEditMangerHandlerType; begin FreeAndNil(FChangesQueuedForMsgWnd); for i:=MarklingProducerCount-1 downto 0 do MarklingProducers[i].Free; FreeAndNil(fProducers); FActiveWindow := nil; FreeCompletionPlugins; FreeSourceWindows; SrcEditorIntf.SourceEditorManagerIntf := nil; // xx move down if EditorMacroForRecording = FMacroRecorder then EditorMacroForRecording := nil; FreeAndNil(FMacroRecorder); FreeAndNil(FCompletionPlugins); FreeAndNil(FSourceWindowList); FreeAndNil(FSourceWindowByFocusList); inherited Destroy; for cr := low(TsemChangeReason) to high(TsemChangeReason) do FreeAndNil(FChangeNotifyLists[cr]); for h:=low(FHandlers) to high(FHandlers) do FreeAndNil(FHandlers[h]); end; procedure TSourceEditorManagerBase.RegisterChangeEvent( AReason: TsemChangeReason; AHandler: TNotifyEvent); begin FChangeNotifyLists[AReason].Add(TMethod(AHandler)); end; procedure TSourceEditorManagerBase.UnRegisterChangeEvent( AReason: TsemChangeReason; AHandler: TNotifyEvent); begin FChangeNotifyLists[AReason].Remove(TMethod(AHandler)); end; procedure TSourceEditorManagerBase.RegisterCopyPasteEvent( AHandler: TSemCopyPasteEvent); begin FHandlers[semhtCopyPaste].Add(TMethod(AHandler)); end; procedure TSourceEditorManagerBase.UnRegisterCopyPasteEvent( AHandler: TSemCopyPasteEvent); begin FHandlers[semhtCopyPaste].Remove(TMethod(AHandler)); end; function TSourceEditorManagerBase.MarklingProducerCount: integer; begin Result:=fProducers.Count; end; procedure TSourceEditorManagerBase.RegisterMarklingProducer( aProducer: TSourceMarklingProducer); begin if fProducers.IndexOf(aProducer)>=0 then RaiseGDBException('TSourceEditorManagerBase.RegisterProducer already registered'); fProducers.Add(aProducer); FreeNotification(aProducer); end; procedure TSourceEditorManagerBase.UnregisterMarklingProducer( aProducer: TSourceMarklingProducer); var i: LongInt; begin i:=fProducers.IndexOf(aProducer); if i<0 then exit; fProducers.Delete(i); RemoveFreeNotification(aProducer); end; procedure TSourceEditorManagerBase.InvalidateMarklingsOfAllFiles( aProducer: TSourceMarklingProducer); var SrcWnd: TSourceEditorWindowInterface; i, j: Integer; SrcEdit: TSourceEditor; begin if aProducer=nil then exit; for i := 0 to SourceWindowCount - 1 do begin SrcWnd:=SourceWindows[i]; for j:=0 to SrcWnd.Count-1 do begin SrcEdit:=TSourceEditor(SrcWnd[j]); SrcEdit.FSharedValues.FMarklingsValid:=false; end; end; end; procedure TSourceEditorManagerBase.InvalidateMarklings( aProducer: TSourceMarklingProducer; aFilename: string); var SrcWnd: TSourceEditorWindowInterface; i: Integer; SrcEdit: TSourceEditor; begin if aProducer=nil then exit; for i := 0 to SourceWindowCount - 1 do begin SrcWnd:=SourceWindows[i]; SrcEdit:=TSourceEditor(SrcWnd.SourceEditorIntfWithFilename(aFilename)); if SrcEdit<>nil then SrcEdit.FSharedValues.FMarklingsValid:=false; end; end; procedure TSourceEditorManagerBase.IncUpdateLockInternal; begin if FUpdateLock = 0 then begin FUpdateFlags := []; // Debugger cause ProcessMessages, which could lead to entering methods in unexpected order DebugBoss.LockCommandProcessing; Screen.BeginWaitCursor; end; inc(FUpdateLock); end; procedure TSourceEditorManagerBase.DecUpdateLockInternal; begin dec(FUpdateLock); if FUpdateLock = 0 then begin try Screen.EndWaitCursor; if (ufShowWindowOnTop in FUpdateFlags) then ShowActiveWindowOnTop(ufShowWindowOnTopFocus in FUpdateFlags); if (ufMgrActiveEditorChanged in FUpdateFlags) then DoActiveEditorChanged; finally DebugBoss.UnLockCommandProcessing; end; end; end; procedure TSourceEditorManagerBase.IncUpdateLock; var i: Integer; begin IncUpdateLockInternal; for i := 0 to SourceWindowCount - 1 do TSourceNotebook(SourceWindows[i]).IncUpdateLockInternal; end; procedure TSourceEditorManagerBase.DecUpdateLock; var i: Integer; begin try for i := 0 to SourceWindowCount - 1 do TSourceNotebook(SourceWindows[i]).DecUpdateLockInternal; finally DecUpdateLockInternal; end; end; procedure TSourceEditorManagerBase.ShowActiveWindowOnTop(Focus: Boolean); begin if ActiveSourceWindow = nil then exit; if FUpdateLock > 0 then begin include(FUpdateFlags, ufShowWindowOnTop); if Focus then include(FUpdateFlags, ufShowWindowOnTopFocus); exit; end; IDEWindowCreators.ShowForm(ActiveSourceWindow,true); if Focus and ActiveSourceWindow.IsVisible then TSourceNotebook(ActiveSourceWindow).FocusEditor; end; { TSourceEditorManager } function TSourceEditorManager.GetActiveSourceNotebook: TSourceNotebook; begin Result := TSourceNotebook(inherited ActiveSourceWindow); end; function TSourceEditorManager.GetActiveSrcEditor: TSourceEditor; begin Result := TSourceEditor(inherited ActiveEditor); end; function TSourceEditorManager.GetSourceEditorsByPage(WindowIndex, PageIndex: integer): TSourceEditor; begin if SourceWindows[WindowIndex] <> nil then Result := SourceWindows[WindowIndex].FindSourceEditorWithPageIndex(PageIndex) else Result := nil; end; function TSourceEditorManager.GetSourceNbByLastFocused(Index: Integer): TSourceNotebook; begin Result := TSourceNotebook(inherited SourceWindowByLastFocused[Index]); end; function TSourceEditorManager.GetSrcEditors(Index: integer): TSourceEditor; begin Result := TSourceEditor(inherited SourceEditors[Index]); end; procedure TSourceEditorManager.SetActiveSourceNotebook(const AValue: TSourceNotebook); begin inherited ActiveSourceWindow := AValue; end; function TSourceEditorManager.GetSourceNotebook(Index: integer): TSourceNotebook; begin Result := TSourceNotebook(inherited SourceWindows[Index]); end; procedure TSourceEditorManager.SetActiveSrcEditor(const AValue: TSourceEditor); begin inherited ActiveEditor := AValue; end; function TSourceEditorManager.SourceWindowWithEditor( const AEditor: TSourceEditorInterface): TSourceNotebook; begin Result := TSourceNotebook(inherited SourceWindowWithEditor(AEditor)); end; function TSourceEditorManager.ActiveOrNewSourceWindow: TSourceNotebook; var i: Integer; begin Result := ActiveSourceWindow; if Result <> nil then exit; if SourceWindowCount>0 then begin for i:=0 to SourceWindowCount-1 do begin Result:=SourceWindows[i]; if Result.FIsClosing then continue; ActiveSourceWindow := Result; exit; end; end; Result := CreateNewWindow(True); ActiveSourceWindow := Result; end; procedure TSourceEditorManager.AddCustomJumpPoint(ACaretXY: TPoint; ATopLine: integer; AEditor: TSourceEditor; DeleteForwardHistory: boolean); begin if Assigned(OnAddJumpPoint) then OnAddJumpPoint(ACaretXY, ATopLine, AEditor, DeleteForwardHistory); end; function TSourceEditorManager.NewSourceWindow: TSourceNotebook; begin Result := CreateNewWindow(True); ActiveSourceWindow := Result; end; procedure TSourceEditorManager.CreateSourceWindow(Sender: TObject; aFormName: string; var AForm: TCustomForm; DoDisableAutoSizing: boolean); var i: integer; begin {$IFDEF VerboseIDEDocking} debugln(['TSourceEditorManager.CreateSourceWindow Sender=',DbgSName(Sender),' FormName="',aFormName,'"']); {$ENDIF} // Get ID from Name i := length(aFormName); while (i >= 1) and (aFormName[i] in ['0'..'9']) do dec(i); inc(i); i := StrToIntDef(copy(aFormName, i, MaxInt), 1)-1; AForm := CreateNewWindow(false,DoDisableAutoSizing, i); AForm.Name:=aFormName; end; procedure TSourceEditorManager.GetDefaultLayout(Sender: TObject; aFormName: string; out aBounds: TRect; out DockSibling: string; out DockAlign: TAlign); var i: LongInt; ScreenR: TRect; begin DockSibling:=''; DockAlign:=alNone; i:=StrToIntDef( copy(aFormName,length(NonModalIDEWindowNames[nmiwSourceNoteBook])+1,length(aFormName)), 0); {$IFDEF VerboseIDEDocking} debugln(['TSourceEditorManager.GetDefaultLayout ',aFormName,' i=',i]); {$ENDIF} ScreenR:=IDEWindowCreators.GetScreenrectForDefaults; if ObjectInspector1<>nil then begin aBounds.Left := ObjectInspector1.Left + ObjectInspector1.Width + ObjectInspector1.Scale96ToForm(5); aBounds.Top := ObjectInspector1.Top; end else begin aBounds.Left := ScreenR.Left+MulDiv(200, Screen.PixelsPerInch, 96); aBounds.Top := ScreenR.Top+MulDiv(120, Screen.PixelsPerInch, 96); end; Inc(aBounds.Left,MulDiv(30, Screen.PixelsPerInch, 96)*i); Inc(aBounds.Top, MulDiv(30, Screen.PixelsPerInch, 96)*i); aBounds.Right:=ScreenR.Right-MulDiv(30, Screen.PixelsPerInch, 96); aBounds.Bottom:=ScreenR.Bottom-MulDiv(200, Screen.PixelsPerInch, 96); if (i=0) and (IDEDockMaster<>nil) then begin DockSibling:=NonModalIDEWindowNames[nmiwMainIDE]; DockAlign:=alBottom; end; end; function TSourceEditorManager.SourceWindowWithPage(const APage: TTabSheet): TSourceNotebook; var i: Integer; begin Result := nil; for i := FSourceWindowList.Count-1 downto 0 do begin if TSourceNotebook(SourceWindows[i]).FNoteBook.IndexOf(APage) >= 0 then begin Result := SourceWindows[i]; break; end; end; end; procedure TSourceEditorManager.SrcEditMenuProcedureJumpGetCaption( Sender: TObject; var ACaption, AHint: string); var ShortFileName, CurFilename: String; MainCodeBuf: TCodeBuffer; CodeTool: TCodeTool; CaretXY: TCodeXYPosition; CleanPos: integer; CodeNode: TCodeTreeNode; ProcNode: TCodeTreeNode; ProcName: String; SrcEdit: TSourceEditor; begin // ask Codetools SrcEdit:=GetActiveSE; if not Assigned(SrcEdit) then Exit; CurFilename:=SrcEdit.FileName; ShortFileName:=ExtractFileName(CurFilename); MainCodeBuf:=nil; if FilenameHasPascalExt(ShortFileName) or FilenameExtIs(ShortFileName,'inc') then MainCodeBuf:=CodeToolBoss.GetMainCode(SrcEdit.CodeBuffer) else if FilenameIsPascalSource(ShortFileName) then MainCodeBuf:=SrcEdit.CodeBuffer; CodeTool:=nil; CaretXY:=CleanCodeXYPosition; CaretXY.Code:=SrcEdit.CodeBuffer; CaretXY.X:=SrcEdit.CursorTextXY.X; CaretXY.Y:=SrcEdit.CursorTextXY.Y; CodeNode:=nil; if MainCodeBuf<>nil then begin CodeToolBoss.Explore(MainCodeBuf,CodeTool,true); if CodeTool<>nil then begin CodeTool.CaretToCleanPos(CaretXY,CleanPos); CodeNode:=CodeTool.FindDeepestNodeAtPos(CleanPos,false); end; end; ProcName:=''; if CodeNode<>nil then begin ProcNode:=CodeNode.GetNodeOfType(ctnProcedure); if ProcNode<>nil then ProcName:=CodeTool.ExtractProcName(ProcNode,[]); end; if ProcName<>'' then ACaption:=Format(lisJumpToProcedure, [ProcName]) else ACaption:=uemProcedureJump; end; function TSourceEditorManager.IndexOfSourceWindowWithID(const AnID: Integer): Integer; begin Result := SourceWindowCount - 1; while Result >= 0 do begin if SourceWindows[Result].WindowID = AnID then break; dec(Result); end; end; function TSourceEditorManager.SourceWindowWithID(const AnID: Integer): TSourceNotebook; var i: Integer; begin i := IndexOfSourceWindowWithID(AnID); if i >= 0 then Result := SourceWindows[i] else Result := CreateNewWindow(False, False, AnID); end; function TSourceEditorManager.SourceEditorCount: integer; var i: Integer; begin Result := 0; for i := 0 to SourceWindowCount - 1 do Result := Result + SourceWindows[i].Count; end; function TSourceEditorManager.GetActiveSE: TSourceEditor; begin Result := TSourceEditor(ActiveEditor); end; procedure TSourceEditorManager.SetWindowByIDAndPage(AWindowID, APageIndex: integer); begin ActiveSourceWindowIndex := IndexOfSourceWindowWithID(AWindowID); ActiveSourceWindow.PageIndex:= APageIndex; end; function TSourceEditorManager.SourceEditorIntfWithFilename( const Filename: string): TSourceEditor; begin Result := TSourceEditor(inherited SourceEditorIntfWithFilename(Filename)); end; function TSourceEditorManager.FindSourceEditorWithEditorComponent( EditorComp: TComponent): TSourceEditor; var i: Integer; begin Result := nil; i := SourceWindowCount - 1; while i >= 0 do begin Result := SourceWindows[i].FindSourceEditorWithEditorComponent(EditorComp); if Result <> nil then break; dec(i); end; end; procedure TSourceEditorManager.NewEditorCreated(AEditor: TSourceEditor); begin if FDefaultCompletionForm <> nil then FDefaultCompletionForm.AddEditor(AEditor.EditorComponent); end; procedure TSourceEditorManager.EditorRemoved(AEditor: TSourceEditor); begin if FDefaultCompletionForm <> nil then begin if FDefaultCompletionForm.Editor = AEditor.EditorComponent then DeactivateCompletionForm; FDefaultCompletionForm.RemoveEditor(AEditor.EditorComponent); end; end; procedure TSourceEditorManager.SendEditorCreated(AEditor: TSourceEditor); begin FChangeNotifyLists[semEditorCreate].CallNotifyEvents(AEditor); end; procedure TSourceEditorManager.SendEditorDestroyed(AEditor: TSourceEditor); begin FChangeNotifyLists[semEditorDestroy].CallNotifyEvents(AEditor); end; procedure TSourceEditorManager.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if Operation=opRemove then begin if AComponent is TSourceNotebook then RemoveWindow(TSourceNotebook(AComponent)); end; end; procedure TSourceEditorManager.ClearErrorLines; var i, j: Integer; SrcWin: TSourceNotebook; begin for i := FSourceWindowList.Count - 1 downto 0 do begin SrcWin := SourceWindows[i]; for j := 0 to SrcWin.EditorCount - 1 do SrcWin.Editors[j].ErrorLine := -1; end; end; procedure TSourceEditorManager.ClearExecutionLines; var i: Integer; begin for i := FSourceWindowList.Count - 1 downto 0 do SourceWindows[i].ClearExecutionLines; end; procedure TSourceEditorManager.ClearExecutionMarks; var i: Integer; begin for i := FSourceWindowList.Count - 1 downto 0 do SourceWindows[i].ClearExecutionMarks; end; procedure TSourceEditorManager.FillExecutionMarks; var i: Integer; SE: TSourceEditor; begin for i := FSourceWindowList.Count - 1 downto 0 do begin SE := SourceWindows[i].GetActiveSE; if SE <> nil then SE.FillExecutionMarks; end; end; procedure TSourceEditorManager.ReloadEditorOptions; var i: Integer; begin for i := FSourceWindowList.Count - 1 downto 0 do SourceWindows[i].ReloadEditorOptions; AutoStartCompletionBoxTimer.Interval:=EditorOpts.AutoDelayInMSec; // reload code templates EditorOpts.LoadCodeTemplates(CodeTemplateModul); CodeTemplateModul.IndentToTokenStart:=EditorOpts.CodeTemplateIndentToTokenStart; FHints.AutoHintTimer.Interval:=EditorOpts.AutoHintDelayInMSec; if FDefaultCompletionForm <> nil then begin FDefaultCompletionForm.LongLineHintTime := EditorOpts.CompletionLongLineHintInMSec; FDefaultCompletionForm.LongLineHintType := EditorOpts.CompletionLongLineHintType; end; end; function TSourceEditorManager.Beautify(const Src: string; const Flags: TSemBeautyFlags): string; var NewIndent, NewTabWidth: Integer; Beauty: TBeautifyCodeOptions; OldDoNotSplitLineInFront, OldDoNotSplitLineAfter: TAtomTypes; begin Beauty:=CodeToolBoss.SourceChangeCache.BeautifyCodeOptions; OldDoNotSplitLineInFront:=Beauty.DoNotSplitLineInFront; OldDoNotSplitLineAfter:=Beauty.DoNotSplitLineAfter; try if sembfNotBreakDots in Flags then begin Include(Beauty.DoNotSplitLineInFront,atPoint); Include(Beauty.DoNotSplitLineAfter,atPoint); end; Result:=CodeToolBoss.Beautifier.BeautifyStatement(Src,2,[bcfDoNotIndentFirstLine]); if (eoTabsToSpaces in EditorOpts.SynEditOptions) or (EditorOpts.BlockTabIndent=0) then NewTabWidth:=0 else NewTabWidth:=EditorOpts.TabWidth; NewIndent:=EditorOpts.BlockTabIndent*EditorOpts.TabWidth+EditorOpts.BlockIndent; Result:=BasicCodeTools.ReIndent(Result,2,0,NewIndent,NewTabWidth); finally Beauty.DoNotSplitLineInFront:=OldDoNotSplitLineInFront; Beauty.DoNotSplitLineAfter:=OldDoNotSplitLineAfter; end; end; procedure TSourceEditorManager.FindClicked(Sender: TObject); begin if ActiveEditor <> nil then ActiveEditor.StartFindAndReplace(false); end; procedure TSourceEditorManager.FindNextClicked(Sender: TObject); begin if ActiveEditor <> nil then ActiveEditor.FindNextUTF8; end; procedure TSourceEditorManager.FindPreviousClicked(Sender: TObject); begin if ActiveEditor <> nil then ActiveEditor.FindPrevious; end; procedure TSourceEditorManager.ReplaceClicked(Sender: TObject); begin if ActiveEditor <> nil then ActiveEditor.StartFindAndReplace(true); end; procedure TSourceEditorManager.IncrementalFindClicked(Sender: TObject); begin if ActiveSourceWindow <> nil then ActiveSourceWindow.BeginIncrementalFind; end; procedure TSourceEditorManager.GotoLineClicked(Sender: TObject); begin if ActiveEditor <> nil then ActiveEditor.ShowGotoLineDialog; end; procedure TSourceEditorManager.HideHint; begin FHints.HideHint; end; procedure TSourceEditorManager.JumpBackClicked(Sender: TObject); begin if ActiveSourceWindow <> nil then HistoryJump(Sender,jhaBack); end; procedure TSourceEditorManager.JumpForwardClicked(Sender: TObject); begin if ActiveSourceWindow <> nil then HistoryJump(Sender,jhaForward); end; procedure TSourceEditorManager.JumpToNextErrorClicked(Sender: TObject); begin LazarusIDE.DoJumpToNextError(true); end; procedure TSourceEditorManager.JumpToPrevErrorClicked(Sender: TObject); begin LazarusIDE.DoJumpToNextError(false); end; procedure TSourceEditorManager.JumpToImplementationClicked(Sender: TObject); begin JumpToSection(jmpImplementation); end; procedure TSourceEditorManager.JumpToImplementationUsesClicked(Sender: TObject); begin JumpToSection(jmpImplementationUses); end; procedure TSourceEditorManager.JumpToInitializationClicked(Sender: TObject); begin JumpToSection(jmpInitialization); end; procedure TSourceEditorManager.JumpToInterfaceClicked(Sender: TObject); begin JumpToSection(jmpInterface); end; procedure TSourceEditorManager.JumpToInterfaceUsesClicked(Sender: TObject); begin JumpToSection(jmpInterfaceUses); end; procedure TSourceEditorManager.JumpToPos(FileName: string; Pos: TCodeXYPosition; TopLine, BlockTopLine, BlockBottomLine: Integer); begin if (LazarusIDE.DoOpenFileAndJumpToPos(Filename ,Point(Pos.X,Pos.Y), TopLine, BlockTopLine, BlockBottomLine, -1,-1 ,[ofRegularFile,ofUseCache]) = mrOk) then ActiveEditor.EditorControl.SetFocus; end; procedure TSourceEditorManager.JumpToPos(FileName: string; Pos: TCodeXYPosition; TopLine: Integer); begin JumpToPos(FileName, Pos, TopLine, TopLine, TopLine); end; procedure TSourceEditorManager.JumpToProcedure(const JumpType: TJumpToProcedureType); const cJumpNames: array[TJumpToProcedureType] of string = ( 'procedure header', 'procedure begin'); var SrcEditor: TSourceEditorInterface; ProcNode: TCodeTreeNode; Tool: TCodeTool; CleanPos: integer; NewPos: TCodeXYPosition; NewTopLine, BlockTopLine, BlockBottomLine: integer; JumpFound: Boolean; begin if not LazarusIDE.BeginCodeTools then Exit; //==> SrcEditor := SourceEditorManagerIntf.ActiveEditor; if not Assigned(SrcEditor) then Exit; //==> if CodeToolBoss.Explore(SrcEditor.CodeToolsBuffer as TCodeBuffer, Tool, false, false) then begin if Tool.CaretToCleanPos( CodeXYPosition(SrcEditor.CursorTextXY.X, SrcEditor.CursorTextXY.Y, SrcEditor.CodeToolsBuffer as TCodeBuffer), CleanPos) <> 0 then Exit; ProcNode := Tool.FindDeepestNodeAtPos(CleanPos{%H-},true); while (ProcNode <> nil) and (ProcNode.Desc <> ctnProcedure) do ProcNode := ProcNode.Parent; if (ProcNode <> nil) and (ProcNode.Desc = ctnProcedure) then begin if JumpType = jmpBegin then JumpFound := Tool.FindJumpPointInProcNode(ProcNode, NewPos, NewTopLine, BlockTopLine, BlockBottomLine) else JumpFound := Tool.JumpToCleanPos(ProcNode.StartPos, ProcNode.StartPos, ProcNode.EndPos, NewPos, NewTopLine, BlockTopLine, BlockBottomLine, True); end else JumpFound := False; if JumpFound then JumpToPos(NewPos.Code.Filename, NewPos, NewTopLine, BlockTopLine, BlockBottomLine) else begin CodeToolBoss.SetError(20170421203224, nil, 0, 0, Format(lisCannotFind, [cJumpNames[JumpType]])); LazarusIDE.DoJumpToCodeToolBossError; end; end else LazarusIDE.DoJumpToCodeToolBossError; end; procedure TSourceEditorManager.JumpToProcedureBeginClicked(Sender: TObject); begin JumpToProcedure(jmpBegin); end; procedure TSourceEditorManager.JumpToProcedureHeaderClicked(Sender: TObject); begin JumpToProcedure(jmpHeader); end; procedure TSourceEditorManager.JumpToSection(JumpType: TJumpToSectionType); const cJumpNames: array[TJumpToSectionType] of string = ( 'Interface', 'Interface uses', 'Implementation', 'Implementation uses', 'Initialization'); var SrcEditor: TSourceEditorInterface; Node: TCodeTreeNode; Tool: TCodeTool; NewTopLine: Integer; NewCodePos: TCodeXYPosition; begin if not LazarusIDE.BeginCodeTools then Exit; //==> SrcEditor := SourceEditorManagerIntf.ActiveEditor; if not Assigned(SrcEditor) then Exit; //==> if CodeToolBoss.Explore(SrcEditor.CodeToolsBuffer as TCodeBuffer, Tool, false, false) then begin case JumpType of jmpInterface: Node := Tool.FindInterfaceNode; jmpInterfaceUses: begin Node := Tool.FindMainUsesNode; if Node = nil then//if the uses section is missing, jump to interface Node := Tool.FindInterfaceNode; end; jmpImplementation: Node := Tool.FindImplementationNode; jmpImplementationUses: begin Node := Tool.FindImplementationUsesNode; if Node = nil then//if the uses section is missing, jump to implementation Node := Tool.FindImplementationNode; end; jmpInitialization: begin Node := Tool.FindInitializationNode; if Node = nil then//if initialization is missing, jump to last end Node := Tool.FindRootNode(ctnEndPoint); end; end; if (Node <> nil) and Tool.CleanPosToCaretAndTopLine(Node.StartPos, NewCodePos, NewTopLine) then JumpToPos(NewCodePos.Code.Filename, NewCodePos, NewTopLine) else begin CodeToolBoss.SetError(20170421203236, nil, 0, 0, Format(lisCannotFind, [cJumpNames[JumpType]])); LazarusIDE.DoJumpToCodeToolBossError; end; end else LazarusIDE.DoJumpToCodeToolBossError; end; procedure TSourceEditorManager.AddJumpPointClicked(Sender: TObject); begin if Assigned(OnAddJumpPoint) and (ActiveEditor <> nil) then with ActiveEditor.EditorComponent do OnAddJumpPoint(LogicalCaretXY, TopLine, ActiveEditor, true); end; procedure TSourceEditorManager.DeleteLastJumpPointClicked(Sender: TObject); begin if Assigned(OnDeleteLastJumpPoint) then OnDeleteLastJumpPoint(Sender); end; procedure TSourceEditorManager.ViewJumpHistoryClicked(Sender: TObject); begin if Assigned(OnViewJumpHistory) then OnViewJumpHistory(Sender); end; procedure TSourceEditorManager.BookMarkToggleClicked(Sender: TObject); begin if Assigned(OnSetBookmark) then OnSetBookmark(ActiveEditor, (Sender as TIDEMenuItem).SectionIndex, True); end; procedure TSourceEditorManager.BookMarkGotoClicked(Sender: TObject); begin if Assigned(OnGotoBookmark) then OnGotoBookmark(ActiveEditor, (Sender as TIDEMenuItem).SectionIndex, False); end; procedure TSourceEditorManager.BookMarkNextClicked(Sender: TObject); begin if Assigned(OnGotoBookmark) then OnGotoBookmark(ActiveEditor, -1, False); end; procedure TSourceEditorManager.BookMarkPrevClicked(Sender: TObject); begin if Assigned(OnGotoBookmark) then OnGotoBookmark(ActiveEditor, -1, True); end; function TSourceEditorManager.MacroFuncCol(const s: string; const Data: PtrInt; var Abort: boolean): string; begin if (ActiveEditor <> nil) then Result:=IntToStr(ActiveEditor.EditorComponent.CaretX) else Result:=''; end; function TSourceEditorManager.MacroFuncRow(const s: string; const Data: PtrInt; var Abort: boolean): string; begin if (ActiveEditor <> nil) then Result:=IntToStr(ActiveEditor.EditorComponent.CaretY) else Result:=''; end; function TSourceEditorManager.MacroFuncEdFile(const s: string; const Data: PtrInt; var Abort: boolean): string; begin if (ActiveEditor <> nil) then Result := ActiveEditor.FileName else Result := ''; end; function TSourceEditorManager.MacroFuncCurToken(const s: string; const Data: PtrInt; var Abort: boolean): string; begin if (ActiveEditor <> nil) then begin with ActiveEditor.EditorComponent do Result := GetWordAtRowCol(LogicalCaretXY) end else Result := ''; end; function TSourceEditorManager.MacroFuncConfirm(const s: string; const Data: PtrInt; var Abort: boolean): string; begin Result:=s; Abort:=(ShowMacroConfirmDialog(Result)<>mrOk); end; function TSourceEditorManager.MacroFuncPrompt(const s: string; const Data: PtrInt; var Abort: boolean): string; begin Result:=s; Abort:=(ShowMacroPromptDialog(Result)<>mrOk); end; function TSourceEditorManager.MacroFuncSave(const s: string; const Data: PtrInt; var Abort: boolean): string; begin Result:=''; if SourceEditorCount > 0 then Abort:=LazarusIDE.DoSaveEditorFile(ActiveEditor,[sfCheckAmbiguousFiles]) <> mrOk; end; function TSourceEditorManager.MacroFuncSaveAll(const s: string; const Data: PtrInt; var Abort: boolean): string; begin Result:=''; Abort:=LazarusIDE.DoSaveAll([sfCheckAmbiguousFiles])<>mrOk; end; procedure TSourceEditorManager.InitMacros(AMacroList: TTransferMacroList); begin AMacroList.Add(TTransferMacro.Create('Col','', lisCursorColumnInCurrentEditor,@MacroFuncCol,[])); AMacroList.Add(TTransferMacro.Create('Row','', lisCursorRowInCUrrentEditor,@MacroFuncRow,[])); AMacroList.Add(TTransferMacro.Create('CurToken','', lisWordAtCursorInCurrentEditor,@MacroFuncCurToken,[])); AMacroList.Add(TTransferMacro.Create('EdFile','', lisExpandedFilenameOfCurrentEditor,@MacroFuncEdFile,[])); AMacroList.Add(TTransferMacro.Create('Confirm','', lisConfirmation,@MacroFuncConfirm,[tmfInteractive])); AMacroList.Add(TTransferMacro.Create('Prompt','', lisPromptForValue,@MacroFuncPrompt,[tmfInteractive])); AMacroList.Add(TTransferMacro.Create('Save','', lisSaveCurrentEditorFile,@MacroFuncSave,[tmfInteractive])); AMacroList.Add(TTransferMacro.Create('SaveAll','', lisSaveAllModified,@MacroFuncSaveAll,[tmfInteractive])); end; procedure TSourceEditorManager.SetupShortCuts; function GetCommand(ACommand: word; ToolButtonClass: TIDEToolButtonClass): TIDECommand; var ToolButton: TIDEButtonCommand; begin Result:=GetIdeCmdAndToolBtn(ACommand, ToolButton); if ToolButton<>nil then ToolButton.ToolButtonClass := ToolButtonClass; end; // See also in ToolBarIntf: // function GetCommand_DropDown // function GetCommand_ButtonDrop var i: Integer; begin {%region *** first static section *** } SrcEditMenuFindDeclaration.Command := GetIdeCmdRegToolBtn(ecFindDeclaration); {%region *** Submenu: Find Section *** } SrcEditMenuProcedureJump.Command := GetIdeCmdRegToolBtn(ecFindProcedureDefinition); SrcEditMenuProcedureJump.OnRequestCaptionHint := @SrcEditMenuProcedureJumpGetCaption; SrcEditMenuFindNextWordOccurrence.Command := GetIdeCmdRegToolBtn(ecFindNextWordOccurrence); SrcEditMenuFindPrevWordOccurrence.Command := GetIdeCmdRegToolBtn(ecFindPrevWordOccurrence); SrcEditMenuFindInFiles.Command := GetIdeCmdRegToolBtn(ecFindInFiles); SrcEditMenuFindIdentifierReferences.Command:=GetIdeCmdRegToolBtn(ecFindIdentifierRefs); SrcEditMenuFindUsedUnitReferences.Command:=GetIdeCmdRegToolBtn(ecFindUsedUnitRefs); {%endregion} {%endregion} {%region *** Pages section ***} SrcEditMenuClosePage.Command := GetIdeCmdRegToolBtn(ecClose); SrcEditMenuCloseOtherPages.OnClick := @SourceEditorManager.CloseOtherPagesClicked; SrcEditMenuCloseOtherPagesToRight.OnClick := @SourceEditorManager.CloseRightPagesClicked; {$IFnDEF SingleSrcWindow} SrcEditMenuEditorLock.Command := GetIdeCmdRegToolBtn(ecLockEditor); SrcEditMenuMoveToNewWindow.SyncProperties := False; SrcEditMenuMoveToNewWindow.Command := GetIdeCmdRegToolBtn(ecMoveEditorNewWindow); SrcEditMenuMoveToOtherWindowNew.SyncProperties := False; SrcEditMenuMoveToOtherWindowNew.Command := GetIdeCmdRegToolBtn(ecMoveEditorNewWindow); SrcEditMenuCopyToNewWindow.SyncProperties := False; SrcEditMenuCopyToNewWindow.Command := GetIdeCmdRegToolBtn(ecCopyEditorNewWindow); SrcEditMenuCopyToOtherWindowNew.SyncProperties := False; SrcEditMenuCopyToOtherWindowNew.Command := GetIdeCmdRegToolBtn(ecCopyEditorNewWindow); {$ENDIF} {%endregion} {%region * Move Page (left/right) *} SrcEditMenuMoveEditorLeft.Command := GetIdeCmdRegToolBtn(ecMoveEditorLeft); SrcEditMenuMoveEditorRight.Command := GetIdeCmdRegToolBtn(ecMoveEditorRight); SrcEditMenuMoveEditorFirst.Command := GetIdeCmdRegToolBtn(ecMoveEditorLeftmost); SrcEditMenuMoveEditorLast.Command := GetIdeCmdRegToolBtn(ecMoveEditorRightmost); {%endregion} SrcEditMenuOpenFileAtCursor.Command := GetIdeCmdRegToolBtn(ecOpenFileAtCursor); {%region * sub menu Flags section *} SrcEditMenuReadOnly.OnClick := @ReadOnlyClicked; SrcEditMenuShowLineNumbers.OnClick := @ToggleLineNumbersClicked; SrcEditMenuDisableI18NForLFM.OnClick := @ToggleI18NForLFMClicked; SrcEditMenuShowUnitInfo.OnClick := @ShowUnitInfo; {%endregion} {%region *** Clipboard section ***} SrcEditMenuCut.Command:=GetIdeCmdRegToolBtn(ecCut); SrcEditMenuCopy.Command:=GetIdeCmdRegToolBtn(ecCopy); SrcEditMenuPaste.Command:=GetIdeCmdRegToolBtn(ecPaste); SrcEditMenuMultiPaste.Command:=GetIdeCmdRegToolBtn(ecMultiPaste); SrcEditMenuCopyFilename.OnClick:=@CopyFilenameClicked; SrcEditMenuSelectAll.Command:=GetIdeCmdRegToolBtn(ecSelectAll); {%endregion} SrcEditMenuNextBookmark.Command:=GetIdeCmdRegToolBtn(ecNextBookmark); SrcEditMenuPrevBookmark.Command:=GetIdeCmdRegToolBtn(ecPrevBookmark); SrcEditMenuSetFreeBookmark.Command:=GetIdeCmdRegToolBtn(ecSetFreeBookmark); SrcEditMenuClearFileBookmark.Command:=GetIdeCmdRegToolBtn(ecClearBookmarkForFile); SrcEditMenuClearAllBookmark.Command:=GetIdeCmdRegToolBtn(ecClearAllBookmark); SrcEditSubMenuGotoBookmarks.Command:=GetCommand(ecGotoBookmarks, TToolButton_GotoBookmarks); SrcEditSubMenuToggleBookmarks.Command:=GetCommand(ecToggleBookmarks, TToolButton_ToggleBookmarks); for i in TBookmarkNumRange do SrcEditMenuGotoBookmark[i].Command := GetIdeCmdRegToolBtn(ecGotoMarker0 + i); for i in TBookmarkNumRange do SrcEditMenuToggleBookmark[i].Command := GetIdeCmdRegToolBtn(ecToggleMarker0 + i); {%region *** Source Section ***} SrcEditMenuEncloseSelection.Command:=GetIdeCmdRegToolBtn(ecSelectionEnclose); SrcEditMenuEncloseInIFDEF.Command:=GetIdeCmdRegToolBtn(ecSelectionEncloseIFDEF); SrcEditMenuCompleteCode.Command:=GetIdeCmdRegToolBtn(ecCompleteCode); SrcEditMenuUseUnit.Command:=GetIdeCmdRegToolBtn(ecUseUnit); {%endregion} {%region *** Refactoring Section ***} SrcEditMenuRenameIdentifier.Command:=GetIdeCmdRegToolBtn(ecRenameIdentifier); SrcEditMenuExtractProc.Command:=GetIdeCmdRegToolBtn(ecExtractProc); SrcEditMenuInvertAssignment.Command:=GetIdeCmdRegToolBtn(ecInvertAssignment); SrcEditMenuShowAbstractMethods.Command:=GetIdeCmdRegToolBtn(ecShowAbstractMethods); SrcEditMenuShowEmptyMethods.Command:=GetIdeCmdRegToolBtn(ecRemoveEmptyMethods); SrcEditMenuShowUnusedUnits.Command:=GetIdeCmdRegToolBtn(ecRemoveUnusedUnits); SrcEditMenuFindOverloads.Command:=GetIdeCmdRegToolBtn(ecFindOverloads); SrcEditMenuMakeResourceString.Command:=GetIdeCmdRegToolBtn(ecMakeResourceString); {%endregion} SrcEditMenuEditorProperties.OnClick:=@EditorPropertiesClicked; DebugBoss.SetupSourceMenuShortCuts; end; function TSourceEditorManager.FindUniquePageName(FileName: string; IgnoreEditor: TSourceEditor): string; var I:integer; ShortName:string; function PageNameExists(const AName:string):boolean; var a:integer; begin Result:=false; for a := 0 to SourceEditorCount - 1 do begin if (SourceEditors[a] <> IgnoreEditor) and (not SourceEditors[a].IsSharedWith(IgnoreEditor)) and (CompareText(AName, SourceEditors[a].PageName) = 0) then begin Result:=true; exit; end; end; end; begin if FileName='' then begin FileName:='unit1'; if not PageNameExists(FileName) then begin Result:=Filename; exit; end; end; if FilenameHasPascalExt(FileName) then ShortName:=ExtractFileNameOnly(Filename) else ShortName:=ExtractFileName(FileName); Result:=ShortName; if PageNameExists(Result) then begin i:=1; repeat inc(i); Result:=ShortName+'('+IntToStr(i)+')'; until PageNameExists(Result)=false; end; end; function TSourceEditorManager.SomethingModified(Verbose: boolean): boolean; var i: integer; begin for i:=0 to SourceEditorCount - 1 do begin if SourceEditors[i].Modified then begin if Verbose then debugln(['TSourceEditorManager.SomethingModified ',SourceEditors[i].FileName]); exit(true); end; end; Result:=false; end; procedure TSourceEditorManager.OnIdle(Sender: TObject; var Done: Boolean); var SrcEdit: TSourceEditor; i, j: Integer; aFilename: String; FreeList, FreeMarklings: boolean; Marklings: TFPList; Markling: TSourceMarkling; begin SrcEdit:=ActiveEditor; if (SrcEdit<>nil) and (not SrcEdit.FSharedValues.FMarklingsValid) then begin //debugln(['TSourceEditorManager.OnIdle ',MarklingProducerCount]); aFilename:=SrcEdit.FileName; SrcEdit.EditorComponent.BeginUpdate(False); for i:=0 to MarklingProducerCount-1 do begin Marklings:=MarklingProducers[i].GetMarklings(aFilename,FreeList,FreeMarklings); for j:=0 to Marklings.Count-1 do begin Markling:=TSourceMarkling(Marklings[j]); if Markling=nil then ; // ToDo: add mark to synedit //debugln(['TSourceEditorManager.OnIdle ',Markling.Id,' ',Markling.Line,',',Markling.Column]); end; if FreeMarklings then for j:=0 to Marklings.Count-1 do TObject(Marklings[j]).Free; if FreeList then Marklings.Free; end; SrcEdit.EditorComponent.EndUpdate; SrcEdit.FSharedValues.FMarklingsValid:=true; end; end; procedure TSourceEditorManager.OnUserInput(Sender: TObject; Msg: Cardinal); begin CodeToolsToSrcEditTimer.Enabled:=true; // Hints if FHints.HintIsComplex then begin // TODO: introduce property, to indicate if hint is interactive if FHints.PtIsOnHint(Mouse.CursorPos) then begin // ignore any action over Hint if FHints.CurHintWindow.Active then exit; if (Msg = WM_MOUSEMOVE) or (Msg = LM_MOUSELEAVE) {$IFDEF WINDOWS} or (Msg = WM_NCMOUSEMOVE) or ((Msg >= WM_MOUSEFIRST) and (Msg <= WM_MOUSELAST)) {$ENDIF} then exit; end; if (Msg = WM_MOUSEMOVE) {$IFDEF WINDOWS} or (Msg = WM_NCMOUSEMOVE){$ENDIF} then begin FHints.HideAutoHintAfterMouseMoved; exit; end; end; //debugln('TSourceEditorManager.OnUserInput'); // don't hide hint if Sender is a hint window or child control if not FHints.SenderIsHintControl(Sender) then FHints.HideAutoHint; end; procedure TSourceEditorManager.LockAllEditorsInSourceChangeCache; // lock all sourceeditors that are to be modified by the CodeToolBoss var i: integer; begin for i:=0 to SourceEditorCount - 1 do begin if CodeToolBoss.SourceChangeCache.BufferIsModified(SourceEditors[i].CodeBuffer) then SourceEditors[i].BeginGlobalUpdate; end; end; procedure TSourceEditorManager.UnlockAllEditorsInSourceChangeCache; // unlock all sourceeditors that were modified by the CodeToolBoss var i: integer; begin for i:=0 to SourceEditorCount - 1 do begin if CodeToolBoss.SourceChangeCache.BufferIsModified(SourceEditors[i].CodeBuffer) then SourceEditors[i].EndGlobalUpdate; end; end; procedure TSourceEditorManager.BeginGlobalUpdate; var i: integer; begin for i:=0 to SourceEditorCount - 1 do SourceEditors[i].BeginGlobalUpdate; end; procedure TSourceEditorManager.EndGlobalUpdate; var i: integer; begin for i:=0 to SourceEditorCount - 1 do SourceEditors[i].EndGlobalUpdate; end; procedure TSourceEditorManager.CloseFile(AEditor: TSourceEditorInterface); var i, j: Integer; begin i := SourceWindowCount - 1; while i >= 0 do begin j := SourceWindows[i].FindPageWithEditor(TSourceEditor(AEditor)); if j >= 0 then begin SourceWindows[i].CloseFile(j); break; end; dec(i); end; end; procedure TSourceEditorManager.HistoryJump(Sender: TObject; JumpAction: TJumpHistoryAction); var NewCaretXY: TPoint; NewTopLine: integer; NewEditor: TSourceEditor; begin if Assigned(OnJumpToHistoryPoint) then begin NewCaretXY.X:=-1; NewEditor:=nil; OnJumpToHistoryPoint(NewCaretXY,NewTopLine,NewEditor,JumpAction); if NewEditor<>nil then begin ActiveEditor := NewEditor; ShowActiveWindowOnTop(True); with NewEditor.EditorComponent do begin if not NewEditor.IsLocked then TopLine:=NewTopLine; LogicalCaretXY:=NewCaretXY; end; end; end; end; procedure TSourceEditorManager.ActivateHint(const ScreenPos: TPoint; const BaseURL, TheHint: string; AutoShown: Boolean; AMouseOffset: Boolean); begin if csDestroying in ComponentState then exit; FHints.ActivateHint(ScreenPos, BaseURL, TheHint, AutoShown, AMouseOffset); end; procedure TSourceEditorManager.ActivateHint(const ScreenRect: TRect; const BaseURL, TheHint: string; AutoShown: Boolean; AMouseOffset: Boolean); begin if csDestroying in ComponentState then exit; FHints.ActivateHint(ScreenRect, BaseURL, TheHint, AutoShown, AMouseOffset); end; procedure TSourceEditorManager.OnCodeTemplateTokenNotFound(Sender: TObject; AToken: string; AnEditor: TCustomSynEdit; var Index: integer); begin if Index=0 then ; //debugln('TSourceNotebook.OnCodeTemplateTokenNotFound ',AToken,',',AnEditor.ReadOnly,',',DefaultCompletionForm.CurrentCompletionType=ctNone); if (AnEditor.ReadOnly=false) and (DefaultCompletionForm.CurrentCompletionType=ctNone) then begin DefaultCompletionForm.CurrentCompletionType:=ctTemplateCompletion; DefaultCompletionForm.Editor:=AnEditor; DefaultCompletionForm.Execute (AToken, GetScreenRectForToken(AnEditor, AnEditor.CaretX-length(AToken), AnEditor.CaretY, AnEditor.CaretX-1)); end; end; procedure TSourceEditorManager.OnCodeTemplateExecuteCompletion( ASynAutoComplete: TCustomSynAutoComplete; Index: integer); var SrcEdit: TSourceEditorInterface; TemplateName: string; TemplateValue: string; TemplateComment: string; TemplateAttr: TStrings; begin SrcEdit:=FindSourceEditorWithEditorComponent(ASynAutoComplete.Editor); if SrcEdit=nil then SrcEdit := ActiveEditor; //debugln('TSourceNotebook.OnCodeTemplateExecuteCompletion A ',dbgsName(SrcEdit),' ',dbgsName(ASynAutoComplete.Editor)); TemplateName:=ASynAutoComplete.Completions[Index]; TemplateValue:=ASynAutoComplete.CompletionValues[Index]; TemplateComment:=ASynAutoComplete.CompletionComments[Index]; TemplateAttr:=ASynAutoComplete.CompletionAttributes[Index]; ExecuteCodeTemplate(SrcEdit,TemplateName,TemplateValue,TemplateComment, ASynAutoComplete.EndOfTokenChr,TemplateAttr, ASynAutoComplete.IndentToTokenStart); end; procedure TSourceEditorManager.CodeToolsToSrcEditTimerTimer(Sender: TObject); var i: Integer; SrcEdit: TSourceEditor; begin CodeToolsToSrcEditTimer.Enabled:=false; for i:=0 to SourceEditorCount-1 do begin SrcEdit:=SourceEditors[i]; if not SrcEdit.EditorComponent.IsVisible then continue; SrcEdit.UpdateIfDefNodeStates; end; end; procedure TSourceEditorManager.OnSourceCompletionTimer(Sender: TObject); function CheckCodeAttribute (XY: TPoint; out CodeAttri: String): Boolean; var SrcEdit: TSourceEditor; begin Result := False; SrcEdit := ActiveEditor; if SrcEdit = nil then exit; dec(XY.X); CodeAttri := SrcEdit.GetCodeAttributeName(XY); Result := CodeAttri <> ''; end; function CheckStartIdentCompletion: boolean; var Line: String; LogCaret: TPoint; SrcEdit: TSourceEditor; CodeAttribute: String; begin Result := false; SrcEdit := ActiveEditor; if SrcEdit = nil then exit; if not (SrcEdit.FEditor.Highlighter is TSynPasSyn) then exit; // only start completion automatically for pascal sources Line := SrcEdit.FEditor.LineText; LogCaret := SrcEdit.FEditor.LogicalCaretXY; //DebugLn(['CheckStartIdentCompletion Line="',Line,'" LogCaret=',dbgs(LogCaret)]); // check if last character is a point if (Line='') or (LogCaret.X<=1) or (LogCaret.X-1>length(Line)) or ((SrcEdit.FCodeCompletionState.State = ccsDot) and (Line[LogCaret.X-1]<>'.')) then exit; if not CheckCodeAttribute(LogCaret, CodeAttribute) then Exit; if (CodeAttribute = SYNS_XML_AttrComment) or (CodeAttribute = SYNS_XML_AttrString) then Exit; // check if range operator '..' if (LogCaret.X>2) and (Line[LogCaret.X-2]='.') then exit; // this is a double point .. // invoke identifier completion SrcEdit.StartIdentCompletionBox(false,false); Result:=true; end; function CheckTemplateCompletion: boolean; begin Result:=false; // execute context sensitive templates //FCodeTemplateModul.ExecuteCompletion(Value,GetActiveSE.EditorComponent); end; var TempEditor: TSourceEditor; begin AutoStartCompletionBoxTimer.Enabled:=false; AutoStartCompletionBoxTimer.AutoEnabled:=false; TempEditor := ActiveEditor; if (TempEditor <> nil) and TempEditor.EditorComponent.Focused and (ComparePoints(TempEditor.EditorComponent.LogicalCaretXY, SourceCompletionCaretXY) = 0) then begin if CheckStartIdentCompletion then begin end else if CheckTemplateCompletion then begin end; end; end; procedure TSourceEditorManager.OnSourceMarksAction(AMark: TSourceMark; AAction: TMarksAction); var Editor: TSourceEditor; begin Editor := TSourceEditor(AMark.SourceEditor); if Editor = nil then Exit; if ( AMark.IsBreakPoint and (Editor.FSharedValues.ExecutionMark <> nil) and (AMark.Line = Editor.ExecutionLine) ) or (AMark = Editor.FSharedValues.ExecutionMark) then Editor.UpdateExecutionSourceMark; end; procedure TSourceEditorManager.OnSourceMarksGetSynEdit(Sender: TObject; aFilename: string; var aSynEdit: TSynEdit); var SrcEdit: TSourceEditor; begin SrcEdit:=SourceEditorIntfWithFilename(aFilename); if SrcEdit=nil then exit; aSynEdit:=SrcEdit.EditorComponent; end; function TSourceEditorManager.GotoDialog: TfrmGoto; begin if FGotoDialog=nil then FGotoDialog := TfrmGoto.Create(self); Result := FGotoDialog; end; procedure TSourceEditorManager.DoConfigureEditorToolbar(Sender: TObject); begin LazarusIDE.DoOpenIDEOptions(TEditorToolbarOptionsFrame, '', [], []); end; constructor TSourceEditorManager.Create(AOwner: TComponent); begin inherited Create(AOwner); FDefaultCompletionForm := nil; // word completion if aWordCompletion=nil then begin AWordCompletion:=TSourceEditorWordCompletion.Create; AWordCompletion.WordBufferCapacity:=100; end; // timer for auto start identifier completion AutoStartCompletionBoxTimer := TIdleTimer.Create(Self); with AutoStartCompletionBoxTimer do begin Name:='AutoStartCompletionBoxTimer'; AutoEnabled := False; Enabled := false; Interval := EditorOpts.AutoDelayInMSec; OnTimer := @OnSourceCompletionTimer; end; // timer for syncing codetools changes to synedit // started on idle // ended on user input // when triggered updates ifdef node states CodeToolsToSrcEditTimer:=TTimer.Create(Self); with CodeToolsToSrcEditTimer do begin Name:='CodeToolsToSrcEditTimer'; Interval:=1000; // one second without user input Enabled:=false; OnTimer:=@CodeToolsToSrcEditTimerTimer; end; // marks SourceEditorMarks:=TSourceMarks.Create(Self); SourceEditorMarks.OnAction:=@OnSourceMarksAction; SourceEditorMarks.ExtToolsMarks.OnGetSynEditOfFile:=@OnSourceMarksGetSynEdit; // HintWindow FHints := TSourceEditorHintWindowManager.Create(Self); FHints.WindowName := Self.Name+'_HintWindow'; FHints.HideInterval := 4000; // code templates FCodeTemplateModul:=TSynEditAutoComplete.Create(Self); EditorOpts.LoadCodeTemplates(FCodeTemplateModul); with FCodeTemplateModul do begin IndentToTokenStart := EditorOpts.CodeTemplateIndentToTokenStart; OnTokenNotFound := @OnCodeTemplateTokenNotFound; OnExecuteCompletion := @OnCodeTemplateExecuteCompletion; EndOfTokenChr:=' ()[]{},.;:"+-*^@$\<>='''; end; // EditorToolBar CreateEditorToolBar(@DoConfigureEditorToolbar); // layout IDEWindowCreators.Add(NonModalIDEWindowNames[nmiwSourceNoteBook], nil,@CreateSourceWindow,'250','100','+70%','+70%', NonModalIDEWindowNames[nmiwMainIDE],alBottom,true,@GetDefaultLayout); Application.AddOnIdleHandler(@OnIdle); Application.AddOnUserInputHandler(@OnUserInput); end; destructor TSourceEditorManager.Destroy; begin FreeAndNil(FHints); SourceEditorMarks.OnAction := nil; Application.RemoveAllHandlersOfObject(Self); // aWordCompletion is released in InternalFinal aWordCompletion.OnGetSource := nil; inherited Destroy; end; function SortSourceWindows(SrcWin1, SrcWin2: TSourceNotebook): Integer; begin Result := AnsiStrComp(PChar(SrcWin1.Caption), PChar(SrcWin2.Caption)); end; function TSourceEditorManager.CreateNewWindow(Activate: Boolean; DoDisableAutoSizing: boolean; AnID: Integer): TSourceNotebook; var i: Integer; begin Result := TSourceNotebook(TSourceNotebook.NewInstance); {$IFDEF DebugDisableAutoSizing} if DoDisableAutoSizing then Result.DisableAutoSizing('TAnchorDockMaster Delayed') else Result.DisableAutoSizing('TSourceEditorManager.CreateNewWindow'); {$ELSE} Result.DisableAutoSizing; {$ENDIF}; if AnID > 0 then Result.Create(Self, AnID) else Result.Create(Self); for i := 1 to FUpdateLock do Result.IncUpdateLockInternal; FSourceWindowList.Add(Result); FSourceWindowList.Sort(TListSortCompare(@SortSourceWindows)); FSourceWindowByFocusList.Add(Result); PasBeautifier.OnGetDesiredIndent := @TSourceNotebook(FSourceWindowList[0]).EditorGetIndent; if Activate then begin ActiveSourceWindow := Result; ShowActiveWindowOnTop(False); end; FChangeNotifyLists[semWindowCreate].CallNotifyEvents(Result); if not DoDisableAutoSizing then Result.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TSourceEditorManager.CreateNewWindow'){$ENDIF}; end; function TSourceEditorManager.SenderToEditor(Sender: TObject): TSourceEditor; begin if Sender is TSourceEditor then Result:=TSourceEditor(Sender) else if Sender is TSourceNotebook then Result:=TSourceNotebook(Sender).ActiveEditor as TSourceEditor else Result:=ActiveEditor; end; procedure TSourceEditorManager.RemoveWindow(AWindow: TSourceNotebook); var i: Integer; begin if FSourceWindowList = nil then exit; i := FSourceWindowList.IndexOf(AWindow); FSourceWindowList.Remove(AWindow); FSourceWindowByFocusList.Remove(AWindow); if SourceWindowCount = 0 then ActiveSourceWindow := nil else if ActiveSourceWindow = AWindow then ActiveSourceWindow := SourceWindows[Max(0, Min(i, SourceWindowCount-1))]; if FSourceWindowList.Count > 0 then PasBeautifier.OnGetDesiredIndent := @TSourceNotebook(FSourceWindowList[0]).EditorGetIndent else PasBeautifier.OnGetDesiredIndent := nil; if i >= 0 then FChangeNotifyLists[semWindowDestroy].CallNotifyEvents(AWindow); end; (* Context Menu handlers *) procedure TSourceEditorManager.CloseOtherPagesClicked(Sender: TObject); begin if Assigned(OnCloseClicked) then OnCloseClicked(Sender, [ceoCloseOthers]); end; procedure TSourceEditorManager.CloseOtherPagesClickedAsync(Sender: PtrInt); begin CloseOtherPagesClicked(TObject(Sender)); end; procedure TSourceEditorManager.CloseRightPagesClicked(Sender: TObject); begin if Assigned(OnCloseClicked) then OnCloseClicked(Sender, [ceoCloseOthersOnRightSide]); end; procedure TSourceEditorManager.CloseRightPagesClickedAsync(Sender: PtrInt); begin CloseRightPagesClicked(TObject(Sender)); end; procedure TSourceEditorManager.ReadOnlyClicked(Sender: TObject); var ActEdit: TSourceEditor; begin ActEdit:=ActiveEditor; if ActEdit = nil then exit; if ActEdit.ReadOnly and (ActEdit.CodeBuffer<>nil) and (not ActEdit.CodeBuffer.IsVirtual) and (not FileIsWritable(ActEdit.CodeBuffer.Filename)) then begin IDEMessageDialog(ueFileROCap, ueFileROText1+ActEdit.CodeBuffer.Filename+ueFileROText2, mtError,[mbCancel]); exit; end; ActEdit.EditorComponent.ReadOnly := not(ActEdit.EditorComponent.ReadOnly); if Assigned(OnReadOnlyChanged) then OnReadOnlyChanged(Self); ActEdit.SourceNotebook.UpdateStatusBar; end; procedure TSourceEditorManager.ToggleLineNumbersClicked(Sender: TObject); var MenuITem: TIDESpecialCommand; ActEdit:TSourceEditor; i: integer; ShowLineNumbers: boolean; begin MenuItem := Sender as TIDESpecialCommand; ActEdit:=ActiveEditor; if ActEdit = nil then exit; MenuItem.Checked := not EditorOpts.ShowLineNumbers; ShowLineNumbers:=MenuItem.Checked; for i:=0 to SourceEditorCount-1 do SourceEditors[i].EditorComponent.Gutter.LineNumberPart.Visible := ShowLineNumbers; EditorOpts.ShowLineNumbers := ShowLineNumbers; EditorOpts.Save; end; procedure TSourceEditorManager.ToggleI18NForLFMClicked(Sender: TObject); begin // end; procedure TSourceEditorManager.ShowUnitInfo(Sender: TObject); begin if Assigned(OnShowUnitInfo) then OnShowUnitInfo(Sender); end; procedure TSourceEditorManager.CopyFilenameClicked(Sender: TObject); var ActSE: TSourceEditor; begin ActSE := GetActiveSE; if ActSE <> nil then Clipboard.AsText:=ActSE.FileName; end; procedure TSourceEditorManager.EditorPropertiesClicked(Sender: TObject); begin LazarusIDE.DoOpenIDEOptions(TEditorGeneralOptionsFrame); end; initialization InternalInit; finalization InternalFinal; end.