// KeyBindingSet.cs // // Author: // Lluis Sanchez Gual // Jeffrey Stedfast // // Copyright (c) 2008 Novell, Inc (http://www.novell.com) // // 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 System; using System.IO; using System.Collections.Generic; using System.Xml; using System.Text; using MonoDevelop.Core; using Unix = Mono.Unix.Native; namespace MonoDevelop.Components.Commands { public class KeyBindingSet { const string version = "1.0"; Dictionary bindings = new Dictionary (); KeyBindingSet parent; public KeyBindingSet() { } public KeyBindingSet (KeyBindingSet parent) { this.parent = parent; } public KeyBindingConflict[] CheckKeyBindingConflicts (IEnumerable commands) { Dictionary> bindings = new Dictionary> (); List conflicts = new List (); // Load all bindings foreach (Command cmd in commands) { string key = GetBinding (cmd); if (key.Length == 0) continue; List clist; if (!bindings.TryGetValue (key, out clist)) { clist = new List (); bindings.Add (key, clist); } clist.Add (cmd); if (clist.Count == 2) // We have a conflict conflicts.Add (new KeyBindingConflict (key, clist)); } // Check if there are conflicts with key combinations prefixes. // For example, ctrl+X would conflict with ctrl+X|A foreach (Command cmd in commands) { string key = GetBinding (cmd); int i = key.IndexOf ('|'); if (i != -1) { key = key.Substring (0, i); List clist; if (bindings.TryGetValue (key, out clist)) { clist.Add (cmd); if (clist.Count == 2) // We have a conflict conflicts.Add (new KeyBindingConflict (key, clist)); } } } return conflicts.ToArray (); } public KeyBindingSet Clone () { KeyBindingSet kset = new KeyBindingSet (parent); kset.bindings = new Dictionary (bindings); return kset; } public bool Equals (KeyBindingSet other) { if (bindings.Count != other.bindings.Count) return false; foreach (KeyValuePair binding in bindings) { string accel; if (!other.bindings.TryGetValue (binding.Key, out accel) || accel != binding.Value) return false; } return true; } public void ClearBindings () { bindings.Clear (); } public void StoreBinding (Command cmd) { SetBinding (cmd, cmd.AccelKey); } public void SetBinding (Command cmd, string accelKey) { string key = KeyBindingService.GetCommandKey (cmd); if (accelKey == null) { bindings.Remove (key); return; } if (parent == null) bindings [key] = accelKey; else { // If the key is the same as the default, remove it from the scheme string pbind = parent.GetBinding (cmd); if ((accelKey == pbind) || (string.IsNullOrEmpty (accelKey) && string.IsNullOrEmpty (pbind))) bindings.Remove (key); else bindings[key] = accelKey; } } public void LoadBinding (Command cmd) { cmd.AccelKey = GetBinding (cmd); } public string GetBinding (Command cmd) { string key = KeyBindingService.GetCommandKey (cmd); // Schemes use default key bindings when not explicitly overriden string accel; if (bindings.TryGetValue (key, out accel)) return accel; else if (parent != null) return parent.GetBinding (cmd); else return string.Empty; } const string commandAttr = "command"; const string shortcutAttr = "shortcut"; internal bool LoadScheme (XmlTextReader reader, string id) { bool foundSchemes = false; bool foundScheme = false; string command; string binding; bindings.Clear (); while (reader.Read ()) { if (reader.IsStartElement ("schemes") || reader.IsStartElement ("scheme")) { foundSchemes = true; break; } } if (!foundSchemes || reader.GetAttribute ("version") != version) return false; if (reader.IsStartElement ("schemes")) { while (reader.Read ()) { if (reader.IsStartElement ("scheme") && reader.GetAttribute ("name") == id) { foundScheme = true; break; } } if (!foundScheme) return false; } while (reader.Read ()) { if (reader.IsStartElement ()) { switch (reader.LocalName) { case "scheme": // this is the beginning of the next scheme return true; case "binding": command = reader.GetAttribute (commandAttr); binding = reader.GetAttribute (shortcutAttr); if (command != null && command != String.Empty) bindings.Add (command, binding); break; } } } return true; } public void Save (XmlTextWriter writer, string id) { writer.WriteStartElement ("scheme"); writer.WriteAttributeString ("name", id); foreach (KeyValuePair binding in bindings) { writer.WriteStartElement ("binding"); writer.WriteAttributeString (commandAttr, binding.Key); writer.WriteAttributeString (shortcutAttr, binding.Value); writer.WriteEndElement (); } writer.WriteEndElement (); } public void Save (string fileName, string id) { XmlTextWriter writer = null; Stream stream = null; bool success = false; try { stream = new FileStream (fileName + '~', FileMode.Create); writer = new XmlTextWriter (stream, Encoding.UTF8); writer.Formatting = Formatting.Indented; writer.IndentChar = ' '; writer.Indentation = 2; writer.WriteStartElement ("schemes"); writer.WriteAttributeString ("version", version); Save (writer, id); writer.WriteEndElement (); success = true; } catch (Exception e) { LoggingService.LogError (e.ToString ()); } finally { if (writer != null) writer.Close (); if (stream != null) stream.Close (); } if (success) FileService.SystemRename (fileName + '~', fileName); } } public class KeyBindingConflict { internal KeyBindingConflict (string key, IEnumerable cmds) { Key = key; Commands = cmds; } public string Key { get; internal set; } public IEnumerable Commands { get; internal set; } } }