From 99bdec334cbaeefcfa15694c899a112ca7a44cb6 Mon Sep 17 00:00:00 2001 From: Syrone Wong Date: Fri, 16 Sep 2016 23:42:16 -0500 Subject: [PATCH] Add global hotkey support (#724) Design: Use the plain string for both config and hotkey parsing and make use of KeyConverter, ModifierKeysConverter to make it more rubust. Find corresponding label (to display operation result) and callback (to do the real work) based on textbox name. In this case, they should be named as ``. ShowLogsTextBox, ShowLogsLabel, ShowLogsCallback. If this requirement doesn't meet, throw an exception at runtime. Logic: How to input keys: 1. put focus in the corresponding textbox 2. press the key combination you want to use 3. when you think it is ready, release all keys 4. the textbox shows your input How to change keys: 1. put focus in the corresponding textbox 2. press BackSpace to clear content 3. re-input new one How to deactivate: 1. clear content in the textbox 2. press OK button Meaning of label color: - Green: this combination is not occupied by other programs and register successfully - Yellow: this combination is occupied by other programs and you have to change to another one - Transparent without color: initial status Signed-off-by: Syrone Wong --- nuget.config | 12 +- .../Controller/ShadowsocksController.cs | 18 + shadowsocks-csharp/Data/cn.txt | 14 + shadowsocks-csharp/Data/zh_tw.txt | 14 + shadowsocks-csharp/Model/Configuration.cs | 5 +- shadowsocks-csharp/Model/HotKeyConfig.cs | 33 ++ shadowsocks-csharp/Program.cs | 2 + shadowsocks-csharp/Util/Hotkeys.cs | 174 +++++++++ shadowsocks-csharp/Util/Util.cs | 2 + shadowsocks-csharp/View/HotkeySettingsForm.cs | 406 +++++++++++++++++++++ .../View/HotkeySettingsForm.designer.cs | 354 ++++++++++++++++++ shadowsocks-csharp/View/HotkeySettingsForm.resx | 183 ++++++++++ shadowsocks-csharp/View/MenuViewController.cs | 34 ++ shadowsocks-csharp/packages.config | 1 + shadowsocks-csharp/shadowsocks-csharp.csproj | 15 + test/UnitTest.cs | 29 ++ test/test.csproj | 5 + 17 files changed, 1294 insertions(+), 7 deletions(-) create mode 100644 shadowsocks-csharp/Model/HotKeyConfig.cs create mode 100644 shadowsocks-csharp/Util/Hotkeys.cs create mode 100644 shadowsocks-csharp/View/HotkeySettingsForm.cs create mode 100644 shadowsocks-csharp/View/HotkeySettingsForm.designer.cs create mode 100644 shadowsocks-csharp/View/HotkeySettingsForm.resx diff --git a/nuget.config b/nuget.config index f5cdc0be..28c78837 100644 --- a/nuget.config +++ b/nuget.config @@ -1,6 +1,6 @@ - - - - - - + + + + + + diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 429061c0..d2df00fe 100644 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -374,6 +374,16 @@ namespace Shadowsocks.Controller } } + public void SaveHotkeyConfig(HotkeyConfig newConfig) + { + _config.hotkey = newConfig; + SaveConfig(_config); + if (ConfigChanged != null) + { + ConfigChanged(this, new EventArgs()); + } + } + public void UpdateLatency(Server server, TimeSpan latency) { if (_config.availabilityStatistics) @@ -565,6 +575,8 @@ namespace Shadowsocks.Controller File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); } + #region Memory Management + private void StartReleasingMemory() { _ramThread = new Thread(new ThreadStart(ReleaseMemory)); @@ -581,6 +593,10 @@ namespace Shadowsocks.Controller } } + #endregion + + #region Traffic Statistics + private void StartTrafficStatistics(int queueMaxSize) { traffic = new QueueLast(); @@ -615,5 +631,7 @@ namespace Shadowsocks.Controller } } + #endregion + } } diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index 4d30e865..001490ce 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -27,6 +27,7 @@ Verbose Logging=详细记录日志 Updates...=更新... Check for Updates...=检查更新 Check for Updates at Startup=启动时检查更新 +Edit Hotkeys...=编辑快捷键... About...=关于... Quit=退出 Edit Servers=编辑服务器 @@ -84,6 +85,17 @@ Edit Online PAC URL=编辑在线 PAC 网址 Edit Online PAC URL...=编辑在线 PAC 网址... Please input PAC Url=请输入 PAC 网址 +# HotkeySettings Form + +Switch system proxy=切换系统代理状态 +Switch to PAC mode=切换到PAC模式 +Switch to Global mode=切换到全局模式 +Switch share over LAN=切换局域网共享 +Show Logs=显示日志 +Switch to prev server=切换上个服务器 +Switch to next server=切换下个服务器 +Reg All=注册全部热键 + # Messages Shadowsocks Error: {0}=Shadowsocks 错误: {0} @@ -116,3 +128,5 @@ Unexpected error, shadowsocks will exit. Please report to=非预期错误,Shad Unsupported operating system, use Windows Vista at least.=不支持的操作系统版本,最低需求为Windows Vista。 Proxy request failed=代理请求失败 Proxy handshake failed=代理握手失败 +Register hotkey failed=注册热键失败 +Cannot parse hotkey: {0}=解析热键失败: {0} diff --git a/shadowsocks-csharp/Data/zh_tw.txt b/shadowsocks-csharp/Data/zh_tw.txt index 97d20708..337ce4f3 100644 --- a/shadowsocks-csharp/Data/zh_tw.txt +++ b/shadowsocks-csharp/Data/zh_tw.txt @@ -27,6 +27,7 @@ Verbose Logging=詳細記錄日誌 Updates...=更新... Check for Updates...=檢查更新 Check for Updates at Startup=啟動時檢查更新 +Edit Hotkeys...=編輯熱鍵... About...=關於... Quit=退出 Edit Servers=編輯伺服器 @@ -84,6 +85,17 @@ Edit Online PAC URL=編輯在線 PAC 網址 Edit Online PAC URL...=編輯在線 PAC 網址... Please input PAC Url=請輸入 PAC 網址 +# HotkeySettings Form + +Switch system proxy=切换系統代理狀態 +Switch to PAC mode=切换為PAC模式 +Switch to Global mode=切换為全局模式 +Switch share over LAN=切换局域網共享 +Show Logs=顯示日誌 +Switch to prev server=切换上個伺服器 +Switch to next server=切换下個伺服器 +Reg All=註冊全部熱鍵 + # Messages Shadowsocks Error: {0}=Shadowsocks 錯誤: {0} @@ -116,3 +128,5 @@ Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shad Unsupported operating system, use Windows Vista at least.=不支持的作業系統版本,最低需求為Windows Vista。 Proxy request failed=代理請求失敗 Proxy handshake failed=代理握手失敗 +Register hotkey failed=註冊熱鍵失敗 +Cannot parse hotkey: {0}=解析熱鍵失敗: {0} diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index c13d637f..78e63238 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -27,6 +27,7 @@ namespace Shadowsocks.Model public bool isVerboseLogging; public LogViewerConfig logViewer; public ProxyConfig proxy; + public HotkeyConfig hotkey; private static string CONFIG_FILE = "gui-config.json"; @@ -60,6 +61,8 @@ namespace Shadowsocks.Model config.logViewer = new LogViewerConfig(); if (config.proxy == null) config.proxy = new ProxyConfig(); + if (config.hotkey == null) + config.hotkey = new HotkeyConfig(); return config; } catch (Exception e) @@ -100,7 +103,7 @@ namespace Shadowsocks.Model } catch (IOException e) { - Console.Error.WriteLine(e); + Logging.LogUsefulException(e); } } diff --git a/shadowsocks-csharp/Model/HotKeyConfig.cs b/shadowsocks-csharp/Model/HotKeyConfig.cs new file mode 100644 index 00000000..2e1d122c --- /dev/null +++ b/shadowsocks-csharp/Model/HotKeyConfig.cs @@ -0,0 +1,33 @@ +using System; + +namespace Shadowsocks.Model +{ + /* + * Format: + * + + * + */ + + [Serializable] + public class HotkeyConfig + { + public string SwitchSystemProxy; + public string ChangeToPac; + public string ChangeToGlobal; + public string SwitchAllowLan; + public string ShowLogs; + public string ServerMoveUp; + public string ServerMoveDown; + + public HotkeyConfig() + { + SwitchSystemProxy = ""; + ChangeToPac = ""; + ChangeToGlobal = ""; + SwitchAllowLan = ""; + ShowLogs = ""; + ServerMoveUp = ""; + ServerMoveDown = ""; + } + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index ce85cd32..99828ff1 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -39,6 +39,7 @@ namespace Shadowsocks SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); + Application.ApplicationExit += (sender, args) => HotKeys.Destroy(); if (!mutex.WaitOne(0, false)) { @@ -65,6 +66,7 @@ namespace Shadowsocks #endif _controller = new ShadowsocksController(); _viewController = new MenuViewController(_controller); + HotKeys.Init(); _controller.Start(); Application.Run(); } diff --git a/shadowsocks-csharp/Util/Hotkeys.cs b/shadowsocks-csharp/Util/Hotkeys.cs new file mode 100644 index 00000000..f5d8e3d8 --- /dev/null +++ b/shadowsocks-csharp/Util/Hotkeys.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Windows.Forms; +using System.Windows.Input; +using GlobalHotKey; + +namespace Shadowsocks.Util +{ + public static class HotKeys + { + private static HotKeyManager _hotKeyManager; + + public delegate void HotKeyCallBackHandler(); + // map key and corresponding handler function + private static Dictionary _keymap = new Dictionary(); + + public static void Init() + { + _hotKeyManager = new HotKeyManager(); + _hotKeyManager.KeyPressed += HotKeyManagerPressed; + } + + public static void Destroy() => _hotKeyManager.Dispose(); + + private static void HotKeyManagerPressed(object sender, KeyPressedEventArgs e) + { + var hotkey = e.HotKey; + HotKeyCallBackHandler callback; + if (_keymap.TryGetValue(hotkey, out callback)) + callback(); + } + + public static bool IsHotkeyExists( HotKey hotKey ) + { + if (hotKey == null) throw new ArgumentNullException(nameof(hotKey)); + return _keymap.Any( v => v.Key.Equals( hotKey ) ); + } + + public static bool IsCallbackExists( HotKeyCallBackHandler cb, out HotKey hotkey) + { + if (cb == null) throw new ArgumentNullException(nameof(cb)); + try + { + var key = _keymap.First(x => x.Value == cb).Key; + hotkey = key; + return true; + } + catch (InvalidOperationException) + { + // not found + hotkey = null; + return false; + } + } + public static string HotKey2Str( HotKey key ) + { + if (key == null) throw new ArgumentNullException(nameof(key)); + return HotKey2Str( key.Key, key.Modifiers ); + } + + public static string HotKey2Str( Key key, ModifierKeys modifier ) + { + if (!Enum.IsDefined(typeof(Key), key)) + throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key)); + try + { + ModifierKeysConverter mkc = new ModifierKeysConverter(); + var keyStr = Enum.GetName(typeof(Key), key); + var modifierStr = mkc.ConvertToInvariantString(modifier); + + return $"{modifierStr}+{keyStr}"; + } + catch (NotSupportedException) + { + // converter exception + return null; + } + } + + public static HotKey Str2HotKey( string s ) { + try + { + if (s.IsNullOrEmpty()) return null; + int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase); + if (offset <= 0) return null; + string modifierStr = s.Substring(0, offset).Trim(); + string keyStr = s.Substring(offset + 1).Trim(); + + KeyConverter kc = new KeyConverter(); + ModifierKeysConverter mkc = new ModifierKeysConverter(); + Key key = (Key) kc.ConvertFrom(keyStr.ToUpper()); + ModifierKeys modifier = (ModifierKeys) mkc.ConvertFrom(modifierStr.ToUpper()); + + return new HotKey(key, modifier); + } + catch (NotSupportedException) + { + // converter exception + return null; + } + catch (NullReferenceException) + { + return null; + } + } + + public static bool Regist( HotKey key, HotKeyCallBackHandler callBack ) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + if (callBack == null) + throw new ArgumentNullException(nameof(callBack)); + try + { + _hotKeyManager.Register(key); + _keymap[key] = callBack; + return true; + } + catch (ArgumentException) + { + // already called this method with the specific hotkey + // return success silently + return true; + } + catch (Win32Exception) + { + // this hotkey already registered by other programs + // notify user to change key + return false; + } + } + + public static bool Regist(Key key, ModifierKeys modifiers, HotKeyCallBackHandler callBack) + { + if (!Enum.IsDefined(typeof(Key), key)) + throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key)); + try + { + var hotkey = _hotKeyManager.Register(key, modifiers); + _keymap[hotkey] = callBack; + return true; + } + catch (ArgumentException) + { + // already called this method with the specific hotkey + // return success silently + return true; + } + catch (Win32Exception) + { + // already registered by other programs + // notify user to change key + return false; + } + } + + public static void UnRegist(HotKey key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + _hotKeyManager.Unregister(key); + if(_keymap.ContainsKey(key)) + _keymap.Remove(key); + } + + public static IEnumerable GetChildControls(this Control control) where TControl : Control + { + var children = control.Controls.Count > 0 ? control.Controls.OfType() : Enumerable.Empty(); + return children.SelectMany(c => GetChildControls(c)).Concat(children); + } + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index b760d3ea..d59cfaf1 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; using Microsoft.Win32; diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.cs b/shadowsocks-csharp/View/HotkeySettingsForm.cs new file mode 100644 index 00000000..bdd2beb3 --- /dev/null +++ b/shadowsocks-csharp/View/HotkeySettingsForm.cs @@ -0,0 +1,406 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Windows.Forms; + +using Shadowsocks.Controller; +using Shadowsocks.Model; +using Shadowsocks.Properties; +using Shadowsocks.Util; + +namespace Shadowsocks.View +{ + public partial class HotkeySettingsForm : Form + { + private ShadowsocksController _controller; + + // this is a copy of configuration that we are working on + private HotkeyConfig _modifiedConfig; + + private StringBuilder _sb = new StringBuilder(); + + private IEnumerable _allTextBoxes; + + private static Label _lb = null; + private static HotKeys.HotKeyCallBackHandler _callBack = null; + + public HotkeySettingsForm(ShadowsocksController controller) + { + InitializeComponent(); + UpdateTexts(); + this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); + + _controller = controller; + _controller.ConfigChanged += controller_ConfigChanged; + + LoadCurrentConfiguration(); + + // get all textboxes belong to this form + _allTextBoxes = HotKeys.GetChildControls(this.tableLayoutPanel1); + if (!_allTextBoxes.Any()) throw new Exception("Cannot get all textboxes"); + } + + private void controller_ConfigChanged(object sender, EventArgs e) + { + LoadCurrentConfiguration(); + } + + private void LoadCurrentConfiguration() + { + _modifiedConfig = _controller.GetConfigurationCopy().hotkey; + LoadConfiguration(_modifiedConfig); + } + + private void LoadConfiguration(HotkeyConfig config) + { + SwitchSystemProxyTextBox.Text = config.SwitchSystemProxy; + ChangeToPacTextBox.Text = config.ChangeToPac; + ChangeToGlobalTextBox.Text = config.ChangeToGlobal; + SwitchAllowLanTextBox.Text = config.SwitchAllowLan; + ShowLogsTextBox.Text = config.ShowLogs; + ServerMoveUpTextBox.Text = config.ServerMoveUp; + ServerMoveDownTextBox.Text = config.ServerMoveDown; + } + + private void UpdateTexts() + { + // I18N stuff + SwitchSystemProxyLabel.Text = I18N.GetString("Switch system proxy"); + ChangeToPacLabel.Text = I18N.GetString("Switch to PAC mode"); + ChangeToGlobalLabel.Text = I18N.GetString("Switch to Global mode"); + SwitchAllowLanLabel.Text = I18N.GetString("Switch share over LAN"); + ShowLogsLabel.Text = I18N.GetString("Show Logs"); + ServerMoveUpLabel.Text = I18N.GetString("Switch to prev server"); + ServerMoveDownLabel.Text = I18N.GetString("Switch to next server"); + btnOK.Text = I18N.GetString("OK"); + btnCancel.Text = I18N.GetString("Cancel"); + btnRegisterAll.Text = I18N.GetString("Reg All"); + this.Text = I18N.GetString("Edit Hotkeys..."); + } + + /// + /// Capture hotkey - Press key + /// + private void HotkeyDown(object sender, KeyEventArgs e) + { + _sb.Length = 0; + //Combination key only + if (e.Modifiers != 0) + { + // XXX: Hotkey parsing depends on the sequence, more specifically, ModifierKeysConverter. + // Windows key is reserved by operating system, we deny this key. + if (e.Control) + { + _sb.Append("Ctrl+"); + } + if (e.Alt) + { + _sb.Append("Alt+"); + } + if (e.Shift) + { + _sb.Append("Shift+"); + } + + Keys keyvalue = (Keys) e.KeyValue; + if ((keyvalue >= Keys.PageUp && keyvalue <= Keys.Down) || + (keyvalue >= Keys.A && keyvalue <= Keys.Z) || + (keyvalue >= Keys.F1 && keyvalue <= Keys.F12)) + { + _sb.Append(e.KeyCode); + } + else if (keyvalue >= Keys.D0 && keyvalue <= Keys.D9) + { + _sb.Append('D').Append((char) e.KeyValue); + } + else if (keyvalue >= Keys.NumPad0 && keyvalue <= Keys.NumPad9) + { + _sb.Append("NumPad").Append((char) (e.KeyValue - 48)); + } + } + ((TextBox) sender).Text = _sb.ToString(); + } + + /// + /// Capture hotkey - Release key + /// + private void HotkeyUp(object sender, KeyEventArgs e) + { + TextBox tb = sender as TextBox; + string content = tb.Text.TrimEnd(); + if (content.Length >= 1 && content[content.Length - 1] == '+') + { + tb.Text = ""; + } + } + + private void TextBox_TextChanged(object sender, EventArgs e) + { + TextBox tb = sender as TextBox; + + if (tb.Text == "") + { + // unreg + UnregHotkey(tb); + } + } + + private void UnregHotkey(TextBox tb) + { + + PrepareForHotkey(tb, out _callBack, out _lb); + + UnregPrevHotkey(_callBack); + } + + private void CancelButton_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void OKButton_Click(object sender, EventArgs e) + { + // try to register, notify to change settings if failed + foreach (var tb in _allTextBoxes) + { + if (tb.Text.IsNullOrEmpty()) + { + continue; + } + if (!TryRegHotkey(tb)) + { + MessageBox.Show(I18N.GetString("Register hotkey failed")); + return; + } + } + + // All check passed, saving + SaveConfig(); + this.Close(); + } + + private void RegisterAllButton_Click(object sender, EventArgs e) + { + foreach (var tb in _allTextBoxes) + { + if (tb.Text.IsNullOrEmpty()) + { + continue; + } + TryRegHotkey(tb); + } + } + + private bool TryRegHotkey(TextBox tb) + { + var hotkey = HotKeys.Str2HotKey(tb.Text); + if (hotkey == null) + { + MessageBox.Show(string.Format(I18N.GetString("Cannot parse hotkey: {0}"), tb.Text)); + tb.Clear(); + return false; + } + + PrepareForHotkey(tb, out _callBack, out _lb); + + UnregPrevHotkey(_callBack); + + // try to register keys + // if already registered by other progs + // notify to change + + // use the corresponding label color to indicate + // reg result. + // Green: not occupied by others and operation succeed + // Yellow: already registered by other program and need action: disable by clear the content + // or change to another one + // Transparent without color: first run or empty config + + bool regResult = HotKeys.Regist(hotkey, _callBack); + _lb.BackColor = regResult ? Color.Green : Color.Yellow; + return regResult; + } + + private static void UnregPrevHotkey(HotKeys.HotKeyCallBackHandler cb) + { + GlobalHotKey.HotKey prevHotKey; + if (HotKeys.IsCallbackExists(cb, out prevHotKey)) + { + // unregister previous one + HotKeys.UnRegist(prevHotKey); + } + } + + private void SaveConfig() + { + _modifiedConfig.SwitchSystemProxy = SwitchSystemProxyTextBox.Text; + _modifiedConfig.ChangeToPac = ChangeToPacTextBox.Text; + _modifiedConfig.ChangeToGlobal = ChangeToGlobalTextBox.Text; + _modifiedConfig.SwitchAllowLan = SwitchAllowLanTextBox.Text; + _modifiedConfig.ShowLogs = ShowLogsTextBox.Text; + _modifiedConfig.ServerMoveUp = ServerMoveUpTextBox.Text; + _modifiedConfig.ServerMoveDown = ServerMoveDownTextBox.Text; + _controller.SaveHotkeyConfig(_modifiedConfig); + } + + #region Callbacks + + private void SwitchSystemProxyCallback() + { + bool enabled = _controller.GetConfigurationCopy().enabled; + _controller.ToggleEnable(!enabled); + } + + private void ChangeToPacCallback() + { + bool enabled = _controller.GetConfigurationCopy().enabled; + if (enabled == false) return; + _controller.ToggleGlobal(false); + } + + private void ChangeToGlobalCallback() + { + bool enabled = _controller.GetConfigurationCopy().enabled; + if (enabled == false) return; + _controller.ToggleGlobal(true); + } + + private void SwitchAllowLanCallback() + { + var status = _controller.GetConfigurationCopy().shareOverLan; + _controller.ToggleShareOverLAN(!status); + } + + private void ShowLogsCallback() + { + // Get the current MenuViewController in this program via reflection + FieldInfo fi = Assembly.GetExecutingAssembly().GetType("Shadowsocks.Program") + .GetField("_viewController", + BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.IgnoreCase); + // To retrieve the value of a static field, pass null here + var mvc = fi.GetValue(null) as MenuViewController; + mvc.ShowLogForm_HotKey(); + } + + private void ServerMoveUpCallback() + { + int currIndex; + int serverCount; + GetCurrServerInfo(out currIndex, out serverCount); + if (currIndex - 1 < 0) + { + // revert to last server + currIndex = serverCount - 1; + } + else + { + currIndex -= 1; + } + _controller.SelectServerIndex(currIndex); + } + + private void ServerMoveDownCallback() + { + int currIndex; + int serverCount; + GetCurrServerInfo(out currIndex, out serverCount); + if (currIndex + 1 == serverCount) + { + // revert to first server + currIndex = 0; + } + else + { + currIndex += 1; + } + _controller.SelectServerIndex(currIndex); + } + + private void GetCurrServerInfo(out int currIndex, out int serverCount) + { + var currConfig = _controller.GetCurrentConfiguration(); + currIndex = currConfig.index; + serverCount = currConfig.configs.Count; + } + + #endregion + + #region Prepare hotkey + + /// + /// Find correct callback and corresponding label + /// + /// + /// + /// + private void PrepareForHotkey(TextBox tb, out HotKeys.HotKeyCallBackHandler cb, out Label lb) + { + /* + * XXX: The labelName, TextBoxName and callbackName + * must follow this rule to make use of reflection + * + * + */ + if (tb == null) + throw new ArgumentNullException(nameof(tb)); + + var pos = tb.Name.LastIndexOf("TextBox", StringComparison.OrdinalIgnoreCase); + var rawName = tb.Name.Substring(0, pos); + var labelName = rawName + "Label"; + var callbackName = rawName + "Callback"; + + var callback = GetDelegateViaMethodName(this.GetType(), callbackName); + if (callback == null) + { + throw new Exception($"{callbackName} not found"); + } + cb = callback as HotKeys.HotKeyCallBackHandler; + + object label = GetFieldViaName(this.GetType(), labelName, this); + if (label == null) + { + throw new Exception($"{labelName} not found"); + } + lb = label as Label; + } + + /// + /// + /// + /// from which type + /// field name + /// pass null if static field + /// + private static object GetFieldViaName(Type type, string name, object obj) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + if (name.IsNullOrEmpty()) throw new ArgumentException(nameof(name)); + // In general, TextBoxes and Labels are private + FieldInfo fi = type.GetField(name, + BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Static); + return fi == null ? null : fi.GetValue(obj); + } + + /// + /// Create hotkey callback handler delegate based on callback name + /// + /// + /// + /// + private Delegate GetDelegateViaMethodName(Type type, string methodname) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + if (methodname.IsNullOrEmpty()) throw new ArgumentException(nameof(methodname)); + //HotkeySettingsForm form = new HotkeySettingsForm(_controller); + Type delegateType = Type.GetType("Shadowsocks.Util.HotKeys").GetNestedType("HotKeyCallBackHandler"); + MethodInfo dynMethod = type.GetMethod(methodname, + BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase); + return dynMethod == null ? null : Delegate.CreateDelegate(delegateType, this, dynMethod); + } + + #endregion + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs b/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs new file mode 100644 index 00000000..5367fc88 --- /dev/null +++ b/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs @@ -0,0 +1,354 @@ +namespace Shadowsocks.View +{ + partial class HotkeySettingsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnRegisterAll = new System.Windows.Forms.Button(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.SwitchSystemProxyLabel = new System.Windows.Forms.Label(); + this.ChangeToPacLabel = new System.Windows.Forms.Label(); + this.ChangeToGlobalLabel = new System.Windows.Forms.Label(); + this.SwitchAllowLanLabel = new System.Windows.Forms.Label(); + this.ShowLogsLabel = new System.Windows.Forms.Label(); + this.ServerMoveUpLabel = new System.Windows.Forms.Label(); + this.ServerMoveDownLabel = new System.Windows.Forms.Label(); + this.SwitchSystemProxyTextBox = new System.Windows.Forms.TextBox(); + this.ChangeToPacTextBox = new System.Windows.Forms.TextBox(); + this.ChangeToGlobalTextBox = new System.Windows.Forms.TextBox(); + this.SwitchAllowLanTextBox = new System.Windows.Forms.TextBox(); + this.ShowLogsTextBox = new System.Windows.Forms.TextBox(); + this.ServerMoveUpTextBox = new System.Windows.Forms.TextBox(); + this.ServerMoveDownTextBox = new System.Windows.Forms.TextBox(); + flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + flowLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // flowLayoutPanel1 + // + this.tableLayoutPanel1.SetColumnSpan(flowLayoutPanel1, 2); + flowLayoutPanel1.Controls.Add(this.btnOK); + flowLayoutPanel1.Controls.Add(this.btnCancel); + flowLayoutPanel1.Controls.Add(this.btnRegisterAll); + flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.BottomUp; + flowLayoutPanel1.Location = new System.Drawing.Point(0, 227); + flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + flowLayoutPanel1.Name = "flowLayoutPanel1"; + flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 0, 16, 0); + flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes; + flowLayoutPanel1.Size = new System.Drawing.Size(475, 44); + flowLayoutPanel1.TabIndex = 6; + // + // btnOK + // + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Location = new System.Drawing.Point(381, 10); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(75, 31); + this.btnOK.TabIndex = 0; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.OKButton_Click); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(300, 10); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 31); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.CancelButton_Click); + // + // btnRegisterAll + // + this.btnRegisterAll.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnRegisterAll.Location = new System.Drawing.Point(219, 10); + this.btnRegisterAll.Name = "btnRegisterAll"; + this.btnRegisterAll.Size = new System.Drawing.Size(75, 31); + this.btnRegisterAll.TabIndex = 2; + this.btnRegisterAll.Text = "Reg All"; + this.btnRegisterAll.UseVisualStyleBackColor = true; + this.btnRegisterAll.Click += new System.EventHandler(this.RegisterAllButton_Click); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyLabel, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.ChangeToPacLabel, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.ChangeToGlobalLabel, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanLabel, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.ShowLogsLabel, 0, 4); + this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpLabel, 0, 5); + this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownLabel, 0, 6); + this.tableLayoutPanel1.Controls.Add(flowLayoutPanel1, 0, 7); + this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyTextBox, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.ChangeToPacTextBox, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.ChangeToGlobalTextBox, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanTextBox, 1, 3); + this.tableLayoutPanel1.Controls.Add(this.ShowLogsTextBox, 1, 4); + this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpTextBox, 1, 5); + this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownTextBox, 1, 6); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 8; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.77778F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.38889F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(475, 271); + this.tableLayoutPanel1.TabIndex = 0; + // + // SwitchSystemProxyLabel + // + this.SwitchSystemProxyLabel.AutoSize = true; + this.SwitchSystemProxyLabel.Dock = System.Windows.Forms.DockStyle.Right; + this.SwitchSystemProxyLabel.Location = new System.Drawing.Point(25, 0); + this.SwitchSystemProxyLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0); + this.SwitchSystemProxyLabel.Name = "SwitchSystemProxyLabel"; + this.SwitchSystemProxyLabel.Size = new System.Drawing.Size(147, 32); + this.SwitchSystemProxyLabel.TabIndex = 0; + this.SwitchSystemProxyLabel.Text = "Enable System Proxy"; + this.SwitchSystemProxyLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // ChangeToPacLabel + // + this.ChangeToPacLabel.AutoSize = true; + this.ChangeToPacLabel.Dock = System.Windows.Forms.DockStyle.Right; + this.ChangeToPacLabel.Location = new System.Drawing.Point(135, 32); + this.ChangeToPacLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0); + this.ChangeToPacLabel.Name = "ChangeToPacLabel"; + this.ChangeToPacLabel.Size = new System.Drawing.Size(37, 32); + this.ChangeToPacLabel.TabIndex = 1; + this.ChangeToPacLabel.Text = "PAC"; + this.ChangeToPacLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // ChangeToGlobalLabel + // + this.ChangeToGlobalLabel.AutoSize = true; + this.ChangeToGlobalLabel.Dock = System.Windows.Forms.DockStyle.Right; + this.ChangeToGlobalLabel.Location = new System.Drawing.Point(119, 64); + this.ChangeToGlobalLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0); + this.ChangeToGlobalLabel.Name = "ChangeToGlobalLabel"; + this.ChangeToGlobalLabel.Size = new System.Drawing.Size(53, 32); + this.ChangeToGlobalLabel.TabIndex = 2; + this.ChangeToGlobalLabel.Text = "Global"; + this.ChangeToGlobalLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // SwitchAllowLanLabel + // + this.SwitchAllowLanLabel.AutoSize = true; + this.SwitchAllowLanLabel.Dock = System.Windows.Forms.DockStyle.Right; + this.SwitchAllowLanLabel.Location = new System.Drawing.Point(8, 96); + this.SwitchAllowLanLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0); + this.SwitchAllowLanLabel.Name = "SwitchAllowLanLabel"; + this.SwitchAllowLanLabel.Size = new System.Drawing.Size(164, 32); + this.SwitchAllowLanLabel.TabIndex = 3; + this.SwitchAllowLanLabel.Text = "Allow Clients from LAN"; + this.SwitchAllowLanLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // ShowLogsLabel + // + this.ShowLogsLabel.AutoSize = true; + this.ShowLogsLabel.Dock = System.Windows.Forms.DockStyle.Right; + this.ShowLogsLabel.Location = new System.Drawing.Point(82, 128); + this.ShowLogsLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0); + this.ShowLogsLabel.Name = "ShowLogsLabel"; + this.ShowLogsLabel.Size = new System.Drawing.Size(90, 32); + this.ShowLogsLabel.TabIndex = 4; + this.ShowLogsLabel.Text = "Show Logs..."; + this.ShowLogsLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // ServerMoveUpLabel + // + this.ServerMoveUpLabel.AutoSize = true; + this.ServerMoveUpLabel.Dock = System.Windows.Forms.DockStyle.Right; + this.ServerMoveUpLabel.Location = new System.Drawing.Point(103, 160); + this.ServerMoveUpLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0); + this.ServerMoveUpLabel.Name = "ServerMoveUpLabel"; + this.ServerMoveUpLabel.Size = new System.Drawing.Size(69, 34); + this.ServerMoveUpLabel.TabIndex = 4; + this.ServerMoveUpLabel.Text = "Move up"; + this.ServerMoveUpLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // ServerMoveDownLabel + // + this.ServerMoveDownLabel.AutoSize = true; + this.ServerMoveDownLabel.Dock = System.Windows.Forms.DockStyle.Right; + this.ServerMoveDownLabel.Location = new System.Drawing.Point(81, 194); + this.ServerMoveDownLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0); + this.ServerMoveDownLabel.Name = "ServerMoveDownLabel"; + this.ServerMoveDownLabel.Size = new System.Drawing.Size(91, 33); + this.ServerMoveDownLabel.TabIndex = 4; + this.ServerMoveDownLabel.Text = "Move Down"; + this.ServerMoveDownLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // SwitchSystemProxyTextBox + // + this.SwitchSystemProxyTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.SwitchSystemProxyTextBox.Location = new System.Drawing.Point(183, 3); + this.SwitchSystemProxyTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3); + this.SwitchSystemProxyTextBox.Name = "SwitchSystemProxyTextBox"; + this.SwitchSystemProxyTextBox.ReadOnly = true; + this.SwitchSystemProxyTextBox.Size = new System.Drawing.Size(276, 25); + this.SwitchSystemProxyTextBox.TabIndex = 7; + this.SwitchSystemProxyTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged); + this.SwitchSystemProxyTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); + this.SwitchSystemProxyTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); + // + // ChangeToPacTextBox + // + this.ChangeToPacTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ChangeToPacTextBox.Location = new System.Drawing.Point(183, 35); + this.ChangeToPacTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3); + this.ChangeToPacTextBox.Name = "ChangeToPacTextBox"; + this.ChangeToPacTextBox.ReadOnly = true; + this.ChangeToPacTextBox.Size = new System.Drawing.Size(276, 25); + this.ChangeToPacTextBox.TabIndex = 8; + this.ChangeToPacTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged); + this.ChangeToPacTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); + this.ChangeToPacTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); + // + // ChangeToGlobalTextBox + // + this.ChangeToGlobalTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ChangeToGlobalTextBox.Location = new System.Drawing.Point(183, 67); + this.ChangeToGlobalTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3); + this.ChangeToGlobalTextBox.Name = "ChangeToGlobalTextBox"; + this.ChangeToGlobalTextBox.ReadOnly = true; + this.ChangeToGlobalTextBox.Size = new System.Drawing.Size(276, 25); + this.ChangeToGlobalTextBox.TabIndex = 9; + this.ChangeToGlobalTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged); + this.ChangeToGlobalTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); + this.ChangeToGlobalTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); + // + // SwitchAllowLanTextBox + // + this.SwitchAllowLanTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.SwitchAllowLanTextBox.Location = new System.Drawing.Point(183, 99); + this.SwitchAllowLanTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3); + this.SwitchAllowLanTextBox.Name = "SwitchAllowLanTextBox"; + this.SwitchAllowLanTextBox.ReadOnly = true; + this.SwitchAllowLanTextBox.Size = new System.Drawing.Size(276, 25); + this.SwitchAllowLanTextBox.TabIndex = 10; + this.SwitchAllowLanTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged); + this.SwitchAllowLanTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); + this.SwitchAllowLanTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); + // + // ShowLogsTextBox + // + this.ShowLogsTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ShowLogsTextBox.Location = new System.Drawing.Point(183, 131); + this.ShowLogsTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3); + this.ShowLogsTextBox.Name = "ShowLogsTextBox"; + this.ShowLogsTextBox.ReadOnly = true; + this.ShowLogsTextBox.Size = new System.Drawing.Size(276, 25); + this.ShowLogsTextBox.TabIndex = 11; + this.ShowLogsTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged); + this.ShowLogsTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); + this.ShowLogsTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); + // + // ServerMoveUpTextBox + // + this.ServerMoveUpTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ServerMoveUpTextBox.Location = new System.Drawing.Point(183, 163); + this.ServerMoveUpTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3); + this.ServerMoveUpTextBox.Name = "ServerMoveUpTextBox"; + this.ServerMoveUpTextBox.ReadOnly = true; + this.ServerMoveUpTextBox.Size = new System.Drawing.Size(276, 25); + this.ServerMoveUpTextBox.TabIndex = 12; + this.ServerMoveUpTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged); + this.ServerMoveUpTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); + this.ServerMoveUpTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); + // + // ServerMoveDownTextBox + // + this.ServerMoveDownTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ServerMoveDownTextBox.Location = new System.Drawing.Point(183, 197); + this.ServerMoveDownTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3); + this.ServerMoveDownTextBox.Name = "ServerMoveDownTextBox"; + this.ServerMoveDownTextBox.ReadOnly = true; + this.ServerMoveDownTextBox.Size = new System.Drawing.Size(276, 25); + this.ServerMoveDownTextBox.TabIndex = 13; + this.ServerMoveDownTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged); + this.ServerMoveDownTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); + this.ServerMoveDownTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); + // + // HotkeySettingsForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 19F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(475, 271); + this.Controls.Add(this.tableLayoutPanel1); + this.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "HotkeySettingsForm"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Edit Hotkeys..."; + flowLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.Label SwitchSystemProxyLabel; + private System.Windows.Forms.Label ChangeToPacLabel; + private System.Windows.Forms.Label ChangeToGlobalLabel; + private System.Windows.Forms.Label SwitchAllowLanLabel; + private System.Windows.Forms.Label ShowLogsLabel; + private System.Windows.Forms.Label ServerMoveUpLabel; + private System.Windows.Forms.Label ServerMoveDownLabel; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.TextBox ShowLogsTextBox; + private System.Windows.Forms.TextBox SwitchAllowLanTextBox; + private System.Windows.Forms.TextBox ChangeToGlobalTextBox; + private System.Windows.Forms.TextBox ChangeToPacTextBox; + private System.Windows.Forms.TextBox SwitchSystemProxyTextBox; + private System.Windows.Forms.TextBox ServerMoveUpTextBox; + private System.Windows.Forms.TextBox ServerMoveDownTextBox; + private System.Windows.Forms.Button btnRegisterAll; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.resx b/shadowsocks-csharp/View/HotkeySettingsForm.resx new file mode 100644 index 00000000..f5b19983 --- /dev/null +++ b/shadowsocks-csharp/View/HotkeySettingsForm.resx @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 115b0c79..4aa270b1 100644 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -49,10 +49,12 @@ namespace Shadowsocks.View private MenuItem editOnlinePACItem; private MenuItem autoCheckUpdatesToggleItem; private MenuItem proxyItem; + private MenuItem hotKeyItem; private MenuItem VerboseLoggingToggleItem; private ConfigForm configForm; private ProxyForm proxyForm; private LogForm logForm; + private HotkeySettingsForm hotkeySettingsForm; private string _urlToOpen; public MenuViewController(ShadowsocksController controller) @@ -276,6 +278,7 @@ namespace Shadowsocks.View new MenuItem("-"), CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ), + this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)), CreateMenuGroup("Updates...", new MenuItem[] { CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)), new MenuItem("-"), @@ -466,6 +469,21 @@ namespace Shadowsocks.View } } + private void ShowHotKeySettingsForm() + { + if (hotkeySettingsForm != null) + { + hotkeySettingsForm.Activate(); + } + else + { + hotkeySettingsForm = new HotkeySettingsForm(controller); + hotkeySettingsForm.Show(); + hotkeySettingsForm.Activate(); + hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed; + } + } + private void ShowLogForm() { if (logForm != null) @@ -505,6 +523,12 @@ namespace Shadowsocks.View Utils.ReleaseMemory(true); } + void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e) + { + hotkeySettingsForm = null; + Utils.ReleaseMemory(true); + } + private void Config_Click(object sender, EventArgs e) { ShowConfigForm(); @@ -811,9 +835,19 @@ namespace Shadowsocks.View ShowProxyForm(); } + private void hotKeyItem_Click(object sender, EventArgs e) + { + ShowHotKeySettingsForm(); + } + private void ShowLogItem_Click(object sender, EventArgs e) { ShowLogForm(); } + + public void ShowLogForm_HotKey() + { + ShowLogForm(); + } } } diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index ea2a7bce..8b1d7274 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index c8c47596..1e7a5392 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -65,6 +65,10 @@ app.manifest + + 3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll + True + @@ -139,6 +143,7 @@ + @@ -178,6 +183,7 @@ + @@ -200,6 +206,12 @@ CalculationControl.cs + + Form + + + HotkeySettingsForm.cs + Form @@ -240,6 +252,9 @@ CalculationControl.cs + + HotkeySettingsForm.cs + LogForm.cs diff --git a/test/UnitTest.cs b/test/UnitTest.cs index 6fa4f21d..6364dba2 100755 --- a/test/UnitTest.cs +++ b/test/UnitTest.cs @@ -2,6 +2,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Shadowsocks.Controller; using Shadowsocks.Encryption; +using Shadowsocks.Util; +using GlobalHotKey; +using System.Windows.Input; using System.Threading; using System.Collections.Generic; @@ -22,6 +25,32 @@ namespace test Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0); } + [ TestMethod ] + public void TestHotKey2Str() { + Assert.AreEqual( "Ctrl+A", HotKeys.HotKey2Str( Key.A, ModifierKeys.Control ) ); + Assert.AreEqual( "Ctrl+Alt+D2", HotKeys.HotKey2Str( Key.D2, (ModifierKeys.Alt | ModifierKeys.Control) ) ); + Assert.AreEqual("Ctrl+Alt+Shift+NumPad7", HotKeys.HotKey2Str(Key.NumPad7, (ModifierKeys.Alt|ModifierKeys.Control|ModifierKeys.Shift))); + Assert.AreEqual( "Ctrl+Alt+Shift+F6", HotKeys.HotKey2Str( Key.F6, (ModifierKeys.Alt|ModifierKeys.Control|ModifierKeys.Shift))); + Assert.AreNotEqual("Ctrl+Shift+Alt+F6", HotKeys.HotKey2Str(Key.F6, (ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Shift))); + } + + [TestMethod] + public void TestStr2HotKey() + { + Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+A").Equals(new HotKey(Key.A, ModifierKeys.Control))); + Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Alt+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt)))); + Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Shift+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Shift)))); + Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Alt+Shift+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt| ModifierKeys.Shift)))); + HotKey testKey0 = HotKeys.Str2HotKey("Ctrl+Alt+Shift+A"); + Assert.IsTrue(testKey0 != null && testKey0.Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); + HotKey testKey1 = HotKeys.Str2HotKey("Ctrl+Alt+Shift+F2"); + Assert.IsTrue(testKey1 != null && testKey1.Equals(new HotKey(Key.F2, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); + HotKey testKey2 = HotKeys.Str2HotKey("Ctrl+Shift+Alt+D7"); + Assert.IsTrue(testKey2 != null && testKey2.Equals(new HotKey(Key.D7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); + HotKey testKey3 = HotKeys.Str2HotKey("Ctrl+Shift+Alt+NumPad7"); + Assert.IsTrue(testKey3 != null && testKey3.Equals(new HotKey(Key.NumPad7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); + } + [TestMethod] public void TestMD5() { diff --git a/test/test.csproj b/test/test.csproj index d1c3fa2e..2d261cfb 100755 --- a/test/test.csproj +++ b/test/test.csproj @@ -35,10 +35,15 @@ + + ..\shadowsocks-csharp\3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll + True + +