using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
namespace ScintillaNet
{
[TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))]
public class Commands : ScintillaHelperBase
{
private Dictionary<KeyBinding, List<BindableCommand>> _boundCommands = new Dictionary<KeyBinding, List<BindableCommand>>();
private CommandComparer _commandComparer = new CommandComparer();
// Hmmm.. This is getting more and more hackyish
internal bool StopProcessingCommands = false;
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.WordPartLeftExtend);
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);
}
internal bool ShouldSerialize()
{
return ShouldSerializeAllowDuplicateBindings();
}
#region AllowDuplicateBindings
private bool _allowDuplicateBindings = false;
public bool AllowDuplicateBindings
{
get
{
return _allowDuplicateBindings;
}
set
{
_allowDuplicateBindings = value;
}
}
private bool ShouldSerializeAllowDuplicateBindings()
{
return _allowDuplicateBindings;
}
private void ResetAllowDuplicateBindings()
{
_allowDuplicateBindings = false;
}
#endregion
#region AddBinding
public void AddBinding(Keys shortcut, Keys modifiers, BindableCommand command)
{
KeyBinding kb = new KeyBinding(shortcut, modifiers);
if (!_boundCommands.ContainsKey(kb))
_boundCommands.Add(kb, new List<BindableCommand>());
List<BindableCommand> l = _boundCommands[kb];
if (_allowDuplicateBindings || !l.Contains(command))
l.Add(command);
}
public void AddBinding(Keys shortcut, BindableCommand command)
{
AddBinding(shortcut, Keys.None, command);
}
public void AddBinding(char shortcut, BindableCommand command)
{
AddBinding(Utilities.GetKeys(shortcut), Keys.None, command);
}
public void AddBinding(char shortcut, Keys modifiers, BindableCommand command)
{
AddBinding(Utilities.GetKeys(shortcut), modifiers, command);
}
#endregion
#region RemoveBinding
public void RemoveBinding(Keys shortcut, Keys modifiers, BindableCommand command)
{
KeyBinding kb = new KeyBinding(shortcut, modifiers);
if (!_boundCommands.ContainsKey(kb))
return;
_boundCommands[kb].Remove(command);
}
public void RemoveBinding(Keys shortcut, BindableCommand command)
{
RemoveBinding(shortcut, Keys.None, command);
}
public void RemoveBinding(char shortcut, BindableCommand command)
{
RemoveBinding(Utilities.GetKeys(shortcut), Keys.None, command);
}
public void RemoveBinding(char shortcut, Keys modifiers, BindableCommand command)
{
RemoveBinding(Utilities.GetKeys(shortcut), modifiers, command);
}
public void RemoveBinding(Keys shortcut, Keys modifiers)
{
_boundCommands.Remove(new KeyBinding(shortcut, modifiers));
}
public void RemoveBinding(Keys shortcut)
{
RemoveBinding(shortcut, Keys.None);
}
public void RemoveBinding(char shortcut)
{
RemoveBinding(Utilities.GetKeys(shortcut), Keys.None);
}
public void RemoveBinding(char shortcut, Keys modifiers)
{
RemoveBinding(Utilities.GetKeys(shortcut), modifiers);
}
#endregion
#region RemoveAllBindings
public void RemoveAllBindings()
{
_boundCommands.Clear();
}
#endregion
#region GetCommands
private List<BindableCommand> GetCommands(Keys shortcut, Keys modifiers)
{
KeyBinding kb = new KeyBinding(shortcut, modifiers);
if (!_boundCommands.ContainsKey(kb))
return new List<BindableCommand>();
return _boundCommands[kb];
}
private List<BindableCommand> GetCommands(Keys shortcut)
{
return GetCommands(shortcut, Keys.None);
}
private List<BindableCommand> GetCommands(char shortcut)
{
return GetCommands(Utilities.GetKeys(shortcut), Keys.None);
}
private List<BindableCommand> GetCommands(char shortcut, Keys modifiers)
{
return GetCommands(Utilities.GetKeys(shortcut), modifiers);
}
#endregion
#region GetKeyBindings
public List<KeyBinding> GetKeyBindings(BindableCommand command)
{
List<KeyBinding> ret = new List<KeyBinding>();
foreach (KeyValuePair<KeyBinding, List<BindableCommand>> item in _boundCommands)
{
if (item.Value.Contains(command))
ret.Add(item.Key);
}
return ret;
}
#endregion
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;
}
return false;
}
internal bool ProcessKey(KeyEventArgs e)
{
StopProcessingCommands = false;
KeyBinding kb = new KeyBinding(e.KeyCode, e.Modifiers);
if (!_boundCommands.ContainsKey(kb))
return false;
List<BindableCommand> 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;
}
private class CommandComparer : IComparer<BindableCommand>
{
private Dictionary<BindableCommand, int> _commandOrder = new Dictionary<BindableCommand, int>();
public Dictionary<BindableCommand, int> CommandOrder
{
get
{
return _commandOrder;
}
set
{
_commandOrder = value;
}
}
private int getCommandOrder(BindableCommand cmd)
{
if (!_commandOrder.ContainsKey(cmd))
return 0;
return _commandOrder[cmd];
}
#region IComparer<BindableCommand> Members
public int Compare(BindableCommand x, BindableCommand y)
{
return getCommandOrder(y).CompareTo(getCommandOrder(x));
}
#endregion
}
}
public struct KeyBinding
{
private Keys _keycode;
public Keys KeyCode
{
get
{
return _keycode;
}
set
{
_keycode = value;
}
}
private Keys _modifiers;
public Keys Modifiers
{
get
{
return _modifiers;
}
set
{
_modifiers = value;
}
}
/// <summary>
/// Initializes a new instance of the KeyBinding structure.
/// </summary>
/// <param name="@char"></param>
/// <param name="modifiers"></param>
public KeyBinding(Keys keycode, Keys modifiers)
{
_keycode = keycode;
_modifiers = modifiers;
}
public override string ToString()
{
return ((int)_keycode).ToString() + ((int)_modifiers).ToString();
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is KeyBinding))
return false;
KeyBinding kb = (KeyBinding)obj;
return _keycode == kb._keycode && _modifiers == kb._modifiers;
}
}
}