You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

HotkeySettingsForm.cs 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using System.Windows.Forms;
  8. using Shadowsocks.Controller;
  9. using Shadowsocks.Model;
  10. using Shadowsocks.Properties;
  11. using Shadowsocks.Util;
  12. namespace Shadowsocks.View
  13. {
  14. public partial class HotkeySettingsForm : Form
  15. {
  16. private ShadowsocksController _controller;
  17. // this is a copy of configuration that we are working on
  18. private HotkeyConfig _modifiedConfig;
  19. private StringBuilder _sb = new StringBuilder();
  20. private IEnumerable<TextBox> _allTextBoxes;
  21. private Label _lb = null;
  22. private HotKeys.HotKeyCallBackHandler _callBack = null;
  23. public HotkeySettingsForm(ShadowsocksController controller)
  24. {
  25. InitializeComponent();
  26. UpdateTexts();
  27. this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
  28. _controller = controller;
  29. _controller.ConfigChanged += controller_ConfigChanged;
  30. LoadCurrentConfiguration();
  31. // get all textboxes belong to this form
  32. _allTextBoxes = HotKeys.GetChildControls<TextBox>(this.tableLayoutPanel1);
  33. if (!_allTextBoxes.Any()) throw new Exception("Cannot get all textboxes");
  34. }
  35. private void controller_ConfigChanged(object sender, EventArgs e)
  36. {
  37. LoadCurrentConfiguration();
  38. }
  39. private void LoadCurrentConfiguration()
  40. {
  41. _modifiedConfig = _controller.GetConfigurationCopy().hotkey;
  42. LoadConfiguration(_modifiedConfig);
  43. }
  44. private void LoadConfiguration(HotkeyConfig config)
  45. {
  46. SwitchSystemProxyTextBox.Text = config.SwitchSystemProxy;
  47. SwitchProxyModeTextBox.Text = config.SwitchSystemProxyMode;
  48. SwitchAllowLanTextBox.Text = config.SwitchAllowLan;
  49. ShowLogsTextBox.Text = config.ShowLogs;
  50. ServerMoveUpTextBox.Text = config.ServerMoveUp;
  51. ServerMoveDownTextBox.Text = config.ServerMoveDown;
  52. }
  53. private void UpdateTexts()
  54. {
  55. // I18N stuff
  56. SwitchSystemProxyLabel.Text = I18N.GetString("Switch system proxy");
  57. SwitchProxyModeLabel.Text = I18N.GetString("Switch system proxy mode");
  58. SwitchAllowLanLabel.Text = I18N.GetString("Switch share over LAN");
  59. ShowLogsLabel.Text = I18N.GetString("Show Logs");
  60. ServerMoveUpLabel.Text = I18N.GetString("Switch to prev server");
  61. ServerMoveDownLabel.Text = I18N.GetString("Switch to next server");
  62. btnOK.Text = I18N.GetString("OK");
  63. btnCancel.Text = I18N.GetString("Cancel");
  64. btnRegisterAll.Text = I18N.GetString("Reg All");
  65. this.Text = I18N.GetString("Edit Hotkeys...");
  66. }
  67. /// <summary>
  68. /// Capture hotkey - Press key
  69. /// </summary>
  70. private void HotkeyDown(object sender, KeyEventArgs e)
  71. {
  72. _sb.Length = 0;
  73. //Combination key only
  74. if (e.Modifiers != 0)
  75. {
  76. // XXX: Hotkey parsing depends on the sequence, more specifically, ModifierKeysConverter.
  77. // Windows key is reserved by operating system, we deny this key.
  78. if (e.Control)
  79. {
  80. _sb.Append("Ctrl+");
  81. }
  82. if (e.Alt)
  83. {
  84. _sb.Append("Alt+");
  85. }
  86. if (e.Shift)
  87. {
  88. _sb.Append("Shift+");
  89. }
  90. Keys keyvalue = (Keys) e.KeyValue;
  91. if ((keyvalue >= Keys.PageUp && keyvalue <= Keys.Down) ||
  92. (keyvalue >= Keys.A && keyvalue <= Keys.Z) ||
  93. (keyvalue >= Keys.F1 && keyvalue <= Keys.F12))
  94. {
  95. _sb.Append(e.KeyCode);
  96. }
  97. else if (keyvalue >= Keys.D0 && keyvalue <= Keys.D9)
  98. {
  99. _sb.Append('D').Append((char) e.KeyValue);
  100. }
  101. else if (keyvalue >= Keys.NumPad0 && keyvalue <= Keys.NumPad9)
  102. {
  103. _sb.Append("NumPad").Append((char) (e.KeyValue - 48));
  104. }
  105. }
  106. ((TextBox) sender).Text = _sb.ToString();
  107. }
  108. /// <summary>
  109. /// Capture hotkey - Release key
  110. /// </summary>
  111. private void HotkeyUp(object sender, KeyEventArgs e)
  112. {
  113. TextBox tb = sender as TextBox;
  114. string content = tb.Text.TrimEnd();
  115. if (content.Length >= 1 && content[content.Length - 1] == '+')
  116. {
  117. tb.Text = "";
  118. }
  119. }
  120. private void TextBox_TextChanged(object sender, EventArgs e)
  121. {
  122. TextBox tb = sender as TextBox;
  123. if (tb.Text == "")
  124. {
  125. // unreg
  126. UnregHotkey(tb);
  127. }
  128. }
  129. private void UnregHotkey(TextBox tb)
  130. {
  131. PrepareForHotkey(tb, out _callBack, out _lb);
  132. UnregPrevHotkey(_callBack);
  133. }
  134. private void CancelButton_Click(object sender, EventArgs e)
  135. {
  136. this.Close();
  137. }
  138. private void OKButton_Click(object sender, EventArgs e)
  139. {
  140. // try to register, notify to change settings if failed
  141. foreach (var tb in _allTextBoxes)
  142. {
  143. if (tb.Text.IsNullOrEmpty())
  144. {
  145. continue;
  146. }
  147. if (!TryRegHotkey(tb))
  148. {
  149. MessageBox.Show(I18N.GetString("Register hotkey failed"));
  150. return;
  151. }
  152. }
  153. // All check passed, saving
  154. SaveConfig();
  155. this.Close();
  156. }
  157. private void RegisterAllButton_Click(object sender, EventArgs e)
  158. {
  159. foreach (var tb in _allTextBoxes)
  160. {
  161. if (tb.Text.IsNullOrEmpty())
  162. {
  163. continue;
  164. }
  165. TryRegHotkey(tb);
  166. }
  167. }
  168. private bool TryRegHotkey(TextBox tb)
  169. {
  170. var hotkey = HotKeys.Str2HotKey(tb.Text);
  171. if (hotkey == null)
  172. {
  173. MessageBox.Show(string.Format(I18N.GetString("Cannot parse hotkey: {0}"), tb.Text));
  174. tb.Clear();
  175. return false;
  176. }
  177. PrepareForHotkey(tb, out _callBack, out _lb);
  178. UnregPrevHotkey(_callBack);
  179. // try to register keys
  180. // if already registered by other progs
  181. // notify to change
  182. // use the corresponding label color to indicate
  183. // reg result.
  184. // Green: not occupied by others and operation succeed
  185. // Yellow: already registered by other program and need action: disable by clear the content
  186. // or change to another one
  187. // Transparent without color: first run or empty config
  188. bool regResult = HotKeys.Regist(hotkey, _callBack);
  189. _lb.BackColor = regResult ? Color.Green : Color.Yellow;
  190. return regResult;
  191. }
  192. private static void UnregPrevHotkey(HotKeys.HotKeyCallBackHandler cb)
  193. {
  194. GlobalHotKey.HotKey prevHotKey;
  195. if (HotKeys.IsCallbackExists(cb, out prevHotKey))
  196. {
  197. // unregister previous one
  198. HotKeys.UnRegist(prevHotKey);
  199. }
  200. }
  201. private void SaveConfig()
  202. {
  203. _modifiedConfig.SwitchSystemProxy = SwitchSystemProxyTextBox.Text;
  204. _modifiedConfig.SwitchSystemProxyMode = SwitchProxyModeTextBox.Text;
  205. _modifiedConfig.SwitchAllowLan = SwitchAllowLanTextBox.Text;
  206. _modifiedConfig.ShowLogs = ShowLogsTextBox.Text;
  207. _modifiedConfig.ServerMoveUp = ServerMoveUpTextBox.Text;
  208. _modifiedConfig.ServerMoveDown = ServerMoveDownTextBox.Text;
  209. _controller.SaveHotkeyConfig(_modifiedConfig);
  210. }
  211. #region Callbacks
  212. private void SwitchSystemProxyCallback()
  213. {
  214. bool enabled = _controller.GetConfigurationCopy().enabled;
  215. _controller.ToggleEnable(!enabled);
  216. }
  217. private void SwitchProxyModeCallback()
  218. {
  219. var config = _controller.GetConfigurationCopy();
  220. if (config.enabled == false) return;
  221. var currStatus = config.global;
  222. _controller.ToggleGlobal(!currStatus);
  223. }
  224. private void SwitchAllowLanCallback()
  225. {
  226. var status = _controller.GetConfigurationCopy().shareOverLan;
  227. _controller.ToggleShareOverLAN(!status);
  228. }
  229. private void ShowLogsCallback()
  230. {
  231. // Get the current MenuViewController in this program via reflection
  232. FieldInfo fi = Assembly.GetExecutingAssembly().GetType("Shadowsocks.Program")
  233. .GetField("_viewController",
  234. BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.IgnoreCase);
  235. // To retrieve the value of a static field, pass null here
  236. var mvc = fi.GetValue(null) as MenuViewController;
  237. mvc.ShowLogForm_HotKey();
  238. }
  239. private void ServerMoveUpCallback()
  240. {
  241. int currIndex;
  242. int serverCount;
  243. GetCurrServerInfo(out currIndex, out serverCount);
  244. if (currIndex - 1 < 0)
  245. {
  246. // revert to last server
  247. currIndex = serverCount - 1;
  248. }
  249. else
  250. {
  251. currIndex -= 1;
  252. }
  253. _controller.SelectServerIndex(currIndex);
  254. }
  255. private void ServerMoveDownCallback()
  256. {
  257. int currIndex;
  258. int serverCount;
  259. GetCurrServerInfo(out currIndex, out serverCount);
  260. if (currIndex + 1 == serverCount)
  261. {
  262. // revert to first server
  263. currIndex = 0;
  264. }
  265. else
  266. {
  267. currIndex += 1;
  268. }
  269. _controller.SelectServerIndex(currIndex);
  270. }
  271. private void GetCurrServerInfo(out int currIndex, out int serverCount)
  272. {
  273. var currConfig = _controller.GetCurrentConfiguration();
  274. currIndex = currConfig.index;
  275. serverCount = currConfig.configs.Count;
  276. }
  277. #endregion
  278. #region Prepare hotkey
  279. /// <summary>
  280. /// Find correct callback and corresponding label
  281. /// </summary>
  282. /// <param name="tb"></param>
  283. /// <param name="cb"></param>
  284. /// <param name="lb"></param>
  285. private void PrepareForHotkey(TextBox tb, out HotKeys.HotKeyCallBackHandler cb, out Label lb)
  286. {
  287. /*
  288. * XXX: The labelName, TextBoxName and callbackName
  289. * must follow this rule to make use of reflection
  290. *
  291. * <BaseName><Control-Type-Name>
  292. */
  293. if (tb == null)
  294. throw new ArgumentNullException(nameof(tb));
  295. var pos = tb.Name.LastIndexOf("TextBox", StringComparison.OrdinalIgnoreCase);
  296. var rawName = tb.Name.Substring(0, pos);
  297. var labelName = rawName + "Label";
  298. var callbackName = rawName + "Callback";
  299. var callback = GetDelegateViaMethodName(this.GetType(), callbackName);
  300. if (callback == null)
  301. {
  302. throw new Exception($"{callbackName} not found");
  303. }
  304. cb = callback as HotKeys.HotKeyCallBackHandler;
  305. object label = GetFieldViaName(this.GetType(), labelName, this);
  306. if (label == null)
  307. {
  308. throw new Exception($"{labelName} not found");
  309. }
  310. lb = label as Label;
  311. }
  312. /// <summary>
  313. ///
  314. /// </summary>
  315. /// <param name="type">from which type</param>
  316. /// <param name="name">field name</param>
  317. /// <param name="obj">pass null if static field</param>
  318. /// <returns></returns>
  319. private static object GetFieldViaName(Type type, string name, object obj)
  320. {
  321. if (type == null) throw new ArgumentNullException(nameof(type));
  322. if (name.IsNullOrEmpty()) throw new ArgumentException(nameof(name));
  323. // In general, TextBoxes and Labels are private
  324. FieldInfo fi = type.GetField(name,
  325. BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Static);
  326. return fi == null ? null : fi.GetValue(obj);
  327. }
  328. /// <summary>
  329. /// Create hotkey callback handler delegate based on callback name
  330. /// </summary>
  331. /// <param name="type"></param>
  332. /// <param name="methodname"></param>
  333. /// <returns></returns>
  334. private Delegate GetDelegateViaMethodName(Type type, string methodname)
  335. {
  336. if (type == null) throw new ArgumentNullException(nameof(type));
  337. if (methodname.IsNullOrEmpty()) throw new ArgumentException(nameof(methodname));
  338. //HotkeySettingsForm form = new HotkeySettingsForm(_controller);
  339. Type delegateType = Type.GetType("Shadowsocks.Util.HotKeys").GetNestedType("HotKeyCallBackHandler");
  340. MethodInfo dynMethod = type.GetMethod(methodname,
  341. BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);
  342. return dynMethod == null ? null : Delegate.CreateDelegate(delegateType, this, dynMethod);
  343. }
  344. #endregion
  345. }
  346. }