PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/settings/KeyBindingPanel.cs

https://github.com/moscrif/ide
C# | 470 lines | 329 code | 99 blank | 42 comment | 75 complexity | 44c670315f0a67ed2b59fbabffc5f5f8 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using Gtk;
  4. using System.Linq;
  5. using Moscrif.IDE.Controls;
  6. using Moscrif.IDE.Workspace;
  7. using Moscrif.IDE.Devices;
  8. using MessageDialogs = Moscrif.IDE.Controls.MessageDialog;
  9. using Moscrif.IDE.Iface.Entities;
  10. using Moscrif.IDE.Iface;
  11. namespace Moscrif.IDE.Option
  12. {
  13. internal class KeyBindingPanel : OptionsPanel
  14. {
  15. KeyBindingWidget widget;
  16. public override Widget CreatePanelWidget ()
  17. {
  18. return widget = new KeyBindingWidget (ParentDialog);
  19. }
  20. public override void ShowPanel()
  21. {
  22. }
  23. public override void ApplyChanges ()
  24. {
  25. widget.Store ();
  26. }
  27. public override bool ValidateChanges ()
  28. {
  29. return true;
  30. }
  31. public override void Initialize (PreferencesDialog dialog, object dataObject)
  32. {
  33. base.Initialize (dialog, dataObject);
  34. }
  35. public override string Label {
  36. get { return "Key Binding"; }
  37. }
  38. public override string Icon {
  39. get { return "keyboard-shortcuts.png"; }
  40. }
  41. }
  42. public partial class KeyBindingWidget : Gtk.Bin
  43. {
  44. const string NOTHING ="Nothing";
  45. const string WIN ="Visual Studio";
  46. const string MACOSX ="XCode";
  47. const string JAVA ="Eclipse";
  48. const string VisualC ="Visual C++";
  49. static readonly int nameCol = 0;
  50. static readonly int descrCol = 1;
  51. static readonly int keyCol = 2;
  52. static readonly int boolCol = 3;
  53. static readonly int keyBindCol = 4;
  54. static Gdk.Keymap keymap = Gdk.Keymap.Default;
  55. static Dictionary<Gdk.Key,Gdk.Key> groupZeroMappings = new Dictionary<Gdk.Key,Gdk.Key> ();
  56. private bool accelComplete;
  57. List<KeyBindingSection> curentBind ;
  58. Gtk.TreeStore keybStore = new Gtk.TreeStore( typeof(string),typeof(string),typeof(string),typeof(int),typeof(KeyBinding));
  59. KeyBindings keyBindFile ;
  60. public KeyBindingWidget(Gtk.Window parent)
  61. {
  62. //parentWindow = parent;
  63. this.Build();
  64. /*if(!isRequired){
  65. cbKeyBinding.AppendText(NOTHING);
  66. cbKeyBinding.Active = 0;
  67. }*/
  68. cbKeyBinding.AppendText(WIN);
  69. cbKeyBinding.AppendText(MACOSX);
  70. cbKeyBinding.AppendText(JAVA);
  71. cbKeyBinding.AppendText(VisualC);
  72. if(MainClass.Platform.IsMac){
  73. cbKeyBinding.Active = 1;
  74. } else {
  75. cbKeyBinding.Active = 0;
  76. }
  77. keymap.KeysChanged += delegate {
  78. groupZeroMappings.Clear ();
  79. };
  80. lblMessage.Text = "";
  81. //string file = System.IO.Path.Combine(MainClass.Paths.ConfingDir, "keybinding");
  82. keyBindFile = MainClass.KeyBinding;//KeyBindings.OpenKeyBindings(file);
  83. //entrAccel.InvisibleChar = 'â—Ź';
  84. //curentBind =keyBindFile.
  85. TreeViewColumn col = new TreeViewColumn ();
  86. col.Title = MainClass.Languages.Translate("name");
  87. col.Spacing = 4;
  88. CellRendererText crp = new CellRendererText ();
  89. col.PackStart (crp, false);
  90. col.AddAttribute (crp, "text", nameCol);
  91. col.AddAttribute (crp, "weight", boolCol);
  92. tvKeyBind.AppendColumn(col);
  93. //tvKeyBind.AppendColumn(MainClass.Languages.Translate("name"), new Gtk.CellRendererText(), "text", 0);
  94. tvKeyBind.AppendColumn(MainClass.Languages.Translate("description"), new Gtk.CellRendererText(), "text", descrCol);
  95. tvKeyBind.AppendColumn(MainClass.Languages.Translate("key"), new Gtk.CellRendererText(), "text", keyCol);
  96. tvKeyBind.Model = keybStore;
  97. tvKeyBind.EnableSearch = false;
  98. curentBind = MainClass.Tools.Clone<KeyBindingSection>(keyBindFile.KeyBindingSection);
  99. foreach(KeyBindingSection kbs in curentBind){
  100. TreeIter tiParent = keybStore.AppendValues(kbs.Name,"","",(int) Pango.Weight.Bold,null);
  101. foreach(KeyBinding kb in kbs.KeyBinding){
  102. keybStore.AppendValues(tiParent,kb.Name,kb.Description,kb.Key,(int) Pango.Weight.Normal,kb);
  103. }
  104. }
  105. tvKeyBind.ExpandAll();
  106. tvKeyBind.ColumnsAutosize ();
  107. entrAccel.Sensitive = true;
  108. btnAccelAply.Sensitive = true;
  109. tvKeyBind.Selection.Changed += OnKeysTreeViewSelectionChange;
  110. }
  111. void OnKeysTreeViewSelectionChange (object sender, EventArgs e)
  112. {
  113. TreeSelection sel = sender as TreeSelection;
  114. TreeModel model;
  115. TreeIter iter;
  116. if (sel.GetSelected (out model, out iter) && model.GetValue (iter,keyBindCol) != null) {
  117. entrAccel.Sensitive = true;
  118. btnAccelAply.Sensitive = true;
  119. KeyBinding kb = (KeyBinding) model.GetValue (iter, keyBindCol);
  120. entrAccel.Text =kb.Key;
  121. entrAccel.GrabFocus ();
  122. } else {
  123. entrAccel.Sensitive = false;
  124. btnAccelAply.Sensitive = false;
  125. }
  126. }
  127. public void Store ()
  128. {
  129. KeyBindings kb = MainClass.KeyBinding;//.KeyBindingSection; = curentBind;
  130. kb.KeyBindingSection= curentBind;
  131. KeyBindings.SaveKeyBindings(kb);
  132. MainClass.KeyBinding.KeyBindingSection = curentBind;
  133. //MainClass.KeyBinding.SaveKeyBindings();
  134. MainClass.MainWindow.ActionUiManager.ReloadKeyBind();
  135. }
  136. protected virtual void OnBtnAccelAplyClicked (object sender, System.EventArgs e)
  137. {
  138. TreeSelection sel = tvKeyBind.Selection;
  139. TreeModel model;
  140. TreeIter iter;
  141. if (sel.GetSelected (out model, out iter) && model.GetValue (iter,keyBindCol) != null) {
  142. entrAccel.Sensitive = true;
  143. btnAccelAply.Sensitive = true;
  144. KeyBinding kb = (KeyBinding) model.GetValue (iter, keyBindCol);
  145. kb.Key = entrAccel.Text;
  146. model.SetValue(iter,keyCol,kb.Key);
  147. }
  148. }
  149. [GLib.ConnectBefore]
  150. protected virtual void OnEntrAccelKeyReleaseEvent (object o, Gtk.KeyReleaseEventArgs args)
  151. {
  152. string accel = entrAccel.Text;
  153. if(accel.EndsWith("+"))
  154. entrAccel.Text = "";
  155. }
  156. [GLib.ConnectBefore]
  157. void OnEntrAccelKeyPressEvent (object o, Gtk.KeyPressEventArgs args)
  158. {
  159. args.RetVal = true;
  160. //Console.WriteLine(args.Event.Key);
  161. Gdk.Key key;
  162. Gdk.ModifierType mt;
  163. //Console.WriteLine("key 1->{0};mt->{1}",args.Event.Key,args.Event.State);
  164. MapRawKeys (args.Event, out key, out mt);
  165. //Console.WriteLine("key 2->{0};mt->{1}",key,mt);
  166. if(accelComplete){
  167. if(key == Gdk.Key.BackSpace){
  168. entrAccel.Text= "";
  169. return;
  170. }
  171. }
  172. bool shiftWasConsumed = ((args.Event.State ^ mt) & Gdk.ModifierType.ShiftMask) != 0;
  173. if(shiftWasConsumed && char.IsUpper((char)Gdk.Keyval.ToUpper((uint)key)))
  174. mt |= Gdk.ModifierType.ShiftMask;
  175. bool isModifier = false ;
  176. string modifier = ModifierToPartialAccel(mt,key,out isModifier);
  177. accelComplete = !isModifier;
  178. if (!isModifier) {
  179. modifier = modifier + KeyToString (key);
  180. }
  181. entrAccel.Text= modifier;
  182. string conflict =FindKeyBindConflict(modifier);
  183. if(string.IsNullOrEmpty(conflict)){
  184. lblMessage.Text = "";
  185. }
  186. else{
  187. lblMessage.Text = String.Format("<b> {0} </b>", MainClass.Languages.Translate("keyBind_conflict",conflict));
  188. lblMessage.UseMarkup= true;
  189. }
  190. }
  191. public static void MapRawKeys (Gdk.EventKey evt, out Gdk.Key key, out Gdk.ModifierType mod)
  192. {
  193. mod = evt.State;
  194. key = evt.Key;
  195. uint keyval;
  196. int effectiveGroup, level;
  197. Gdk.ModifierType consumedModifiers;
  198. keymap.TranslateKeyboardState (evt.HardwareKeycode, evt.State, evt.Group, out keyval, out effectiveGroup,
  199. out level, out consumedModifiers);
  200. key = (Gdk.Key)keyval;
  201. mod = evt.State & ~consumedModifiers;
  202. if (MainClass.Platform.IsX11) {
  203. //this is a workaround for a common X mapping issue
  204. //where the alt key is mapped to the meta key when the shift modifier is active
  205. if (key.Equals (Gdk.Key.Meta_L) || key.Equals (Gdk.Key.Meta_R))
  206. key = Gdk.Key.Alt_L;
  207. }
  208. //HACK: the MAC GTK+ port currently does some horrible, un-GTK-ish key mappings
  209. // so we work around them by playing some tricks to remap and decompose modifiers.
  210. // We also decompose keys to the root physical key so that the Mac command
  211. // combinations appear as expected, e.g. shift-{ is treated as shift-[.
  212. if (MainClass.Platform.IsMac && !MainClass.Platform.IsX11) {
  213. // Mac GTK+ maps the command key to the Mod1 modifier, which usually means alt/
  214. // We map this instead to meta, because the Mac GTK+ has mapped the cmd key
  215. // to the meta key (yay inconsistency!). IMO super would have been saner.
  216. if ((mod & Gdk.ModifierType.Mod1Mask) != 0) {
  217. mod ^= Gdk.ModifierType.Mod1Mask;
  218. mod |= Gdk.ModifierType.MetaMask;
  219. }
  220. // If Mod5 is active it *might* mean that opt/alt is active,
  221. // so we can unset this and map it back to the normal modifier.
  222. if ((mod & Gdk.ModifierType.Mod5Mask) != 0) {
  223. mod ^= Gdk.ModifierType.Mod5Mask;
  224. mod |= Gdk.ModifierType.Mod1Mask;
  225. }
  226. // When opt modifier is active, we need to decompose this to make the command appear correct for Mac.
  227. // In addition, we can only inspect whether the opt/alt key is pressed by examining
  228. // the key's "group", because the Mac GTK+ treats opt as a group modifier and does
  229. // not expose it as an actual GDK modifier.
  230. if (evt.Group == (byte) 1) {
  231. mod |= Gdk.ModifierType.Mod1Mask;
  232. key = GetGroupZeroKey (key, evt);
  233. }
  234. }
  235. //fix shift-tab weirdness. There isn't a nice name for untab, so make it shift-tab
  236. if (key == Gdk.Key.ISO_Left_Tab) {
  237. key = Gdk.Key.Tab;
  238. mod |= Gdk.ModifierType.ShiftMask;
  239. }
  240. }
  241. static Gdk.Key GetGroupZeroKey (Gdk.Key mappedKey, Gdk.EventKey evt)
  242. {
  243. Gdk.Key ret;
  244. if (groupZeroMappings.TryGetValue (mappedKey, out ret))
  245. return ret;
  246. //LookupKey isn't implemented on Mac, so we have to use this workaround
  247. uint[] keyvals;
  248. Gdk.KeymapKey [] keys;
  249. keymap.GetEntriesForKeycode (evt.HardwareKeycode, out keys, out keyvals);
  250. //find the key that has the same level (so we preserve shift) but with group 0
  251. for (uint i = 0; i < keyvals.Length; i++)
  252. if (keyvals[i] == (uint)mappedKey)
  253. for (uint j = 0; j < keys.Length; j++)
  254. if (keys[j].Group == 0 && keys[j].Level == keys[i].Level)
  255. return groupZeroMappings[mappedKey] = ret = (Gdk.Key)keyvals[j];
  256. //failed, but avoid looking it up again
  257. return groupZeroMappings[mappedKey] = mappedKey;
  258. }
  259. private string FindKeyBindConflict(string key){
  260. string conflicted ="";
  261. foreach(KeyBindingSection kbs in curentBind){
  262. KeyBinding kb =kbs.KeyBinding.Find(x=>x.Key== key);
  263. if(kb != null)
  264. conflicted = conflicted+kb.Name+", ";
  265. }
  266. if(!string.IsNullOrEmpty(conflicted)){
  267. int i =conflicted.Length-3;
  268. conflicted = conflicted.Remove(i);
  269. }
  270. return conflicted;
  271. }
  272. static string ModifierToPartialAccel (Gdk.ModifierType mod, Gdk.Key key, out bool keyIsModifier)
  273. {
  274. //Console.WriteLine("PRESSED MOD: {0} ; KEY {1}",mod,key);
  275. string labelMod = String.Empty;
  276. string labelKey = String.Empty;
  277. if ((mod & Gdk.ModifierType.ControlMask) != 0)
  278. labelMod += "Control+";
  279. if ((mod & Gdk.ModifierType.Mod1Mask) != 0)
  280. labelMod += "Alt+";
  281. if ((mod & Gdk.ModifierType.ShiftMask) != 0)
  282. labelMod += "Shift+";
  283. if ((mod & Gdk.ModifierType.MetaMask) != 0)
  284. labelMod += "Command+";//labelMod += "Meta+";
  285. if ((mod & Gdk.ModifierType.SuperMask) != 0)
  286. labelMod += "Super+";
  287. //Console.WriteLine("labelMod-> {0}",labelMod);
  288. keyIsModifier = true;
  289. if (key.Equals (Gdk.Key.Control_L) || key.Equals (Gdk.Key.Control_R))
  290. labelKey += "Control+";
  291. else if (key.Equals (Gdk.Key.Alt_L) || key.Equals (Gdk.Key.Alt_R))
  292. labelKey += "Alt+";
  293. else if (key.Equals (Gdk.Key.Shift_L) || key.Equals (Gdk.Key.Shift_R))
  294. labelKey += "Shift+";
  295. else if (key.Equals (Gdk.Key.Meta_L) || key.Equals (Gdk.Key.Meta_R))
  296. labelKey += "Command+";//labelKey += "Meta+";
  297. else if (key.Equals (Gdk.Key.Super_L) || key.Equals (Gdk.Key.Super_L))
  298. labelKey += "Super+";
  299. else
  300. keyIsModifier = false;
  301. //Console.WriteLine("labelKey-> {0}",labelKey);
  302. if(labelMod.Contains(labelKey)){
  303. return labelMod;
  304. } else return labelMod+labelKey;
  305. }
  306. static string KeyToString (Gdk.Key key)
  307. {
  308. char c = (char) Gdk.Keyval.ToUnicode ((uint) key);
  309. if (c != 0) {
  310. if (c == ' ')
  311. return "Space";
  312. return Char.ToUpper (c).ToString ();
  313. }
  314. //HACK: Page_Down and Next are synonyms for the same enum value, but alphabetically, Next gets returned
  315. // first by enum ToString(). Similarly, Page_Up and Prior are synonyms, but Page_Up is returned. Hence the
  316. // default pairing is Next and Page_Up, which is confusingly inconsistent, so we fix this here.
  317. //
  318. //The same problem applies to some other common keys, so we ensure certain values are mapped
  319. // to the most common name.
  320. switch (key) {
  321. case Gdk.Key.Next:
  322. return "Page_Down";
  323. case Gdk.Key.L1:
  324. return "F11";
  325. case Gdk.Key.L2:
  326. return "F12";
  327. }
  328. return key.ToString ();
  329. }
  330. protected virtual void OnEntrAccelChanged (object sender, System.EventArgs e)
  331. {
  332. }
  333. protected void OnBtnAccelAply1Clicked (object sender, System.EventArgs e)
  334. {
  335. string active = cbKeyBinding.ActiveText;
  336. string file = System.IO.Path.Combine(MainClass.Paths.SettingDir, "keybinding");
  337. switch (active) {
  338. case WIN:{
  339. KeyBindings.CreateKeyBindingsWin(file);
  340. break;
  341. }
  342. case MACOSX:{
  343. KeyBindings.CreateKeyBindingsMac(file);
  344. break;
  345. }
  346. case JAVA:{
  347. KeyBindings.CreateKeyBindingsJava(file);
  348. break;
  349. }
  350. case VisualC:{
  351. KeyBindings.CreateKeyBindingsVisualC(file);
  352. break;
  353. }
  354. default:
  355. break;
  356. }
  357. keyBindFile = MainClass.KeyBinding;
  358. curentBind = MainClass.Tools.Clone<KeyBindingSection>(keyBindFile.KeyBindingSection);
  359. keybStore.Clear();
  360. foreach(KeyBindingSection kbs in curentBind){
  361. TreeIter tiParent = keybStore.AppendValues(kbs.Name,"","",(int) Pango.Weight.Bold,null);
  362. foreach(KeyBinding kb in kbs.KeyBinding){
  363. keybStore.AppendValues(tiParent,kb.Name,kb.Description,kb.Key,(int) Pango.Weight.Normal,kb);
  364. }
  365. }
  366. tvKeyBind.ExpandAll();
  367. tvKeyBind.ColumnsAutosize ();
  368. //MainClass.Settings.SaveSettings();
  369. }
  370. }
  371. }