From 0a5042a856ce92e46464f8ee4911e13e1dc321c2 Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 21 Jul 2009 22:44:09 +0000 Subject: [PATCH] SynEdit: Added Syncron-edit mode git-svn-id: trunk@20917 - --- .gitattributes | 2 + components/synedit/allsyneditunits.pp | 2 +- components/synedit/synedit.pp | 46 +- components/synedit/syneditmarkup.pp | 15 +- components/synedit/syneditmiscclasses.pp | 12 +- components/synedit/syneditmousecmds.pp | 72 +- components/synedit/synpluginsyncroedit.pp | 1456 +++++++++++++++++ .../synedit/synpluginsyncronizededitbase.pp | 666 ++++++-- components/synedit/synplugintemplateedit.pp | 226 +-- ide/editoroptions.pp | 65 +- ide/keymapping.pp | 153 +- ide/lazarusidestrconsts.pas | 15 + ide/main.pp | 4 + ide/sourceeditor.pp | 8 +- ideintf/idecommands.pas | 6 + images/bookmark.lrs | 27 + images/bookmark_list.txt | 1 + images/sourceeditor/tsynsyncroedit.png | Bin 0 -> 618 bytes 18 files changed, 2434 insertions(+), 342 deletions(-) create mode 100644 components/synedit/synpluginsyncroedit.pp create mode 100644 images/sourceeditor/tsynsyncroedit.png diff --git a/.gitattributes b/.gitattributes index ab71df63e7..5b222c7355 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1634,6 +1634,7 @@ components/synedit/synhighlightervb.pas svneol=native#text/plain components/synedit/synhighlighterxml.pas svneol=native#text/pascal components/synedit/synmacrorecorder.pas svneol=native#text/pascal components/synedit/synmemo.pas svneol=native#text/pascal +components/synedit/synpluginsyncroedit.pp svneol=native#text/pascal components/synedit/synpluginsyncronizededitbase.pp svneol=native#text/pascal components/synedit/synplugintemplateedit.pp svneol=native#text/pascal components/synedit/synpropertyeditobjectlist.lfm svneol=native#text/plain @@ -3627,6 +3628,7 @@ images/sourceeditor/bookmark6.png -text images/sourceeditor/bookmark7.png -text images/sourceeditor/bookmark8.png -text images/sourceeditor/bookmark9.png -text +images/sourceeditor/tsynsyncroedit.png -text images/splash_logo.bat svneol=native#text/x-msdos-program images/splash_logo.lrs svneol=native#text/pascal images/splash_logo.png -text svneol=unset#image/png diff --git a/components/synedit/allsyneditunits.pp b/components/synedit/allsyneditunits.pp index 4eb877e6ca..b1f5585b3d 100644 --- a/components/synedit/allsyneditunits.pp +++ b/components/synedit/allsyneditunits.pp @@ -19,7 +19,7 @@ uses SynEdit, SynEditHighlighter, SynCompletion, SynEditAutoComplete, SynEditLazDsgn, SynRegExpr, SynEditRegexSearch, SynEditExport, SynExportHTML, SynMemo, SynMacroRecorder, SynEditPlugins, - SynPluginSyncronizedEditBase, SynPluginTemplateEdit, + SynPluginSyncronizedEditBase, SynPluginTemplateEdit, SynPluginSyncroEdit, SynHighlighterAny, SynhighlighterCPP, SynHighlighterCss, diff --git a/components/synedit/synedit.pp b/components/synedit/synedit.pp index 27f2648579..309fbf35d4 100644 --- a/components/synedit/synedit.pp +++ b/components/synedit/synedit.pp @@ -333,7 +333,6 @@ type private fFirstLine: integer; fBlockIndent: integer; - FBlockSelection: TSynEditSelection; {$IFDEF SYN_LAZARUS} FCaret: TSynEditCaret; FInternalCaret: TSynEditCaret; @@ -407,6 +406,8 @@ type FCaretWidth: Integer; // Width of caret in chars (for Overwrite caret) FKeyStrokes, FLastKeyStrokes: TSynEditKeyStrokes; FMouseActions, FMouseSelActions: TSynEditMouseActions; + FMouseActionSearchHandlerList: TSynEditMouseActionSearchList; + FMouseActionExecHandlerList: TSynEditMouseActionExecList; fModified: Boolean; fMarkList: TSynEditMarkList; fExtraLineSpacing: integer; @@ -811,6 +812,12 @@ type procedure RegisterCommandHandler(AHandlerProc: THookedCommandEvent; AHandlerData: pointer); procedure UnregisterCommandHandler(AHandlerProc: THookedCommandEvent); + + procedure RegisterMouseActionSearchHandler(AHandlerProc: TSynEditMouseActionSearchProc); + procedure UnregisterMouseActionSearchHandler(AHandlerProc: TSynEditMouseActionSearchProc); + procedure RegisterMouseActionExecHandler(AHandlerProc: TSynEditMouseActionExecProc); + procedure UnregisterMouseActionExecHandler(AHandlerProc: TSynEditMouseActionExecProc); + procedure RegisterKeyTranslationHandler(AHandlerProc: THookedKeyTranslationEvent); procedure UnRegisterKeyTranslationHandler(AHandlerProc: THookedKeyTranslationEvent); function RowColumnToPixels( @@ -1640,6 +1647,9 @@ begin FMouseSelActions := TSynEditMouseSelActions.Create(Self); FMouseActions.ResetDefaults; FMouseSelActions.ResetDefaults; + FMouseActionSearchHandlerList := TSynEditMouseActionSearchList.Create; + FMouseActionExecHandlerList := TSynEditMouseActionExecList.Create; + fMarkList := TSynEditMarkList.Create(self, FTheLinesView); fMarkList.OnChange := {$IFDEF FPC}@{$ENDIF}MarkListChange; fRightEdgeColor := clSilver; @@ -1786,6 +1796,8 @@ begin FreeAndNil(fMarkList); FreeAndNil(fBookMarkOpt); FreeAndNil(fKeyStrokes); + FreeAndNil(FMouseActionSearchHandlerList); + FreeAndNil(FMouseActionExecHandlerList); FreeAndNil(FMouseActions); FreeAndNil(FMouseSelActions); FreeAndNil(fUndoList); @@ -2330,7 +2342,12 @@ begin ACommand := AnAction.Command; AnInfo.CaretDone := False; - Result := FGutter.DoHandleMouseAction(AnAction, AnInfo); + // Plugins/External + Result := FMouseActionExecHandlerList.CallExecHandlers(AnAction, AnInfo); + // Gutter + if not Result then + Result := FGutter.DoHandleMouseAction(AnAction, AnInfo); + if Result then begin if (not AnInfo.CaretDone) and AnAction.MoveCaret then MoveCaret; @@ -2457,6 +2474,11 @@ begin CCount := ACCount; Dir := ADir; end; + // Check plugins/external handlers + if FMouseActionSearchHandlerList.CallSearchHandlers(Info, + {$IFDEF FPC}@{$ENDIF}DoHandleMouseAction) + then + exit; // mouse event occured in Gutter ? if (X <= fGutterWidth) then begin FGutter.MaybeHandleMouseAction(Info, {$IFDEF FPC}@{$ENDIF}DoHandleMouseAction); @@ -8216,6 +8238,26 @@ begin {$ENDIF} end; +procedure TCustomSynEdit.RegisterMouseActionSearchHandler(AHandlerProc: TSynEditMouseActionSearchProc); +begin + FMouseActionSearchHandlerList.Add(TMethod(AHandlerProc)); +end; + +procedure TCustomSynEdit.UnregisterMouseActionSearchHandler(AHandlerProc: TSynEditMouseActionSearchProc); +begin + FMouseActionSearchHandlerList.Remove(TMethod(AHandlerProc)); +end; + +procedure TCustomSynEdit.RegisterMouseActionExecHandler(AHandlerProc: TSynEditMouseActionExecProc); +begin + FMouseActionExecHandlerList.Add(TMethod(AHandlerProc)); +end; + +procedure TCustomSynEdit.UnregisterMouseActionExecHandler(AHandlerProc: TSynEditMouseActionExecProc); +begin + FMouseActionExecHandlerList.Remove(TMethod(AHandlerProc)); +end; + procedure TCustomSynEdit.RegisterKeyTranslationHandler(AHandlerProc: THookedKeyTranslationEvent); begin FHookedKeyTranslationList.Add(TMEthod(AHandlerProc)); diff --git a/components/synedit/syneditmarkup.pp b/components/synedit/syneditmarkup.pp index d338f4040f..b909f0085a 100644 --- a/components/synedit/syneditmarkup.pp +++ b/components/synedit/syneditmarkup.pp @@ -80,6 +80,7 @@ type function ScreenRowToRow(aRow : Integer) : Integer; function RowToScreenRow(aRow : Integer) : Integer; function LogicalToPhysicalPos(const p: TPoint): TPoint; + function PhysicalToLogicalPos(const p: TPoint): TPoint; function Highlighter: TSynCustomHighlighter; function OwnedByMgr: Boolean; virtual; // overwrite, do prevent destruction by mgr @@ -129,7 +130,7 @@ type constructor Create(ASynEdit : TSynEditBase); destructor Destroy; override; - Procedure AddMarkUp(aMarkUp : TSynEditMarkup); + Procedure AddMarkUp(aMarkUp : TSynEditMarkup; AsFirst: Boolean = False); Procedure RemoveMarkUp(aMarkUp : TSynEditMarkup); function Count: Integer; property Markup[Index: integer]: TSynEditMarkup @@ -306,6 +307,11 @@ begin Result := FLines.LogicalToPhysicalPos(p); end; +function TSynEditMarkup.PhysicalToLogicalPos(const p: TPoint): TPoint; +begin + Result := FLines.PhysicalToLogicalPos(p); +end; + function TSynEditMarkup.Highlighter : TSynCustomHighlighter; begin Result := TSynEdit(SynEdit).Highlighter; @@ -379,9 +385,12 @@ begin inherited Destroy; end; -procedure TSynEditMarkupManager.AddMarkUp(aMarkUp : TSynEditMarkup); +procedure TSynEditMarkupManager.AddMarkUp(aMarkUp : TSynEditMarkup; AsFirst: Boolean = False); begin - fMarkUpList.Add(aMarkUp); + if AsFirst then + fMarkUpList.Insert(0, aMarkUp) + else + fMarkUpList.Add(aMarkUp); aMarkUp.Lines := Lines; aMarkUp.Caret := Caret; aMarkUp.TopLine := TopLine; diff --git a/components/synedit/syneditmiscclasses.pp b/components/synedit/syneditmiscclasses.pp index 1908b25bdf..80eb59e1a9 100644 --- a/components/synedit/syneditmiscclasses.pp +++ b/components/synedit/syneditmiscclasses.pp @@ -94,6 +94,7 @@ type TSynEditBase = class(TCustomControl) protected FWordBreaker: TSynWordBreaker; + FBlockSelection: TSynEditSelection; FIsUndoing, FIsRedoing: Boolean; function GetMarkupMgr: TObject; virtual; abstract; function GetLines: TStrings; virtual; abstract; @@ -119,12 +120,14 @@ type function GetIsRedoing: Boolean; function GetIsUndoing: Boolean; function GetMarkupMgr: TObject; + function GetSelectionObj: TSynEditSelection; function GetViewedTextBuffer: TSynEditStrings; function GetWordBreaker: TSynWordBreaker; protected property FriendEdit: TSynEditBase read FFriendEdit write FFriendEdit; property ViewedTextBuffer: TSynEditStrings read GetViewedTextBuffer; // As viewed internally (with uncommited spaces / TODO: expanded tabs, folds). This may change, use with care property CaretObj: TSynEditCaret read GetCaretObj; + property SelectionObj: TSynEditSelection read GetSelectionObj; property MarkupMgr: TObject read GetMarkupMgr; property IsUndoing: Boolean read GetIsUndoing; property IsRedoing: Boolean read GetIsRedoing; @@ -398,6 +401,11 @@ begin Result := FFriendEdit.MarkupMgr; end; +function TSynEditFriend.GetSelectionObj: TSynEditSelection; +begin + Result := FFriendEdit.FBlockSelection; +end; + function TSynEditFriend.GetIsRedoing: Boolean; begin Result := FFriendEdit.FIsRedoing; @@ -1143,8 +1151,8 @@ function TSynWordBreaker.NextWordStart(aLine: String; aX: Integer; var len: Integer; begin - if aX < 1 then exit(-1); len := Length(aLine); + if (aX < 1) or (aX > len + 1) then exit(-1); if not aIncludeCurrent then inc(aX); if (aX > 1) and (aLine[aX - 1] in FWordChars) then @@ -1160,8 +1168,8 @@ function TSynWordBreaker.NextWordEnd(aLine: String; aX: Integer; var len: Integer; begin - if aX < 1 then exit(-1); len := Length(aLine); + if (aX < 1) or (aX > len + 1) then exit(-1); if not aIncludeCurrent then inc(aX); if (aX = 1) or not(aLine[aX - 1] in FWordChars) then diff --git a/components/synedit/syneditmousecmds.pp b/components/synedit/syneditmousecmds.pp index 0af6ff3b52..602749c979 100644 --- a/components/synedit/syneditmousecmds.pp +++ b/components/synedit/syneditmousecmds.pp @@ -35,7 +35,8 @@ unit SynEditMouseCmds; interface uses - Classes, Controls, SysUtils, SynEditStrConst, SynEditPointClasses, Dialogs; + Classes, Controls, SysUtils, SynEditStrConst, SynEditPointClasses, Dialogs, + LCLProc; const // EditorMouseCommands @@ -65,6 +66,8 @@ const emcMax = 17; + emcPluginFirst = 20000; + // Options emcoSelectionStart = 0; emcoSelectionContinue = 1; @@ -81,6 +84,13 @@ const emcoCodeFoldExpandOne = 0; emcoCodeFoldExpandAll = 1; +// Plugins don't know of other plugins, so they need to map the codes +// Plugins all start at ecPluginFirst (overlapping) +// If ask by SynEdit they add an offset + +// Return the next offset +function AllocatePluginMouseRange(Count: Integer): integer; + type TSynEditorMouseCommand = type word; @@ -192,6 +202,32 @@ type TSynEditMouseActionHandler = function(AnActionList: TSynEditMouseActions; AnInfo: TSynEditMouseActionInfo): Boolean of object; + // Called by SynEdit + // Should Call "HandleActionProc" for each ActionList it want's to check + TSynEditMouseActionSearchProc = function(var AnInfo: TSynEditMouseActionInfo; + HandleActionProc: TSynEditMouseActionHandler): Boolean of object; + + // Called by "HandleActionProc", if an Action was found in the list + TSynEditMouseActionExecProc = function(AnAction: TSynEditMouseAction; + var AnInfo: TSynEditMouseActionInfo): Boolean of object; + + { TSynEditMouseActionSearchList } + + TSynEditMouseActionSearchList = Class(TMethodList) + public + function CallSearchHandlers(var AnInfo: TSynEditMouseActionInfo; + HandleActionProc: TSynEditMouseActionHandler): Boolean; + end; + + { TSynEditMouseActionExecList } + + TSynEditMouseActionExecList = Class(TMethodList) + public + function CallExecHandlers(AnAction: TSynEditMouseAction; + var AnInfo: TSynEditMouseActionInfo): Boolean; + end; + + function MouseCommandName(emc: TSynEditorMouseCommand): String; function MouseCommandConfigName(emc: TSynEditorMouseCommand): String; @@ -200,6 +236,14 @@ const implementation +function AllocatePluginMouseRange(Count: Integer): integer; +const + CurOffset : integer = 0; +begin + Result := CurOffset; + inc(CurOffset, Count); +end; + function MouseCommandName(emc: TSynEditorMouseCommand): String; begin case emc of @@ -596,5 +640,31 @@ begin AddCommand(emcMouseLink, False, mbLeft, ccSingle, cdUp, [SYNEDIT_LINK_MODIFIER], [ssShift, ssAlt, ssCtrl]); end; +{ TSynEditMouseActionSearchList } + +function TSynEditMouseActionSearchList.CallSearchHandlers(var AnInfo: TSynEditMouseActionInfo; + HandleActionProc: TSynEditMouseActionHandler): Boolean; +var + i: LongInt; +begin + i:=Count; + Result := False; + while NextDownIndex(i) and (not Result) do + Result := TSynEditMouseActionSearchProc(Items[i])(AnInfo, HandleActionProc); +end; + +{ TSynEditMouseActionExecList } + +function TSynEditMouseActionExecList.CallExecHandlers(AnAction: TSynEditMouseAction; + var AnInfo: TSynEditMouseActionInfo): Boolean; +var + i: LongInt; +begin + i:=Count; + Result := False; + while NextDownIndex(i) and (not Result) do + Result := TSynEditMouseActionExecProc(Items[i])(AnAction, AnInfo); +end; + end. diff --git a/components/synedit/synpluginsyncroedit.pp b/components/synedit/synpluginsyncroedit.pp new file mode 100644 index 0000000000..74a82f195e --- /dev/null +++ b/components/synedit/synpluginsyncroedit.pp @@ -0,0 +1,1456 @@ +{------------------------------------------------------------------------------- +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +the specific language governing rights and limitations under the License. + +Alternatively, the contents of this file may be used under the terms of the +GNU General Public License Version 2 or later (the "GPL"), in which case +the provisions of the GPL are applicable instead of those above. +If you wish to allow use of your version of this file only under the terms +of the GPL and not to allow others to use your version of this file +under the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the GPL. +If you do not delete the provisions above, a recipient may use your version +of this file under either the MPL or the GPL. + +-------------------------------------------------------------------------------} +unit SynPluginSyncroEdit; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, Controls, SysUtils, math, LCLProc, Forms, Graphics, SynEditMiscClasses, + LCLType, SynEdit, SynPluginSyncronizedEditBase, SynEditTextBase, SynEditMiscProcs, + SynEditMouseCmds, SynEditKeyCmds; + +type + + TSynPluginSyncroEditLowerLineCacheEntry = record + LineIndex: Integer; + LineText: String; + end; + + { TSynPluginSyncroEditLowerLineCache } + + TSynPluginSyncroEditLowerLineCache = class + private + FLines: TSynEditStrings; + FLower: Array of TSynPluginSyncroEditLowerLineCacheEntry; + function GetLowLine(aIndex: Integer): String; + procedure SetLines(const AValue: TSynEditStrings); + protected + Procedure LineTextChanged(Sender: TSynEditStrings; AIndex, ACount : Integer); + public + destructor Destroy; override; + procedure Clear; + property Lines: TSynEditStrings read FLines write SetLines; + property LowLines[aIndex: Integer]: String read GetLowLine; default; + end; + + TSynPluginSyncroEditWordsHashEntry = record + Count, Hash: Integer; + LineIdx, BytePos, Len: Integer; + Next: Integer; + GrpId: Integer; + end; + PSynPluginSyncroEditWordsHashEntry = ^TSynPluginSyncroEditWordsHashEntry; + + { TSynPluginSyncroEditWordsList } + + TSynPluginSyncroEditWordsList = class + private + FCount: Integer; + FFirstUnused, FFirstGap: Integer; + function GetItem(aIndex: Integer): TSynPluginSyncroEditWordsHashEntry; + procedure SetItem(aIndex: Integer; const AValue: TSynPluginSyncroEditWordsHashEntry); + protected + FList: Array of TSynPluginSyncroEditWordsHashEntry; + public + constructor Create; + destructor Destroy; override; + procedure Clear; + + function InsertEntry(aEntry: TSynPluginSyncroEditWordsHashEntry) : Integer; + procedure DeleteEntry(aIndex: Integer); + property Item[aIndex: Integer]: TSynPluginSyncroEditWordsHashEntry + read GetItem write SetItem; default; + property Count: Integer read FCount; + end; + + { TSynPluginSyncroEditWordsHash } + + TSynPluginSyncroEditWordsHash = class + private + FLowerLines: TSynPluginSyncroEditLowerLineCache; + FTableSize: Integer; + FTable: Array of TSynPluginSyncroEditWordsHashEntry; + FEntryCount: Integer; + FWordCount, FMultiWordCount: Integer; + FNextList: TSynPluginSyncroEditWordsList; + + function CalcHash(aWord: PChar;aLen: Integer): Integer; + function CompareEntry(aEntry1, aEntry2: TSynPluginSyncroEditWordsHashEntry; + aWord1, aWord2: PChar): Boolean; + function GetEntry(aModHash, aIndex: Integer): TSynPluginSyncroEditWordsHashEntry; + + procedure InsertEntry(aEntry: TSynPluginSyncroEditWordsHashEntry; aWord: PChar); + procedure DeleteEntry(aEntry: TSynPluginSyncroEditWordsHashEntry; aWord: PChar); + + procedure Resize(ANewSize: Integer); + public + constructor Create; + destructor Destroy; override; + procedure Clear; + + // Excpects PChat to an already lowercase word + procedure AddWord(aLineIdx, aBytePos, aLen: Integer; aWord: PChar); + procedure RemoveWord(aLen: Integer; aWord: PChar); + function GetWord(aWord: PChar; aLen: Integer): TSynPluginSyncroEditWordsHashEntry; + function GetWordP(aWord: PChar; aLen: Integer): PSynPluginSyncroEditWordsHashEntry; + function GetWordModHash(aWord: PChar; aLen: Integer): Integer; + + property LowerLines: TSynPluginSyncroEditLowerLineCache + read FLowerLines write FLowerLines; + property HashEntry[aModHash, aIndex: Integer]: TSynPluginSyncroEditWordsHashEntry + read GetEntry; + property HashSize: Integer read FTableSize; + property EntryCount: Integer read FEntryCount; + property WordCount: Integer read FWordCount; + property MultiWordCount: Integer read FMultiWordCount; + end; + + { TSynPluginSyncroEditMarkup } + + TSynPluginSyncroEditMarkup = class(TSynPluginSyncronizedEditMarkup) + private + FGlyphAtLine: Integer; + FGutterGlyph: TBitmap; + function GetGutterGlyphRect: TRect; + procedure SetGlyphAtLine(const AValue: Integer); + procedure SetGutterGlyph(const AValue: TBitmap); + procedure DoInvalidate; + protected + procedure DoCaretChanged(Sender: TObject); override; + procedure DoTopLineChanged(OldTopLine : Integer); override; + procedure DoLinesInWindoChanged(OldLinesInWindow : Integer); override; + procedure DoEnabledChanged(Sender: TObject); override; + public + constructor Create(ASynEdit: TSynEditBase); + destructor Destroy; override; + procedure EndMarkup; override; + + property GlyphAtLine: Integer read FGlyphAtLine write SetGlyphAtLine; // -1 for caret + property GutterGlyph: TBitmap read FGutterGlyph write SetGutterGlyph; + property GutterGlyphRect: TRect read GetGutterGlyphRect; + end; + + { TSynPluginSyncroEditMouseActions } + + TSynPluginSyncroEditMouseActions = class(TSynEditMouseActions) + public + procedure ResetDefaults; override; + end; + + { TSynEditSyncroEditKeyStrokesSelecting } + + TSynEditSyncroEditKeyStrokesSelecting = class(TSynEditKeyStrokes) + public + procedure ResetDefaults; override; + end; + + { TSynEditSyncroEditKeyStrokes } + + TSynEditSyncroEditKeyStrokes = class(TSynEditKeyStrokes) + public + procedure ResetDefaults; override; + end; + + { TSynEditSyncroEditKeyStrokesOffCell } + + TSynEditSyncroEditKeyStrokesOffCell = class(TSynEditKeyStrokes) + public + procedure ResetDefaults; override; + end; + + TSynPluginSyncroEditModes = (spseIncative, spseSelecting, spseEditing); + { TSynPluginSyncroEdit } + + TSynPluginSyncroEdit = class(TSynPluginCustomSyncroEdit) + private + FGutterGlyph: TBitmap; + FLowerLines: TSynPluginSyncroEditLowerLineCache; + FWordIndex: TSynPluginSyncroEditWordsHash; + FWordScanCount: Integer; + FCallQueued: Boolean; + FEditModeQueued: Boolean; + FLastSelStart, FLastSelEnd: TPoint; + FParsedStart, FParsedStop: TPoint; + FMouseActions: TSynPluginSyncroEditMouseActions; + FMode: TSynPluginSyncroEditModes; + + FKeystrokesSelecting: TSynEditKeyStrokes; + FKeystrokes, FKeyStrokesOffCell: TSynEditKeyStrokes; + procedure SetKeystrokesSelecting(const AValue: TSynEditKeyStrokes); + procedure SetKeystrokes(const AValue: TSynEditKeyStrokes); + procedure SetKeystrokesOffCell(const AValue: TSynEditKeyStrokes); + function GetMarkup: TSynPluginSyncroEditMarkup; + function Scan(AFrom, aTo: TPoint; BackWard: Boolean): TPoint; + procedure SetGutterGlyph(const AValue: TBitmap); + function UnScan(AFrom, aTo: TPoint; BackWard: Boolean): TPoint; + procedure StartSyncroMode; + procedure StopSyncroMode; + protected + procedure DoImageChanged(Sender: TObject); + function CreateMarkup: TSynPluginSyncronizedEditMarkup; override; + procedure DoSelectionChanged(Sender: TObject); + procedure DoScanSelection(Data: PtrInt); + procedure DoOnDeactivate; override; + + function MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo; + HandleActionProc: TSynEditMouseActionHandler): Boolean; + function DoHandleMouseAction(AnAction: TSynEditMouseAction; + var AnInfo: TSynEditMouseActionInfo): Boolean; + + procedure SetEditor(const AValue: TCustomSynEdit); override; + procedure DoClear; override; + + procedure TranslateKey(Sender: TObject; Code: word; SState: TShiftState; + var Data: pointer; var IsStartOfCombo: boolean; var Handled: boolean; + var Command: TSynEditorCommand; FinishComboOnly: Boolean; + var ComboKeyStrokes: TSynEditKeyStrokes); + procedure ProcessSynCommand(Sender: TObject; AfterProcessing: boolean; + var Handled: boolean; var Command: TSynEditorCommand; + var AChar: TUTF8Char; Data: pointer; HandlerData: pointer); + + property Markup: TSynPluginSyncroEditMarkup read GetMarkup; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + + property GutterGlyph: TBitmap read FGutterGlyph write SetGutterGlyph; + class function ConvertCommandToBase(Command: TSynEditorCommand): TSynEditorCommand; + class function ConvertBaseToCommand(Command: TSynEditorCommand): TSynEditorCommand; + class function ConvertCommandToBaseOff(Command: TSynEditorCommand): TSynEditorCommand; + class function ConvertBaseToCommandOff(Command: TSynEditorCommand): TSynEditorCommand; + class function ConvertCommandToBaseSel(Command: TSynEditorCommand): TSynEditorCommand; + class function ConvertBaseToCommandSel(Command: TSynEditorCommand): TSynEditorCommand; + + property KeystrokesSelecting: TSynEditKeyStrokes + read FKeystrokesSelecting write SetKeystrokesSelecting; + property Keystrokes: TSynEditKeyStrokes + read FKeystrokes write SetKeystrokes; + property KeystrokesOffCell: TSynEditKeyStrokes + read FKeystrokesOffCell write SetKeystrokesOffCell; + end; + +const + emcSynPSyncroEdGutterGlyph = emcPluginFirst + 0; + + emcSynPSyncroEdCount = 1; + + ecSynPSyncroEdNextCell = ecPluginFirst + 0; + ecSynPSyncroEdNextCellSel = ecPluginFirst + 1; + ecSynPSyncroEdPrevCell = ecPluginFirst + 2; + ecSynPSyncroEdPrevCellSel = ecPluginFirst + 3; + ecSynPSyncroEdCellHome = ecPluginFirst + 4; + ecSynPSyncroEdCellEnd = ecPluginFirst + 5; + ecSynPSyncroEdCellSelect = ecPluginFirst + 6; + ecSynPSyncroEdEscape = ecPluginFirst + 7; + + ecSynPSyncroEdCount = 8; + + ecSynPSyncroEdStart = ecPluginFirst + 0; + ecSynPSyncroEdSelModeCount = 1; + + +implementation + +var + MouseOffset, KeyOffsetSel, KeyOffset, KeyOffsetOff: integer; + +const + MAX_CACHE = 50; // Amount of lower-cased lines cached + MAX_SYNC_ED_WORDS = 50;// 250; + MAX_WORDS_PER_SCAN = 50000; + +Operator = (P1, P2 : TPoint) : Boolean; +begin + Result := (P1.Y = P2.Y) and (P1.X = P2.X); +end; + +Operator < (P1, P2 : TPoint) : Boolean; +begin + Result := (P1.Y < P2.Y) or ( (P1.Y = P2.Y) and (P1.X < P2.X) ); +end; + +Operator <= (P1, P2 : TPoint) : Boolean; +begin + Result := (P1.Y < P2.Y) or ( (P1.Y = P2.Y) and (P1.X <= P2.X) ); +end; + +Operator > (P1, P2 : TPoint) : Boolean; +begin + Result := (P1.Y > P2.Y) or ( (P1.Y = P2.Y) and (P1.X > P2.X) ); +end; + +Operator >= (P1, P2 : TPoint) : Boolean; +begin + Result := (P1.Y > P2.Y) or ( (P1.Y = P2.Y) and (P1.X >= P2.X) ); +end; + +{ TSynPluginSyncroEditLowerLineCache } + +function TSynPluginSyncroEditLowerLineCache.GetLowLine(aIndex: Integer): String; +var + i, l: Integer; +begin + l := length(FLower); + for i := 0 to l-1 do + if FLower[i].LineIndex = aIndex then + exit(FLower[i].LineText); + if l < MAX_CACHE then begin + inc(l); + SetLength(FLower, l); + end; + for i := l-1 downto 1 do begin + FLower[i].LineIndex := FLower[i-1].LineIndex; + FLower[i].LineText := FLower[i-1].LineText; + end; + FLower[0].LineIndex := aIndex; + FLower[0].LineText := UTF8LowerCase(FLines[aIndex]); + Result := FLower[0].LineText; +end; + +procedure TSynPluginSyncroEditLowerLineCache.SetLines(const AValue: TSynEditStrings); +begin + Clear; + if FLines <> nil then begin + fLines.RemoveChangeHandler(senrLineChange, {$IFDEF FPC}@{$ENDIF}LineTextChanged); + fLines.RemoveChangeHandler(senrLineCount, {$IFDEF FPC}@{$ENDIF}LineTextChanged); + end; + FLines := AValue; + if FLines <> nil then begin + fLines.AddChangeHandler(senrLineChange, {$IFDEF FPC}@{$ENDIF}LineTextChanged); + fLines.AddChangeHandler(senrLineCount, {$IFDEF FPC}@{$ENDIF}LineTextChanged); + end; +end; + +procedure TSynPluginSyncroEditLowerLineCache.LineTextChanged(Sender: TSynEditStrings; AIndex, + ACount: Integer); +begin + Clear; +end; + +destructor TSynPluginSyncroEditLowerLineCache.Destroy; +begin + Lines := nil; + Clear; + inherited Destroy; +end; + +procedure TSynPluginSyncroEditLowerLineCache.Clear; +begin + FLower := nil; +end; + +{ TSynPluginSyncroEditWordsList } + +function TSynPluginSyncroEditWordsList.GetItem(aIndex: Integer): TSynPluginSyncroEditWordsHashEntry; +begin + Result := FList[aIndex]; +end; + +procedure TSynPluginSyncroEditWordsList.SetItem(aIndex: Integer; + const AValue: TSynPluginSyncroEditWordsHashEntry); +begin + FList[aIndex] := AValue; +end; + +constructor TSynPluginSyncroEditWordsList.Create; +begin + inherited; + clear; +end; + +destructor TSynPluginSyncroEditWordsList.Destroy; +begin + inherited Destroy; + clear; +end; + +procedure TSynPluginSyncroEditWordsList.Clear; +begin + FList := nil; + FFirstGap := -1; + FFirstUnused := 0; + FCount := 0; +end; + +function TSynPluginSyncroEditWordsList.InsertEntry(aEntry: TSynPluginSyncroEditWordsHashEntry): Integer; +begin + inc(FCount); + if FFirstGap >= 0 then begin + Result := FFirstGap; + FFirstGap := FList[Result].Next; + end else begin + if FFirstUnused >= length(FList) then + SetLength(FList, Max(1024, length(FList)) * 4); + Result := FFirstUnused; + inc(FFirstUnused); + end; + FList[Result] := aEntry; +end; + +procedure TSynPluginSyncroEditWordsList.DeleteEntry(aIndex: Integer); +begin + dec(FCount); + FList[aIndex].Next := FFirstGap; + FFirstGap := aIndex; +end; + +{ TSynPluginSyncroEditWordsHash } + +function TSynPluginSyncroEditWordsHash.CalcHash(aWord: PChar; aLen: Integer): Integer; +var + v, n, p, a, b, c, c1, i: Integer; +begin + a := 0; + b := 0; + c := 0; + c1 := 0; + n := 1; + p := 0; + i := aLen; + while i > 0 do begin + v := ord(aWord^); + a := a + v * (1 + (n mod 8)); + if a > 550 then a := a mod 550; + b := b * 3 + v * n - p; + if b > 550 then b := b mod 550; + c1 := c1 + v * (1 + (a mod 11)); + c := c + c1; + if c > 550 then c := c mod 550; + dec(i); + inc(aWord); + inc(n); + p := v; + end; + + Result := (((aLen mod 11) * 550 + b) * 550 + c) * 550 + a; +end; + +function TSynPluginSyncroEditWordsHash.CompareEntry(aEntry1, + aEntry2: TSynPluginSyncroEditWordsHashEntry; aWord1, aWord2: PChar): Boolean; +var + Line1, Line2: String; +begin + Result := (aEntry1.Len = aEntry2.Len) and (aEntry1.Hash = aEntry2.Hash); + if not Result then exit; + + if aWord1 = nil then begin + Line1 := FLowerLines[aEntry1.LineIdx]; + aWord1 := @Line1[aEntry1.BytePos]; + end; + if aWord2 = nil then begin + Line2 := FLowerLines[aEntry2.LineIdx]; + aWord2 := @Line2[aEntry2.BytePos]; + end; + + Result := CompareMem(aWord1, aWord2, aEntry1.Len); +end; + +function TSynPluginSyncroEditWordsHash.GetEntry(aModHash, + aIndex: Integer): TSynPluginSyncroEditWordsHashEntry; +begin + Result:= FTable[aModHash]; + while aIndex > 0 do begin + if Result.Next < 0 then begin + Result.Count := 0; + Result.Hash := -1; + exit; + end; + Result := FNextList[Result.Next]; + dec(aIndex); + end; +end; + +procedure TSynPluginSyncroEditWordsHash.InsertEntry + (aEntry: TSynPluginSyncroEditWordsHashEntry; aWord: PChar); +var + j: LongInt; + ModHash: Integer; +begin + aEntry.GrpId := 0; + if (FEntryCount >= FTableSize * 2 div 3) or (FNextList.Count > FTableSize div 8) then + Resize(Max(FTableSize, 1024) * 4); + + ModHash := aEntry.Hash mod FTableSize; + + if (FTable[ModHash].Count > 0) then begin + if CompareEntry(aEntry, FTable[ModHash], aWord, nil) then begin + if FTable[ModHash].Count = 1 then + inc(FMultiWordCount); + FTable[ModHash].Count := FTable[ModHash].Count + aEntry.Count; + exit; + end; + + j := FTable[ModHash].Next; + while j >= 0 do begin + if CompareEntry(aEntry, FNextList[j], aWord, nil) then begin + if FNextList[j].Count = 1 then + inc(FMultiWordCount); + FNextList.FList[j].Count := FNextList.FList[j].Count + aEntry.Count; + exit; + end; + j := FNextList[j].Next; + end; + + j := FNextList.InsertEntry(aEntry); + FNextList.FList[j].Next := FTable[ModHash].Next; + FTable[ModHash].Next := j; + inc(FWordCount); + + exit; + end; + + inc(FEntryCount); + inc(FWordCount); + //if (FEntryCount<20) or (FEntryCount mod 8192=0) then debugln(['entry add ', FEntryCount]); + FTable[ModHash] := aEntry; + FTable[ModHash].Next:= -1; +end; + +procedure TSynPluginSyncroEditWordsHash.DeleteEntry(aEntry: TSynPluginSyncroEditWordsHashEntry; + aWord: PChar); +var + j, i: Integer; + ModHash: Integer; +begin + ModHash := aEntry.Hash mod FTableSize; + + if (FTable[ModHash].Count > 0) then begin + if CompareEntry(aEntry, FTable[ModHash], aWord, nil) then begin + FTable[ModHash].Count := FTable[ModHash].Count - 1; + if FTable[ModHash].Count = 0 then begin + j := FTable[ModHash].Next; + if j >= 0 then begin + FTable[ModHash] := FNextList[j]; + FNextList.DeleteEntry(j); + end + else + dec(FEntryCount); + dec(FWordCount); + end + else if FTable[ModHash].Count = 1 then + dec(FMultiWordCount); + exit; + end; + + j := FTable[ModHash].Next; + while j >= 0 do begin + if CompareEntry(aEntry, FNextList[j], aWord, nil) then begin + FNextList.FList[j].Count := FNextList.FList[j].Count - 1; + if FNextList[j].Count = 0 then begin + i := FNextList[j].Next; + if i >= 0 then begin + FNextList[j] := FNextList[i]; + FNextList.DeleteEntry(i); + end; + dec(FWordCount); + end + else if FNextList[j].Count = 1 then + dec(FMultiWordCount); + exit; + end; + j := FNextList[j].Next; + end; + + end; + // ?? there was no entry ?? +end; + +procedure TSynPluginSyncroEditWordsHash.Resize(ANewSize: Integer); +var + OldTable: Array of TSynPluginSyncroEditWordsHashEntry; + OldSize, i, j, k: Integer; +begin + FEntryCount := 0; + FWordCount := 0; + FMultiWordCount := 0; + if FTableSize = 0 then begin + SetLength(FTable, ANewSize); + FTableSize := ANewSize; + exit; + end; + + //debugln(['TSynPluginSyncroEditWordsHash.Resize ', ANewSize]); + OldSize := FTableSize; + SetLength(OldTable, FTableSize); + System.Move(FTable[0], OldTable[0], FTableSize * SizeOf(TSynPluginSyncroEditWordsHashEntry)); + FillChar(FTable[0], FTableSize * SizeOf(TSynPluginSyncroEditWordsHashEntry), 0); + SetLength(FTable, ANewSize); + FTableSize := ANewSize; + + for i := 0 to OldSize - 1 do begin + if OldTable[i].Count > 0 then begin + InsertEntry(OldTable[i], nil); + j := OldTable[i].Next; + while j >= 0 do begin + InsertEntry(FNextList[j], nil); + k := j; + j := FNextList[j].Next; + FNextList.DeleteEntry(k); + end; + end; + end; +end; + +constructor TSynPluginSyncroEditWordsHash.Create; +begin + inherited; + FNextList := TSynPluginSyncroEditWordsList.Create; + Clear; +end; + +destructor TSynPluginSyncroEditWordsHash.Destroy; +begin + Clear; + inherited Destroy; + FreeAndNil(FNextList); +end; + +procedure TSynPluginSyncroEditWordsHash.Clear; +begin + FTable := nil; + FTableSize := 0; + FEntryCount := 0; + FWordCount := 0; + FMultiWordCount := 0; + FNextList.Clear; +end; + +procedure TSynPluginSyncroEditWordsHash.AddWord(aLineIdx, aBytePos, aLen: Integer; + aWord: PChar); +var + NewEntry: TSynPluginSyncroEditWordsHashEntry; +begin + NewEntry.Hash := CalcHash(aWord, aLen); + NewEntry.LineIdx := aLineIdx; + NewEntry.BytePos := aBytePos; + NewEntry.Len := aLen; + NewEntry.Count := 1; + InsertEntry(NewEntry, aWord); +end; + +procedure TSynPluginSyncroEditWordsHash.RemoveWord(aLen: Integer; aWord: PChar); +var + OldEntry: TSynPluginSyncroEditWordsHashEntry; +begin + OldEntry.Count := 1; + OldEntry.Hash := CalcHash(aWord, aLen); + oldEntry.Len := aLen; + DeleteEntry(OldEntry, aWord); +end; + +function TSynPluginSyncroEditWordsHash.GetWord(aWord: PChar; + aLen: Integer): TSynPluginSyncroEditWordsHashEntry; +var + SearchEntry: TSynPluginSyncroEditWordsHashEntry; +begin + Result.Hash := -1; + Result.Count:= 0; + if FTableSize < 1 then exit; + + SearchEntry.Hash := CalcHash(aWord, aLen); + SearchEntry.Len := aLen; + Result := FTable[SearchEntry.Hash mod FTableSize]; + while Result.Count > 0 do begin + if CompareEntry(Result, SearchEntry, nil, aWord) then exit; + if Result.Next < 0 then break; + Result := FNextList[Result.Next]; + end; + Result.Hash := -1; + Result.Count:= 0; +end; + +function TSynPluginSyncroEditWordsHash.GetWordP(aWord: PChar; + aLen: Integer): PSynPluginSyncroEditWordsHashEntry; +var + SearchEntry: TSynPluginSyncroEditWordsHashEntry; +begin + Result := nil; + if FTableSize < 1 then exit; + + SearchEntry.Hash := CalcHash(aWord, aLen); + SearchEntry.Len := aLen; + Result := @FTable[SearchEntry.Hash mod FTableSize]; + while Result^.Count > 0 do begin + if CompareEntry(Result^, SearchEntry, nil, aWord) then exit; + if Result^.Next < 0 then break; + Result := @FNextList.FList[Result^.Next]; + end; + Result := nil; +end; + +function TSynPluginSyncroEditWordsHash.GetWordModHash(aWord: PChar; aLen: Integer): Integer; +begin + if FTableSize < 1 then exit(-1); + Result := CalcHash(aWord, aLen) mod FTableSize; +end; + +{ TSynPluginSyncroEditMarkup } + +procedure TSynPluginSyncroEditMarkup.DoInvalidate; +begin + // TODO: GutterPaint does not trigger Markup modules + TSynEdit(SynEdit).Invalidate; + //if TSynEdit(SynEdit).GutterWidth < FGutterGlyph.Width then + // TSynEdit(SynEdit).Invalidate + //else + // TSynEdit(SynEdit).InvalidateGutter; +end; + +procedure TSynPluginSyncroEditMarkup.DoCaretChanged(Sender: TObject); +begin + inherited DoCaretChanged(Sender); + DoInvalidate; +end; + +procedure TSynPluginSyncroEditMarkup.DoTopLineChanged(OldTopLine: Integer); +begin + inherited DoTopLineChanged(OldTopLine); + DoInvalidate; +end; + +procedure TSynPluginSyncroEditMarkup.DoLinesInWindoChanged(OldLinesInWindow: Integer); +begin + inherited DoLinesInWindoChanged(OldLinesInWindow); + DoInvalidate; +end; + +procedure TSynPluginSyncroEditMarkup.DoEnabledChanged(Sender: TObject); +begin + inherited DoEnabledChanged(Sender); + DoInvalidate; +end; + +procedure TSynPluginSyncroEditMarkup.EndMarkup; +var + src, dst: TRect; +begin + inherited EndMarkup; + if (FGutterGlyph.Height > 0) then begin + src := Classes.Rect(0, 0, FGutterGlyph.Width, FGutterGlyph.Height); + dst := GutterGlyphRect; + TSynEdit(SynEdit).Canvas.CopyRect(dst, FGutterGlyph.Canvas, src); + end; +end; + +procedure TSynPluginSyncroEditMarkup.SetGutterGlyph(const AValue: TBitmap); +begin + if FGutterGlyph = AValue then exit; + if FGutterGlyph = nil then + FGutterGlyph := TBitMap.Create; + FGutterGlyph.Assign(AValue); + DoInvalidate; +end; + +function TSynPluginSyncroEditMarkup.GetGutterGlyphRect: TRect; +var + y: Integer; +begin + Result := Classes.Rect(0, 0, FGutterGlyph.Width, FGutterGlyph.Height); + y := GlyphAtLine; + if y = -1 then + y := TSynEdit(SynEdit).CaretY; + Result.Top := Max( Min( RowToScreenRow(y) + * TSynEdit(SynEdit).LineHeight, + TSynEdit(SynEdit).ClientHeight - FGutterGlyph.Height), + 0); + Result.Bottom := Result.Bottom + Result.Top; +end; + +procedure TSynPluginSyncroEditMarkup.SetGlyphAtLine(const AValue: Integer); +begin + if FGlyphAtLine = AValue then exit; + FGlyphAtLine := AValue; + DoInvalidate; +end; + +constructor TSynPluginSyncroEditMarkup.Create(ASynEdit: TSynEditBase); +begin + FGutterGlyph := TBitMap.Create; + inherited; +end; + +destructor TSynPluginSyncroEditMarkup.Destroy; +begin + inherited Destroy; + FreeAndNil(FGutterGlyph); +end; + +{ TSynPluginSyncroEdit } + +function TSynPluginSyncroEdit.Scan(AFrom, aTo: TPoint; BackWard: Boolean): TPoint; +var + x2: Integer; + Line: String; +begin + Result := AFrom; + if BackWard then begin + Line := FLowerLines[AFrom.y - 1]; + while (AFrom >= aTo) do begin + AFrom.x := WordBreaker.PrevWordEnd(Line, AFrom.x, True); + if AFrom.x < 0 then begin + dec(AFrom.y); + Line := FLowerLines[AFrom.y-1]; + AFrom.x := length(Line) + 1; + continue; + end; + x2 := WordBreaker.PrevWordStart(Line, AFrom.x, True); + if (AFrom.y > ATo.y) or (x2 >= ATo.x) then begin + FWordIndex.AddWord(AFrom.y - 1, x2, AFrom.x - x2, @Line[x2]); + Result := AFrom; + Result.x := x2; + inc(FWordScanCount); + if FWordScanCount > MAX_WORDS_PER_SCAN then break; + end; + AFrom.x := x2; + end; + end + else begin + Line := FLowerLines[AFrom.y - 1]; + while (AFrom <= aTo) do begin + AFrom.x := WordBreaker.NextWordStart(Line, AFrom.x, True); + if AFrom.x < 0 then begin + inc(AFrom.y); + AFrom.x := 1; + Line := FLowerLines[AFrom.y-1]; + continue; + end; + x2 := WordBreaker.NextWordEnd(Line, AFrom.x, True); + if (AFrom.y < ATo.y) or (x2 <= ATo.x) then begin + FWordIndex.AddWord(AFrom.y - 1, AFrom.x, x2-AFrom.x, @Line[AFrom.x]); + Result := AFrom; + Result.x := x2; + inc(FWordScanCount); + if FWordScanCount > MAX_WORDS_PER_SCAN then break; + end; + AFrom.x := x2; + end; + end; +end; + +procedure TSynPluginSyncroEdit.SetKeystrokesSelecting(const AValue: TSynEditKeyStrokes); +begin + if AValue = nil then + FKeystrokesSelecting.Clear + else + FKeystrokesSelecting.Assign(AValue); +end; + +procedure TSynPluginSyncroEdit.SetKeystrokes(const AValue: TSynEditKeyStrokes); +begin + if AValue = nil then + FKeystrokes.Clear + else + FKeystrokes.Assign(AValue); +end; + +procedure TSynPluginSyncroEdit.SetKeystrokesOffCell(const AValue: TSynEditKeyStrokes); +begin + if AValue = nil then + FKeyStrokesOffCell.Clear + else + FKeyStrokesOffCell.Assign(AValue); +end; + +function TSynPluginSyncroEdit.GetMarkup: TSynPluginSyncroEditMarkup; +begin + Result := TSynPluginSyncroEditMarkup(FMarkup); +end; + +procedure TSynPluginSyncroEdit.SetGutterGlyph(const AValue: TBitmap); +begin + if FGutterGlyph = AValue then exit; + if FGutterGlyph = nil then begin + FGutterGlyph := TBitMap.Create; + FGutterGlyph.OnChange := @DoImageChanged; + end; + FGutterGlyph.Assign(AValue); +end; + +function TSynPluginSyncroEdit.UnScan(AFrom, aTo: TPoint; BackWard: Boolean): TPoint; +var + x2: Integer; + Line: String; +begin + Result := AFrom; + if BackWard then begin + Line := FLowerLines[AFrom.y - 1]; + while (AFrom > aTo) do begin + AFrom.x := WordBreaker.PrevWordEnd(Line, AFrom.x, True); + if AFrom.x < 0 then begin + dec(AFrom.y); + Line := FLowerLines[AFrom.y-1]; + AFrom.x := length(Line) + 1; + continue; + end; + x2 := WordBreaker.PrevWordStart(Line, AFrom.x, True); + FWordIndex.RemoveWord(AFrom.x - x2, @Line[x2]); + AFrom.x := x2; + Result := AFrom; + inc(FWordScanCount); + if FWordScanCount > MAX_WORDS_PER_SCAN then break; + end; + end + else begin + Line := FLowerLines[AFrom.y - 1]; + while (AFrom < aTo) do begin + AFrom.x := WordBreaker.NextWordStart(Line, AFrom.x, True); + if AFrom.x < 0 then begin + inc(AFrom.y); + AFrom.x := 1; + Line := FLowerLines[AFrom.y-1]; + continue; + end; + x2 := WordBreaker.NextWordEnd(Line, AFrom.x, True); + FWordIndex.RemoveWord(x2-AFrom.x, @Line[AFrom.x]); + AFrom.x := x2; + Result := AFrom; + inc(FWordScanCount); + if FWordScanCount > MAX_WORDS_PER_SCAN then break; + end; + end; +end; + +procedure TSynPluginSyncroEdit.StartSyncroMode; +var + Pos, EndPos: TPoint; + Line: String; + x2, g: Integer; + entry: PSynPluginSyncroEditWordsHashEntry; +begin + if FCallQueued then begin + FEditModeQueued := True; + exit; + end; + FEditModeQueued := False; + + FMode := spseEditing; + AreaMarkupEnabled := True; + + Pos := SelectionObj.FirstLineBytePos; + EndPos := SelectionObj.LastLineBytePos; + + with Cells.AddNew do begin + LogStart := Pos; + LogEnd := EndPos; + Group := -1; + end; + MarkupArea.CellGroupForArea := -1; + Markup.GlyphAtLine := Pos.y; + + g := 1; + Line := FLowerLines[Pos.y-1]; + while (Pos <= EndPos) do begin + Pos.x := WordBreaker.NextWordStart(Line, Pos.x, True); + if Pos.x < 0 then begin + inc(Pos.y); + Pos.x := 1; + Line := FLowerLines[Pos.y-1]; + continue; + end; + x2 := WordBreaker.NextWordEnd(Line, Pos.x, True); + if (Pos.y < EndPos.y) or (x2 <= EndPos.x) then begin + entry := FWordIndex.GetWordP(@Line[Pos.x], x2-Pos.x); + if (entry <> nil) and (entry^.Count > 1) then begin; + if (entry^.GrpId = 0) and (g <= MAX_SYNC_ED_WORDS) then begin + entry^.GrpId := g; + inc(g); + end; + if (entry^.GrpId > 0) then + with Cells.AddNew do begin + LogStart := Pos; + LogEnd := Point(x2, Pos.y); + Group := entry^.GrpId; + end; + end; + + end; + Pos.x := x2; + end; + FWordIndex.Clear; + + CurrentCell := 1; + SelectCurrentCell; + if g = 1 then StopSyncroMode; +end; + +procedure TSynPluginSyncroEdit.StopSyncroMode; +begin + Active := False; +end; + +procedure TSynPluginSyncroEdit.DoImageChanged(Sender: TObject); +begin + Markup.GutterGlyph := FGutterGlyph; +end; + +function TSynPluginSyncroEdit.CreateMarkup: TSynPluginSyncronizedEditMarkup; +begin + Result := TSynPluginSyncroEditMarkup.Create(Editor); + if FGutterGlyph <> nil then + TSynPluginSyncroEditMarkup(Result).GutterGlyph := FGutterGlyph; +end; + +procedure TSynPluginSyncroEdit.DoSelectionChanged(Sender: TObject); +begin + if FMode = spseEditing then exit; + If not SelectionObj.SelAvail then begin + FLastSelStart := Point(-1,-1); + FLastSelEnd := Point(-1,-1); + FWordIndex.Clear; + if Active then Editor.Invalidate; + Active := False; + FMode := spseIncative; + exit; + end; + + if FMode = spseIncative then begin + Cells.Clear; + AreaMarkupEnabled := False; + end; + FMode := spseSelecting; + Markup.GlyphAtLine := -1; + if not FCallQueued then + Application.QueueAsyncCall(@DoScanSelection, 0); + FCallQueued := True; +end; + +procedure TSynPluginSyncroEdit.DoScanSelection(Data: PtrInt); +var + NewPos, NewEnd: TPoint; + + function InitParsedPoints: Boolean; + // Find the first begin of a word, inside the block (if any) + var + x, y: Integer; + begin + if FParsedStart.y >= 0 then exit(True); + y := NewPos.y; + x := NewPos.x; + while y <= NewEnd.y do begin + x := WordBreaker.NextWordStart(FLowerLines[y-1], x, True); + if (x > 0) and ((y < NewEnd.Y) or (x <= NewEnd.x)) then begin + FParsedStart.y := y; + FParsedStart.x := x; + FParsedStop := FParsedStart; + break; + end; + inc(y); + x := 1; + end; + Result := FParsedStart.Y >= 0; + end; + +var + i, j: Integer; +begin + Repeat + FCallQueued := False; + FWordScanCount := 0; + + NewPos := SelectionObj.FirstLineBytePos; + NewEnd := SelectionObj.LastLineBytePos; + i := FLastSelEnd.y - FLastSelStart.y; + j := NewEnd.y - NewPos.y; + if (j < 1) or (j < i div 2) or + (NewEnd <= FLastSelStart) or (NewPos >= FLastSelEnd ) + then begin + // Scan from scratch + FLastSelStart := Point(-1,-1); + FLastSelEnd := Point(-1,-1); + FWordIndex.Clear; + end; + + if FLastSelStart.Y < 0 then begin + FLastSelStart := NewPos; + FLastSelEnd := FLastSelStart; + FParsedStart := Point(-1,-1); + FParsedStop := Point(-1,-1); + end; + + if (NewPos = NewEnd) or (not InitParsedPoints) then begin + if Active then Editor.Invalidate; + Active := False; + exit; + end; + + if (NewPos < FLastSelStart) then + FParsedStart := Scan(FParsedStart, NewPos, True) // NewPos is the smaller point; + else + if (NewPos > FParsedStart) then + FParsedStart := UnScan(FParsedStart, NewPos, False); + + if FWordScanCount > MAX_WORDS_PER_SCAN then begin + FLastSelStart := FParsedStart; + end + else begin + FLastSelStart := NewPos; + + if (NewEnd > FLastSelEnd) then + FParsedStop := Scan(FParsedStop, NewEnd, False) // NewPos is the greater point; + else + if (NewEnd < FParsedStop) then + FParsedStop := UnScan(FParsedStop, NewEnd, True); + + FLastSelEnd := NewEnd; + if FWordScanCount > MAX_WORDS_PER_SCAN then + FLastSelEnd := FParsedStop; + end; + + Active := FWordIndex.MultiWordCount > 0; + //debugln(['COUNTS: ', FWordIndex.WordCount,' mult=',FWordIndex.MultiWordCount, ' hash=',FWordIndex.EntryCount]); + + if FWordScanCount > MAX_WORDS_PER_SCAN then begin + FCallQueued := True; + Application.ProcessMessages; + end; + until not FCallQueued; + if FEditModeQueued then + StartSyncroMode; +end; + +procedure TSynPluginSyncroEdit.DoOnDeactivate; +begin + FMode := spseIncative; + AreaMarkupEnabled := False; + Cells.Clear; + inherited DoOnDeactivate; +end; + +function TSynPluginSyncroEdit.MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo; + HandleActionProc: TSynEditMouseActionHandler): Boolean; +var + r: TRect; +begin + Result := Active; + if not Result then exit; + + r := Markup.GutterGlyphRect; + Result := (AnInfo.MouseX >= r.Left) and (AnInfo.MouseX < r.Right) and + (AnInfo.MouseY >= r.Top) and (AnInfo.MouseY < r.Bottom); + + if Result then + Result := HandleActionProc(FMouseActions, AnInfo); +end; + +function TSynPluginSyncroEdit.DoHandleMouseAction(AnAction: TSynEditMouseAction; + var AnInfo: TSynEditMouseActionInfo): Boolean; +begin + Result := False; + + if AnAction.Command = MouseOffset + emcSynPSyncroEdGutterGlyph then begin + if FMode = spseSelecting then + StartSyncroMode + else + StopSyncroMode; + Result := true; + end; +end; + +procedure TSynPluginSyncroEdit.SetEditor(const AValue: TCustomSynEdit); +begin + if Editor = AValue then exit; + if Editor <> nil then begin + SelectionObj.RemoveChangeHandler(@DoSelectionChanged); + Editor.UnregisterCommandHandler(@ProcessSynCommand); + Editor.UnRegisterKeyTranslationHandler(@TranslateKey); + Editor.UnregisterMouseActionSearchHandler(@MaybeHandleMouseAction); + Editor.UnregisterMouseActionExecHandler(@DoHandleMouseAction); + FLowerLines.Lines := nil; + end; + inherited SetEditor(AValue); + if Editor <> nil then begin + FLowerLines.Lines := ViewedTextBuffer; + Editor.RegisterMouseActionSearchHandler(@MaybeHandleMouseAction); + Editor.RegisterMouseActionExecHandler(@DoHandleMouseAction); + Editor.RegisterCommandHandler(@ProcessSynCommand, nil); + Editor.RegisterKeyTranslationHandler(@TranslateKey); + SelectionObj.AddChangeHandler(@DoSelectionChanged); + end; +end; + +procedure TSynPluginSyncroEdit.DoClear; +begin + FWordIndex.Clear; + inherited DoClear; +end; + +procedure TSynPluginSyncroEdit.TranslateKey(Sender: TObject; Code: word; SState: TShiftState; + var Data: pointer; var IsStartOfCombo: boolean; var Handled: boolean; + var Command: TSynEditorCommand; FinishComboOnly: Boolean; + var ComboKeyStrokes: TSynEditKeyStrokes); +var + keys: TSynEditKeyStrokes; +begin + if (not Active) or Handled then + exit; + + keys := nil; + if FMode = spseSelecting then + keys := FKeystrokesSelecting; + + if FMode = spseEditing then begin + if CurrentCell < 0 then begin + keys := FKeyStrokesOffCell; + FKeyStrokes.ResetKeyCombo; + end + else begin + keys := FKeyStrokes; + FKeyStrokesOffCell.ResetKeyCombo; + end; + end; + if keys = nil then exit; + + if (FinishComboOnly and (ComboKeyStrokes <> keys)) then begin + keys.ResetKeyCombo; + exit; + end; + + IsStartOfCombo := False; + try + keys.UsePluginOffset := True; + Command := keys.FindKeycodeEx(Code, SState, Data, IsStartOfCombo, + FinishComboOnly); + finally + keys.UsePluginOffset := False; + end; + Handled := (Command <> ecNone) or IsStartOfCombo; + if Handled then begin + ComboKeyStrokes := keys; + end; +end; + +procedure TSynPluginSyncroEdit.ProcessSynCommand(Sender: TObject; AfterProcessing: boolean; + var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; + HandlerData: pointer); +var + Cmd: TSynEditorCommand; +begin + if Handled or AfterProcessing or not Active then exit; + + if FMode = spseSelecting then begin + Cmd := ConvertCommandToBaseSel(Command); + Handled := True; + case Cmd of + ecSynPSyncroEdStart: StartSyncroMode; + else + Handled := False; + end; + end; + + if FMode = spseEditing then begin + Cmd := ConvertCommandToBase(Command); + if Cmd = ecNone then + Cmd := ConvertCommandToBaseOff(Command); + + Handled := True; + case Cmd of + ecSynPSyncroEdNextCell: NextCell(False); + ecSynPSyncroEdNextCellSel: NextCell(True); + ecSynPSyncroEdPrevCell: PreviousCell(False); + ecSynPSyncroEdPrevCellSel: PreviousCell(True); + ecSynPSyncroEdCellHome: CellCaretHome; + ecSynPSyncroEdCellEnd: CellCaretEnd; + ecSynPSyncroEdCellSelect: SelectCurrentCell; + ecSynPSyncroEdEscape: + begin + Clear; + Active := False; + end; + else + Handled := False; + end; + end; +end; + +constructor TSynPluginSyncroEdit.Create(AOwner: TComponent); +begin + FMode := spseIncative; + FEditModeQueued := False; + + FMouseActions := TSynPluginSyncroEditMouseActions.Create(self); + FMouseActions.ResetDefaults; + + FKeystrokes := TSynEditSyncroEditKeyStrokes.Create(Self); + FKeystrokes.ResetDefaults; + FKeystrokes.PluginOffset := KeyOffset; + + FKeyStrokesOffCell := TSynEditSyncroEditKeyStrokesOffCell.Create(self); + FKeyStrokesOffCell.ResetDefaults; + FKeyStrokesOffCell.PluginOffset := KeyOffsetOff; + + FKeystrokesSelecting := TSynEditSyncroEditKeyStrokesSelecting.Create(Self); + FKeystrokesSelecting.ResetDefaults; + FKeystrokesSelecting.PluginOffset := KeyOffsetSel; + + FGutterGlyph := TBitMap.Create; + FGutterGlyph.OnChange := @DoImageChanged; + + FLowerLines := TSynPluginSyncroEditLowerLineCache.Create; + FWordIndex := TSynPluginSyncroEditWordsHash.Create; + FWordIndex.LowerLines := FLowerLines; + inherited Create(AOwner); + MarkupInfoArea.Background := clMoneyGreen; + MarkupInfo.FrameColor := TColor($98b498) +end; + +destructor TSynPluginSyncroEdit.Destroy; +begin + inherited Destroy; + FreeAndNil(FWordIndex); + FreeAndNil(FLowerLines); + FreeAndNil(FGutterGlyph); + FreeAndNil(FMouseActions); + FreeAndNil(FKeystrokes); + FreeAndNil(FKeyStrokesOffCell); + FreeAndNil(FKeystrokesSelecting); +end; + +class function TSynPluginSyncroEdit.ConvertCommandToBase(Command: TSynEditorCommand): TSynEditorCommand; +begin + if (Command >= ecPluginFirst + KeyOffset) and + (Command <= ecPluginFirst + KeyOffset + ecSynPSyncroEdCount) + then Result := Command - KeyOffset + else Result := ecNone; +end; + +class function TSynPluginSyncroEdit.ConvertBaseToCommand(Command: TSynEditorCommand): TSynEditorCommand; +begin + if (Command >= ecPluginFirst) and (Command <= ecPluginFirst + ecSynPSyncroEdCount) + then Result := Command + KeyOffset + else Result := ecNone; +end; + +class function TSynPluginSyncroEdit.ConvertCommandToBaseOff(Command: TSynEditorCommand): TSynEditorCommand; +begin + if (Command >= ecPluginFirst + KeyOffsetOff) and + (Command <= ecPluginFirst + KeyOffsetOff + ecSynPSyncroEdCount) + then Result := Command - KeyOffsetOff + else Result := ecNone; +end; + +class function TSynPluginSyncroEdit.ConvertBaseToCommandOff(Command: TSynEditorCommand): TSynEditorCommand; +begin + if (Command >= ecPluginFirst) and (Command <= ecPluginFirst + ecSynPSyncroEdCount) + then Result := Command + KeyOffsetOff + else Result := ecNone; +end; + +class function TSynPluginSyncroEdit.ConvertCommandToBaseSel(Command: TSynEditorCommand): TSynEditorCommand; +begin + if (Command >= ecPluginFirst + KeyOffsetSel) and + (Command <= ecPluginFirst + KeyOffsetSel + ecSynPSyncroEdSelModeCount) + then Result := Command - KeyOffsetSel + else Result := ecNone; +end; + +class function TSynPluginSyncroEdit.ConvertBaseToCommandSel(Command: TSynEditorCommand): TSynEditorCommand; +begin + if (Command >= ecPluginFirst) and (Command <= ecPluginFirst + ecSynPSyncroEdSelModeCount) + then Result := Command + KeyOffsetSel + else Result := ecNone; +end; + +{ TSynPluginSyncroEditMouseActions } + +procedure TSynPluginSyncroEditMouseActions.ResetDefaults; +begin + Clear; + AddCommand(MouseOffset + emcSynPSyncroEdGutterGlyph, False, mbLeft, ccAny, cdDown, [], []); +end; + +{ TSynEditSyncroEditKeyStrokesSelecting } + +procedure TSynEditSyncroEditKeyStrokesSelecting.ResetDefaults; + procedure AddKey(const ACmd: TSynEditorCommand; const AKey: word; + const AShift: TShiftState); + begin + with Add do + begin + Key := AKey; + Shift := AShift; + Command := ACmd; + end; + end; +begin + Clear; + AddKey(ecSynPSyncroEdStart, VK_J, [ssCtrl]); +end; + +{ TSynEditSyncroEditKeyStrokes } + +procedure TSynEditSyncroEditKeyStrokes.ResetDefaults; + procedure AddKey(const ACmd: TSynEditorCommand; const AKey: word; + const AShift: TShiftState); + begin + with Add do + begin + Key := AKey; + Shift := AShift; + Command := ACmd; + end; + end; +begin + Clear; + AddKey(ecSynPSyncroEdNextCell, VK_RIGHT, [ssCtrl]); + AddKey(ecSynPSyncroEdNextCellSel, VK_TAB, []); + AddKey(ecSynPSyncroEdPrevCell, VK_LEFT, [ssCtrl]); + AddKey(ecSynPSyncroEdPrevCellSel, VK_TAB, [ssShift]); + + AddKey(ecSynPSyncroEdCellHome, VK_HOME, []); + AddKey(ecSynPSyncroEdCellEnd, VK_END, []); + AddKey(ecSynPSyncroEdCellSelect, VK_A, [ssCtrl]); + AddKey(ecSynPSyncroEdEscape, VK_ESCAPE, []); +end; + +{ TSynEditSyncroEditKeyStrokesOffCell } + +procedure TSynEditSyncroEditKeyStrokesOffCell.ResetDefaults; + procedure AddKey(const ACmd: TSynEditorCommand; const AKey: word; + const AShift: TShiftState); + begin + with Add do + begin + Key := AKey; + Shift := AShift; + Command := ACmd; + end; + end; +begin + Clear; + AddKey(ecSynPSyncroEdNextCell, VK_RIGHT, [ssCtrl]); + AddKey(ecSynPSyncroEdNextCellSel, VK_TAB, []); + AddKey(ecSynPSyncroEdPrevCell, VK_LEFT, [ssCtrl]); + AddKey(ecSynPSyncroEdPrevCellSel, VK_TAB, [ssShift]); + + AddKey(ecSynPSyncroEdEscape, VK_ESCAPE, []); +end; + +initialization + MouseOffset := AllocatePluginMouseRange(emcSynPSyncroEdCount); + KeyOffsetSel := AllocatePluginKeyRange(ecSynPSyncroEdSelModeCount); + KeyOffset := AllocatePluginKeyRange(ecSynPSyncroEdCount); + KeyOffsetOff := AllocatePluginKeyRange(ecSynPSyncroEdCount); + +end. + diff --git a/components/synedit/synpluginsyncronizededitbase.pp b/components/synedit/synpluginsyncronizededitbase.pp index 82e38965ef..104e0b6840 100644 --- a/components/synedit/synpluginsyncronizededitbase.pp +++ b/components/synedit/synpluginsyncronizededitbase.pp @@ -27,7 +27,8 @@ interface uses Classes, SysUtils, math, Graphics, - SynEditMiscClasses, SynEdit, SynEditMarkup, SynEditMiscProcs, SynEditTextBase; + SynEditMiscClasses, SynEdit, SynEditMarkup, SynEditMiscProcs, SynEditTextBase, + SynEditTextTrimmer; type @@ -77,31 +78,62 @@ type read GetGroupCell; end; - { TSynPluginSyncronizedEditMarkup } + { TSynPluginSyncronizedEditMarkupBase } - TSynPluginSyncronizedEditMarkup = class(TSynEditMarkup) + TSynPluginSyncronizedEditMarkupBase = class(TSynEditMarkup) private FCells: TSynPluginSyncronizedEditList; + procedure SetCells(const AValue: TSynPluginSyncronizedEditList); + protected + procedure CellChanged(aIndex: Integer; aOldVal, aNewVal: TSynPluginSyncronizedEditCell); virtual; abstract; + function OwnedByMgr: Boolean; override; + procedure DoEnabledChanged(Sender: TObject); override; + property Cells: TSynPluginSyncronizedEditList read FCells write SetCells; + public + constructor Create(ASynEdit: TSynEditBase); + destructor Destroy; override; + end; + + { TSynPluginSyncronizedEditMarkup } + + TSynPluginSyncronizedEditMarkup = class(TSynPluginSyncronizedEditMarkupBase) + private FCurrentCell: Integer; fMarkupInfoCurrent: TSynSelectedColor; fMarkupInfoSync: TSynSelectedColor; - procedure SetCells(const AValue: TSynPluginSyncronizedEditList); - procedure CellChanged(aIndex: Integer; aOldVal, aNewVal: TSynPluginSyncronizedEditCell); + FPreparedRow: Integer; + FPreparedCellFrom, FPreparedCellTo: Integer; + FPreparedCellTop, FPreparedCellBottom: Integer; procedure SetCurrentCell(const AValue: Integer); protected - function OwnedByMgr: Boolean; override; - procedure DoEnabledChanged(Sender: TObject); override; + procedure CellChanged(aIndex: Integer; aOldVal, aNewVal: TSynPluginSyncronizedEditCell); override; property CurrentCell: Integer read FCurrentCell write SetCurrentCell; - property Cells: TSynPluginSyncronizedEditList read FCells write SetCells; public constructor Create(ASynEdit: TSynEditBase); destructor Destroy; override; function GetMarkupAttributeAtRowCol(const aRow, aCol: Integer): TSynSelectedColor; override; function GetNextMarkupColAfterRowCol(const aRow, aCol: Integer): Integer; override; + Procedure PrepareMarkupForRow(aRow : Integer); override; + Procedure EndMarkup; override; + property MarkupInfoCurrent: TSynSelectedColor read fMarkupInfoCurrent; property MarkupInfoSync: TSynSelectedColor read fMarkupInfoSync; end; + { TSynPluginSyncronizedEditMarkupArea } + + TSynPluginSyncronizedEditMarkupArea = class(TSynPluginSyncronizedEditMarkupBase) + private + FCellIdForArea: Integer; + protected + procedure CellChanged(aIndex: Integer; aOldVal, aNewVal: TSynPluginSyncronizedEditCell); override; + public + function GetMarkupAttributeAtRowCol(const aRow, aCol: Integer): TSynSelectedColor; override; + function GetNextMarkupColAfterRowCol(const aRow, aCol: Integer): Integer; override; + + property CellGroupForArea: Integer read FCellIdForArea write FCellIdForArea; + end; + { TSynPluginSyncronizedEditBase } TSynPluginSyncronizedEditBase = class(TSynEditPlugin) @@ -109,30 +141,78 @@ type FActive: Boolean; FCells: TSynPluginSyncronizedEditList; FCurrentCell: Integer; + FAreaMarkupEnabled: Boolean; FEnabled: Boolean; - FMarkup: TSynPluginSyncronizedEditMarkup; FEditing: Boolean; + + fMarkupInfo: TSynSelectedColor; + fMarkupInfoSync: TSynSelectedColor; + fMarkupInfoCurrent: TSynSelectedColor; + fMarkupInfoArea: TSynSelectedColor; + function GetActive: Boolean; procedure SetActive(const AValue: Boolean); procedure SetCurrentCell(const AValue: Integer); + procedure SetAreaMarkupEnabled(const AValue: Boolean); procedure SetEnabled(const AValue: Boolean); protected + FMarkup: TSynPluginSyncronizedEditMarkup; + FMarkupArea: TSynPluginSyncronizedEditMarkupArea; + procedure MarkupChanged(AMarkup: TObject); + function CreateMarkup: TSynPluginSyncronizedEditMarkup; virtual; procedure SetEditor(const AValue: TCustomSynEdit); override; procedure DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount, aLineBrkCnt: Integer; aText: String); procedure DoBeforeEdit(aX, aY: Integer); virtual; procedure DoAfterEdit(aX, aY: Integer); virtual; + procedure DoClear; virtual; procedure DoOnActivate; virtual; procedure DoOnDeactivate; virtual; property CurrentCell: Integer read FCurrentCell write SetCurrentCell; property Cells: TSynPluginSyncronizedEditList read FCells; property Markup: TSynPluginSyncronizedEditMarkup read FMarkup; + property MarkupArea: TSynPluginSyncronizedEditMarkupArea read FMarkupArea; + property AreaMarkupEnabled: Boolean read FAreaMarkupEnabled write SetAreaMarkupEnabled; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Clear; property Enabled: Boolean read FEnabled write SetEnabled; property Active: Boolean read GetActive write SetActive; + + property MarkupInfo: TSynSelectedColor read FMarkupInfo; + property MarkupInfoCurrent: TSynSelectedColor read FMarkupInfoCurrent; + property MarkupInfoSync: TSynSelectedColor read FMarkupInfoSync; + property MarkupInfoArea: TSynSelectedColor read FMarkupInfoArea; + end; + + (* TSynPluginCustomSyncroEdit implements: + - Locking of TrimTrailingSpace + - CurrentCell follows Caret / LastCell + - DeActivate if Edit outside Cell + - various helpers, to set the caret/block + *) + + TSynPluginCustomSyncroEdit = class(TSynPluginSyncronizedEditBase) + private + FLastCell: Integer; + protected + procedure SetEditor(const AValue: TCustomSynEdit); override; + procedure DoOnActivate; override; + procedure DoOnDeactivate; override; + procedure DoBeforeEdit(aX, aY: Integer); override; + procedure UpdateCurrentCell; + procedure DoCaretChanged(Sender: TObject); + property LastCell: Integer read FLastCell; + protected + procedure SelectCurrentCell(Reverse: Boolean = False); + procedure PreviousCell(SetSelect: Boolean = True); + procedure NextCell(SetSelect: Boolean = True); + procedure CellCaretHome; + procedure CellCaretEnd; + public + //constructor Create(AOwner: TComponent); override; + //destructor Destroy; override; end; implementation @@ -253,7 +333,8 @@ begin else a := 0;; for i := 0 to Count -1 do begin - if ( (FCells[i].LogStart.Y < aY) or + if (FCells[i].Group >= 0) and + ( (FCells[i].LogStart.Y < aY) or ((FCells[i].LogStart.Y = aY) and (FCells[i].LogStart.X <= aX)) ) and ( (FCells[i].LogEnd.Y > aY) or ((FCells[i].LogEnd.Y = aY) and (FCells[i].LogEnd.X + a > aX)) ) @@ -279,9 +360,9 @@ Result := 0; inc(Result); end; -{ TSynPluginSyncronizedEditMarkup } +{ TSynPluginSyncronizedEditMarkupBase } -procedure TSynPluginSyncronizedEditMarkup.SetCells(const AValue: TSynPluginSyncronizedEditList); +procedure TSynPluginSyncronizedEditMarkupBase.SetCells(const AValue: TSynPluginSyncronizedEditList); begin if FCells = AValue then exit; if FCells <> nil then @@ -291,15 +372,36 @@ begin FCells.OnCellChange := @CellChanged; end; -procedure TSynPluginSyncronizedEditMarkup.CellChanged(aIndex: Integer; aOldVal, - aNewVal: TSynPluginSyncronizedEditCell); +function TSynPluginSyncronizedEditMarkupBase.OwnedByMgr: Boolean; begin - if aOldVal <> nil then - InvalidateSynLines(aOldVal.LogStart.Y, aOldVal.LogEnd.Y); - if aNewVal <> nil then - InvalidateSynLines(aNewVal.LogStart.Y, aNewVal.LogEnd.Y); + Result := False; end; +procedure TSynPluginSyncronizedEditMarkupBase.DoEnabledChanged(Sender: TObject); +var + i: Integer; +begin + if FCells.Count > 100 then + InvalidateSynLines(-1, -1) + else + for i := 0 to FCells.Count - 1 do + CellChanged(i, Cells[i], Cells[i]); +end; + +constructor TSynPluginSyncronizedEditMarkupBase.Create(ASynEdit: TSynEditBase); +begin + FCells := nil; + inherited; +end; + +destructor TSynPluginSyncronizedEditMarkupBase.Destroy; +begin + Cells := nil; + inherited Destroy; +end; + +{ TSynPluginSyncronizedEditMarkup } + procedure TSynPluginSyncronizedEditMarkup.SetCurrentCell(const AValue: Integer); var i, j: Integer; @@ -320,27 +422,187 @@ begin end; end; -function TSynPluginSyncronizedEditMarkup.OwnedByMgr: Boolean; +procedure TSynPluginSyncronizedEditMarkup.CellChanged(aIndex: Integer; aOldVal, + aNewVal: TSynPluginSyncronizedEditCell); begin - Result := False; -end; - -procedure TSynPluginSyncronizedEditMarkup.DoEnabledChanged(Sender: TObject); -var - i: Integer; -begin - for i := 0 to FCells.Count - 1 do - CellChanged(i, Cells[i], Cells[i]); + if (aOldVal <> nil) and (aOldVal.Group >= 0) then + InvalidateSynLines(aOldVal.LogStart.Y, aOldVal.LogEnd.Y); + if (aNewVal <> nil) and (aNewVal.Group >= 0) then + InvalidateSynLines(aNewVal.LogStart.Y, aNewVal.LogEnd.Y); end; constructor TSynPluginSyncronizedEditMarkup.Create(ASynEdit: TSynEditBase); begin - FCells := nil; - inherited; + inherited Create(ASynEdit); fMarkupInfoCurrent := TSynSelectedColor.Create; fMarkupInfoCurrent.OnChange := @MarkupChanged; fMarkupInfoSync := TSynSelectedColor.Create; fMarkupInfoSync.OnChange := @MarkupChanged; + FPreparedRow := -1; +end; + +destructor TSynPluginSyncronizedEditMarkup.Destroy; +begin + FreeAndNil(fMarkupInfoCurrent); + FreeAndNil(fMarkupInfoSync); + inherited Destroy; +end; + +function TSynPluginSyncronizedEditMarkup.GetMarkupAttributeAtRowCol(const aRow, + aCol: Integer): TSynSelectedColor; +var + i, col: Integer; +begin + col := PhysicalToLogicalPos(Point(aCol, aRow)).x; + Result := nil; + for i := FPreparedCellFrom to FPreparedCellTo do begin + if ( ((Cells[i].LogStart.y = aRow) and (Cells[i].LogStart.x <= Col)) or + (Cells[i].LogStart.y < aRow) ) and + ( ((Cells[i].LogEnd.y = aRow) and (Cells[i].LogEnd.x > Col)) or + (Cells[i].LogEnd.y > aRow) ) and + (Cells[i].Group >= 0) // dont't display negative groups + then begin + if i = CurrentCell then + Result := MarkupInfoCurrent + else + if (CurrentCell >= 0) and (Cells[i].Group = Cells[CurrentCell].Group) then + Result := MarkupInfoSync + else + Result := MarkupInfo; + + Result.StartX := LogicalToPhysicalPos(Cells[i].LogStart).x; + if Cells[i].LogStart.y < aRow then + Result.StartX := -1; + Result.EndX := LogicalToPhysicalPos(Cells[i].LogEnd).x - 1; + if Cells[i].LogEnd.y > aRow then + Result.EndX := -1; + break; + end; + end; +end; + +function TSynPluginSyncronizedEditMarkup.GetNextMarkupColAfterRowCol(const aRow, + aCol: Integer): Integer; +var + i, col: Integer; +begin + col := PhysicalToLogicalPos(Point(aCol, aRow)).x; + Result := -1; + for i := FPreparedCellFrom to FPreparedCellTo do begin + if Cells[i].Group < 0 then continue; + if (Cells[i].LogStart.y = aRow) and (Cells[i].LogStart.x > Col) and + ( (Cells[i].LogStart.x < Result) or (Result < 0) ) + then + Result := Cells[i].LogStart.x; + if (Cells[i].LogEnd.y = aRow) and (Cells[i].LogEnd.x > Col) and + ( (Cells[i].LogEnd.x < Result) or (Result < 0) ) + then + Result := Cells[i].LogEnd.x; + end; + if Result >= 0 then + Result := LogicalToPhysicalPos(Point(Result, aRow)).x; +end; + +procedure TSynPluginSyncronizedEditMarkup.PrepareMarkupForRow(aRow: Integer); +var + i, j, t, b: Integer; +begin + inherited PrepareMarkupForRow(aRow); + if FPreparedRow < 0 then begin + i := 0; + j := Cells.Count - 1; + t := TopLine; + b := ScreenRowToRow(LinesInWindow + 1) + 1; + while (i <= j) and ((Cells[i].Group < 0 ) or ((Cells[i].LogStart.y < t) and (Cells[i].LogEnd.y < t))) do + inc(i); + FPreparedCellTop := i; + while (i <= j) and ((Cells[j].Group < 0 ) or ((Cells[j].LogStart.y > b) and (Cells[j].LogEnd.y > b))) do + dec(j); + FPreparedCellBottom := j; + end;; + i := FPreparedCellTop; + j := FPreparedCellBottom; + if FPreparedRow >= 0 then begin + if FPreparedRow < aRow then + i := FPreparedCellFrom + else + j := FPreparedCellTo; + end; + FPreparedRow := aRow; + while (i <= j) and ((Cells[j].Group < 0 ) or ((Cells[i].LogStart.y < aRow) and (Cells[i].LogEnd.y < aRow))) do + inc(i); + FPreparedCellFrom := i; + while (i <= j) and ((Cells[j].Group < 0 ) or ((Cells[j].LogStart.y > aRow) and (Cells[j].LogEnd.y > aRow))) do + dec(j); + FPreparedCellTo := j; +end; + +procedure TSynPluginSyncronizedEditMarkup.EndMarkup; +begin + inherited EndMarkup; + FPreparedRow := -1; +end; + +{ TSynPluginSyncronizedEditMarkupArea } + +procedure TSynPluginSyncronizedEditMarkupArea.CellChanged(aIndex: Integer; aOldVal, + aNewVal: TSynPluginSyncronizedEditCell); +begin + if (aOldVal <> nil) and (aOldVal.Group = CellGroupForArea) then + InvalidateSynLines(aOldVal.LogStart.Y, aOldVal.LogEnd.Y); + if (aNewVal <> nil) and (aNewVal.Group = CellGroupForArea) then + InvalidateSynLines(aNewVal.LogStart.Y, aNewVal.LogEnd.Y); +end; + +function TSynPluginSyncronizedEditMarkupArea.GetMarkupAttributeAtRowCol(const aRow, + aCol: Integer): TSynSelectedColor; +var + ac: TSynPluginSyncronizedEditCell; +begin + Result := nil; + if MarkupInfo.IsEnabled then begin + ac := Cells.GroupCell[CellGroupForArea, 0]; + if (ac <> nil) and + ( ((ac.LogStart.y = aRow) and (ac.LogStart.x <= aCol)) or (ac.LogStart.y < aRow)) and + ( ((ac.LogEnd.y = aRow) and (ac.LogEnd.x > aCol)) or (ac.LogEnd.y > aRow)) + then + Result := MarkupInfo; + end; +end; + +function TSynPluginSyncronizedEditMarkupArea.GetNextMarkupColAfterRowCol(const aRow, + aCol: Integer): Integer; +var + ac: TSynPluginSyncronizedEditCell; +begin + Result := -1; + if MarkupInfo.IsEnabled then begin + ac := Cells.GroupCell[CellGroupForArea, 0]; + if ac <> nil then begin + if (ac.LogStart.y = aRow) and (ac.LogStart.x > aCol) and + ( (ac.LogStart.x < Result) or (Result < 0) ) + then + Result := ac.LogStart.x; + if (ac.LogEnd.y = aRow) and (ac.LogEnd.x > aCol) and + ( (ac.LogEnd.x < Result) or (Result < 0) ) + then + Result := ac.LogEnd.x; + end; + end; +end; + +{ TSynPluginSyncronizedEditBase } + +constructor TSynPluginSyncronizedEditBase.Create(AOwner: TComponent); +begin + fMarkupInfo := TSynSelectedColor.Create; + fMarkupInfo.OnChange := @MarkupChanged; + fMarkupInfoSync := TSynSelectedColor.Create; + fMarkupInfoSync.OnChange := @MarkupChanged; + fMarkupInfoCurrent := TSynSelectedColor.Create; + fMarkupInfoCurrent.OnChange := @MarkupChanged; + fMarkupInfoArea := TSynSelectedColor.Create; + fMarkupInfoArea.OnChange := @MarkupChanged; MarkupInfo.FrameColor := clMaroon; MarkupInfo.Background := clNone; @@ -353,72 +615,14 @@ begin MarkupInfoSync.FrameColor := clFuchsia; MarkupInfoSync.Background := clNone; MarkupInfoSync.Foreground := clNone; -end; -destructor TSynPluginSyncronizedEditMarkup.Destroy; -begin - Cells := nil; - FreeAndNil(fMarkupInfoCurrent); - FreeAndNil(fMarkupInfoSync); - inherited Destroy; -end; + MarkupInfoArea.FrameColor := clNone; + MarkupInfoArea.Background := clNone; + MarkupInfoArea.Foreground := clNone; -function TSynPluginSyncronizedEditMarkup.GetMarkupAttributeAtRowCol(const aRow, - aCol: Integer): TSynSelectedColor; -var - i: Integer; - m : TSynSelectedColor; -begin - Result := nil; - for i := 0 to Cells.Count -1 do begin - if ( ((Cells[i].LogStart.y = aRow) and (Cells[i].LogStart.x <= aCol)) or - (Cells[i].LogStart.y < aRow) ) and - ( ((Cells[i].LogEnd.y = aRow) and (Cells[i].LogEnd.x > aCol)) or - (Cells[i].LogEnd.y > aRow) ) and - (Cells[i].Group >= 0) // dont't display negative groups - then begin - if i = CurrentCell then - m := MarkupInfoCurrent - else - if (CurrentCell >= 0) and (Cells[i].Group = Cells[CurrentCell].Group) then - m := MarkupInfoSync - else - m := MarkupInfo; - m.StartX := Cells[i].LogStart.x; - if Cells[i].LogStart.y < aRow then - m.StartX := -1; - m.EndX := Cells[i].LogEnd.x - 1; - if Cells[i].LogEnd.y > aRow then - m.EndX := -1; - exit(m); - end; - end; -end; - -function TSynPluginSyncronizedEditMarkup.GetNextMarkupColAfterRowCol(const aRow, - aCol: Integer): Integer; -var - i: Integer; -begin - Result := -1; - for i := 0 to Cells.Count -1 do begin - if (Cells[i].LogStart.y = aRow) and (Cells[i].LogStart.x > aCol) and - ( (Cells[i].LogStart.x < Result) or (Result < 0) ) - then - Result := Cells[i].LogStart.x; - if (Cells[i].LogEnd.y = aRow) and (Cells[i].LogEnd.x > aCol) and - ( (Cells[i].LogEnd.x < Result) or (Result < 0) ) - then - Result := Cells[i].LogEnd.x; - end -end; - -{ TSynPluginSyncronizedEditBase } - -constructor TSynPluginSyncronizedEditBase.Create(AOwner: TComponent); -begin FCells := TSynPluginSyncronizedEditList.Create; CurrentCell := -1; + AreaMarkupEnabled := False; inherited Create(AOwner); FEnabled := True; Active := False; @@ -429,7 +633,12 @@ destructor TSynPluginSyncronizedEditBase.Destroy; begin Editor := nil; FreeAndNil(FMarkup); + FreeAndNil(FMarkupArea); FreeAndNil(FCells); + FreeAndNil(fMarkupInfo); + FreeAndNil(fMarkupInfoSync); + FreeAndNil(fMarkupInfoCurrent); + FreeAndNil(fMarkupInfoArea); inherited; end; @@ -438,6 +647,7 @@ begin FCells.Clear; CurrentCell := -1; Active := False; + DoClear; end; procedure TSynPluginSyncronizedEditBase.SetEditor(const AValue: TCustomSynEdit); @@ -450,14 +660,23 @@ begin TSynEditMarkupManager(MarkupMgr).RemoveMarkUp(FMarkup); FreeAndNil(FMarkup); end; + if FMarkupArea <> nil then begin + TSynEditMarkupManager(MarkupMgr).RemoveMarkUp(FMarkupArea); + FreeAndNil(FMarkupArea); + end; end; inherited SetEditor(AValue); if AValue <> nil then begin - FMarkup := TSynPluginSyncronizedEditMarkup.Create(Editor); + FMarkup := CreateMarkup; FMarkup.Cells := FCells; FMarkup.CurrentCell := FCurrentCell; FMarkup.Enabled := Active; TSynEditMarkupManager(MarkupMgr).AddMarkUp(FMarkup); + FMarkupArea := TSynPluginSyncronizedEditMarkupArea.Create(Editor); + FMarkupArea.Cells := FCells; + FMarkupArea.Enabled := Active; + TSynEditMarkupManager(MarkupMgr).AddMarkUp(FMarkupArea, True); + MarkupChanged(nil); ViewedTextBuffer.AddEditHandler(@DoLinesEdited); end; end; @@ -470,6 +689,14 @@ begin FMarkup.CurrentCell := FCurrentCell; end; +procedure TSynPluginSyncronizedEditBase.SetAreaMarkupEnabled(const AValue: Boolean); +begin + if FAreaMarkupEnabled = AValue then exit; + FAreaMarkupEnabled := AValue; + if FMarkupArea <> nil then + FMarkupArea.Enabled := Active and FAreaMarkupEnabled; +end; + procedure TSynPluginSyncronizedEditBase.SetEnabled(const AValue: Boolean); var IsActive: Boolean; @@ -478,6 +705,8 @@ begin FEnabled := AValue; if FMarkup <> nil then FMarkup.Enabled := Active; + if FMarkupArea <> nil then + FMarkupArea.Enabled := Active and FAreaMarkupEnabled; if IsActive <> Active then begin if Active then DoOnActivate @@ -485,6 +714,22 @@ begin end; end; +procedure TSynPluginSyncronizedEditBase.MarkupChanged(AMarkup: TObject); +begin + if FMarkup <> nil then begin + FMarkup.MarkupInfo.Assign(fMarkupInfo); + FMarkup.MarkupInfoSync.Assign(fMarkupInfoSync); + FMarkup.MarkupInfoCurrent.Assign(fMarkupInfoCurrent); + end; + if FMarkupArea <> nil then + FMarkupArea.MarkupInfo.Assign(fMarkupInfoArea); +end; + +function TSynPluginSyncronizedEditBase.CreateMarkup: TSynPluginSyncronizedEditMarkup; +begin + Result := TSynPluginSyncronizedEditMarkup.Create(Editor); +end; + function TSynPluginSyncronizedEditBase.GetActive: Boolean; begin Result := FActive and FEnabled and (Editor <> nil); @@ -498,6 +743,8 @@ begin FActive := AValue; if FMarkup <> nil then FMarkup.Enabled := Active; + if FMarkupArea <> nil then + FMarkupArea.Enabled := Active and FAreaMarkupEnabled; if IsActive <> Active then begin if Active then DoOnActivate @@ -510,23 +757,25 @@ procedure TSynPluginSyncronizedEditBase.DoLinesEdited(Sender: TSynEditStrings; var Pos, Pos2: TPoint; - function AdjustPoint(aPoint: Tpoint): TPoint; + function AdjustPoint(aPoint: Tpoint; var Changed: Boolean): TPoint; inline; begin Result := aPoint; if aLineBrkCnt < 0 then begin (* Lines Deleted *) - if aPoint.y > aLinePos then + if aPoint.y > aLinePos then begin Result.y := Max(aLinePos, Result.y + aLineBrkCnt); - if Result.y = aLinePos then - Result.x := Result.x + Pos.x - 1; + if Result.y = aLinePos then + Result.x := Result.x + Pos.x - 1; + end; end else if aLineBrkCnt > 0 then begin (* Lines Inserted *) - if aPoint.y = aLinePos then - Result.x := Result.x - Pos.x + 1; - if aPoint.y >= aLinePos then + if aPoint.y >= aLinePos then begin + if (aPoint.y = aLinePos) and (aPoint.x > Pos.x) then + Result.x := Result.x - Pos.x + 1; Result.y := Result.y + aLineBrkCnt; + end; end else if aCount <> 0 then begin @@ -534,27 +783,33 @@ var if (aPoint.y = aLinePos) and (aPoint.x >= Pos.x) then Result.x := Max(Pos.x, Result.x + aCount); end; + Changed := Changed or (aPoint.x <> Result.x) or (aPoint.y <> Result.y); end; var i, a: Integer; CurCell: TSynPluginSyncronizedEditCell; Y2, X2: Integer; + chg: Boolean; + CaretPos: TPoint; begin if (not Active) or (FCells.Count = 0) then exit; - Pos := ViewedTextBuffer.LogicalToPhysicalPos(Point(aBytePos, aLinePos)); + Pos := Point(aBytePos, aLinePos); Pos2 := Pos; - if Not FEditing then + if (not (FEditing or IsUndoing or IsRedoing)) then DoBeforeEdit(Pos.x, Pos.y); + for i := 0 to FCells.Count - 1 do begin CurCell := Cells[i]; + chg := False; a := CompareCarets(Pos, CurCell.LogStart); if (a > 0) then - CurCell.LogStart := AdjustPoint(CurCell.LogStart); + CurCell.LogStart := AdjustPoint(CurCell.LogStart, chg); a := CompareCarets(Pos, CurCell.LogEnd); if (a > 0) or ((a = 0) and ((i = FCurrentCell) or FEditing)) then - CurCell.LogEnd := AdjustPoint(CurCell.LogEnd); - Cells[i] := CurCell; + CurCell.LogEnd := AdjustPoint(CurCell.LogEnd, chg); + if chg then + Cells[i] := CurCell; end; if (not (FEditing or IsUndoing or IsRedoing)) and @@ -562,7 +817,10 @@ begin (CompareCarets(Pos, FCells[FCurrentCell].LogStart) <= 0) and (CompareCarets(Pos, FCells[FCurrentCell].LogEnd) >= 0) then begin + ViewedTextBuffer.BeginUpdate; + try FEditing := True; + CaretPos := CaretObj.LineBytePos; CurCell := FCells[FCurrentCell]; a := CurCell.Group; Pos.Y := Pos.Y - CurCell.LogStart.y; @@ -581,29 +839,50 @@ begin X2 := X2 + FCells[i].LogStart.X; if aLineBrkCnt = -1 then begin ViewedTextBuffer.EditLineJoin(Y2); + if (CaretPos.y = Y2 + 1) then begin + dec(CaretPos.y); + inc(CaretPos.x, X2 - 1); + end; end else if aLineBrkCnt < -1 then begin ViewedTextBuffer.EditLinesDelete(Y2, -aLineBrkCnt); + if (CaretPos.y > Y2) then + inc(CaretPos.y, aLineBrkCnt); end else if aLineBrkCnt = 1 then begin ViewedTextBuffer.EditLineBreak(X2, Y2); + if (CaretPos.y = Y2) and (CaretPos.x > X2) then begin + inc(CaretPos.y); + dec(CaretPos.x, X2 - 1); + end; end else if aLineBrkCnt > 1 then begin ViewedTextBuffer.EditLinesInsert(Y2, aLineBrkCnt); + if (CaretPos.y > Y2) then + inc(CaretPos.y, aLineBrkCnt); end else if aCount < 0 then begin ViewedTextBuffer.EditDelete(X2, Y2, -aCount); + if (CaretPos.y = Y2) and (CaretPos.X > X2) then + inc(CaretPos.X, aCount); end else if aCount > 0 then begin ViewedTextBuffer.EditInsert(X2, Y2, aText); + if (CaretPos.y = Y2) and (CaretPos.X > X2) then + inc(CaretPos.X, aCount); end; end; + finally + ViewedTextBuffer.EndUpdate; + end; + CaretObj.LineBytePos := CaretPos; FEditing := False; if Pos.y = 0 then pos2.x := pos.x + CurCell.LogStart.x; Pos2.y := Pos.y + CurCell.LogStart.y; end; - if Not FEditing then + + if (not (FEditing or IsUndoing or IsRedoing)) then DoAfterEdit(Pos2.x, Pos2.y); end; @@ -617,6 +896,11 @@ begin (* Do Nothing *); end; +procedure TSynPluginSyncronizedEditBase.DoClear; +begin + (* Do Nothing *); +end; + procedure TSynPluginSyncronizedEditBase.DoOnActivate; begin (* Do Nothing *); @@ -637,5 +921,175 @@ begin FGroup := Src.FGroup; end; +{ TSynPluginCustomSyncroEdit } + +procedure TSynPluginCustomSyncroEdit.SetEditor(const AValue: TCustomSynEdit); +begin + if Editor = AValue then exit; + if Editor <> nil then begin + CaretObj.RemoveChangeHandler(@DoCaretChanged); + end; + inherited SetEditor(AValue); + if Editor <> nil then begin + CaretObj.AddChangeHandler(@DoCaretChanged); + end; +end; + +procedure TSynPluginCustomSyncroEdit.DoOnActivate; +var + b: TSynEditStrings; +begin + b := ViewedTextBuffer; + while b <> nil do begin + if b is TSynEditStringTrimmingList then TSynEditStringTrimmingList(b).Lock; + if b is TSynEditStringsLinked then + b := TSynEditStringsLinked(b).NextLines + else + b := nil; + end; +end; + +procedure TSynPluginCustomSyncroEdit.DoOnDeactivate; +var + b: TSynEditStrings; +begin + b := ViewedTextBuffer; + while b <> nil do begin + if b is TSynEditStringTrimmingList then TSynEditStringTrimmingList(b).UnLock; + if b is TSynEditStringsLinked then + b := TSynEditStringsLinked(b).NextLines + else + b := nil; + end; +end; + +procedure TSynPluginCustomSyncroEdit.DoBeforeEdit(aX, aY: Integer); +begin + if not Active then exit; + UpdateCurrentCell; + if CurrentCell < 0 then begin + Clear; + Active := False; + end; +end; + +procedure TSynPluginCustomSyncroEdit.UpdateCurrentCell; +var + i: Integer; +begin + i := Cells.IndexOf(CaretObj.BytePos, CaretObj.LinePos, True); + if (i <> CurrentCell) and (CurrentCell >= 0) then + FLastCell := CurrentCell; + CurrentCell := i; +end; + +procedure TSynPluginCustomSyncroEdit.DoCaretChanged(Sender: TObject); +begin + if not Active then exit; + UpdateCurrentCell; +end; + +procedure TSynPluginCustomSyncroEdit.SelectCurrentCell(Reverse: Boolean); +begin + if (CurrentCell < 0) and (LastCell >= 0) then + CurrentCell := LastCell; + if (CurrentCell < 0) then + exit; + if Reverse then begin + CaretObj.LineBytePos := Cells[CurrentCell].LogStart; + Editor.BlockBegin := Cells[CurrentCell].LogEnd; + Editor.BlockEnd := Cells[CurrentCell].LogStart; + end else begin + CaretObj.LineBytePos := Cells[CurrentCell].LogEnd; + Editor.BlockBegin := Cells[CurrentCell].LogStart; + Editor.BlockEnd := Cells[CurrentCell].LogEnd; + end; +end; + +procedure TSynPluginCustomSyncroEdit.PreviousCell(SetSelect: Boolean); +var + i, j: Integer; + Pos: TPoint; +begin + Pos := CaretObj.LineBytePos; + i := Cells.IndexOf(Pos.x, Pos.y, True); + if i < 0 then begin + i := 0; + while (i < Cells.Count) and + ((Cells[i].Group < 0) or (CompareCarets(Cells[i].LogEnd, Pos) >= 0)) + do + inc(i); + end; + + j := 0; + Repeat + dec(i); + inc(j); + if i < 0 then + i := Cells.Count - 1; + until (j > Cells.Count) or (Cells[i].Group >= 0); + CurrentCell := i; + + if CurrentCell < 0 then + exit; + CaretObj.LineBytePos := Cells[CurrentCell].LogEnd; + if SetSelect then + SelectCurrentCell + else + Editor.BlockBegin := Cells[CurrentCell].LogEnd; +end; + +procedure TSynPluginCustomSyncroEdit.NextCell(SetSelect: Boolean); +var + Pos: TPoint; + i, j: Integer; +begin + Pos := CaretObj.LineBytePos; + i := Cells.IndexOf(Pos.x, Pos.y, True); + if i < 0 then begin + i := Cells.Count - 1; + while (i >= 0) and + ((Cells[i].Group < 0) or (CompareCarets(Cells[i].LogEnd, Pos) <= 0)) + do + dec(i); + end; + + j := 0; + Repeat + inc(i); + inc(j); + if i >= Cells.Count then + i := 0 + until (j > Cells.Count) or (Cells[i].Group >= 0); + CurrentCell := i; + if CurrentCell < 0 then + exit; + CaretObj.LineBytePos := Cells[CurrentCell].LogStart; + if SetSelect then + SelectCurrentCell(True) + else + Editor.BlockBegin := Cells[CurrentCell].LogStart; +end; + +procedure TSynPluginCustomSyncroEdit.CellCaretHome; +begin + if (CurrentCell < 0) and (LastCell >= 0) then + CurrentCell := LastCell; + if (CurrentCell < 0) then + exit; + CaretObj.LineBytePos := Cells[CurrentCell].LogStart; + Editor.BlockBegin := Cells[CurrentCell].LogStart; +end; + +procedure TSynPluginCustomSyncroEdit.CellCaretEnd; +begin + if (CurrentCell < 0) and (LastCell >= 0) then + CurrentCell := LastCell; + if (CurrentCell < 0) then + exit; + CaretObj.LineBytePos := Cells[CurrentCell].LogEnd; + Editor.BlockBegin := Cells[CurrentCell].LogEnd; +end; + end. diff --git a/components/synedit/synplugintemplateedit.pp b/components/synedit/synplugintemplateedit.pp index a64ad63f04..75c71dfe45 100644 --- a/components/synedit/synplugintemplateedit.pp +++ b/components/synedit/synplugintemplateedit.pp @@ -28,7 +28,7 @@ interface uses Classes, SysUtils, math, Graphics, LCLType, SynEditMiscClasses, SynPluginSyncronizedEditBase, SynEditKeyCmds, SynEdit, SynEditMiscProcs, - SynEditTextTrimmer, SynEditTextBase, LCLProc; + SynEditTextBase, LCLProc; type @@ -45,26 +45,18 @@ type public procedure ResetDefaults; override; end; + { TSynPluginTemplateEdit } - TSynPluginTemplateEdit = class(TSynPluginSyncronizedEditBase) + TSynPluginTemplateEdit = class(TSynPluginCustomSyncroEdit) private FCellParserEnabled: Boolean; FKeystrokes, FKeyStrokesOffCell: TSynEditKeyStrokes; FStartPoint: TPoint; - FLastCell: Integer; - function GetMarkupInfo: TSynSelectedColor; - function GetMarkupInfoCurrent: TSynSelectedColor; - function GetMarkupInfoSync: TSynSelectedColor; procedure SetKeystrokes(const AValue: TSynEditKeyStrokes); procedure SetKeystrokesOffCell(const AValue: TSynEditKeyStrokes); protected procedure SetEditor(const AValue: TCustomSynEdit); override; - procedure DoBeforeEdit(aX, aY: Integer); override; - procedure DoOnActivate; override; - procedure DoOnDeactivate; override; - procedure UpdateCurrentCell; - procedure DoCaretChanged(Sender: TObject); virtual; procedure TranslateKey(Sender: TObject; Code: word; SState: TShiftState; var Data: pointer; var IsStartOfCombo: boolean; var Handled: boolean; var Command: TSynEditorCommand; FinishComboOnly: Boolean; @@ -73,11 +65,7 @@ type var Handled: boolean; var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; HandlerData: pointer); - procedure SelectCurrentCell(Reverse: Boolean = False); - procedure PreviousCell(SetSelect: Boolean = True); - procedure NextCell(SetSelect: Boolean = True; CycleToFirst: Boolean = False); - procedure CellCaretHome; - procedure CellCaretEnd; + procedure NextCellOrFinal(SetSelect: Boolean = True); procedure SetFinalCaret; public constructor Create(AOwner: TComponent); override; @@ -96,9 +84,6 @@ type read FKeystrokes write SetKeystrokes; property KeystrokesOffCell: TSynEditKeyStrokes read FKeystrokesOffCell write SetKeystrokesOffCell; - property MarkupInfo: TSynSelectedColor read GetMarkupInfo; - property MarkupInfoCurrent: TSynSelectedColor read GetMarkupInfoCurrent; - property MarkupInfoSync: TSynSelectedColor read GetMarkupInfoSync; end; const @@ -114,7 +99,7 @@ const ecSynPTmplEdFinish = ecPluginFirst + 9; ecSynPTmplEdEscape = ecPluginFirst + 10; - ecSynPTmplEdLast = ecPluginFirst + 10; + ecSynPTmplEdCount = 10; implementation @@ -146,47 +131,38 @@ class function TSynPluginTemplateEdit.ConvertCommandToBase (Command: TSynEditorCommand): TSynEditorCommand; begin if (Command >= ecPluginFirst + KeyOffset) and - (Command <= ecPluginFirst + KeyOffset + ecSynPTmplEdLast) - then - Result := Command - KeyOffset - else - Result := ecNone; + (Command <= ecPluginFirst + KeyOffset + ecSynPTmplEdCount) + then Result := Command - KeyOffset + else Result := ecNone; end; class function TSynPluginTemplateEdit.ConvertBaseToCommand(Command: TSynEditorCommand): TSynEditorCommand; begin - if (Command >= ecPluginFirst) and (Command <= ecPluginFirst + ecSynPTmplEdLast) - then - Result := Command + KeyOffset - else - Result := ecNone; + if (Command >= ecPluginFirst) and (Command <= ecPluginFirst + ecSynPTmplEdCount) + then Result := Command + KeyOffset + else Result := ecNone; end; class function TSynPluginTemplateEdit.ConvertCommandToBaseOff (Command: TSynEditorCommand): TSynEditorCommand; begin if (Command >= ecPluginFirst + KeyOffsetOff) and - (Command <= ecPluginFirst + KeyOffsetOff + ecSynPTmplEdLast) - then - Result := Command - KeyOffsetOff - else - Result := ecNone; + (Command <= ecPluginFirst + KeyOffsetOff + ecSynPTmplEdCount) + then Result := Command - KeyOffsetOff + else Result := ecNone; end; class function TSynPluginTemplateEdit.ConvertBaseToCommandOff(Command: TSynEditorCommand): TSynEditorCommand; begin - if (Command >= ecPluginFirst) and (Command <= ecPluginFirst + ecSynPTmplEdLast) - then - Result := Command + KeyOffsetOff - else - Result := ecNone; + if (Command >= ecPluginFirst) and (Command <= ecPluginFirst + ecSynPTmplEdCount) + then Result := Command + KeyOffsetOff + else Result := ecNone; end; procedure TSynPluginTemplateEdit.SetEditor(const AValue: TCustomSynEdit); begin if Editor = AValue then exit; if Editor <> nil then begin - CaretObj.RemoveChangeHandler(@DoCaretChanged); Editor.UnRegisterKeyTranslationHandler(@TranslateKey); Editor.UnregisterCommandHandler(@ProcessSynCommand); end; @@ -194,7 +170,6 @@ begin if Editor <> nil then begin Editor.RegisterCommandHandler(@ProcessSynCommand, nil); Editor.RegisterKeyTranslationHandler(@TranslateKey); - CaretObj.AddChangeHandler(@DoCaretChanged); end; end; @@ -214,74 +189,6 @@ begin FKeyStrokesOffCell.Assign(AValue); end; -function TSynPluginTemplateEdit.GetMarkupInfo: TSynSelectedColor; -begin - Result := Markup.MarkupInfo; -end; - -function TSynPluginTemplateEdit.GetMarkupInfoCurrent: TSynSelectedColor; -begin - Result := Markup.MarkupInfoCurrent; -end; - -function TSynPluginTemplateEdit.GetMarkupInfoSync: TSynSelectedColor; -begin - Result := Markup.MarkupInfoSync; -end; - -procedure TSynPluginTemplateEdit.DoBeforeEdit(aX, aY: Integer); -begin - UpdateCurrentCell; - if CurrentCell < 0 then begin - Clear; - Active := False; - end; -end; - -procedure TSynPluginTemplateEdit.DoOnActivate; -var - b: TSynEditStrings; -begin - b := ViewedTextBuffer; - while b <> nil do begin - if b is TSynEditStringTrimmingList then TSynEditStringTrimmingList(b).Lock; - if b is TSynEditStringsLinked then - b := TSynEditStringsLinked(b).NextLines - else - b := nil;; - end; -end; - -procedure TSynPluginTemplateEdit.DoOnDeactivate; -var - b: TSynEditStrings; -begin - b := ViewedTextBuffer; - while b <> nil do begin - if b is TSynEditStringTrimmingList then TSynEditStringTrimmingList(b).UnLock; - if b is TSynEditStringsLinked then - b := TSynEditStringsLinked(b).NextLines - else - b := nil;; - end; -end; - -procedure TSynPluginTemplateEdit.UpdateCurrentCell; -var - i: Integer; -begin - i := Cells.IndexOf(CaretObj.BytePos, CaretObj.LinePos, True); - if (i <> CurrentCell) and (CurrentCell >= 0) then - FLastCell := CurrentCell; - CurrentCell := i; -end; - -procedure TSynPluginTemplateEdit.DoCaretChanged(Sender: TObject); -begin - if not Active then exit; - UpdateCurrentCell; -end; - procedure TSynPluginTemplateEdit.TranslateKey(Sender: TObject; Code: word; SState: TShiftState; var Data: pointer; var IsStartOfCombo: boolean; var Handled: boolean; var Command: TSynEditorCommand; FinishComboOnly: Boolean; @@ -333,10 +240,10 @@ begin Handled := True; case Cmd of - ecSynPTmplEdNextCell: NextCell(False); - ecSynPTmplEdNextCellSel: NextCell(True); - ecSynPTmplEdNextCellRotate: NextCell(False, True); - ecSynPTmplEdNextCellSelRotate: NextCell(True, True); + ecSynPTmplEdNextCell: NextCellOrFinal(False); + ecSynPTmplEdNextCellSel: NextCellOrFinal(True); + ecSynPTmplEdNextCellRotate: NextCell(False); + ecSynPTmplEdNextCellSelRotate: NextCell(True); ecSynPTmplEdPrevCell: PreviousCell(False); ecSynPTmplEdPrevCellSel: PreviousCell(True); ecSynPTmplEdCellHome: CellCaretHome; @@ -353,61 +260,10 @@ begin end; end; -procedure TSynPluginTemplateEdit.SelectCurrentCell(Reverse: Boolean = False); -begin - if (CurrentCell < 0) and (FLastCell >= 0) then - CurrentCell := FLastCell; - if (CurrentCell < 0) then - exit; - if Reverse then begin - CaretObj.LineBytePos := Cells[CurrentCell].LogStart; - Editor.BlockBegin := Cells[CurrentCell].LogEnd; - Editor.BlockEnd := Cells[CurrentCell].LogStart; - end else begin - CaretObj.LineBytePos := Cells[CurrentCell].LogEnd; - Editor.BlockBegin := Cells[CurrentCell].LogStart; - Editor.BlockEnd := Cells[CurrentCell].LogEnd; - end; -end; - -procedure TSynPluginTemplateEdit.PreviousCell(SetSelect: Boolean = True); -var - i, j: Integer; - Pos: TPoint; -begin - Pos := CaretObj.LineBytePos; - i := Cells.IndexOf(Pos.x, Pos.y, True); - if i < 0 then begin - i := 0; - while (i < Cells.Count) and - ((Cells[i].Group < 0) or (CompareCarets(Cells[i].LogEnd, Pos) >= 0)) - do - inc(i); - end; - - j := 0; - Repeat - dec(i); - inc(j); - if i < 0 then - i := Cells.Count - 1; - until (j > Cells.Count) or (Cells[i].Group >= 0); - CurrentCell := i; - - if CurrentCell < 0 then - exit; - CaretObj.LineBytePos := Cells[CurrentCell].LogEnd; - if SetSelect then - SelectCurrentCell - else - Editor.BlockBegin := Cells[CurrentCell].LogEnd; -end; - -procedure TSynPluginTemplateEdit.NextCell(SetSelect: Boolean = True; - CycleToFirst: Boolean = False); +procedure TSynPluginTemplateEdit.NextCellOrFinal(SetSelect: Boolean); var Pos: TPoint; - i, j: Integer; + i: Integer; begin Pos := CaretObj.LineBytePos; i := Cells.IndexOf(Pos.x, Pos.y, True); @@ -419,19 +275,13 @@ begin dec(i); end; - j := 0; Repeat inc(i); - inc(j); if i >= Cells.Count then begin - if CycleToFirst then - i := 0 - else begin - SetFinalCaret; - exit; - end; + SetFinalCaret; + exit; end; - until (j > Cells.Count) or (Cells[i].Group >= 0); + until (Cells[i].Group >= 0); CurrentCell := i; if CurrentCell < 0 then exit; @@ -442,26 +292,6 @@ begin Editor.BlockBegin := Cells[CurrentCell].LogStart; end; -procedure TSynPluginTemplateEdit.CellCaretHome; -begin - if (CurrentCell < 0) and (FLastCell >= 0) then - CurrentCell := FLastCell; - if (CurrentCell < 0) then - exit; - CaretObj.LineBytePos := Cells[CurrentCell].LogStart; - Editor.BlockBegin := Cells[CurrentCell].LogStart; -end; - -procedure TSynPluginTemplateEdit.CellCaretEnd; -begin - if (CurrentCell < 0) and (FLastCell >= 0) then - CurrentCell := FLastCell; - if (CurrentCell < 0) then - exit; - CaretObj.LineBytePos := Cells[CurrentCell].LogEnd; - Editor.BlockBegin := Cells[CurrentCell].LogEnd; -end; - procedure TSynPluginTemplateEdit.SetFinalCaret; var c: TSynPluginSyncronizedEditCell; @@ -675,8 +505,8 @@ begin end; initialization - KeyOffset := AllocatePluginKeyRange(ecSynPTmplEdLast + 1); - KeyOffsetOff := AllocatePluginKeyRange(ecSynPTmplEdLast + 1); + KeyOffset := AllocatePluginKeyRange(ecSynPTmplEdCount + 1); + KeyOffsetOff := AllocatePluginKeyRange(ecSynPTmplEdCount + 1); end. diff --git a/ide/editoroptions.pp b/ide/editoroptions.pp index bc1b962b85..41e843ef70 100644 --- a/ide/editoroptions.pp +++ b/ide/editoroptions.pp @@ -46,7 +46,7 @@ uses SynHighlighterPas, SynHighlighterPerl, SynHighlighterPHP, SynHighlighterSQL, SynHighlighterPython, SynHighlighterUNIXShellScript, SynHighlighterXML, SynHighlighterJScript, SynEditMiscClasses, SynBeautifier, SynEditTextTrimmer, - SynEditMouseCmds, SynPluginTemplateEdit, + SynEditMouseCmds, SynPluginTemplateEdit, SynPluginSyncroEdit, // codetools LinkScanner, CodeToolManager, Laz_XMLCfg, // IDEIntf @@ -70,10 +70,11 @@ type phaString, phaSymbol ); - TLazSynPluginTemplateEditForm = class(TForm) - end; - TLazSynPluginTemplateEditFormOff = class(TForm) - end; + TLazSynPluginTemplateEditForm = class(TForm) end; + TLazSynPluginTemplateEditFormOff = class(TForm) end; + TLazSynPluginSyncroEditFormSel = class(TForm) end; + TLazSynPluginSyncroEditForm = class(TForm) end; + TLazSynPluginSyncroEditFormOff = class(TForm) end; const PascalHilightAttributeNames: array[TPascalHilightAttribute] of String = ( @@ -99,7 +100,8 @@ type ahaErrorLine, ahaIncrementalSearch, ahaHighlightAll, ahaBracketMatch, ahaMouseLink, ahaLineNumber, ahaLineHighlight, ahaModifiedLine, ahaCodeFoldingTree, ahaHighlightWord, ahaFoldedCode, ahaWordGroup, - ahaTemplateEditCur, ahaTemplateEditSync, ahaTemplateEditOther); + ahaTemplateEditCur, ahaTemplateEditSync, ahaTemplateEditOther, + ahaSyncroEditCur, ahaSyncroEditSync, ahaSyncroEditOther, ahaSyncroEditArea); TSingleColorAttribute = (scaGutter, scaRightMargin); @@ -127,7 +129,11 @@ const 'Word-Brackets', 'TemplateEdit Current', 'TemplateEdit Sync', - 'TemplateEdit Cells' + 'TemplateEdit Cells', + 'SyncronEdit Current Cells', + 'SyncronEdit Syncron Cells', + 'SyncronEdit Other Cells', + 'SyncronEdit Range' ); SingleColorAttributes: array[TSingleColorAttribute] of String = @@ -208,7 +214,11 @@ const { ahaWordGroup } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), { ahaTemplateEditCur } (BG: clNone; FG: clNone; FC: clAqua; Styles: []; StylesMask: []), { ahaTemplateEditSync } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), - { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []) + { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []), + { ahaSyncroEditCur } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), + { ahaSyncroEditSync } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), + { ahaSyncroEditOther } (BG: clNone; FG: clNone; FC: $94b094; Styles: []; StylesMask: []), + { ahaSyncroEditArea } (BG: clMoneyGreen; FG: clNone; FC: clNone; Styles: []; StylesMask: []) ); Single: ( { shaGutter } clBtnFace, @@ -250,7 +260,11 @@ const { ahaWordGroup } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), { ahaTemplateEditCur } (BG: clNone; FG: clNone; FC: clAqua; Styles: []; StylesMask: []), { ahaTemplateEditSync } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), - { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []) + { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []), + { ahaSyncroEditCur } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), + { ahaSyncroEditSync } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), + { ahaSyncroEditOther } (BG: clNone; FG: clNone; FC: $94b094; Styles: []; StylesMask: []), + { ahaSyncroEditArea } (BG: clMoneyGreen; FG: clNone; FC: clNone; Styles: []; StylesMask: []) ); Single: ( { shaGutter } clBtnFace, @@ -292,7 +306,11 @@ const { ahaWordGroup } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), { ahaTemplateEditCur } (BG: clNone; FG: clNone; FC: clAqua; Styles: []; StylesMask: []), { ahaTemplateEditSync } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), - { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []) + { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []), + { ahaSyncroEditCur } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), + { ahaSyncroEditSync } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), + { ahaSyncroEditOther } (BG: clNone; FG: clNone; FC: $94b094; Styles: []; StylesMask: []), + { ahaSyncroEditArea } (BG: clMoneyGreen; FG: clNone; FC: clNone; Styles: []; StylesMask: []) ); Single: ( { shaGutter } clBtnFace, @@ -334,7 +352,11 @@ const { ahaWordGroup } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), { ahaTemplateEditCur } (BG: clNone; FG: clNone; FC: clAqua; Styles: []; StylesMask: []), { ahaTemplateEditSync } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), - { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []) + { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []), + { ahaSyncroEditCur } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), + { ahaSyncroEditSync } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), + { ahaSyncroEditOther } (BG: clNone; FG: clNone; FC: $94b094; Styles: []; StylesMask: []), + { ahaSyncroEditArea } (BG: clMoneyGreen; FG: clNone; FC: clNone; Styles: []; StylesMask: []) ); Single: ( { shaGutter } clBtnFace, @@ -376,7 +398,11 @@ const { ahaWordGroup } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), { ahaTemplateEditCur } (BG: clNone; FG: clNone; FC: clAqua; Styles: []; StylesMask: []), { ahaTemplateEditSync } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), - { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []) + { ahaTemplateEditOther } (BG: clNone; FG: clNone; FC: clMaroon; Styles: []; StylesMask: []), + { ahaSyncroEditCur } (BG: clNone; FG: clNone; FC: clFuchsia; Styles: []; StylesMask: []), + { ahaSyncroEditSync } (BG: clNone; FG: clNone; FC: clRed; Styles: []; StylesMask: []), + { ahaSyncroEditOther } (BG: clNone; FG: clNone; FC: $94b094; Styles: []; StylesMask: []), + { ahaSyncroEditArea } (BG: clMoneyGreen; FG: clNone; FC: clNone; Styles: []; StylesMask: []) ); Single: ( { shaGutter } clBtnFace, @@ -3204,7 +3230,20 @@ begin TSynPluginTemplateEdit(aSynEd.Plugin[i]).MarkupInfoCurrent); SetMarkupColor(aSynEd.Highlighter, ahaTemplateEditSync, TSynPluginTemplateEdit(aSynEd.Plugin[i]).MarkupInfoSync); - end + end; + i := aSynEd.PluginCount - 1; + while (i >= 0) and not(aSynEd.Plugin[i] is TSynPluginSyncroEdit) do + dec(i); + if i >= 0 then begin + SetMarkupColor(aSynEd.Highlighter, ahaSyncroEditOther, + TSynPluginSyncroEdit(aSynEd.Plugin[i]).MarkupInfo); + SetMarkupColor(aSynEd.Highlighter, ahaSyncroEditCur, + TSynPluginSyncroEdit(aSynEd.Plugin[i]).MarkupInfoCurrent); + SetMarkupColor(aSynEd.Highlighter, ahaSyncroEditSync, + TSynPluginSyncroEdit(aSynEd.Plugin[i]).MarkupInfoSync); + SetMarkupColor(aSynEd.Highlighter, ahaSyncroEditArea, + TSynPluginSyncroEdit(aSynEd.Plugin[i]).MarkupInfoArea); + end; end; procedure TEditorOptions.SetMarkupColor(Syn : TSrcIDEHighlighter; diff --git a/ide/keymapping.pp b/ide/keymapping.pp index 3e3d6ebce5..9704f4b4c5 100644 --- a/ide/keymapping.pp +++ b/ide/keymapping.pp @@ -35,7 +35,7 @@ uses LCLIntf, LCLType, LCLProc, Forms, Classes, SysUtils, Buttons, LResources, StdCtrls, Controls, Dialogs, StringHashList, ExtCtrls, - SynEditKeyCmds, SynPluginTemplateEdit, Laz_XMLCfg, + SynEditKeyCmds, SynPluginTemplateEdit, SynPluginSyncroEdit, Laz_XMLCfg, PropEdits, IDECommands, LazarusIDEStrConsts; type @@ -406,20 +406,7 @@ begin ecRemoveEmptyMethods: SetResult(VK_UNKNOWN,[],VK_UNKNOWN,[]); ecRemoveUnusedUnits: SetResult(VK_UNKNOWN,[],VK_UNKNOWN,[]); ecFindOverloads: SetResult(VK_UNKNOWN,[],VK_UNKNOWN,[]); -{ - // edit templates - ecIDESynTemplateEdit + ecSynPTmplEdNextCell = srkmecSynPTmplEdNextCell; - ecIDESynTemplateEdit + ecSynPTmplEdNextCellSel = srkmecSynPTmplEdNextCellSel; - ecIDESynTemplateEdit + ecSynPTmplEdNextCellRotate = srkmecSynPTmplEdNextCellRotate; - ecIDESynTemplateEdit + ecSynPTmplEdNextCellSelRotate = srkmecSynPTmplEdNextCellSelRotate; - ecIDESynTemplateEdit + ecSynPTmplEdPrevCell = srkmecSynPTmplEdPrevCell; - ecIDESynTemplateEdit + ecSynPTmplEdPrevCellSel = srkmecSynPTmplEdPrevCellSel; - ecIDESynTemplateEdit + ecSynPTmplEdCellHome = srkmecSynPTmplEdCellHome; - ecIDESynTemplateEdit + ecSynPTmplEdCellEnd = srkmecSynPTmplEdCellEnd; - ecIDESynTemplateEdit + ecSynPTmplEdCellSelect = srkmecSynPTmplEdCellSelect; - ecIDESynTemplateEdit + ecSynPTmplEdFinish = srkmecSynPTmplEdFinish; - ecIDESynTemplateEdit + ecSynPTmplEdEscape = srkmecSynPTmplEdEscape; -} + // source notebook ecNextEditor: SetResult(VK_TAB, [ssCtrl], VK_UNKNOWN, []); ecPrevEditor: SetResult(VK_TAB, [ssShift,ssCtrl], VK_UNKNOWN, []); @@ -599,6 +586,32 @@ begin ecSynPTmplEdFinish: SetResult(VK_RETURN,[],VK_UNKNOWN,[]); ecSynPTmplEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[]); end; + case TSynPluginSyncroEdit.ConvertCommandToBase(Command) of + // SyncroEdit + ecSynPSyncroEdNextCell: SetResult(VK_RIGHT,[ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdNextCellSel: SetResult(VK_TAB, [], VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCell: SetResult(VK_LEFT, [ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCellSel: SetResult(VK_TAB, [ssShift],VK_UNKNOWN,[]); + ecSynPSyncroEdCellHome: SetResult(VK_HOME, [],VK_UNKNOWN,[]); + ecSynPSyncroEdCellEnd: SetResult(VK_END, [],VK_UNKNOWN,[]); + ecSynPSyncroEdCellSelect: SetResult(VK_A, [ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[]); + end; + case TSynPluginSyncroEdit.ConvertCommandToBaseOff(Command) of + // SyncroEdit + ecSynPSyncroEdNextCell: SetResult(VK_RIGHT,[ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdNextCellSel: SetResult(VK_TAB, [], VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCell: SetResult(VK_LEFT, [ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCellSel: SetResult(VK_TAB, [ssShift],VK_UNKNOWN,[]); + ecSynPSyncroEdCellHome: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[]); + ecSynPSyncroEdCellEnd: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[]); + ecSynPSyncroEdCellSelect: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[]); + ecSynPSyncroEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[]); + end; + case TSynPluginSyncroEdit.ConvertCommandToBaseSel(Command) of + // SyncroEdit, during selection + ecSynPSyncroEdStart: SetResult(VK_J,[ssCtrl],VK_UNKNOWN,[]); + end; end; end; end; @@ -973,6 +986,32 @@ begin ecSynPTmplEdFinish: SetResult(VK_RETURN,[],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); ecSynPTmplEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); end; + case TSynPluginSyncroEdit.ConvertCommandToBase(Command) of + // SyncroEdit + ecSynPSyncroEdNextCell: SetResult(VK_RIGHT,[ssCtrl],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdNextCellSel: SetResult(VK_TAB, [], VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCell: SetResult(VK_LEFT, [ssCtrl],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCellSel: SetResult(VK_TAB, [ssShift],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdCellHome: SetResult(VK_HOME, [],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdCellEnd: SetResult(VK_END, [],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdCellSelect: SetResult(VK_A, [ssCtrl],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + end; + case TSynPluginSyncroEdit.ConvertCommandToBaseOff(Command) of + // SyncroEdit + ecSynPSyncroEdNextCell: SetResult(VK_RIGHT,[ssCtrl],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdNextCellSel: SetResult(VK_TAB, [], VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCell: SetResult(VK_LEFT, [ssCtrl],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCellSel: SetResult(VK_TAB, [ssShift],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdCellHome: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdCellEnd: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdCellSelect: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + ecSynPSyncroEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + end; + case TSynPluginSyncroEdit.ConvertCommandToBaseSel(Command) of + // SyncroEdit, during selection + ecSynPSyncroEdStart: SetResult(VK_J,[ssCtrl],VK_UNKNOWN,[],VK_UNKNOWN,[],VK_UNKNOWN,[]); + end; end; end; (*//F1 Topic Search @@ -1537,6 +1576,32 @@ begin ecSynPTmplEdFinish: SetResult(VK_RETURN,[],VK_UNKNOWN,[]); ecSynPTmplEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[]); end; + case TSynPluginSyncroEdit.ConvertCommandToBase(Command) of + // SyncroEdit + ecSynPSyncroEdNextCell: SetResult(VK_RIGHT,[ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdNextCellSel: SetResult(VK_TAB, [], VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCell: SetResult(VK_LEFT, [ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCellSel: SetResult(VK_TAB, [ssShift],VK_UNKNOWN,[]); + ecSynPSyncroEdCellHome: SetResult(VK_HOME, [],VK_UNKNOWN,[]); + ecSynPSyncroEdCellEnd: SetResult(VK_END, [],VK_UNKNOWN,[]); + ecSynPSyncroEdCellSelect: SetResult(VK_A, [ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[]); + end; + case TSynPluginSyncroEdit.ConvertCommandToBaseOff(Command) of + // SyncroEdit + ecSynPSyncroEdNextCell: SetResult(VK_RIGHT,[ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdNextCellSel: SetResult(VK_TAB, [], VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCell: SetResult(VK_LEFT, [ssCtrl],VK_UNKNOWN,[]); + ecSynPSyncroEdPrevCellSel: SetResult(VK_TAB, [ssShift],VK_UNKNOWN,[]); + ecSynPSyncroEdCellHome: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[]); + ecSynPSyncroEdCellEnd: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[]); + ecSynPSyncroEdCellSelect: SetResult(VK_UNKNOWN, [],VK_UNKNOWN,[]); + ecSynPSyncroEdEscape: SetResult(VK_ESCAPE,[],VK_UNKNOWN,[]); + end; + case TSynPluginSyncroEdit.ConvertCommandToBaseSel(Command) of + // SyncroEdit, during selection + ecSynPSyncroEdStart: SetResult(VK_J,[ssCtrl],VK_UNKNOWN,[]); + end; end; end; end; @@ -1977,6 +2042,34 @@ begin ecSynPTmplEdFinish: Result := srkmecSynPTmplEdFinish; ecSynPTmplEdEscape: Result := srkmecSynPTmplEdEscape; end; + + case TSynPluginSyncroEdit.ConvertCommandToBase(cmd) of + // SyncroEdit + ecSynPSyncroEdNextCell: Result := srkmecSynPSyncroEdNextCell; + ecSynPSyncroEdNextCellSel: Result := srkmecSynPSyncroEdNextCellSel; + ecSynPSyncroEdPrevCell: Result := srkmecSynPSyncroEdPrevCell; + ecSynPSyncroEdPrevCellSel: Result := srkmecSynPSyncroEdPrevCellSel; + ecSynPSyncroEdCellHome: Result := srkmecSynPSyncroEdCellHome; + ecSynPSyncroEdCellEnd: Result := srkmecSynPSyncroEdCellEnd; + ecSynPSyncroEdCellSelect: Result := srkmecSynPSyncroEdCellSelect; + ecSynPSyncroEdEscape: Result := srkmecSynPSyncroEdEscape; + end; + case TSynPluginSyncroEdit.ConvertCommandToBaseOff(cmd) of + // SyncroEdit + ecSynPSyncroEdNextCell: Result := srkmecSynPSyncroEdNextCell; + ecSynPSyncroEdNextCellSel: Result := srkmecSynPSyncroEdNextCellSel; + ecSynPSyncroEdPrevCell: Result := srkmecSynPSyncroEdPrevCell; + ecSynPSyncroEdPrevCellSel: Result := srkmecSynPSyncroEdPrevCellSel; + ecSynPSyncroEdCellHome: Result := srkmecSynPSyncroEdCellHome; + ecSynPSyncroEdCellEnd: Result := srkmecSynPSyncroEdCellEnd; + ecSynPSyncroEdCellSelect: Result := srkmecSynPSyncroEdCellSelect; + ecSynPSyncroEdEscape: Result := srkmecSynPSyncroEdEscape; + end; + case TSynPluginSyncroEdit.ConvertCommandToBaseSel(cmd) of + // SyncroEdit, during selection + ecSynPSyncroEdStart: Result := srkmecSynPSyncroEdStart; + end; + end; end; end; @@ -2395,6 +2488,36 @@ begin AddDefault(C, 'Edit Template (off) Finish', srkmecSynPTmplEdFinish, ecSynPTmplEdFinish + o); AddDefault(C, 'Edit Template (off) Escape', srkmecSynPTmplEdEscape, ecSynPTmplEdEscape + o); + + // Syncro editing + C:=Categories[AddCategory('Syncro Edit', srkmCatSyncroEdit, IDECmdScopeSrcEditOnlySyncroEdit)]; + o := TSynPluginSyncroEdit.ConvertBaseToCommand(ecPluginFirst) - ecPluginFirst; + AddDefault(C, 'Edit Syncro Next Cell', srkmecSynPSyncroEdNextCell, ecSynPSyncroEdNextCell + o); + AddDefault(C, 'Edit Syncro Next Cell (all selected)', srkmecSynPSyncroEdNextCellSel, ecSynPSyncroEdNextCellSel + o); + AddDefault(C, 'Edit Syncro Previous Cell', srkmecSynPSyncroEdPrevCell, ecSynPSyncroEdPrevCell + o); + AddDefault(C, 'Edit Syncro Previous Cell (all selected)', srkmecSynPSyncroEdPrevCellSel, ecSynPSyncroEdPrevCellSel + o); + AddDefault(C, 'Edit Syncro Goto first pos in cell', srkmecSynPSyncroEdCellHome, ecSynPSyncroEdCellHome + o); + AddDefault(C, 'Edit Syncro Goto last pos in cell', srkmecSynPSyncroEdCellEnd, ecSynPSyncroEdCellEnd + o); + AddDefault(C, 'Edit Syncro Select cell', srkmecSynPSyncroEdCellSelect, ecSynPSyncroEdCellSelect + o); + AddDefault(C, 'Edit Syncro Escape', srkmecSynPSyncroEdEscape, ecSynPSyncroEdEscape + o); + + // Syncro editing not in cell + C:=Categories[AddCategory('Syncro Edit Off', srkmCatSyncroEditOff, IDECmdScopeSrcEditOnlySyncroEditOff)]; + o := TSynPluginSyncroEdit.ConvertBaseToCommandOff(ecPluginFirst) - ecPluginFirst; + AddDefault(C, 'Edit Syncro (off) Next Cell', srkmecSynPSyncroEdNextCell, ecSynPSyncroEdNextCell + o); + AddDefault(C, 'Edit Syncro (off) Next Cell (all selected)', srkmecSynPSyncroEdNextCellSel, ecSynPSyncroEdNextCellSel + o); + AddDefault(C, 'Edit Syncro (off) Previous Cell', srkmecSynPSyncroEdPrevCell, ecSynPSyncroEdPrevCell + o); + AddDefault(C, 'Edit Syncro (off) Previous Cell (all selected)', srkmecSynPSyncroEdPrevCellSel, ecSynPSyncroEdPrevCellSel + o); + AddDefault(C, 'Edit Syncro (off) Goto first pos in cell', srkmecSynPSyncroEdCellHome, ecSynPSyncroEdCellHome + o); + AddDefault(C, 'Edit Syncro (off) Goto last pos in cell', srkmecSynPSyncroEdCellEnd, ecSynPSyncroEdCellEnd + o); + AddDefault(C, 'Edit Syncro (off) Select cell', srkmecSynPSyncroEdCellSelect, ecSynPSyncroEdCellSelect + o); + AddDefault(C, 'Edit Syncro (off) Escape', srkmecSynPSyncroEdEscape, ecSynPSyncroEdEscape + o); + + // Syncro editing still selecting + C:=Categories[AddCategory('Syncro Edit Sel', srkmCatSyncroEditSel, IDECmdScopeSrcEditOnlySyncroEditSel)]; + o := TSynPluginSyncroEdit.ConvertBaseToCommandSel(ecPluginFirst) - ecPluginFirst; + AddDefault(C, 'Edit Syncro (sel) Start', srkmecSynPSyncroEdStart, ecSynPSyncroEdStart + o); + // source notebook - without menu items in the IDE bar C:=Categories[AddCategory('SourceNotebook',srkmCatSrcNoteBook, IDECmdScopeSrcEdit)]; diff --git a/ide/lazarusidestrconsts.pas b/ide/lazarusidestrconsts.pas index 7571b9538b..c40da422a4 100644 --- a/ide/lazarusidestrconsts.pas +++ b/ide/lazarusidestrconsts.pas @@ -2199,6 +2199,17 @@ resourcestring srkmecSynPTmplEdFinish = 'Finish'; srkmecSynPTmplEdEscape = 'Escape'; + // Plugin Syncro Edit + srkmecSynPSyncroEdNextCell = 'Next Cell'; + srkmecSynPSyncroEdNextCellSel = 'Next Cell (all selected)'; + srkmecSynPSyncroEdPrevCell = 'Previous Cell'; + srkmecSynPSyncroEdPrevCellSel = 'Previous Cell (all selected)'; + srkmecSynPSyncroEdCellHome = 'Goto first pos in cell'; + srkmecSynPSyncroEdCellEnd = 'Goto last pos in cell'; + srkmecSynPSyncroEdCellSelect = 'Select Cell'; + srkmecSynPSyncroEdEscape = 'Escape'; + srkmecSynPSyncroEdStart = 'Start Syncro edit'; + // run menu srkmecBuild = 'build program/project'; srkmecBuildAll = 'build all files of program/project'; @@ -2259,6 +2270,10 @@ resourcestring srkmCatCodeTools = 'CodeTools commands'; srkmCatTemplateEdit = 'Template Editing'; srkmCatTemplateEditOff= 'Template Editing (not in Cell)'; + srkmCatSyncroEdit = 'Syncron Editing'; + srkmCatSyncroEditOff = 'Syncron Editing (not in Cell)'; + srkmCatSyncroEditSel = 'Syncron Editing (while selecting)'; + srkmCatSrcNoteBook = 'Source Notebook commands'; srkmCatFileMenu = 'File menu commands'; lisKMGoToSourceEditor10 = 'Go to source editor 10'; diff --git a/ide/main.pp b/ide/main.pp index ea2c577d20..e4b81e7b87 100644 --- a/ide/main.pp +++ b/ide/main.pp @@ -1911,6 +1911,10 @@ begin IDECmdScopeSrcEditOnlyTmplEdit.AddWindowClass(TLazSynPluginTemplateEditForm); IDECmdScopeSrcEditOnlyTmplEditOff.AddWindowClass(TLazSynPluginTemplateEditFormOff); + IDECmdScopeSrcEditOnlySyncroEditSel.AddWindowClass(TLazSynPluginSyncroEditFormSel); + IDECmdScopeSrcEditOnlySyncroEdit.AddWindowClass(TLazSynPluginSyncroEditForm); + IDECmdScopeSrcEditOnlySyncroEditOff.AddWindowClass(TLazSynPluginSyncroEditFormOff); + EditorOpts.KeyMap.CreateDefaultMapping; end; diff --git a/ide/sourceeditor.pp b/ide/sourceeditor.pp index 45bbf53494..51865696f5 100644 --- a/ide/sourceeditor.pp +++ b/ide/sourceeditor.pp @@ -51,7 +51,7 @@ uses SynEditStrConst, SynEditTypes, SynEdit, SynRegExpr, SynEditHighlighter, SynEditAutoComplete, SynEditKeyCmds, SynCompletion, SynEditMiscClasses, SynEditMarkupHighAll, SynGutterLineNumber, SynEditMarks, SynBeautifier, - SynEditTextBase, SynPluginTemplateEdit, + SynEditTextBase, SynPluginTemplateEdit, SynPluginSyncroEdit, // IDE interface MacroIntf, ProjectIntf, SrcEditorIntf, MenuIntf, LazIDEIntf, PackageIntf, IDEDialogs, IDEHelpIntf, IDEWindowIntf, IDEImagesIntf, @@ -2512,6 +2512,8 @@ Procedure TSourceEditor.CreateEditor(AOwner: TComponent; AParent: TWinControl); var NewName: string; i: integer; + sync: TSynPluginSyncroEdit; + bmp: TCustomBitmap; Begin {$IFDEF IDE_DEBUG} writeln('TSourceEditor.CreateEditor A '); @@ -2554,6 +2556,10 @@ Begin if aCompletion<>nil then aCompletion.AddEditor(FEditor); TSynPluginTemplateEdit.Create(FEditor); + sync := TSynPluginSyncroEdit.Create(FEditor); + bmp := CreateBitmapFromLazarusResource('tsynsyncroedit'); + sync.GutterGlyph.Assign(bmp); + bmp.Free; RefreshEditorSettings; FEditor.EndUpdate; end else begin diff --git a/ideintf/idecommands.pas b/ideintf/idecommands.pas index dfd71d9ebb..be44615190 100644 --- a/ideintf/idecommands.pas +++ b/ideintf/idecommands.pas @@ -495,6 +495,9 @@ var IDECmdScopeSrcEditOnly: TIDECommandScope; IDECmdScopeSrcEditOnlyTmplEdit: TIDECommandScope; IDECmdScopeSrcEditOnlyTmplEditOff: TIDECommandScope; + IDECmdScopeSrcEditOnlySyncroEditSel: TIDECommandScope; + IDECmdScopeSrcEditOnlySyncroEdit: TIDECommandScope; + IDECmdScopeSrcEditOnlySyncroEditOff: TIDECommandScope; IDECmdScopeDesignerOnly: TIDECommandScope; IDECmdScopeObjectInspectorOnly: TIDECommandScope; @@ -585,6 +588,9 @@ begin IDECmdScopeSrcEditOnly:=RegisterIDECommandScope('SourceEditorOnly'); IDECmdScopeSrcEditOnlyTmplEdit:=RegisterIDECommandScope('SourceEditorOnlyTemplateEdit'); IDECmdScopeSrcEditOnlyTmplEditOff:=RegisterIDECommandScope('SourceEditorOnlyTemplateEditOff'); + IDECmdScopeSrcEditOnlySyncroEditSel:=RegisterIDECommandScope('SourceEditorOnlySyncroEditSel'); + IDECmdScopeSrcEditOnlySyncroEdit:=RegisterIDECommandScope('SourceEditorOnlySyncroEdit'); + IDECmdScopeSrcEditOnlySyncroEditOff:=RegisterIDECommandScope('SourceEditorOnlySyncroEdit'); IDECmdScopeDesignerOnly:=RegisterIDECommandScope('DesignerOnly'); IDECmdScopeObjectInspectorOnly:=RegisterIDECommandScope('ObjectInspectorOnly'); end; diff --git a/images/bookmark.lrs b/images/bookmark.lrs index 5b9de51412..37b2063a90 100644 --- a/images/bookmark.lrs +++ b/images/bookmark.lrs @@ -343,3 +343,30 @@ LazarusResources.Add('UnknownBreakPoint','PNG',[ +#223#242'oy'#137#253#170'Cp0N'#247#196#4#253'O'#158'1'#155#140#155#22#20#15#0 +'A'#8#163#169'A'#166#192#8#0#0#0#0'IEND'#174'B`'#130 ]); +LazarusResources.Add('tsynsyncroedit','PNG',[ + #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#16#0#0#0#16#8#6#0#0#0#31#243#255'a' + +#0#0#0#4'gAMA'#0#0#175#200'7'#5#138#233#0#0#0#25'tEXtSoftware'#0'Adobe Image' + +'Readyq'#201'e<'#0#0#1#252'IDAT8'#203#141#147#223'KSa'#24#199'WEDB'#16#212 + +#133#208#141#225'}'#8'a? '#132#130'P3B'#168#11#139#238#178#22']t'#177#11'+$a' + +'A'#9#18#20#130#132#24'I'#20#8#141#168#197'X^'#164'ns'#139'\[sK'#183'p;;g' + +#231#156#253#236#211#251#30#182#177#201#166#30#248'p'#14#188#207#247's'#222 + +#231#188#231#177#1'6q'#29#18't'#9#186'w'#225#176'`'#191#204#212#176'U'#5']' + +#249'|^)'#149'J'#149'r'#185'L+'#196':N'#167'sb'#187#164'&'#232#150#225'\.' + +#135#174#235'u'#178#217','#153'L'#134'd2iI'#226#129'YB'#211#151#248#249#214 + +#193#210#212')'#150#159'v'#14#201#240'>)'#144#5#166'iR('#20#172#187'D'#211'4' + +#20'E!'#149'J'#161#197#223#176#225#26'EYy'#7'Z'#130#204#202','#158#199#189 + +#155'M'#2#25#174'!'#5#134'a'#160#170'*'#155'?fH'#175'>'#160#176'>'#207#198 + +#167'G'#24#190'9'#202'Q7'#190#23#215#204'&'#129'h'#163#222#179'|'#150'}+'#235 + +#11#164#253#247#169#24'n'#140#240#13#182#190#221'$'#248'j'#152#239#207#7#21 + +#247#248#249'cM'#130#237#168#137#143#164'}w'#169#152#30#244#181'+'#152#177#1 + +#212'U;'#177#151'}'#140#156'92X'#205#182#22'$'#252#162#231#197#209'jx'#8#243 + +'w?Y'#255'='#194#147'g'#209#19'~'#170#199#218'Z'#144#8#204#19'\'#184'-'#182 + +#253#25'32L>v'#25'E'#236'd'#237#217'i'#212#228'/'#171#166#173' '#248'e'#154 + +#192'{;'#203#31#158#16#247'^D'#143#12#176#181't'#135#240'T'#31#250#223#136 + +#245']v'#20#188#30#235#1'5Jh'#242#2'.'#251'A'#190#138#183'z'#198'{'#209#254 + +#132#172#240#174#2#199#245#227#20'$>5Y&axjp2O=VLu>*f>1L;s0)kkvKC!*u?s6CVL=HJ6oP~pNfZc; zsKr=bq;7MITw8NXw{SZm%59TId2x_9BQ zV86`NuvGI!>o^V!Na!=$7GJE{Cq`b+XwknM{UcGHFTTfmuS+ zm-zYC!P3+zmY;SG$?!fYkOih`QYaLxyF}A86h$GGN}kFj)_o*0e zjPMP%zTG7FYMAfO2Nn1D`D0Cj?Wl>5q%@CE10nX)KxpNmwk+!IWkzywiYD( zqUXiYYIq3qcRyMGJ;IY`(Gz~E$J$zu2+R{)xGlE*88b3WK6V*J>}2iPY1HH|tER0W z_+^^FdppY?o)Gt5M2`%xwRDH@R3G}^i1l4|6uchm0X0f!@&YdVLB5K&dd7Rv{)DXX zt^&vP;}kqj3f>94j+4xd93>s|Q!Ezi>?r8(Il$P}PFxSqu{d*!Y%*#cX(R0f|Juz# z3o0_xI14Al->1uky@W-rCI_%l&>