// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.AvalonEdit.Utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace ICSharpCode.AvalonEdit.Editing
{
///
/// A set of input bindings and event handlers for the text area.
///
///
///
/// There is one active input handler per text area (), plus
/// a number of active stacked input handlers.
///
///
/// The text area also stores a reference to a default input handler, but that is not necessarily active.
///
///
/// Stacked input handlers work in addition to the set of currently active handlers (without detaching them).
/// They are detached in the reverse order of being attached.
///
///
public interface ITextAreaInputHandler
{
///
/// Gets the text area that the input handler belongs to.
///
TextArea TextArea {
get;
}
///
/// Attaches an input handler to the text area.
///
void Attach();
///
/// Detaches the input handler from the text area.
///
void Detach();
}
///
/// Stacked input handler.
/// Uses OnEvent-methods instead of registering event handlers to ensure that the events are handled in the correct order.
///
public abstract class TextAreaStackedInputHandler : ITextAreaInputHandler
{
readonly TextArea textArea;
///
public TextArea TextArea {
get { return textArea; }
}
///
/// Creates a new TextAreaInputHandler.
///
protected TextAreaStackedInputHandler(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
}
///
public virtual void Attach()
{
}
///
public virtual void Detach()
{
}
///
/// Called for the PreviewKeyDown event.
///
public virtual void OnPreviewKeyDown(KeyEventArgs e)
{
}
///
/// Called for the PreviewKeyUp event.
///
public virtual void OnPreviewKeyUp(KeyEventArgs e)
{
}
}
///
/// Default-implementation of .
///
///
public class TextAreaInputHandler : ITextAreaInputHandler
{
readonly ObserveAddRemoveCollection commandBindings;
readonly ObserveAddRemoveCollection inputBindings;
readonly ObserveAddRemoveCollection nestedInputHandlers;
readonly TextArea textArea;
bool isAttached;
///
/// Creates a new TextAreaInputHandler.
///
public TextAreaInputHandler(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.textArea = textArea;
commandBindings = new ObserveAddRemoveCollection(CommandBinding_Added, CommandBinding_Removed);
inputBindings = new ObserveAddRemoveCollection(InputBinding_Added, InputBinding_Removed);
nestedInputHandlers = new ObserveAddRemoveCollection(NestedInputHandler_Added, NestedInputHandler_Removed);
}
///
public TextArea TextArea {
get { return textArea; }
}
///
/// Gets whether the input handler is currently attached to the text area.
///
public bool IsAttached {
get { return isAttached; }
}
#region CommandBindings / InputBindings
///
/// Gets the command bindings of this input handler.
///
public ICollection CommandBindings {
get { return commandBindings; }
}
void CommandBinding_Added(CommandBinding commandBinding)
{
if (isAttached)
textArea.CommandBindings.Add(commandBinding);
}
void CommandBinding_Removed(CommandBinding commandBinding)
{
if (isAttached)
textArea.CommandBindings.Remove(commandBinding);
}
///
/// Gets the input bindings of this input handler.
///
public ICollection InputBindings {
get { return inputBindings; }
}
void InputBinding_Added(InputBinding inputBinding)
{
if (isAttached)
textArea.InputBindings.Add(inputBinding);
}
void InputBinding_Removed(InputBinding inputBinding)
{
if (isAttached)
textArea.InputBindings.Remove(inputBinding);
}
///
/// Adds a command and input binding.
///
/// The command ID.
/// The modifiers of the keyboard shortcut.
/// The key of the keyboard shortcut.
/// The event handler to run when the command is executed.
public void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
{
this.CommandBindings.Add(new CommandBinding(command, handler));
this.InputBindings.Add(new KeyBinding(command, key, modifiers));
}
#endregion
#region NestedInputHandlers
///
/// Gets the collection of nested input handlers. NestedInputHandlers are activated and deactivated
/// together with this input handler.
///
public ICollection NestedInputHandlers {
get { return nestedInputHandlers; }
}
void NestedInputHandler_Added(ITextAreaInputHandler handler)
{
if (handler == null)
throw new ArgumentNullException("handler");
if (handler.TextArea != textArea)
throw new ArgumentException("The nested handler must be working for the same text area!");
if (isAttached)
handler.Attach();
}
void NestedInputHandler_Removed(ITextAreaInputHandler handler)
{
if (isAttached)
handler.Detach();
}
#endregion
#region Attach/Detach
///
public virtual void Attach()
{
if (isAttached)
throw new InvalidOperationException("Input handler is already attached");
isAttached = true;
textArea.CommandBindings.AddRange(commandBindings);
textArea.InputBindings.AddRange(inputBindings);
foreach (ITextAreaInputHandler handler in nestedInputHandlers)
handler.Attach();
}
///
public virtual void Detach()
{
if (!isAttached)
throw new InvalidOperationException("Input handler is not attached");
isAttached = false;
foreach (CommandBinding b in commandBindings)
textArea.CommandBindings.Remove(b);
foreach (InputBinding b in inputBindings)
textArea.InputBindings.Remove(b);
foreach (ITextAreaInputHandler handler in nestedInputHandlers)
handler.Detach();
}
#endregion
}
}