/// <summary> /// Adds a mouse scroll definition. /// </summary> /// <param name="id">The identifier of the key.</param> /// <param name="elements">The kb file elements describing the key.</param> /// <param name="definition">The keyboard definition to add the key to.</param> /// <param name="keyCode">The keycode to add.</param> private static void AddMouseScroll(int id, string[] elements, KeyboardDefinition definition, int keyCode) { if (elements.Length < 10) { throw new LegacyFileParseException("A key definition line should have at least 10 elements."); } // Parse the individual elements of the line. var posX = ParseInt(elements[2], "X-Position"); var posY = ParseInt(elements[3], "Y-Position"); var width = ParseInt(elements[4], "Width"); var height = ParseInt(elements[5], "Height"); var normalText = LegacyCharacterMapping.Replace(elements[6]); // Construct the new key definition. var newKeyDefinition = new MouseScrollDefinition( id: id, boundaries: new List <TPoint> { new TPoint(posX, posY), new TPoint(posX + width, posY), new TPoint(posX + width, posY + height), new TPoint(posX, posY + height) }, keyCode: keyCode, text: normalText); AddOrOverlap(definition, keyCode, newKeyDefinition); }
/// <summary> /// Handles a selection change in the styles list, loading that style in the main form. /// </summary> private void StyleList_SelectedIndexChanged(object sender, System.EventArgs e) { this.DefinitionChanged?.Invoke( KeyboardDefinition.Load(this.SelectedCategory, this.SelectedDefinition), KeyboardStyle.Load(this.SelectedStyle.Name, this.SelectedStyle.Global), this.SelectedStyle.Global); }
/// <summary> /// Handles the change of the location, sets the new location and invokes the changed event. /// </summary> private void txtSize_ValueChanged(VectorTextBox sender, TPoint newSize) { this.currentDefinition = this.currentDefinition.Resize(new Size(newSize.X, newSize.Y)); // Only allow reasonable values to be set. if (newSize.X >= 25 && newSize.Y >= 25 && newSize.X <= 4096 && newSize.Y <= 4096) { this.DefinitionChanged?.Invoke(this.currentDefinition); } }
/// <summary> /// Handles the MouseDown event for the main form, which can start editing an element, the mouse is pointing /// at one. /// </summary> private void MainForm_MouseDown(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left) { return; } if (!this.mnuToggleEditMode.Checked || this.menuOpen) { return; } ElementDefinition toManipulate; if (this.selectedDefinition != null) { // Try to manipulate the selected definition, if one is selected. this.selectedDefinition.StartManipulating(e.Location, KeyboardState.AltDown, translateOnly: KeyboardState.CtrlDown); toManipulate = this.selectedDefinition; } else { // If none is selected, allow any key to become the element to manipulate. toManipulate = GlobalSettings.CurrentDefinition.Elements .LastOrDefault(x => x.StartManipulating(e.Location, KeyboardState.AltDown, translateOnly: KeyboardState.CtrlDown)); } if (toManipulate == null) { this.currentlyManipulating = null; this.selectedDefinition = null; // Moving everything at once. if (KeyboardState.AltDown) { this.movingEverythingStart = GlobalSettings.CurrentDefinition.Clone(); this.PushUndoHistory(); this.movingEverythingFrom = e.Location; } return; } var indexToManipulate = GlobalSettings.CurrentDefinition.Elements.IndexOf(toManipulate); this.currentlyManipulating = Tuple.Create(indexToManipulate, toManipulate); this.highlightedDefinition = null; this.manipulationStart = toManipulate; this.cumulManipulation = new Size(); this.PushUndoHistory(); GlobalSettings.CurrentDefinition = GlobalSettings.CurrentDefinition.RemoveElement(toManipulate); this.ResetBackBrushes(); }
/// <summary> /// Handles a selection change in the definition list, re-populates the styles list for this definition. /// Loads the definition in the main form. /// </summary> private void DefinitionsList_SelectedIndexChanged(object sender, System.EventArgs e) { var kbDef = KeyboardDefinition.Load(this.SelectedCategory, this.SelectedDefinition); this.LoadStyles(); if (this.StyleList.Items.Count > 0) { this.StyleList.SelectedIndex = 0; } else { this.DefinitionChanged?.Invoke(kbDef, null, false); } }
/// <summary> /// Handles a selection change in the styles list, loading that style in the main form. /// </summary> private void StyleList_SelectedIndexChanged(object sender, System.EventArgs e) { try { this.DefinitionChanged?.Invoke( KeyboardDefinition.Load(this.SelectedCategory, this.SelectedDefinition), KeyboardStyle.Load(this.SelectedStyle.Name, this.SelectedStyle.Global), this.SelectedStyle.Global); } catch (Exception ex) { MessageBox.Show($"Failed to load keyboard {this.SelectedDefinition}: {ex.Message}"); return; } }
/// <summary> /// Adds a mouse movement definition. /// </summary> /// <param name="id">The identifier of the key.</param> /// <param name="elements">The kb file elements describing the key.</param> /// <param name="definition">The keyboard definition to add the key to.</param> private static void AddMouseMovement(int id, string[] elements, KeyboardDefinition definition) { if (elements.Length < 5) { throw new LegacyFileParseException("A mouse speed indicator line should have at least 5 elements."); } // Parse the individual elements of the line. var posX = ParseInt(elements[2], "X-Position"); var posY = ParseInt(elements[3], "Y-Position"); var width = ParseInt(elements[4], "Width"); var center = new Point(posX + width / 2, posY + width / 2); definition.Elements.Add(new MouseSpeedIndicatorDefinition(id, center, width / 2)); }
/// <summary> /// Checks if the key overlaps with any other keys of the same type and keycode. If so, it removes those /// keys from the keyboar definition and creates a new one that unifies them. Then the new unified /// key is added to the keyboard definition, /// </summary> /// <typeparam name="TKey">The key type to overlap.</typeparam> /// <param name="keyboard">The keyboard definition.</param> /// <param name="keyCode">The keycode of the key.</param> /// <param name="newKey">The key definition to add or overlap.</param> private static void AddOrOverlap <TKey>(KeyboardDefinition keyboard, int keyCode, TKey newKey) where TKey : KeyDefinition { var overlappingEqualKeys = keyboard.Elements.OfType <TKey>() // Legacy files don't support multiple keycodes per key. .Where(x => x.KeyCodes.Count == 1 && x.KeyCodes.Single() == keyCode) .Where(x => x.BordersWith(newKey)) .ToList(); foreach (var key in overlappingEqualKeys) { keyboard.Elements.Remove(key); } keyboard.Elements.Add(newKey.UnionWith(overlappingEqualKeys.ConvertAll <KeyDefinition>(x => x))); }
/// <summary> /// Handles a selection change in the definition list, re-populates the styles list for this definition. /// Loads the definition in the main form. /// </summary> private void DefinitionsList_SelectedIndexChanged(object sender, System.EventArgs e) { try { var kbDef = KeyboardDefinition.Load(this.SelectedCategory, this.SelectedDefinition); this.LoadStyles(); if (this.StyleList.Items.Count == 0) { this.DefinitionChanged?.Invoke(kbDef, null, false); } } catch (Exception ex) { MessageBox.Show($"Failed to load keyboard {this.SelectedDefinition}: {ex.Message}"); return; } }
/// <summary> /// Handles the loading of the form, all settings are read, hooks are created and the keyboard is initialized. /// </summary> private void MainForm_Load(object sender, EventArgs e) { // Load the settings if (!GlobalSettings.Load()) { MessageBox.Show( this, $"Failed to load the settings: {GlobalSettings.Errors}", "Failed to load settings"); } this.Location = new Point(GlobalSettings.Settings.X, GlobalSettings.Settings.Y); var title = GlobalSettings.Settings.WindowTitle; this.Text = string.IsNullOrWhiteSpace(title) ? $"NohBoard {Version.Get}" : title; this.GetLatestVersion().Start(); // Load a definition if possible. if (GlobalSettings.Settings.LoadedKeyboard != null && GlobalSettings.Settings.LoadedCategory != null) { try { GlobalSettings.Settings.UpdateDefinition(KeyboardDefinition .Load(GlobalSettings.Settings.LoadedCategory, GlobalSettings.Settings.LoadedKeyboard), false); } catch (Exception ex) { MessageBox.Show( "There was an error loading the saved keyboard definition file:" + $"{Environment.NewLine}{ex.Message}"); GlobalSettings.Settings.LoadedCategory = null; GlobalSettings.Settings.LoadedKeyboard = null; } } // Load a style if possible. if (GlobalSettings.CurrentDefinition != null && GlobalSettings.Settings.LoadedStyle != null) { try { GlobalSettings.Settings.UpdateStyle(KeyboardStyle.Load( GlobalSettings.Settings.LoadedStyle, GlobalSettings.Settings.LoadedGlobalStyle), false); this.LoadKeyboard(); this.ResetBackBrushes(); } catch { GlobalSettings.Settings.LoadedStyle = null; MessageBox.Show( $"Failed to load style {GlobalSettings.Settings.LoadedStyle}, loading default style.", "Error loading style."); } // Enable the mouse hook only if there are mouse keys on the screen. if (GlobalSettings.CurrentDefinition.Elements.Any(x => !(x is KeyboardKeyDefinition))) { HookManager.EnableMouseHook(); } // Enable the keyboard hook only if there are keyboard keys on the screen. if (GlobalSettings.CurrentDefinition.Elements.Any(x => x is KeyboardKeyDefinition)) { HookManager.EnableKeyboardHook(); } } this.UpdateTimer.Interval = GlobalSettings.Settings.UpdateInterval; this.UpdateTimer.Enabled = true; this.KeyCheckTimer.Enabled = true; this.Activate(); this.ApplySettings(); }
/// <summary> /// Handles the loading of the form, all settings are read, hooks are created and the keyboard is initialized. /// </summary> private void MainForm_Load(object sender, EventArgs e) { // Load the settings if (!GlobalSettings.Load()) { MessageBox.Show( this, $"Failed to load the settings: {GlobalSettings.Errors}", "Failed to load settings"); } this.Location = new Point(GlobalSettings.Settings.X, GlobalSettings.Settings.Y); this.Text = $"NohBoard {Version.Get}"; this.GetLatestVersion().Start(); // Load a definition if possible. if (GlobalSettings.Settings.LoadedKeyboard != null && GlobalSettings.Settings.LoadedCategory != null) { try { GlobalSettings.CurrentDefinition = KeyboardDefinition .Load(GlobalSettings.Settings.LoadedCategory, GlobalSettings.Settings.LoadedKeyboard); } catch (Exception ex) { MessageBox.Show( "There was an error loading the saved keyboard definition file:" + $"{Environment.NewLine}{ex.Message}"); GlobalSettings.Settings.LoadedCategory = null; GlobalSettings.Settings.LoadedKeyboard = null; } } // Load a style if possible. if (GlobalSettings.CurrentDefinition != null && GlobalSettings.Settings.LoadedStyle != null) { try { GlobalSettings.CurrentStyle = KeyboardStyle.Load( GlobalSettings.Settings.LoadedStyle, GlobalSettings.Settings.LoadedGlobalStyle); this.ResetBackBrushes(); } catch { GlobalSettings.Settings.LoadedStyle = null; MessageBox.Show( $"Failed to load style {GlobalSettings.Settings.LoadedStyle}, loading default style.", "Error loading style."); } } HookManager.EnableMouseHook(); HookManager.EnableKeyboardHook(); this.UpdateTimer.Enabled = true; this.KeyCheckTimer.Enabled = true; this.Activate(); this.ApplySettings(); }
/// <summary> /// Parses an old keyboard file. /// </summary> /// <param name="fileName">The filename of the legacy keyboard file.</param> /// <returns>The parsed keyboard definition.</returns> public static KeyboardDefinition Parse(string fileName) { var file = new FileInfo(fileName); if (!file.Exists) { throw new LegacyFileParseException($"{fileName} does not exist."); } if (file.Extension != ".kb") { throw new LegacyFileParseException($"{fileName} does not have the '.kb' extension."); } var definition = new KeyboardDefinition { Name = file.Name.Substring(0, file.Name.Length - file.Extension.Length), Version = Constants.KeyboardVersion, Elements = new List <ElementDefinition>() }; var reader = new StreamReader(fileName); var index = 0; var elementId = 0; string line; while ((line = reader.ReadLine()) != null) { var elements = line.Split(' '); index++; try { switch (elements[0]) { case "key": var keyInfo = LegacyKeyCodeMapping.Map(int.Parse(elements[1])); switch (keyInfo.Item1) { case KeyType.Keyboard: AddKeyboardKey(elementId++, elements, definition, keyInfo.Item2); break; case KeyType.Mouse: AddMouseKey(elementId++, elements, definition, keyInfo.Item2); break; case KeyType.MouseScroll: AddMouseScroll(elementId++, elements, definition, keyInfo.Item2); break; case KeyType.MouseMovement: AddMouseMovement(elementId++, elements, definition); break; default: throw new ArgumentOutOfRangeException(); } break; case "width": if (elements.Length < 2) { throw new LegacyFileParseException( "A keyboard property line should have at least 2 elements."); } definition.Width = ParseInt(elements[1], "Width"); break; case "height": if (elements.Length < 2) { throw new LegacyFileParseException( "A keyboard property line should have at least 2 elements."); } definition.Height = ParseInt(elements[1], "Height"); break; case "category": if (elements.Length < 2) { throw new LegacyFileParseException( "A keyboard property line should have at least 2 elements."); } definition.Category = elements[1]; break; } } catch (Exception ex) { throw new LegacyFileParseException($"Unable to parse key file line {index}: '{line}'", ex); } } // Fix the size of the keyboard and the position of the keys. Use the standard of 9 pixels on all edges. var bb = definition.GetBoundingBox(); definition.Elements = definition.Elements.Select(x => x.Translate(9 - bb.Left, 9 - bb.Top)).ToList(); var bb2 = definition.GetBoundingBox(); definition.Width = bb2.Right + 9; definition.Height = bb2.Bottom + 9; return(definition); }
/// <summary> /// Initializes a new instance of the <see cref="KeyboardPropertiesForm" /> class. /// </summary> public KeyboardPropertiesForm(KeyboardDefinition initialDefinition) { this.initialDefinition = initialDefinition ?? throw new ArgumentNullException(nameof(initialDefinition)); this.currentDefinition = this.initialDefinition.Clone(); this.InitializeComponent(); }