Files
Syrone Wong c25e70e888 hotkey: Refine system proxy mode switching logic
Merge 'Switch to PAC' and 'Switch to Global'

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-11-15 18:14:56 +08:00

397 lines
14 KiB
C#

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 Label _lb = null;
private 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;
SwitchProxyModeTextBox.Text = config.SwitchSystemProxyMode;
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");
SwitchProxyModeLabel.Text = I18N.GetString("Switch system proxy 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.SwitchSystemProxyMode = SwitchProxyModeTextBox.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 SwitchProxyModeCallback()
{
var config = _controller.GetConfigurationCopy();
if (config.enabled == false) return;
var currStatus = config.global;
_controller.ToggleGlobal(!currStatus);
}
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
}
}