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 `<Feature-Name><Control-Name>`. 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 <wong.syrone@gmail.com>tags/3.3.1
@@ -1,6 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<configuration> | |||
<config> | |||
<add key="repositoryPath" value="shadowsocks-csharp\3rd" /> | |||
</config> | |||
</configuration> | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<configuration> | |||
<config> | |||
<add key="repositoryPath" value="shadowsocks-csharp\3rd" /> | |||
</config> | |||
</configuration> |
@@ -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<TrafficPerSecond>(); | |||
@@ -615,5 +631,7 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
#endregion | |||
} | |||
} |
@@ -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} |
@@ -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} |
@@ -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); | |||
} | |||
} | |||
@@ -0,0 +1,33 @@ | |||
using System; | |||
namespace Shadowsocks.Model | |||
{ | |||
/* | |||
* Format: | |||
* <modifiers-combination>+<key> | |||
* | |||
*/ | |||
[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 = ""; | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
@@ -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<HotKey, HotKeyCallBackHandler> _keymap = new Dictionary<HotKey, HotKeyCallBackHandler>(); | |||
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<TControl> GetChildControls<TControl>(this Control control) where TControl : Control | |||
{ | |||
var children = control.Controls.Count > 0 ? control.Controls.OfType<TControl>() : Enumerable.Empty<TControl>(); | |||
return children.SelectMany(c => GetChildControls<TControl>(c)).Concat(children); | |||
} | |||
} | |||
} |
@@ -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; | |||
@@ -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<TextBox> _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<TextBox>(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..."); | |||
} | |||
/// <summary> | |||
/// Capture hotkey - Press key | |||
/// </summary> | |||
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(); | |||
} | |||
/// <summary> | |||
/// Capture hotkey - Release key | |||
/// </summary> | |||
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 | |||
/// <summary> | |||
/// Find correct callback and corresponding label | |||
/// </summary> | |||
/// <param name="tb"></param> | |||
/// <param name="cb"></param> | |||
/// <param name="lb"></param> | |||
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 | |||
* | |||
* <BaseName><Control-Type-Name> | |||
*/ | |||
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; | |||
} | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="type">from which type</param> | |||
/// <param name="name">field name</param> | |||
/// <param name="obj">pass null if static field</param> | |||
/// <returns></returns> | |||
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); | |||
} | |||
/// <summary> | |||
/// Create hotkey callback handler delegate based on callback name | |||
/// </summary> | |||
/// <param name="type"></param> | |||
/// <param name="methodname"></param> | |||
/// <returns></returns> | |||
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 | |||
} | |||
} |
@@ -0,0 +1,354 @@ | |||
namespace Shadowsocks.View | |||
{ | |||
partial class HotkeySettingsForm | |||
{ | |||
/// <summary> | |||
/// Required designer variable. | |||
/// </summary> | |||
private System.ComponentModel.IContainer components = null; | |||
/// <summary> | |||
/// Clean up any resources being used. | |||
/// </summary> | |||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> | |||
protected override void Dispose(bool disposing) | |||
{ | |||
if (disposing && (components != null)) | |||
{ | |||
components.Dispose(); | |||
} | |||
base.Dispose(disposing); | |||
} | |||
#region Windows Form Designer generated code | |||
/// <summary> | |||
/// Required method for Designer support - do not modify | |||
/// the contents of this method with the code editor. | |||
/// </summary> | |||
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; | |||
} | |||
} |
@@ -0,0 +1,183 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<root> | |||
<!-- | |||
Microsoft ResX Schema | |||
Version 2.0 | |||
The primary goals of this format is to allow a simple XML format | |||
that is mostly human readable. The generation and parsing of the | |||
various data types are done through the TypeConverter classes | |||
associated with the data types. | |||
Example: | |||
... ado.net/XML headers & schema ... | |||
<resheader name="resmimetype">text/microsoft-resx</resheader> | |||
<resheader name="version">2.0</resheader> | |||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | |||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | |||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | |||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | |||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | |||
<value>[base64 mime encoded serialized .NET Framework object]</value> | |||
</data> | |||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | |||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | |||
<comment>This is a comment</comment> | |||
</data> | |||
There are any number of "resheader" rows that contain simple | |||
name/value pairs. | |||
Each data row contains a name, and value. The row also contains a | |||
type or mimetype. Type corresponds to a .NET class that support | |||
text/value conversion through the TypeConverter architecture. | |||
Classes that don't support this are serialized and stored with the | |||
mimetype set. | |||
The mimetype is used for serialized objects, and tells the | |||
ResXResourceReader how to depersist the object. This is currently not | |||
extensible. For a given mimetype the value must be set accordingly: | |||
Note - application/x-microsoft.net.object.binary.base64 is the format | |||
that the ResXResourceWriter will generate, however the reader can | |||
read any of the formats listed below. | |||
mimetype: application/x-microsoft.net.object.binary.base64 | |||
value : The object must be serialized with | |||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | |||
: and then encoded with base64 encoding. | |||
mimetype: application/x-microsoft.net.object.soap.base64 | |||
value : The object must be serialized with | |||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter | |||
: and then encoded with base64 encoding. | |||
mimetype: application/x-microsoft.net.object.bytearray.base64 | |||
value : The object must be serialized into a byte array | |||
: using a System.ComponentModel.TypeConverter | |||
: and then encoded with base64 encoding. | |||
--> | |||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | |||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | |||
<xsd:element name="root" msdata:IsDataSet="true"> | |||
<xsd:complexType> | |||
<xsd:choice maxOccurs="unbounded"> | |||
<xsd:element name="metadata"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" use="required" type="xsd:string" /> | |||
<xsd:attribute name="type" type="xsd:string" /> | |||
<xsd:attribute name="mimetype" type="xsd:string" /> | |||
<xsd:attribute ref="xml:space" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="assembly"> | |||
<xsd:complexType> | |||
<xsd:attribute name="alias" type="xsd:string" /> | |||
<xsd:attribute name="name" type="xsd:string" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="data"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | |||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | |||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | |||
<xsd:attribute ref="xml:space" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="resheader"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" use="required" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
</xsd:choice> | |||
</xsd:complexType> | |||
</xsd:element> | |||
</xsd:schema> | |||
<resheader name="resmimetype"> | |||
<value>text/microsoft-resx</value> | |||
</resheader> | |||
<resheader name="version"> | |||
<value>2.0</value> | |||
</resheader> | |||
<resheader name="reader"> | |||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
<resheader name="writer"> | |||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
<metadata name="flowLayoutPanel1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>False</value> | |||
</metadata> | |||
<metadata name="flowLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="tableLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="SwitchSystemProxyLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ChangeToPacLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ChangeToGlobalLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="SwitchAllowLanLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ShowLogsLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ServerMoveUpLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ServerMoveDownLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="SwitchSystemProxyTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ChangeToPacTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ChangeToGlobalTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="SwitchAllowLanTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ShowLogsTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ServerMoveUpTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="ServerMoveDownTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="btnOK.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="btnCancel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="btnRegisterAll.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>True</value> | |||
</metadata> | |||
</root> |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -3,6 +3,7 @@ | |||
<package id="Caseless.Fody" version="1.4.2" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="GlobalHotKey" version="1.1.0" targetFramework="net40-client" /> | |||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40-client" /> | |||
<package id="StringEx.CS" version="0.3.1" targetFramework="net40-client" developmentDependency="true" /> | |||
</packages> |
@@ -65,6 +65,10 @@ | |||
<ApplicationManifest>app.manifest</ApplicationManifest> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="GlobalHotKey, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL"> | |||
<HintPath>3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.CSharp" /> | |||
<Reference Include="Microsoft.VisualBasic" /> | |||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | |||
@@ -139,6 +143,7 @@ | |||
<Compile Include="3rd\zxing\ResultPoint.cs" /> | |||
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> | |||
<Compile Include="3rd\zxing\WriterException.cs" /> | |||
<Compile Include="Model\HotKeyConfig.cs" /> | |||
<Compile Include="Model\ProxyConfig.cs" /> | |||
<Compile Include="Proxy\DirectConnect.cs" /> | |||
<Compile Include="Proxy\IProxy.cs" /> | |||
@@ -178,6 +183,7 @@ | |||
<Compile Include="Controller\Strategy\IStrategy.cs" /> | |||
<Compile Include="Proxy\Socks5Proxy.cs" /> | |||
<Compile Include="StringEx.cs" /> | |||
<Compile Include="Util\Hotkeys.cs" /> | |||
<Compile Include="Util\ProcessManagement\Job.cs" /> | |||
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" /> | |||
<Compile Include="Util\SocketUtil.cs" /> | |||
@@ -200,6 +206,12 @@ | |||
<Compile Include="View\CalculationControl.Designer.cs"> | |||
<DependentUpon>CalculationControl.cs</DependentUpon> | |||
</Compile> | |||
<Compile Include="View\HotkeySettingsForm.cs"> | |||
<SubType>Form</SubType> | |||
</Compile> | |||
<Compile Include="View\HotkeySettingsForm.designer.cs"> | |||
<DependentUpon>HotkeySettingsForm.cs</DependentUpon> | |||
</Compile> | |||
<Compile Include="View\LogForm.cs"> | |||
<SubType>Form</SubType> | |||
</Compile> | |||
@@ -240,6 +252,9 @@ | |||
<EmbeddedResource Include="View\CalculationControl.resx"> | |||
<DependentUpon>CalculationControl.cs</DependentUpon> | |||
</EmbeddedResource> | |||
<EmbeddedResource Include="View\HotkeySettingsForm.resx"> | |||
<DependentUpon>HotkeySettingsForm.cs</DependentUpon> | |||
</EmbeddedResource> | |||
<EmbeddedResource Include="View\LogForm.resx"> | |||
<DependentUpon>LogForm.cs</DependentUpon> | |||
</EmbeddedResource> | |||
@@ -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() | |||
{ | |||
@@ -35,10 +35,15 @@ | |||
<StartupObject /> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="GlobalHotKey, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL"> | |||
<HintPath>..\shadowsocks-csharp\3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Net" /> | |||
<Reference Include="System.Net.Http" /> | |||
<Reference Include="System.Net.Http.WebRequest" /> | |||
<Reference Include="WindowsBase" /> | |||
</ItemGroup> | |||
<Choose> | |||
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> | |||