{ Mouse Command Configuration for SynEdit Copyright (C) 2009 Martn Friebe 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. A copy of the GNU General Public License is available on the World Wide Web at . You can also obtain it by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. } unit SynEditMouseCmds; {$I synedit.inc} interface uses LazSynEditMouseCmdsTypes, Classes, Controls, SysUtils, SynEditStrConst, SynEditPointClasses, Dialogs, LCLProc; type TSynEditorMouseOption = ( emUseMouseActions, emAltSetsColumnMode, // Alt modifier, triggers column mode selection emDragDropEditing, // Allows you to select a block of text and drag it within the document to another location emRightMouseMovesCursor, // When clicking with the right mouse for a popup menu, move the cursor to that location emDoubleClickSelectsLine, // Select line on double click emShowCtrlMouseLinks, // Pressing Ctrl (SYNEDIT_LINK_MODIFIER) will highlight the word under the mouse cursor emCtrlWheelZoom ); TSynEditorMouseOptions = set of TSynEditorMouseOption; TSynEditorMouseCommand = type word; TSynEditorMouseCommandOpt = type word; TSynMouseButton = LazSynEditMouseCmdsTypes.TSynMouseButton; TSynMAClickCount = (ccSingle, ccDouble, ccTriple, ccQuad, ccAny); TSynMAClickDir = (cdUp, cdDown); ESynMouseCmdError = class(Exception); const mbXLeft = LazSynEditMouseCmdsTypes.mbLeft; mbXRight = LazSynEditMouseCmdsTypes.mbRight; mbXMiddle = LazSynEditMouseCmdsTypes.mbMiddle; mbXExtra1 = LazSynEditMouseCmdsTypes.mbExtra1; mbXExtra2 = LazSynEditMouseCmdsTypes.mbExtra2; mbXWheelUp = LazSynEditMouseCmdsTypes.mbWheelUp; mbXWheelDown = LazSynEditMouseCmdsTypes.mbWheelDown; SynMouseButtonMap: Array [TMouseButton] of TSynMouseButton = (mbXLeft, mbXRight, mbXMiddle, mbXExtra1, mbXExtra2); SynMouseButtonBackMap: Array [TSynMouseButton] of TMouseButton = (Controls.mbLeft, Controls.mbRight, Controls.mbMiddle, Controls.mbExtra1, Controls.mbExtra2, Controls.mbLeft, Controls.mbLeft); type TSynEditMouseActionInfo = record NewCaret: TSynEditCaret; Button: TSynMouseButton; Shift: TShiftState; MouseX, MouseY, WheelDelta: Integer; CCount: TSynMAClickCount; Dir: TSynMAClickDir; CaretDone: Boolean; // Return Value IgnoreUpClick: Boolean; end; { TSynEditMouseAction } TSynEditMouseAction = class(TCollectionItem) private FClickDir: TSynMAClickDir; FIgnoreUpClick: Boolean; FOption: TSynEditorMouseCommandOpt; FOption2: Integer; FPriority: TSynEditorMouseCommandOpt; FShift, FShiftMask: TShiftState; FButton: TSynMouseButton; FClickCount: TSynMAClickCount; FCommand: TSynEditorMouseCommand; FMoveCaret: Boolean; procedure SetButton(const AValue: TSynMouseButton); procedure SetClickCount(const AValue: TSynMAClickCount); procedure SetClickDir(AValue: TSynMAClickDir); procedure SetCommand(const AValue: TSynEditorMouseCommand); procedure SetIgnoreUpClick(AValue: Boolean); procedure SetMoveCaret(const AValue: Boolean); procedure SetOption(const AValue: TSynEditorMouseCommandOpt); procedure SetOption2(AValue: Integer); procedure SetPriority(const AValue: TSynEditorMouseCommandOpt); procedure SetShift(const AValue: TShiftState); procedure SetShiftMask(const AValue: TShiftState); protected function GetDisplayName: string; override; public procedure Assign(Source: TPersistent); override; procedure Clear; function IsMatchingShiftState(AShift: TShiftState): Boolean; function IsMatchingClick(ABtn: TSynMouseButton; ACCount: TSynMAClickCount; ACDir: TSynMAClickDir): Boolean; function IsFallback: Boolean; function Conflicts(Other: TSynEditMouseAction): Boolean; function Equals(Other: TSynEditMouseAction; IgnoreCmd: Boolean = False): Boolean; reintroduce; published property Shift: TShiftState read FShift write SetShift default []; property ShiftMask: TShiftState read FShiftMask write SetShiftMask default []; property Button: TSynMouseButton read FButton write SetButton default mbXLeft; property ClickCount: TSynMAClickCount read FClickCount write SetClickCount default ccSingle; property ClickDir: TSynMAClickDir read FClickDir write SetClickDir default cdUp; property Command: TSynEditorMouseCommand read FCommand write SetCommand; property MoveCaret: Boolean read FMoveCaret write SetMoveCaret default False; property IgnoreUpClick: Boolean read FIgnoreUpClick write SetIgnoreUpClick default False; // only for mouse down property Option: TSynEditorMouseCommandOpt read FOption write SetOption default 0; property Option2: Integer read FOption2 write SetOption2 default 0; property Priority: TSynEditorMouseCommandOpt read FPriority write SetPriority default 0; end; { TSynEditMouseActions } TSynEditMouseActions = class(TCollection) private FOwner: TPersistent; FAssertLock: Integer; function GetItem(Index: Integer): TSynEditMouseAction; procedure SetItem(Index: Integer; const AValue: TSynEditMouseAction); protected function GetOwner: TPersistent; override; procedure Update(Item: TCollectionItem); override; public constructor Create(AOwner: TPersistent); function Add: TSynEditMouseAction; procedure Assign(Source: TPersistent); override; procedure AssertNoConflict(MAction: TSynEditMouseAction); function Equals(Other: TSynEditMouseActions): Boolean; reintroduce; function FindCommand(AnInfo: TSynEditMouseActionInfo; APrevious: TSynEditMouseAction = nil): TSynEditMouseAction; procedure ResetDefaults; virtual; procedure IncAssertLock; procedure DecAssertLock; function IndexOf(MAction: TSynEditMouseAction; IgnoreCmd: Boolean = False): Integer; procedure AddCommand(const ACmd: TSynEditorMouseCommand; const AMoveCaret: Boolean; const AButton: TSynMouseButton; const AClickCount: TSynMAClickCount; const ADir: TSynMAClickDir; const AShift, AShiftMask: TShiftState; const AOpt: TSynEditorMouseCommandOpt = 0; const APrior: Integer = 0; const AOpt2: integer = 0; const AIgnoreUpClick: Boolean = False); public property Items[Index: Integer]: TSynEditMouseAction read GetItem write SetItem; default; end; { TSynEditMouseInternalActions } TSynEditMouseInternalActions = class(TSynEditMouseActions) private FOptions, FInternOptions: TSynEditorMouseOptions; FUserActions: TSynEditMouseActions; procedure SetOptions(AValue: TSynEditorMouseOptions); procedure SetUserActions(AValue: TSynEditMouseActions); protected procedure InitForOptions(AnOptions: TSynEditorMouseOptions); virtual; public constructor Create(AOwner: TPersistent); destructor Destroy; override; procedure ResetDefaults; override; procedure ResetUserActions; function GetActionsForOptions(AnOptions: TSynEditorMouseOptions): TSynEditMouseActions; property Options: TSynEditorMouseOptions read FOptions write SetOptions; property UserActions: TSynEditMouseActions read FUserActions write SetUserActions; end; 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; const // EditorMouseCommands emcNone = TSynEditorMouseCommand(0); emcStartSelections = TSynEditorMouseCommand(1); // Start BlockSelection (Default Left Mouse Btn) emcStartColumnSelections = TSynEditorMouseCommand(3); // Column BlockSelection (Default Alt - Left Mouse Btn) emcStartLineSelections = TSynEditorMouseCommand(4); // Line BlockSelection (Default Alt - Left Mouse Btn) emcSelectWord = TSynEditorMouseCommand(6); emcSelectLine = TSynEditorMouseCommand(7); emcSelectPara = TSynEditorMouseCommand(8); emcStartDragMove = TSynEditorMouseCommand(9); emcPasteSelection = TSynEditorMouseCommand(10); emcMouseLink = TSynEditorMouseCommand(11); emcContextMenu = TSynEditorMouseCommand(12); emcOnMainGutterClick = TSynEditorMouseCommand(13); // OnGutterClick emcCodeFoldCollaps = TSynEditorMouseCommand(14); emcCodeFoldExpand = TSynEditorMouseCommand(15); emcCodeFoldContextMenu = TSynEditorMouseCommand(16); emcSynEditCommand = TSynEditorMouseCommand(17); // Key-Commands emcWheelScrollDown = TSynEditorMouseCommand(18); emcWheelScrollUp = TSynEditorMouseCommand(19); emcWheelVertScrollDown = TSynEditorMouseCommand(20); emcWheelVertScrollUp = TSynEditorMouseCommand(21); emcWheelHorizScrollDown = TSynEditorMouseCommand(22); emcWheelHorizScrollUp = TSynEditorMouseCommand(23); emcWheelZoomOut = TSynEditorMouseCommand(24); emcWheelZoomIn = TSynEditorMouseCommand(25); emcWheelZoomNorm = TSynEditorMouseCommand(26); emcMax = 26; emcPluginFirst = 20000; // Options emcoSelectionStart = TSynEditorMouseCommandOpt(0); emcoSelectionContinue = TSynEditorMouseCommandOpt(1); emcoSelectLineSmart = TSynEditorMouseCommandOpt(0); emcoSelectLineFull = TSynEditorMouseCommandOpt(1); emcoMouseLinkShow = TSynEditorMouseCommandOpt(0); emcoMouseLinkHide = TSynEditorMouseCommandOpt(1); emcoCodeFoldCollapsOne = TSynEditorMouseCommandOpt(0); emcoCodeFoldCollapsAll = TSynEditorMouseCommandOpt(1); emcoCodeFoldCollapsAtCaret = TSynEditorMouseCommandOpt(2); emcoCodeFoldCollapsPreCaret = TSynEditorMouseCommandOpt(3); emcoCodeFoldExpandOne = TSynEditorMouseCommandOpt(0); emcoCodeFoldExpandAll = TSynEditorMouseCommandOpt(1); // menu, and caret move emcoSelectionCaretMoveNever = TSynEditorMouseCommandOpt(0); emcoSelectionCaretMoveOutside = TSynEditorMouseCommandOpt(1); // click is outside selected area emcoSelectionCaretMoveAlways = TSynEditorMouseCommandOpt(2); emcoWheelScrollSystem = TSynEditorMouseCommandOpt(0); // Opt2 > 0 ==> percentage emcoWheelScrollLines = TSynEditorMouseCommandOpt(1); // Opt2 > 0 ==> amount of lines emcoWheelScrollPages = TSynEditorMouseCommandOpt(2); // Opt2 > 0 ==> percentage emcoWheelScrollPagesLessOne = TSynEditorMouseCommandOpt(3); // Opt2 > 0 ==> percentage // 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; OffsetOnly: Boolean = False): integer; function MouseCommandName(emc: TSynEditorMouseCommand): String; function MouseCommandConfigName(emc: TSynEditorMouseCommand): String; function SynMouseCmdToIdent(SynMouseCmd: Longint; out Ident: String): Boolean; function IdentToSynMouseCmd(const Ident: string; out SynMouseCmd: Longint): Boolean; const SYNEDIT_LINK_MODIFIER = {$IFDEF LCLcarbon}ssMeta{$ELSE}ssCtrl{$ENDIF}; implementation const SynMouseCommandNames: array [0..24] of TIdentMapEntry = ( (Value: emcNone; Name: 'emcNone'), (Value: emcStartSelections; Name: 'emcStartSelections'), (Value: emcStartColumnSelections; Name: 'emcStartColumnSelections'), (Value: emcStartLineSelections; Name: 'emcStartLineSelections'), (Value: emcSelectWord; Name: 'emcSelectWord'), (Value: emcSelectLine; Name: 'emcSelectLine'), (Value: emcSelectPara; Name: 'emcSelectPara'), (Value: emcStartDragMove; Name: 'emcStartDragMove'), (Value: emcPasteSelection; Name: 'emcPasteSelection'), (Value: emcMouseLink; Name: 'emcMouseLink'), (Value: emcContextMenu; Name: 'emcContextMenu'), (Value: emcOnMainGutterClick; Name: 'emcOnMainGutterClick'), (Value: emcCodeFoldCollaps; Name: 'emcCodeFoldCollaps'), (Value: emcCodeFoldExpand; Name: 'emcCodeFoldExpand'), (Value: emcCodeFoldContextMenu; Name: 'emcCodeFoldContextMenu'), (Value: emcSynEditCommand; Name: 'emcSynEditCommand'), (Value: emcWheelScrollDown; Name: 'emcWheelScrollDown'), (Value: emcWheelScrollUp; Name: 'emcWheelScrollUp'), (Value: emcWheelVertScrollDown; Name: 'emcWheelVertScrollDown'), (Value: emcWheelVertScrollUp; Name: 'emcWheelVertScrollUp'), (Value: emcWheelHorizScrollDown; Name: 'emcWheelHorizScrollDown'), (Value: emcWheelHorizScrollUp; Name: 'emcWheelHorizScrollUp'), (Value: emcWheelZoomOut; Name: 'emcWheelZoomOut'), (Value: emcWheelZoomIn; Name: 'emcWheelZoomIn'), (Value: emcWheelZoomNorm; Name: 'emcWheelZoomNorm') ); function AllocatePluginMouseRange(Count: Integer; OffsetOnly: Boolean = False): integer; const CurOffset : integer = 0; begin Result := CurOffset; inc(CurOffset, Count); if not OffsetOnly then inc(Result, emcPluginFirst); end; function MouseCommandName(emc: TSynEditorMouseCommand): String; begin case emc of emcNone: Result := SYNS_emcNone; emcStartSelections: Result := SYNS_emcStartSelection; emcStartColumnSelections: Result := SYNS_emcStartColumnSelections; emcStartLineSelections: Result := SYNS_emcStartLineSelections; emcSelectWord: Result := SYNS_emcSelectWord; emcSelectLine: Result := SYNS_emcSelectLine; emcSelectPara: Result := SYNS_emcSelectPara; emcStartDragMove: Result := SYNS_emcStartDragMove; emcPasteSelection: Result := SYNS_emcPasteSelection; emcMouseLink: Result := SYNS_emcMouseLink; emcContextMenu: Result := SYNS_emcContextMenu; emcOnMainGutterClick: Result := SYNS_emcBreakPointToggle; emcCodeFoldCollaps: Result := SYNS_emcCodeFoldCollaps; emcCodeFoldExpand: Result := SYNS_emcCodeFoldExpand; emcCodeFoldContextMenu: Result := SYNS_emcCodeFoldContextMenu; emcSynEditCommand: Result := SYNS_emcSynEditCommand; emcWheelScrollDown: Result := SYNS_emcWheelScrollDown; emcWheelScrollUp: Result := SYNS_emcWheelScrollUp; emcWheelHorizScrollDown: Result := SYNS_emcWheelHorizScrollDown; emcWheelHorizScrollUp: Result := SYNS_emcWheelHorizScrollUp; emcWheelVertScrollDown: Result := SYNS_emcWheelVertScrollDown; emcWheelVertScrollUp: Result := SYNS_emcWheelVertScrollUp; emcWheelZoomOut: Result := SYNS_emcWheelZoomOut; emcWheelZoomIn: Result := SYNS_emcWheelZoomIn; emcWheelZoomNorm: Result := SYNS_emcWheelZoomNorm; else Result := '' end; end; function MouseCommandConfigName(emc: TSynEditorMouseCommand): String; begin case emc of emcStartSelections, emcStartColumnSelections, emcStartLineSelections: Result := SYNS_emcSelection_opt; emcSelectLine: Result := SYNS_emcSelectLine_opt; emcMouseLink: Result := SYNS_emcMouseLink_opt; emcCodeFoldCollaps: Result := SYNS_emcCodeFoldCollaps_opt; emcCodeFoldExpand: Result := SYNS_emcCodeFoldExpand_opt; emcContextMenu: Result := SYNS_emcContextMenuCaretMove_opt; emcWheelScrollDown..emcWheelVertScrollUp: Result := SYNS_emcWheelScroll_opt; else Result := '' end; end; function SynMouseCmdToIdent(SynMouseCmd: Longint; out Ident: String): Boolean; begin Ident := ''; Result := IntToIdent(SynMouseCmd, Ident, SynMouseCommandNames); end; function IdentToSynMouseCmd(const Ident: string; out SynMouseCmd: Longint): Boolean; begin SynMouseCmd := 0; Result := IdentToInt(Ident, SynMouseCmd, SynMouseCommandNames); end; { TSynEditMouseInternalActions } procedure TSynEditMouseInternalActions.SetOptions(AValue: TSynEditorMouseOptions); begin FOptions := AValue; if emUseMouseActions in FOptions then exit; AValue := AValue - [emUseMouseActions]; if (FInternOptions = AValue) and (Count > 0) then exit; FInternOptions := AValue; InitForOptions(FInternOptions); end; procedure TSynEditMouseInternalActions.SetUserActions(AValue: TSynEditMouseActions); begin if AValue =nil then FUserActions.Clear else FUserActions.Assign(AValue); end; procedure TSynEditMouseInternalActions.InitForOptions(AnOptions: TSynEditorMouseOptions); begin Clear; end; constructor TSynEditMouseInternalActions.Create(AOwner: TPersistent); begin FOptions := []; FUserActions := TSynEditMouseActions.Create(AOwner); inherited Create(AOwner); end; destructor TSynEditMouseInternalActions.Destroy; begin FreeAndNil(FUserActions); inherited Destroy; end; procedure TSynEditMouseInternalActions.ResetDefaults; begin InitForOptions(FOptions); end; procedure TSynEditMouseInternalActions.ResetUserActions; begin if emUseMouseActions in FOptions then begin if (FInternOptions <> FOptions - [emUseMouseActions]) or (Count = 0) then begin FInternOptions := FOptions - [emUseMouseActions]; InitForOptions(FInternOptions); end; FUserActions.Assign(Self); end else begin FUserActions.Clear; end; end; function TSynEditMouseInternalActions.GetActionsForOptions(AnOptions: TSynEditorMouseOptions): TSynEditMouseActions; begin Options := AnOptions; if emUseMouseActions in FOptions then Result := FUserActions else Result := Self; end; { TSynEditMouseAction } procedure TSynEditMouseAction.SetButton(const AValue: TSynMouseButton); begin if FButton = AValue then exit; FButton := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); if FButton in [mbXWheelUp, mbXWheelDown] then ClickDir := cdDown; end; procedure TSynEditMouseAction.SetClickCount(const AValue: TSynMAClickCount); begin if FClickCount = AValue then exit; FClickCount := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetClickDir(AValue: TSynMAClickDir); begin if FButton in [mbXWheelUp, mbXWheelDown] then AValue := cdDown; if FClickDir = AValue then exit; FClickDir := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetCommand(const AValue: TSynEditorMouseCommand); begin if FCommand = AValue then exit; FCommand := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetIgnoreUpClick(AValue: Boolean); begin if FIgnoreUpClick = AValue then Exit; FIgnoreUpClick := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetMoveCaret(const AValue: Boolean); begin if FMoveCaret = AValue then exit; FMoveCaret := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetOption(const AValue: TSynEditorMouseCommandOpt); begin if FOption = AValue then exit; FOption := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetOption2(AValue: Integer); begin if FOption2 = AValue then Exit; FOption2 := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetPriority(const AValue: TSynEditorMouseCommandOpt); begin if FPriority = AValue then exit; FPriority := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetShift(const AValue: TShiftState); begin if FShift = AValue then exit; FShift := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.SetShiftMask(const AValue: TShiftState); begin if FShiftMask = AValue then exit; FShiftMask := AValue; if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; function TSynEditMouseAction.GetDisplayName: string; begin Result := MouseCommandName(FCommand); end; procedure TSynEditMouseAction.Assign(Source: TPersistent); begin if Source is TSynEditMouseAction then begin FCommand := TSynEditMouseAction(Source).Command; FClickCount := TSynEditMouseAction(Source).ClickCount; FClickDir := TSynEditMouseAction(Source).ClickDir; FButton := TSynEditMouseAction(Source).Button; FShift := TSynEditMouseAction(Source).Shift; FShiftMask := TSynEditMouseAction(Source).ShiftMask; FMoveCaret := TSynEditMouseAction(Source).MoveCaret; FIgnoreUpClick := TSynEditMouseAction(Source).IgnoreUpClick; FOption := TSynEditMouseAction(Source).FOption; FOption2 := TSynEditMouseAction(Source).FOption2; FPriority := TSynEditMouseAction(Source).Priority; end else inherited Assign(Source); if Collection <> nil then TSynEditMouseActions(Collection).AssertNoConflict(self); end; procedure TSynEditMouseAction.Clear; begin FCommand := 0; FClickCount := ccSingle; FClickDir := cdUp; FButton := mbXLeft; FShift := []; FShiftMask := []; FMoveCaret := False; FOption := 0; FPriority := 0; FIgnoreUpClick := False; end; function TSynEditMouseAction.IsMatchingShiftState(AShift: TShiftState): Boolean; begin Result := AShift * FShiftMask = FShift; end; function TSynEditMouseAction.IsMatchingClick(ABtn: TSynMouseButton; ACCount: TSynMAClickCount; ACDir: TSynMAClickDir): Boolean; begin Result := (Button = ABtn) and ((ClickCount = ACCount) or (ClickCount = ccAny)) and (ClickDir = ACDir) end; function TSynEditMouseAction.IsFallback: Boolean; begin Result := FShiftMask = []; end; function TSynEditMouseAction.Conflicts(Other: TSynEditMouseAction): Boolean; begin If (Other = nil) or (Other = self) then exit(False); Result := (Other.Button = self.Button) and ((Other.ClickCount = self.ClickCount) or (self.ClickCount = ccAny) or (Other.ClickCount = ccAny)) and (Other.ClickDir = self.ClickDir) and (Other.Shift * self.ShiftMask = self.Shift * Other.ShiftMask) and ((Other.Command <> self.Command) or // Only conflicts, if Command differs (Other.MoveCaret <> self.MoveCaret) or (Other.IgnoreUpClick <> self.IgnoreUpClick) or (Other.Option <> self.Option) or (Other.Option2 <> self.Option2) ) and not(Other.IsFallback xor self.IsFallback) and (Other.Priority = self.Priority); end; function TSynEditMouseAction.Equals(Other: TSynEditMouseAction; IgnoreCmd: Boolean = False): Boolean; begin Result := (Other.Button = self.Button) and (Other.ClickCount = self.ClickCount) and (Other.ClickDir = self.ClickDir) and (Other.Shift = self.Shift) and (Other.ShiftMask = self.ShiftMask) and (Other.Priority = self.Priority) and ((Other.Command = self.Command) or IgnoreCmd) and ((Other.Option = self.Option) or IgnoreCmd) and ((Other.Option2 = self.Option2) or IgnoreCmd) and ((Other.MoveCaret = self.MoveCaret) or IgnoreCmd) and ((Other.IgnoreUpClick = self.IgnoreUpClick) or IgnoreCmd); end; { TSynEditMouseActions } function TSynEditMouseActions.GetItem(Index: Integer): TSynEditMouseAction; begin Result := TSynEditMouseAction(inherited GetItem(Index)); end; procedure TSynEditMouseActions.SetItem(Index: Integer; const AValue: TSynEditMouseAction); begin inherited SetItem(Index, AValue); end; function TSynEditMouseActions.GetOwner: TPersistent; begin Result := FOwner; end; procedure TSynEditMouseActions.Update(Item: TCollectionItem); var i: Integer; Err : ESynMouseCmdError; begin inherited Update(Item); i := Count - 1; Err := nil; while i > 0 do begin try AssertNoConflict(Items[i]); except on E : ESynMouseCmdError do begin Delete(i); if assigned(Owner) and (csDesigning in TComponent(Owner).ComponentState) then MessageDlg(SYNS_EDuplicateShortCut, E.Message + LineEnding + Items[i].DisplayName, mtWarning, [mbOK], '') else Err := E; //if not(assigned(Owner) and (csLoading in TComponent(Owner).ComponentState)) //then // raise E; end; end; dec(i); end; if assigned(Err) then raise Err; end; constructor TSynEditMouseActions.Create(AOwner: TPersistent); begin inherited Create(TSynEditMouseAction); FOwner := AOwner; FAssertLock := 0; end; function TSynEditMouseActions.Add: TSynEditMouseAction; begin Result := TSynEditMouseAction(inherited Add); end; procedure TSynEditMouseActions.Assign(Source: TPersistent); var i: Integer; begin if Source is TSynEditMouseActions then begin Clear; BeginUpdate; for i := 0 to TSynEditMouseActions(Source).Count-1 do Add.Assign(TSynEditMouseActions(Source)[i]); EndUpdate; end else inherited Assign(Source); end; procedure TSynEditMouseActions.AssertNoConflict(MAction: TSynEditMouseAction); var i: Integer; begin if (FAssertLock > 0) or (UpdateCount > 0) then exit; for i := 0 to Count-1 do begin if Items[i].Conflicts(MAction) then raise ESynMouseCmdError.Create(SYNS_EDuplicateShortCut); end; end; function TSynEditMouseActions.Equals(Other: TSynEditMouseActions): Boolean; var i: Integer; begin Result := False; if Count <> Other.Count then exit; for i := 0 to Count - 1 do if Other.IndexOf(Items[i]) < 0 then exit; Result := True; end; function TSynEditMouseActions.FindCommand(AnInfo: TSynEditMouseActionInfo; APrevious: TSynEditMouseAction = nil): TSynEditMouseAction; var i, MinPriority: Integer; act, found, fback: TSynEditMouseAction; begin MinPriority := 0; if assigned(APrevious) then MinPriority := APrevious.Priority + 1; fback := nil; found := nil; for i := 0 to Count-1 do begin act := Items[i]; if act.Priority < MinPriority then continue; if act.IsMatchingClick(AnInfo.Button, AnInfo.CCount, AnInfo.Dir) and act.IsMatchingShiftState(AnInfo.Shift) then begin if act.IsFallback then begin if (fback = nil) or (act.Priority < fback.Priority) then fback := act; end else begin if (found = nil) or (act.Priority < found.Priority) then found := act; end; end; end; if found <> nil then begin if (fback <> nil) and (fback.Priority < found.Priority) then Result := fback else Result := found; end else if fback <> nil then Result := fback else Result := nil; end; procedure TSynEditMouseActions.AddCommand(const ACmd: TSynEditorMouseCommand; const AMoveCaret: Boolean; const AButton: TSynMouseButton; const AClickCount: TSynMAClickCount; const ADir: TSynMAClickDir; const AShift, AShiftMask: TShiftState; const AOpt: TSynEditorMouseCommandOpt = 0; const APrior: Integer = 0; const AOpt2: integer = 0; const AIgnoreUpClick: Boolean = False); var new: TSynEditMouseAction; begin inc(FAssertLock); try new := Add; with new do begin Command := ACmd; MoveCaret := AMoveCaret; Button := AButton; ClickCount := AClickCount; ClickDir := ADir; Shift := AShift; ShiftMask := AShiftMask; Option := AOpt; Priority := APrior; Option2 := AOpt2; IgnoreUpClick := AIgnoreUpClick; end; finally dec(FAssertLock); end; try AssertNoConflict(new); except Delete(Count-1); raise; end; end; procedure TSynEditMouseActions.ResetDefaults; begin Clear; end; procedure TSynEditMouseActions.IncAssertLock; begin inc(FAssertLock); end; procedure TSynEditMouseActions.DecAssertLock; begin dec(FAssertLock); end; function TSynEditMouseActions.IndexOf(MAction: TSynEditMouseAction; IgnoreCmd: Boolean = False): Integer; begin Result := Count - 1; while Result >= 0 do begin if Items[Result].Equals(MAction, IgnoreCmd) then exit; Dec(Result); end; 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; initialization RegisterIntegerConsts(TypeInfo(TSynEditorMouseCommand), TIdentToInt(@IdentToSynMouseCmd), TIntToIdent(@SynMouseCmdToIdent)); end.