#region Using Directives using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; #endregion Using Directives namespace ScintillaNET { /// /// Manages commands, which are actions in ScintillaNET that can be bound to key combinations. /// [TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))] public class Commands : TopLevelHelper { #region Fields private Dictionary> _boundCommands = new Dictionary>(); private CommandComparer _commandComparer = new CommandComparer(); // Hmmm.. This is getting more and more hackyish internal bool StopProcessingCommands = false; private bool _allowDuplicateBindings = true; #endregion Fields #region Methods /// /// Adds a key combination to a Command /// /// Character corresponding to a (keyboard) key to trigger command /// Command to execute public void AddBinding(char shortcut, BindableCommand command) { AddBinding(Utilities.GetKeys(shortcut), Keys.None, command); } /// /// Adds a key combination to a Command /// /// Character corresponding to a (keyboard) key to trigger command /// Shift, alt, ctrl /// Command to execute public void AddBinding(char shortcut, Keys modifiers, BindableCommand command) { AddBinding(Utilities.GetKeys(shortcut), modifiers, command); } /// /// Adds a key combination to a Command /// /// Key to trigger command /// Command to execute public void AddBinding(Keys shortcut, BindableCommand command) { AddBinding(shortcut, Keys.None, command); } /// /// Adds a key combination to a Command /// /// Key to trigger command /// Shift, alt, ctrl /// Command to execute public void AddBinding(Keys shortcut, Keys modifiers, BindableCommand command) { KeyBinding kb = new KeyBinding(shortcut, modifiers); if (!_boundCommands.ContainsKey(kb)) _boundCommands.Add(kb, new List()); List l = _boundCommands[kb]; if (_allowDuplicateBindings || !l.Contains(command)) l.Add(command); } /// /// Executes a Command /// /// Any /// Value to indicate whether other bound commands should continue to execute public bool Execute(BindableCommand command) { if ((int)command < 10000) { NativeScintilla.SendMessageDirect((uint)command, IntPtr.Zero, IntPtr.Zero); return true; } switch (command) { case BindableCommand.AutoCShow: Scintilla.AutoComplete.Show(); return true; case BindableCommand.AcceptActiveSnippets: return Scintilla.Snippets.AcceptActiveSnippets(); case BindableCommand.CancelActiveSnippets: return Scintilla.Snippets.CancelActiveSnippets(); case BindableCommand.DoSnippetCheck: return Scintilla.Snippets.DoSnippetCheck(); case BindableCommand.NextSnippetRange: return Scintilla.Snippets.NextSnippetRange(); case BindableCommand.PreviousSnippetRange: return Scintilla.Snippets.PreviousSnippetRange(); case BindableCommand.DropMarkerCollect: Scintilla.DropMarkers.Collect(); return false; case BindableCommand.DropMarkerDrop: Scintilla.DropMarkers.Drop(); return true; case BindableCommand.Print: Scintilla.Printing.Print(); return true; case BindableCommand.PrintPreview: Scintilla.Printing.PrintPreview(); return true; case BindableCommand.ShowFind: Scintilla.FindReplace.ShowFind(); return true; case BindableCommand.ShowReplace: Scintilla.FindReplace.ShowReplace(); return true; case BindableCommand.FindNext: Scintilla.FindReplace.Window.FindNext(); return true; case BindableCommand.FindPrevious: Scintilla.FindReplace.Window.FindPrevious(); return true; case BindableCommand.IncrementalSearch: Scintilla.FindReplace.IncrementalSearch(); return true; case BindableCommand.LineComment: Scintilla.Lexing.LineComment(); return true; case BindableCommand.LineUncomment: Scintilla.Lexing.LineUncomment(); return true; case BindableCommand.DocumentNavigateForward: Scintilla.DocumentNavigation.NavigateForward(); return true; case BindableCommand.DocumentNavigateBackward: Scintilla.DocumentNavigation.NavigateBackward(); return true; case BindableCommand.ToggleLineComment: Scintilla.Lexing.ToggleLineComment(); return true; case BindableCommand.StreamComment: Scintilla.Lexing.StreamComment(); return true; case BindableCommand.ShowSnippetList: Scintilla.Snippets.ShowSnippetList(); return true; case BindableCommand.ShowSurroundWithList: Scintilla.Snippets.ShowSurroundWithList(); return true; case BindableCommand.ShowGoTo: Scintilla.GoTo.ShowGoToDialog(); break; } return false; } /// /// Returns a list of Commands bound to a keyboard shortcut /// /// Character corresponding to a (keyboard) key to trigger command /// List of Commands bound to a keyboard shortcut private List GetCommands(char shortcut) { return GetCommands(Utilities.GetKeys(shortcut), Keys.None); } /// /// Returns a list of Commands bound to a keyboard shortcut /// /// Character corresponding to a (keyboard) key to trigger command /// Shift, alt, ctrl /// List of Commands bound to a keyboard shortcut private List GetCommands(char shortcut, Keys modifiers) { return GetCommands(Utilities.GetKeys(shortcut), modifiers); } /// /// Returns a list of Commands bound to a keyboard shortcut /// /// Key to trigger command /// List of Commands bound to a keyboard shortcut private List GetCommands(Keys shortcut) { return GetCommands(shortcut, Keys.None); } /// /// Returns a list of Commands bound to a keyboard shortcut /// /// Key to trigger command /// Shift, alt, ctrl /// List of Commands bound to a keyboard shortcut private List GetCommands(Keys shortcut, Keys modifiers) { KeyBinding kb = new KeyBinding(shortcut, modifiers); if (!_boundCommands.ContainsKey(kb)) return new List(); return _boundCommands[kb]; } /// /// Returns a list of KeyBindings bound to a given command /// /// Command to execute /// List of KeyBindings bound to the given command public List GetKeyBindings(BindableCommand command) { List ret = new List(); foreach (KeyValuePair> item in _boundCommands) { if (item.Value.Contains(command)) ret.Add(item.Key); } return ret; } internal bool ProcessKey(KeyEventArgs e) { StopProcessingCommands = false; KeyBinding kb = new KeyBinding(e.KeyCode, e.Modifiers); if (!_boundCommands.ContainsKey(kb)) return false; List cmds = _boundCommands[kb]; if (cmds.Count == 0) return false; cmds.Sort(_commandComparer); bool ret = false; foreach (BindableCommand cmd in cmds) { ret |= Execute(cmd); if (StopProcessingCommands) return ret; } return ret; } /// /// Removes all key command bindings /// /// /// Performing this action will make ScintillaNET virtually unusable until you assign new command bindings. /// This removes even basic functionality like arrow keys, common clipboard commands, home/_end, etc. /// public void RemoveAllBindings() { _boundCommands.Clear(); } /// /// Removes all commands bound to a keyboard shortcut /// /// Character corresponding to a (keyboard) key to trigger command public void RemoveBinding(char shortcut) { RemoveBinding(Utilities.GetKeys(shortcut), Keys.None); } /// /// Removes a keyboard shortcut / command combination /// /// Character corresponding to a (keyboard) key to trigger command /// Command to execute public void RemoveBinding(char shortcut, BindableCommand command) { RemoveBinding(Utilities.GetKeys(shortcut), Keys.None, command); } /// /// Removes all commands bound to a keyboard shortcut /// /// Character corresponding to a (keyboard) key to trigger command /// Shift, alt, ctrl public void RemoveBinding(char shortcut, Keys modifiers) { RemoveBinding(Utilities.GetKeys(shortcut), modifiers); } /// /// Removes a keyboard shortcut / command combination /// /// Character corresponding to a (keyboard) key to trigger command /// Shift, alt, ctrl /// Command to execute public void RemoveBinding(char shortcut, Keys modifiers, BindableCommand command) { RemoveBinding(Utilities.GetKeys(shortcut), modifiers, command); } /// /// Removes all commands bound to a keyboard shortcut /// /// Key to trigger command public void RemoveBinding(Keys shortcut) { RemoveBinding(shortcut, Keys.None); } /// /// Removes a keyboard shortcut / command combination /// /// Key to trigger command /// Command to execute public void RemoveBinding(Keys shortcut, BindableCommand command) { RemoveBinding(shortcut, Keys.None, command); } /// /// Removes all commands bound to a keyboard shortcut /// /// Key to trigger command /// Shift, alt, ctrl public void RemoveBinding(Keys shortcut, Keys modifiers) { _boundCommands.Remove(new KeyBinding(shortcut, modifiers)); } /// /// Removes a keyboard shortcut / command combination /// /// Key to trigger command /// Shift, alt, ctrl /// Command to execute public void RemoveBinding(Keys shortcut, Keys modifiers, BindableCommand command) { KeyBinding kb = new KeyBinding(shortcut, modifiers); if (!_boundCommands.ContainsKey(kb)) return; _boundCommands[kb].Remove(command); } private void ResetAllowDuplicateBindings() { _allowDuplicateBindings = true; } internal bool ShouldSerialize() { return ShouldSerializeAllowDuplicateBindings(); } private bool ShouldSerializeAllowDuplicateBindings() { return !_allowDuplicateBindings; } #endregion Methods #region Properties /// /// Gets/Sets if a key combination can be bound to more than one command. (default is true) /// /// /// When set to false only the first command bound to a key combination is kept. /// Subsequent requests are ignored. /// public bool AllowDuplicateBindings { get { return _allowDuplicateBindings; } set { _allowDuplicateBindings = value; } } #endregion Properties #region Constructors internal Commands(Scintilla scintilla) : base(scintilla) { // Ha Ha Ha Ha all your commands are belong to us! NativeScintilla.ClearAllCmdKeys(); // The reason we're doing this is because ScintillaNET is going to own // all the command bindings. There are two reasons for this: #1 it makes // it easier to handle ScintillaNET specific commands, we don't have to // do special logic if its a native command vs. ScintillaNET extension. // #2 Scintilla's built in support for commands binding only allows 1 // command per key combination. Our key handling allows for any number // of commands to be bound to a keyboard combination. // Other future enhancements that I want to do in the future are: // Visual Studioesque Key/Chord commands like Ctrl+D, w // Binding contexts. This is another CodeRush inspired idea where // commands can only execute if a given context is satisfied (or not). // Some examples are "At beginning of line", "In comment", // "Autocomplete window active", "In Snippet Range". // OK in order for these commands to play nice with each other some of them // have to have knowledge of each other AND they have to execute in a certain // order. // Since all the native Scintilla Commands already know how to work together // properly they all have the same order. But our commands have to execute first _commandComparer.CommandOrder.Add(BindableCommand.AutoCShow, 100); _commandComparer.CommandOrder.Add(BindableCommand.AutoCComplete, 100); _commandComparer.CommandOrder.Add(BindableCommand.AutoCCancel, 100); _commandComparer.CommandOrder.Add(BindableCommand.DoSnippetCheck, 200); _commandComparer.CommandOrder.Add(BindableCommand.AcceptActiveSnippets, 200); _commandComparer.CommandOrder.Add(BindableCommand.CancelActiveSnippets, 200); _commandComparer.CommandOrder.Add(BindableCommand.NextSnippetRange, 200); _commandComparer.CommandOrder.Add(BindableCommand.PreviousSnippetRange, 200); AddBinding(Keys.Down , Keys.None, BindableCommand.LineDown); AddBinding(Keys.Down , Keys.Shift, BindableCommand.LineDownExtend); AddBinding(Keys.Down , Keys.Control, BindableCommand.LineScrollDown); AddBinding(Keys.Down , Keys.Alt | Keys.Shift, BindableCommand.LineDownRectExtend); AddBinding(Keys.Up , Keys.None, BindableCommand.LineUp); AddBinding(Keys.Up , Keys.Shift, BindableCommand.LineUpExtend); AddBinding(Keys.Up , Keys.Control, BindableCommand.LineScrollUp); AddBinding(Keys.Up , Keys.Alt | Keys.Shift, BindableCommand.LineUpRectExtend); AddBinding('[', Keys.Control, BindableCommand.ParaUp); AddBinding('[' , Keys.Control | Keys.Shift, BindableCommand.ParaUpExtend); AddBinding(']' , Keys.Control, BindableCommand.ParaDown); AddBinding(']' , Keys.Control | Keys.Shift, BindableCommand.ParaDownExtend); AddBinding(Keys.Left , Keys.None, BindableCommand.CharLeft); AddBinding(Keys.Left , Keys.Shift, BindableCommand.CharLeftExtend); AddBinding(Keys.Left , Keys.Control, BindableCommand.WordLeft); AddBinding(Keys.Left , Keys.Control | Keys.Shift, BindableCommand.WordLeftExtend); AddBinding(Keys.Left , Keys.Alt | Keys.Shift, BindableCommand.CharLeftRectExtend); AddBinding(Keys.Right , Keys.None, BindableCommand.CharRight); AddBinding(Keys.Right , Keys.Shift, BindableCommand.CharRightExtend); AddBinding(Keys.Right , Keys.Control, BindableCommand.WordRight); AddBinding(Keys.Right , Keys.Control | Keys.Shift, BindableCommand.WordRightExtend); AddBinding(Keys.Right , Keys.Alt | Keys.Shift, BindableCommand.CharRightRectExtend); AddBinding('/' , Keys.Control, BindableCommand.WordPartLeft); AddBinding('/' , Keys.Control | Keys.Shift, BindableCommand.WordPartLeftExtend); AddBinding('\\' , Keys.Control, BindableCommand.WordPartRight); AddBinding('\\' , Keys.Control | Keys.Shift, BindableCommand.WordPartRightExtend); AddBinding(Keys.Home , Keys.None, BindableCommand.VCHome); AddBinding(Keys.Home , Keys.Shift, BindableCommand.VCHomeExtend); AddBinding(Keys.Home , Keys.Control, BindableCommand.DocumentStart); AddBinding(Keys.Home , Keys.Control | Keys.Shift, BindableCommand.DocumentStartExtend); AddBinding(Keys.Home , Keys.Alt, BindableCommand.HomeDisplay); AddBinding(Keys.Home , Keys.Alt | Keys.Shift, BindableCommand.VCHomeRectExtend); AddBinding(Keys.End , Keys.None, BindableCommand.LineEnd); AddBinding(Keys.End , Keys.Shift, BindableCommand.LineEndExtend); AddBinding(Keys.End , Keys.Control, BindableCommand.DocumentEnd); AddBinding(Keys.End , Keys.Control | Keys.Shift, BindableCommand.DocumentEndExtend); AddBinding(Keys.End , Keys.Alt, BindableCommand.LineEndDisplay); AddBinding(Keys.End , Keys.Alt | Keys.Shift, BindableCommand.LineEndRectExtend); AddBinding(Keys.PageUp , Keys.None, BindableCommand.PageUp); AddBinding(Keys.PageUp , Keys.Shift, BindableCommand.PageUpExtend); AddBinding(Keys.PageUp , Keys.Alt | Keys.Shift, BindableCommand.PageUpRectExtend); AddBinding(Keys.PageDown , Keys.None, BindableCommand.PageDown); AddBinding(Keys.PageDown , Keys.Shift, BindableCommand.PageDownExtend); AddBinding(Keys.PageDown , Keys.Alt | Keys.Shift, BindableCommand.PageDownRectExtend); AddBinding(Keys.Delete , Keys.None, BindableCommand.Clear); AddBinding(Keys.Delete , Keys.Shift, BindableCommand.Cut); AddBinding(Keys.Delete , Keys.Control, BindableCommand.DelWordRight); AddBinding(Keys.Delete , Keys.Control | Keys.Shift, BindableCommand.DelLineRight); AddBinding(Keys.Insert , Keys.None, BindableCommand.EditToggleOvertype); AddBinding(Keys.Insert , Keys.Shift, BindableCommand.Paste); AddBinding(Keys.Insert , Keys.Control, BindableCommand.Copy); AddBinding(Keys.Escape , Keys.None, BindableCommand.Cancel); AddBinding(Keys.Back , Keys.None, BindableCommand.DeleteBack); AddBinding(Keys.Back , Keys.Shift, BindableCommand.DeleteBack); AddBinding(Keys.Back , Keys.Control, BindableCommand.DelWordLeft); AddBinding(Keys.Back , Keys.Alt, BindableCommand.Undo); AddBinding(Keys.Back , Keys.Control | Keys.Shift, BindableCommand.DelLineLeft); AddBinding(Keys.Z, Keys.Control, BindableCommand.Undo); AddBinding(Keys.Y, Keys.Control, BindableCommand.Redo); AddBinding(Keys.X, Keys.Control, BindableCommand.Cut); AddBinding(Keys.C, Keys.Control, BindableCommand.Copy); AddBinding(Keys.V, Keys.Control, BindableCommand.Paste); AddBinding(Keys.A, Keys.Control, BindableCommand.SelectAll); AddBinding(Keys.Tab , Keys.None, BindableCommand.Tab); AddBinding(Keys.Tab , Keys.Shift, BindableCommand.BackTab); AddBinding(Keys.Enter , Keys.None, BindableCommand.NewLine); AddBinding(Keys.Enter , Keys.Shift, BindableCommand.NewLine); AddBinding(Keys.Add , Keys.Control, BindableCommand.ZoomIn); AddBinding(Keys.Subtract , Keys.Control, BindableCommand.ZoomOut); AddBinding(Keys.Divide, Keys.Control, BindableCommand.SetZoom); AddBinding(Keys.L, Keys.Control, BindableCommand.LineCut); AddBinding(Keys.L, Keys.Control | Keys.Shift, BindableCommand.LineDelete); AddBinding(Keys.T , Keys.Control | Keys.Shift, BindableCommand.LineCopy); AddBinding(Keys.T, Keys.Control, BindableCommand.LineTranspose); AddBinding(Keys.D, Keys.Control, BindableCommand.SelectionDuplicate); AddBinding(Keys.U, Keys.Control, BindableCommand.LowerCase); AddBinding(Keys.U, Keys.Control | Keys.Shift, BindableCommand.UpperCase); AddBinding(Keys.Space, Keys.Control, BindableCommand.AutoCShow); AddBinding(Keys.Tab, BindableCommand.DoSnippetCheck); AddBinding(Keys.Tab, BindableCommand.NextSnippetRange); AddBinding(Keys.Tab, Keys.Shift, BindableCommand.PreviousSnippetRange); AddBinding(Keys.Escape, BindableCommand.CancelActiveSnippets); AddBinding(Keys.Enter, BindableCommand.AcceptActiveSnippets); AddBinding(Keys.P, Keys.Control, BindableCommand.Print); AddBinding(Keys.P, Keys.Control | Keys.Shift, BindableCommand.PrintPreview); AddBinding(Keys.F, Keys.Control, BindableCommand.ShowFind); AddBinding(Keys.H, Keys.Control, BindableCommand.ShowReplace); AddBinding(Keys.F3, BindableCommand.FindNext); AddBinding(Keys.F3, Keys.Shift, BindableCommand.FindPrevious); AddBinding(Keys.I, Keys.Control, BindableCommand.IncrementalSearch); AddBinding(Keys.Q, Keys.Control, BindableCommand.LineComment); AddBinding(Keys.Q, Keys.Control | Keys.Shift, BindableCommand.LineUncomment); AddBinding('-', Keys.Control, BindableCommand.DocumentNavigateBackward); AddBinding('-', Keys.Control | Keys.Shift, BindableCommand.DocumentNavigateForward); AddBinding(Keys.J, Keys.Control, BindableCommand.ShowSnippetList); AddBinding(Keys.M, Keys.Control, BindableCommand.DropMarkerDrop); AddBinding(Keys.Escape, BindableCommand.DropMarkerCollect); AddBinding(Keys.G, Keys.Control, BindableCommand.ShowGoTo); } #endregion Constructors #region Types private class CommandComparer : IComparer { #region Fields private Dictionary _commandOrder = new Dictionary(); #endregion Fields #region Methods public int Compare(BindableCommand x, BindableCommand y) { return GetCommandOrder(y).CompareTo(GetCommandOrder(x)); } private int GetCommandOrder(BindableCommand cmd) { if (!_commandOrder.ContainsKey(cmd)) return 0; return _commandOrder[cmd]; } #endregion Methods #region Properties public Dictionary CommandOrder { get { return _commandOrder; } set { _commandOrder = value; } } #endregion Properties } #endregion Types } }