using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using ICSharpCode.Core; using ICSharpCode.Core.Presentation; namespace ICSharpCode.ShortcutsManagement.Dialogs { /// /// Notification type enumeration /// public enum NotificationType { /// /// A valid gesture is entered so far /// Valid, /// /// Gesture is being entered, but is not valid yet /// Invalid, /// /// Gesture was successfully added to gestures collection /// Added, /// /// Failed to add gesture to gestures collection /// Failed, /// /// Notification message is not displayed /// None } /// /// Represents a textbox suited for entering key gestures /// public partial class MultiKeyGestureTextBox : UserControl { /// /// Identifies dependency property /// public static readonly DependencyProperty TextBoxBorderThicknessProperty = DependencyProperty.Register( "TextBoxBorderThickness", typeof(int), typeof(MultiKeyGestureTextBox), new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsRender)); /// /// Gets or sets text box border thickness /// public int TextBoxBorderThickness { get { return (int)GetValue(TextBoxBorderThicknessProperty); } set { SetValue(TextBoxBorderThicknessProperty, value); } } /// /// Gets entered in text box /// public KeyGesture Gesture { get { if (enteredKeyGestureSequence == null || enteredKeyGestureSequence.Count == 0) { return null; } if (enteredKeyGestureSequence.Count == 1) { return new PartialKeyGesture(enteredKeyGestureSequence.First()); } return new MultiKeyGesture(enteredKeyGestureSequence); } } /// /// Occurs when gesture entered in text box changes /// public event EventHandler GestureChanged; /// /// Identifies dependency property /// public static readonly DependencyProperty NotificationVisibilityProperty = DependencyProperty.Register( "NotificationVisibility", typeof(Visibility), typeof(MultiKeyGestureTextBox), new FrameworkPropertyMetadata(Visibility.Visible, FrameworkPropertyMetadataOptions.AffectsRender)); /// /// Gets or sets value which specifies whether notifications are displayed under textbox control /// public Visibility NotificationVisibility { get { return (Visibility)GetValue(NotificationVisibilityProperty); } set { SetValue(NotificationVisibilityProperty, value); } } /// /// Identifies dependency property /// public static readonly DependencyProperty NotificationTypeProperty = DependencyProperty.Register( "NotificationType", typeof(NotificationType), typeof(MultiKeyGestureTextBox), new FrameworkPropertyMetadata(NotificationType.None, FrameworkPropertyMetadataOptions.AffectsRender)); /// /// Gets or sets notification type /// public NotificationType NotificationType { get { return (NotificationType)GetValue(NotificationTypeProperty); } set { SetValue(NotificationTypeProperty, value); } } /// /// Identifies dependency property /// public static readonly DependencyProperty NotificationTextProperty = DependencyProperty.Register( "NotificationText", typeof(string), typeof(MultiKeyGestureTextBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)); /// /// Gets or sets displayed notification text /// public string NotificationText { get { return (string)GetValue(NotificationTextProperty); } set { SetValue(NotificationTextProperty, value); } } /// /// Last entered chords /// private List enteredKeyGestureSequence = new List(); /// /// Time when last successfull chord was entered /// private DateTime lastEnterTime = DateTime.Now; /// /// Creates instance of /// public MultiKeyGestureTextBox() { InitializeComponent(); } /// /// Displays notifications under gesture text box /// /// Notification text /// Notification type public void DisplayNotification(string notificationText, NotificationType type) { NotificationText = notificationText; NotificationType = type; } /// /// Clears all text area, chords and hides notification /// public void Clear() { enteredKeyGestureSequence = new List(); shortcutTextBox.Text = ""; DisplayNotification("", NotificationType.None); } /// /// Executed when clicked on "Clear" button to the right from gesture text box /// /// Sender object /// supporting data private void clearTextBox_Click(object sender, RoutedEventArgs e) { Clear(); } /// /// Executed when text inside textbox changes /// /// Sender object /// Event arguments void shortcutTextBox_TextChanged(object sender, TextChangedEventArgs e) { if (GestureChanged != null) { GestureChanged.Invoke(sender, new EventArgs()); } } /// /// Raised before user presses any key inside text box /// /// Sender object /// Event argument private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e) { e.Handled = true; // If a key is holded for a longer time key event is raised repeatedly. // We don't want to handle this kind of events if (e.IsRepeat) { return; } // If delete or backspace button is pressed if (e.Key == Key.Back || e.Key == Key.Delete) { Clear(); return; } // Check whether time given for chord entry haven't expired yet if (DateTime.Now - lastEnterTime > MultiKeyGesture.DelayBetweenChords) { if (enteredKeyGestureSequence.Count > 0) { DisplayNotification(StringParser.Parse("${res:ShortcutsManagement.GestureTextBox.TimeExpired}"), NotificationType.Invalid); } Clear(); lastEnterTime = DateTime.Now; } var partialKeyGesture = new PartialKeyGesture(e); var lastGesture = enteredKeyGestureSequence.Count > 0 ? enteredKeyGestureSequence.LastOrDefault() : null; var isLastGestureSpecialKey = lastGesture != null && (lastGesture.Key >= Key.F1) && (lastGesture.Key <= Key.F24); var isLastGestureComplete = lastGesture != null && (lastGesture.Key != Key.None || isLastGestureSpecialKey); var isContinuedGesture = lastGesture != null && partialKeyGesture.Modifiers - (partialKeyGesture.Modifiers ^ lastGesture.Modifiers) >= 0; // If continuing previous chord if (!isLastGestureComplete && isContinuedGesture) { enteredKeyGestureSequence.RemoveAt(enteredKeyGestureSequence.Count - 1); } else if (!isLastGestureComplete) { // If previous chord is unfinished and second chord is already entered // start from scratch. DisplayNotification(StringParser.Parse("${res:ShortcutsManagement.GestureTextBox.SequenceIsNotCoherent}"), NotificationType.Invalid); Clear(); } // If successfully finished another chord give more time if (partialKeyGesture.Key != Key.None) { lastEnterTime = DateTime.Now; } enteredKeyGestureSequence.Add(partialKeyGesture); // Create a multi key gesture if entered more than one chord if (enteredKeyGestureSequence.Count > 0) { var multiKeyGesture = new MultiKeyGesture(enteredKeyGestureSequence); var multiKeyGestureString = new MultiKeyGestureConverter().ConvertToInvariantString(multiKeyGesture); shortcutTextBox.Text = multiKeyGestureString; } else { Clear(); } if (!enteredKeyGestureSequence[0].IsFull) { DisplayNotification(StringParser.Parse("${res:ShortcutsManagement.GestureTextBox.FirstChordIsIncomplete}"), NotificationType.Invalid); } else if (partialKeyGesture.Key == Key.None) { DisplayNotification(StringParser.Parse("${res:ShortcutsManagement.GestureTextBox.LastChordIsIncomplete}"), NotificationType.Invalid); } else { DisplayNotification(StringParser.Parse("${res:ShortcutsManagement.GestureTextBox.GestureIsValid}"), NotificationType.Valid); } } } }