@@ -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> |
@@ -35,10 +35,7 @@ namespace Shadowsocks.Controller | |||
|| (length < 2 || firstPacket[0] != 5)) | |||
return false; | |||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | |||
TCPHandler handler = new TCPHandler(this, _config); | |||
handler.connection = socket; | |||
handler.controller = _controller; | |||
handler.tcprelay = this; | |||
TCPHandler handler = new TCPHandler(_controller, _config, this, socket); | |||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | |||
lock (Handlers) | |||
@@ -130,68 +127,67 @@ namespace Shadowsocks.Controller | |||
public static readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth | |||
public static readonly int BufferSize = RecvSize + RecvReserveSize + 32; | |||
// public Encryptor encryptor; | |||
public IEncryptor encryptor; | |||
public Server server; | |||
// Client socket. | |||
private AsyncSession _currentRemoteSession; | |||
public DateTime lastActivity; | |||
public Socket connection; | |||
public ShadowsocksController controller; | |||
public TCPRelay tcprelay; | |||
private ShadowsocksController _controller; | |||
private Configuration _config; | |||
private TCPRelay _tcprelay; | |||
private Socket _connection; | |||
public DateTime lastActivity; | |||
private IEncryptor _encryptor; | |||
private Server _server; | |||
private const int MaxRetry = 4; | |||
private int _retryCount = 0; | |||
private bool _proxyConnected; | |||
private bool _destConnected; | |||
private AsyncSession _currentRemoteSession; | |||
private const int MaxRetry = 4; | |||
private int _retryCount = 0; | |||
private bool _proxyConnected; | |||
private bool _destConnected; | |||
private byte _command; | |||
private byte[] _firstPacket; | |||
private int _firstPacketLength; | |||
private byte _command; | |||
private byte[] _firstPacket; | |||
private int _firstPacketLength; | |||
private int _totalRead = 0; | |||
private int _totalWrite = 0; | |||
private int _totalRead = 0; | |||
private int _totalWrite = 0; | |||
private byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||
private byte[] _remoteSendBuffer = new byte[BufferSize]; | |||
private byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||
private byte[] _connetionSendBuffer = new byte[BufferSize]; | |||
private byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||
private byte[] _remoteSendBuffer = new byte[BufferSize]; | |||
private byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||
private byte[] _connetionSendBuffer = new byte[BufferSize]; | |||
private bool _connectionShutdown = false; | |||
private bool _remoteShutdown = false; | |||
private bool _closed = false; | |||
private bool _connectionShutdown = false; | |||
private bool _remoteShutdown = false; | |||
private bool _closed = false; | |||
private object _encryptionLock = new object(); | |||
private object _decryptionLock = new object(); | |||
private object _encryptionLock = new object(); | |||
private object _decryptionLock = new object(); | |||
private DateTime _startConnectTime; | |||
private DateTime _startReceivingTime; | |||
private DateTime _startSendingTime; | |||
private int _bytesToSend; | |||
private TCPRelay _tcprelay; // TODO: is _tcprelay equals tcprelay declared above? | |||
private Configuration _config; | |||
public TCPHandler(TCPRelay tcprelay, Configuration config) | |||
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) | |||
{ | |||
this._tcprelay = tcprelay; | |||
this._controller = controller; | |||
this._config = config; | |||
this._tcprelay = tcprelay; | |||
this._connection = socket; | |||
} | |||
public void CreateRemote() | |||
{ | |||
Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint); | |||
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint); | |||
if (server == null || server.server == "") | |||
throw new ArgumentException("No server configured"); | |||
lock (_encryptionLock) | |||
{ | |||
lock (_decryptionLock) | |||
{ | |||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); | |||
_encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); | |||
} | |||
} | |||
this.server = server; | |||
this._server = server; | |||
} | |||
public void Start(byte[] firstPacket, int length) | |||
@@ -210,18 +206,19 @@ namespace Shadowsocks.Controller | |||
public void Close() | |||
{ | |||
lock (tcprelay.Handlers) | |||
lock (this) | |||
{ | |||
tcprelay.Handlers.Remove(this); | |||
} | |||
lock (this) { | |||
if (_closed) return; | |||
_closed = true; | |||
} | |||
lock (_tcprelay.Handlers) | |||
{ | |||
_tcprelay.Handlers.Remove(this); | |||
} | |||
try | |||
{ | |||
connection?.Shutdown(SocketShutdown.Both); | |||
connection?.Close(); | |||
_connection?.Shutdown(SocketShutdown.Both); | |||
_connection?.Close(); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -241,7 +238,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
lock (_decryptionLock) | |||
{ | |||
encryptor?.Dispose(); | |||
_encryptor?.Dispose(); | |||
} | |||
} | |||
} | |||
@@ -261,7 +258,7 @@ namespace Shadowsocks.Controller | |||
response = new byte[] { 0, 91 }; | |||
Logging.Error("socks 5 protocol error"); | |||
} | |||
connection?.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null); | |||
_connection?.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null); | |||
} | |||
else | |||
Close(); | |||
@@ -278,7 +275,7 @@ namespace Shadowsocks.Controller | |||
if (_closed) return; | |||
try | |||
{ | |||
connection.EndSend(ar); | |||
_connection.EndSend(ar); | |||
// +-----+-----+-------+------+----------+----------+ | |||
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | | |||
@@ -287,7 +284,7 @@ namespace Shadowsocks.Controller | |||
// +-----+-----+-------+------+----------+----------+ | |||
// Skip first 3 bytes | |||
// TODO validate | |||
connection.BeginReceive(_connetionRecvBuffer, 0, 3, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null); | |||
_connection.BeginReceive(_connetionRecvBuffer, 0, 3, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -301,14 +298,14 @@ namespace Shadowsocks.Controller | |||
if (_closed) return; | |||
try | |||
{ | |||
int bytesRead = connection.EndReceive(ar); | |||
int bytesRead = _connection.EndReceive(ar); | |||
if (bytesRead >= 3) | |||
{ | |||
_command = _connetionRecvBuffer[1]; | |||
if (_command == 1) | |||
{ | |||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | |||
connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ResponseCallback), null); | |||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ResponseCallback), null); | |||
} | |||
else if (_command == 3) | |||
HandleUDPAssociate(); | |||
@@ -328,7 +325,7 @@ namespace Shadowsocks.Controller | |||
private void HandleUDPAssociate() | |||
{ | |||
IPEndPoint endPoint = (IPEndPoint)connection.LocalEndPoint; | |||
IPEndPoint endPoint = (IPEndPoint)_connection.LocalEndPoint; | |||
byte[] address = endPoint.Address.GetAddressBytes(); | |||
int port = endPoint.Port; | |||
byte[] response = new byte[4 + address.Length + 2]; | |||
@@ -345,7 +342,7 @@ namespace Shadowsocks.Controller | |||
address.CopyTo(response, 4); | |||
response[response.Length - 1] = (byte)(port & 0xFF); | |||
response[response.Length - 2] = (byte)((port >> 8) & 0xFF); | |||
connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true); | |||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true); | |||
} | |||
private void ReadAll(IAsyncResult ar) | |||
@@ -355,15 +352,15 @@ namespace Shadowsocks.Controller | |||
{ | |||
if (ar.AsyncState != null) | |||
{ | |||
connection.EndSend(ar); | |||
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||
_connection.EndSend(ar); | |||
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||
} | |||
else | |||
{ | |||
int bytesRead = connection.EndReceive(ar); | |||
int bytesRead = _connection.EndReceive(ar); | |||
if (bytesRead > 0) | |||
{ | |||
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||
} | |||
else | |||
Close(); | |||
@@ -380,7 +377,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
connection?.EndSend(ar); | |||
_connection?.EndSend(ar); | |||
StartConnect(); | |||
} | |||
catch (Exception e) | |||
@@ -420,10 +417,10 @@ namespace Shadowsocks.Controller | |||
// Setting up proxy | |||
IProxy remote; | |||
EndPoint proxyEP; | |||
if (_config.useProxy) | |||
if (_config.proxy.useProxy) | |||
{ | |||
remote = new Socks5Proxy(); | |||
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort); | |||
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort); | |||
} | |||
else | |||
{ | |||
@@ -440,8 +437,8 @@ namespace Shadowsocks.Controller | |||
proxyTimer.Enabled = true; | |||
proxyTimer.Session = session; | |||
proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port); | |||
proxyTimer.Server = server; | |||
proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(_server.server, _server.server_port); | |||
proxyTimer.Server = _server; | |||
_proxyConnected = false; | |||
@@ -542,7 +539,7 @@ namespace Shadowsocks.Controller | |||
var session = timer.Session; | |||
Server server = timer.Server; | |||
IStrategy strategy = controller.GetCurrentStrategy(); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.SetFailure(server); | |||
Logging.Info($"{server.FriendlyName()} timed out"); | |||
session.Remote.Close(); | |||
@@ -568,7 +565,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
var session = (AsyncSession<ServerTimer>) ar.AsyncState; | |||
ServerTimer timer = session.State; | |||
server = timer.Server; | |||
_server = timer.Server; | |||
timer.Elapsed -= destConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
@@ -581,13 +578,13 @@ namespace Shadowsocks.Controller | |||
if (_config.isVerboseLogging) | |||
{ | |||
Logging.Info($"Socket connected to ss server: {server.FriendlyName()}"); | |||
Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}"); | |||
} | |||
var latency = DateTime.Now - _startConnectTime; | |||
IStrategy strategy = controller.GetCurrentStrategy(); | |||
strategy?.UpdateLatency(server, latency); | |||
_tcprelay.UpdateLatency(server, latency); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLatency(_server, latency); | |||
_tcprelay.UpdateLatency(_server, latency); | |||
StartPipe(session); | |||
} | |||
@@ -596,10 +593,10 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
if (server != null) | |||
if (_server != null) | |||
{ | |||
IStrategy strategy = controller.GetCurrentStrategy(); | |||
strategy?.SetFailure(server); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.SetFailure(_server); | |||
} | |||
Logging.LogUsefulException(e); | |||
RetryConnect(); | |||
@@ -613,7 +610,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
_startReceivingTime = DateTime.Now; | |||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | |||
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), | |||
_connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), | |||
new AsyncSession<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */); | |||
} | |||
catch (Exception e) | |||
@@ -631,7 +628,7 @@ namespace Shadowsocks.Controller | |||
var session = (AsyncSession) ar.AsyncState; | |||
int bytesRead = session.Remote.EndReceive(ar); | |||
_totalRead += bytesRead; | |||
_tcprelay.UpdateInboundCounter(server, bytesRead); | |||
_tcprelay.UpdateInboundCounter(_server, bytesRead); | |||
if (bytesRead > 0) | |||
{ | |||
lastActivity = DateTime.Now; | |||
@@ -639,15 +636,15 @@ namespace Shadowsocks.Controller | |||
lock (_decryptionLock) | |||
{ | |||
if (_closed) return; | |||
encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||
} | |||
connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session); | |||
IStrategy strategy = controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastRead(server); | |||
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastRead(_server); | |||
} | |||
else | |||
{ | |||
connection.Shutdown(SocketShutdown.Send); | |||
_connection.Shutdown(SocketShutdown.Send); | |||
_connectionShutdown = true; | |||
CheckClose(); | |||
} | |||
@@ -664,8 +661,8 @@ namespace Shadowsocks.Controller | |||
if (_closed) return; | |||
try | |||
{ | |||
if(connection == null) return; | |||
int bytesRead = connection.EndReceive(ar); | |||
if(_connection == null) return; | |||
int bytesRead = _connection.EndReceive(ar); | |||
_totalWrite += bytesRead; | |||
var session = (AsyncSession<bool>) ar.AsyncState; | |||
@@ -713,14 +710,13 @@ namespace Shadowsocks.Controller | |||
lock (_encryptionLock) | |||
{ | |||
if (_closed) return; | |||
encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend); | |||
_encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend); | |||
} | |||
_tcprelay.UpdateOutboundCounter(server, bytesToSend); | |||
_tcprelay.UpdateOutboundCounter(_server, bytesToSend); | |||
_startSendingTime = DateTime.Now; | |||
_bytesToSend = bytesToSend; | |||
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session); | |||
IStrategy strategy = controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastWrite(server); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastWrite(_server); | |||
} | |||
else | |||
{ | |||
@@ -743,7 +739,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
var session = (AsyncSession)ar.AsyncState; | |||
session.Remote.EndSend(ar); | |||
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session); | |||
_connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -758,7 +754,7 @@ namespace Shadowsocks.Controller | |||
try | |||
{ | |||
var session = (AsyncSession)ar.AsyncState; | |||
connection?.EndSend(ar); | |||
_connection?.EndSend(ar); | |||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); | |||
} | |||
catch (Exception e) | |||
@@ -217,15 +217,15 @@ namespace Shadowsocks.Controller | |||
public void DisableProxy() | |||
{ | |||
_config.useProxy = false; | |||
_config.proxy.useProxy = false; | |||
SaveConfig(_config); | |||
} | |||
public void EnableProxy(string proxy, int port) | |||
{ | |||
_config.useProxy = true; | |||
_config.proxyServer = proxy; | |||
_config.proxyPort = port; | |||
_config.proxy.useProxy = true; | |||
_config.proxy.proxyServer = proxy; | |||
_config.proxy.proxyPort = port; | |||
SaveConfig(_config); | |||
} | |||
@@ -348,12 +348,40 @@ namespace Shadowsocks.Controller | |||
{ | |||
_config.autoCheckUpdate = enabled; | |||
Configuration.Save(_config); | |||
if (ConfigChanged != null) | |||
{ | |||
ConfigChanged(this, new EventArgs()); | |||
} | |||
} | |||
public void SaveLogViewerConfig(LogViewerConfig newConfig) | |||
{ | |||
_config.logViewer = newConfig; | |||
Configuration.Save(_config); | |||
if (ConfigChanged != null) | |||
{ | |||
ConfigChanged(this, new EventArgs()); | |||
} | |||
} | |||
public void SaveProxyConfig(ProxyConfig newConfig) | |||
{ | |||
_config.proxy = newConfig; | |||
Configuration.Save(_config); | |||
if (ConfigChanged != null) | |||
{ | |||
ConfigChanged(this, new EventArgs()); | |||
} | |||
} | |||
public void SaveHotkeyConfig(HotkeyConfig newConfig) | |||
{ | |||
_config.hotkey = newConfig; | |||
SaveConfig(_config); | |||
if (ConfigChanged != null) | |||
{ | |||
ConfigChanged(this, new EventArgs()); | |||
} | |||
} | |||
public void UpdateLatency(Server server, TimeSpan latency) | |||
@@ -547,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)); | |||
@@ -563,6 +593,10 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
#endregion | |||
#region Traffic Statistics | |||
private void StartTrafficStatistics(int queueMaxSize) | |||
{ | |||
traffic = new QueueLast<TrafficPerSecond>(); | |||
@@ -597,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,12 +85,23 @@ 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} | |||
Port already in use=連接埠號碼已被占用 | |||
Illegal port number format=非法連接埠號碼格式 | |||
Please add at least one server=請新增至少壹個伺服器 | |||
Please add at least one server=請新增至少一個伺服器 | |||
Server IP can not be blank=伺服器 IP 不能為空 | |||
Password can not be blank=密碼不能為空 | |||
Port out of range=連接埠號碼超出範圍 | |||
@@ -107,7 +119,7 @@ No updates found. Please report to GFWList if you have problems with it.=未發 | |||
No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近屏幕中間的位置 | |||
Shadowsocks is already running.=Shadowsocks 已經在運行。 | |||
Find Shadowsocks icon in your notify tray.=請在工作列裏尋找 Shadowsocks 圖標。 | |||
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制壹份到別的目錄。 | |||
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制一份到別的目錄。 | |||
Failed to decode QRCode=無法解析 QR 碼 | |||
Failed to update registry=無法修改登錄檔 | |||
System Proxy On: =系統代理已啟用: | |||
@@ -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} |
@@ -2,6 +2,22 @@ | |||
namespace Shadowsocks.Encryption | |||
{ | |||
public struct EncryptorInfo | |||
{ | |||
public string name; | |||
public int key_size; | |||
public int iv_size; | |||
public int type; | |||
public EncryptorInfo(string name, int key_size, int iv_size, int type) | |||
{ | |||
this.name = name; | |||
this.key_size = key_size; | |||
this.iv_size = iv_size; | |||
this.type = type; | |||
} | |||
} | |||
public abstract class EncryptorBase | |||
: IEncryptor | |||
{ | |||
@@ -16,8 +16,7 @@ namespace Shadowsocks.Encryption | |||
protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE]; | |||
protected Dictionary<string, Dictionary<string, int[]>> ciphers; | |||
protected Dictionary<string, int[]> ciphersDetail; | |||
protected Dictionary<string, EncryptorInfo> ciphers; | |||
private static readonly ConcurrentDictionary<string, byte[]> CachedKeys = new ConcurrentDictionary<string, byte[]>(); | |||
protected byte[] _encryptIV; | |||
@@ -28,7 +27,7 @@ namespace Shadowsocks.Encryption | |||
protected int _cipher; | |||
// cipher name in MbedTLS, useless when using LibSodium | |||
protected string _cipherMbedName; | |||
protected int[] _cipherInfo; | |||
protected EncryptorInfo _cipherInfo; | |||
protected byte[] _key; | |||
protected int keyLen; | |||
protected int ivLen; | |||
@@ -39,7 +38,7 @@ namespace Shadowsocks.Encryption | |||
InitKey(method, password); | |||
} | |||
protected abstract Dictionary<string, Dictionary<string, int[]>> getCiphers(); | |||
protected abstract Dictionary<string, EncryptorInfo> getCiphers(); | |||
private void InitKey(string method, string password) | |||
{ | |||
@@ -47,16 +46,15 @@ namespace Shadowsocks.Encryption | |||
_method = method; | |||
string k = method + ":" + password; | |||
ciphers = getCiphers(); | |||
ciphersDetail = ciphers[_method]; | |||
_cipherMbedName = ciphersDetail.Keys.FirstOrDefault(); | |||
_cipherInfo = ciphers[_method][_cipherMbedName]; | |||
_cipher = _cipherInfo[2]; | |||
_cipherInfo = ciphers[_method]; | |||
_cipherMbedName = _cipherInfo.name; | |||
_cipher = _cipherInfo.type; | |||
if (_cipher == 0) | |||
{ | |||
throw new Exception("method not found"); | |||
} | |||
keyLen = _cipherInfo[0]; | |||
ivLen = _cipherInfo[1]; | |||
keyLen = _cipherInfo.key_size; | |||
ivLen = _cipherInfo.iv_size; | |||
_key = CachedKeys.GetOrAdd(k, (nk) => | |||
{ | |||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | |||
@@ -20,18 +20,18 @@ namespace Shadowsocks.Encryption | |||
{ | |||
} | |||
private static Dictionary<string, Dictionary<string, int[]>> _ciphers = new Dictionary<string, Dictionary<string, int[]>> { | |||
{ "aes-128-cfb", new Dictionary<string, int[]> { { "AES-128-CFB128", new int[] { 16, 16, CIPHER_AES } } } }, | |||
{ "aes-192-cfb", new Dictionary<string, int[]> { { "AES-192-CFB128", new int[] { 24, 16, CIPHER_AES } } } }, | |||
{ "aes-256-cfb", new Dictionary<string, int[]> { { "AES-256-CFB128", new int[] { 32, 16, CIPHER_AES } } } }, | |||
{ "aes-128-ctr", new Dictionary<string, int[]> { { "AES-128-CTR", new int[] { 16, 16, CIPHER_AES } } } }, | |||
{ "aes-192-ctr", new Dictionary<string, int[]> { { "AES-192-CTR", new int[] { 24, 16, CIPHER_AES } } } }, | |||
{ "aes-256-ctr", new Dictionary<string, int[]> { { "AES-256-CTR", new int[] { 32, 16, CIPHER_AES } } } }, | |||
{ "bf-cfb", new Dictionary<string, int[]> { { "BLOWFISH-CFB64", new int[] { 16, 8, CIPHER_BLOWFISH } } } }, | |||
{ "camellia-128-cfb", new Dictionary<string, int[]> { { "CAMELLIA-128-CFB128", new int[] { 16, 16, CIPHER_CAMELLIA } } } }, | |||
{ "camellia-192-cfb", new Dictionary<string, int[]> { { "CAMELLIA-192-CFB128", new int[] { 24, 16, CIPHER_CAMELLIA } } } }, | |||
{ "camellia-256-cfb", new Dictionary<string, int[]> { { "CAMELLIA-256-CFB128", new int[] { 32, 16, CIPHER_CAMELLIA } } } }, | |||
{ "rc4-md5", new Dictionary<string, int[]> { { "ARC4-128", new int[] { 16, 16, CIPHER_RC4 } } } } | |||
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> { | |||
{ "aes-128-cfb", new EncryptorInfo("AES-128-CFB128", 16, 16, CIPHER_AES) }, | |||
{ "aes-192-cfb", new EncryptorInfo("AES-192-CFB128", 24, 16, CIPHER_AES) }, | |||
{ "aes-256-cfb", new EncryptorInfo("AES-256-CFB128", 32, 16, CIPHER_AES) }, | |||
{ "aes-128-ctr", new EncryptorInfo("AES-128-CTR", 16, 16, CIPHER_AES) }, | |||
{ "aes-192-ctr", new EncryptorInfo("AES-192-CTR", 24, 16, CIPHER_AES) }, | |||
{ "aes-256-ctr", new EncryptorInfo("AES-256-CTR", 32, 16, CIPHER_AES) }, | |||
{ "bf-cfb", new EncryptorInfo("BLOWFISH-CFB64", 16, 8, CIPHER_BLOWFISH) }, | |||
{ "camellia-128-cfb", new EncryptorInfo("CAMELLIA-128-CFB128", 16, 16, CIPHER_CAMELLIA) }, | |||
{ "camellia-192-cfb", new EncryptorInfo("CAMELLIA-192-CFB128", 24, 16, CIPHER_CAMELLIA) }, | |||
{ "camellia-256-cfb", new EncryptorInfo("CAMELLIA-256-CFB128", 32, 16, CIPHER_CAMELLIA) }, | |||
{ "rc4-md5", new EncryptorInfo("ARC4-128", 16, 16, CIPHER_RC4) } | |||
}; | |||
public static List<string> SupportedCiphers() | |||
@@ -39,7 +39,7 @@ namespace Shadowsocks.Encryption | |||
return new List<string>(_ciphers.Keys); | |||
} | |||
protected override Dictionary<string, Dictionary<string, int[]>> getCiphers() | |||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
@@ -24,13 +24,13 @@ namespace Shadowsocks.Encryption | |||
{ | |||
} | |||
private static Dictionary<string, Dictionary<string, int[]>> _ciphers = new Dictionary<string, Dictionary<string, int[]>> { | |||
{ "salsa20", new Dictionary<string, int[]> { { "salsa20", new int[] { 32, 8, CIPHER_SALSA20 } } } }, | |||
{ "chacha20", new Dictionary<string, int[]> { { "chacha20", new int[] { 32, 8, CIPHER_CHACHA20 } } } }, | |||
{ "chacha20-ietf", new Dictionary<string, int[]> { { "chacha20-ietf", new int[] { 32, 12, CIPHER_CHACHA20_IETF } } } } | |||
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> { | |||
{ "salsa20", new EncryptorInfo("salsa20", 32, 8, CIPHER_SALSA20) }, | |||
{ "chacha20", new EncryptorInfo("chacha20", 32, 8, CIPHER_CHACHA20) }, | |||
{ "chacha20-ietf", new EncryptorInfo("chacha20-ietf", 32, 12, CIPHER_CHACHA20_IETF) } | |||
}; | |||
protected override Dictionary<string, Dictionary<string, int[]>> getCiphers() | |||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
@@ -26,9 +26,8 @@ namespace Shadowsocks.Model | |||
public bool autoCheckUpdate; | |||
public bool isVerboseLogging; | |||
public LogViewerConfig logViewer; | |||
public bool useProxy; | |||
public string proxyServer; | |||
public int proxyPort; | |||
public ProxyConfig proxy; | |||
public HotkeyConfig hotkey; | |||
private static string CONFIG_FILE = "gui-config.json"; | |||
@@ -58,6 +57,12 @@ namespace Shadowsocks.Model | |||
config.localPort = 1080; | |||
if (config.index == -1 && config.strategy == null) | |||
config.index = 0; | |||
if (config.logViewer == null) | |||
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) | |||
@@ -98,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 = ""; | |||
} | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
using Shadowsocks.View; | |||
using System; | |||
using System.Drawing; | |||
using System.Windows.Forms; | |||
namespace Shadowsocks.Model | |||
{ | |||
[Serializable] | |||
public class ProxyConfig | |||
{ | |||
public bool useProxy; | |||
public string proxyServer; | |||
public int proxyPort; | |||
public ProxyConfig() | |||
{ | |||
useProxy = false; | |||
proxyServer = ""; | |||
proxyPort = 0; | |||
} | |||
} | |||
} |
@@ -13,7 +13,9 @@ namespace Shadowsocks | |||
{ | |||
static class Program | |||
{ | |||
static ShadowsocksController controller; | |||
private static ShadowsocksController _controller; | |||
// XXX: Don't change this name | |||
private static MenuViewController _viewController; | |||
/// <summary> | |||
/// 应用程序的主入口点。 | |||
@@ -37,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)) | |||
{ | |||
@@ -61,9 +64,10 @@ namespace Shadowsocks | |||
#else | |||
Logging.OpenLogFile(); | |||
#endif | |||
controller = new ShadowsocksController(); | |||
MenuViewController viewController = new MenuViewController(controller); | |||
controller.Start(); | |||
_controller = new ShadowsocksController(); | |||
_viewController = new MenuViewController(_controller); | |||
HotKeys.Init(); | |||
_controller.Start(); | |||
Application.Run(); | |||
} | |||
} | |||
@@ -88,10 +92,10 @@ namespace Shadowsocks | |||
{ | |||
case PowerModes.Resume: | |||
Logging.Info("os wake up"); | |||
controller?.Start(); | |||
_controller?.Start(); | |||
break; | |||
case PowerModes.Suspend: | |||
controller?.Stop(); | |||
_controller?.Stop(); | |||
Logging.Info("os suspend"); | |||
break; | |||
} | |||
@@ -99,10 +103,10 @@ namespace Shadowsocks | |||
private static void Application_ApplicationExit(object sender, EventArgs e) | |||
{ | |||
if (controller != null) | |||
if (_controller != null) | |||
{ | |||
controller.Stop(); | |||
controller = null; | |||
_controller.Stop(); | |||
_controller = null; | |||
} | |||
} | |||
} | |||
@@ -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; | |||
@@ -9,6 +11,20 @@ using Shadowsocks.Controller; | |||
namespace Shadowsocks.Util | |||
{ | |||
public struct BandwidthScaleInfo | |||
{ | |||
public float value; | |||
public string unit_name; | |||
public long unit; | |||
public BandwidthScaleInfo(float value, string unit_name, long unit) | |||
{ | |||
this.value = value; | |||
this.unit_name = unit_name; | |||
this.unit = unit; | |||
} | |||
} | |||
public class Utils | |||
{ | |||
private static bool? _portableMode; | |||
@@ -114,7 +130,7 @@ namespace Shadowsocks.Util | |||
public static string FormatBandwidth(long n) | |||
{ | |||
var result = GetBandwidthScale(n); | |||
return $"{result.Item1:0.##}{result.Item2}"; | |||
return $"{result.value:0.##}{result.unit_name}"; | |||
} | |||
public static string FormatBytes(long bytes) | |||
@@ -127,32 +143,32 @@ namespace Shadowsocks.Util | |||
const long E = P * 1024L; | |||
if (bytes >= P * 990) | |||
return (bytes / (double)E).ToString("F5") + "EB"; | |||
return (bytes / (double)E).ToString("F5") + "EiB"; | |||
if (bytes >= T * 990) | |||
return (bytes / (double)P).ToString("F5") + "PB"; | |||
return (bytes / (double)P).ToString("F5") + "PiB"; | |||
if (bytes >= G * 990) | |||
return (bytes / (double)T).ToString("F5") + "TB"; | |||
return (bytes / (double)T).ToString("F5") + "TiB"; | |||
if (bytes >= M * 990) | |||
{ | |||
return (bytes / (double)G).ToString("F4") + "GB"; | |||
return (bytes / (double)G).ToString("F4") + "GiB"; | |||
} | |||
if (bytes >= M * 100) | |||
{ | |||
return (bytes / (double)M).ToString("F1") + "MB"; | |||
return (bytes / (double)M).ToString("F1") + "MiB"; | |||
} | |||
if (bytes >= M * 10) | |||
{ | |||
return (bytes / (double)M).ToString("F2") + "MB"; | |||
return (bytes / (double)M).ToString("F2") + "MiB"; | |||
} | |||
if (bytes >= K * 990) | |||
{ | |||
return (bytes / (double)M).ToString("F3") + "MB"; | |||
return (bytes / (double)M).ToString("F3") + "MiB"; | |||
} | |||
if (bytes > K * 2) | |||
{ | |||
return (bytes / (double)K).ToString("F1") + "KB"; | |||
return (bytes / (double)K).ToString("F1") + "KiB"; | |||
} | |||
return bytes.ToString(); | |||
return bytes.ToString() + "B"; | |||
} | |||
/// <summary> | |||
@@ -160,11 +176,9 @@ namespace Shadowsocks.Util | |||
/// </summary> | |||
/// <param name="n">Raw bandwidth</param> | |||
/// <returns> | |||
/// Item1: float, bandwidth with suitable scale (eg. 56) | |||
/// Item2: string, scale unit name (eg. KiB) | |||
/// Item3: long, scale unit (eg. 1024) | |||
/// The BandwidthScaleInfo struct | |||
/// </returns> | |||
public static Tuple<float, string, long> GetBandwidthScale(long n) | |||
public static BandwidthScaleInfo GetBandwidthScale(long n) | |||
{ | |||
long scale = 1; | |||
float f = n; | |||
@@ -193,7 +207,7 @@ namespace Shadowsocks.Util | |||
scale <<= 10; | |||
unit = "TiB"; | |||
} | |||
return new Tuple<float, string, long>(f, unit, scale); | |||
return new BandwidthScaleInfo(f, unit, scale); | |||
} | |||
public static RegistryKey OpenUserRegKey( string name, bool writable ) { | |||
@@ -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> |
@@ -10,9 +10,22 @@ using Shadowsocks.Controller; | |||
using Shadowsocks.Properties; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Util; | |||
using System.Text; | |||
namespace Shadowsocks.View | |||
{ | |||
struct TrafficInfo | |||
{ | |||
public long inbound; | |||
public long outbound; | |||
public TrafficInfo(long inbound, long outbound) | |||
{ | |||
this.inbound = inbound; | |||
this.outbound = outbound; | |||
} | |||
} | |||
public partial class LogForm : Form | |||
{ | |||
long lastOffset; | |||
@@ -23,7 +36,7 @@ namespace Shadowsocks.View | |||
#region chart | |||
long lastMaxSpeed; | |||
public ShadowsocksController.QueueLast<Tuple<long, long>> traffic = new ShadowsocksController.QueueLast<Tuple<long, long>>(); | |||
ShadowsocksController.QueueLast<TrafficInfo> traffic = new ShadowsocksController.QueueLast<TrafficInfo>(); | |||
#endregion | |||
public LogForm(ShadowsocksController controller, string filename) | |||
@@ -34,19 +47,13 @@ namespace Shadowsocks.View | |||
Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||
if (config == null) | |||
{ | |||
config = new LogViewerConfig(); | |||
} | |||
else | |||
{ | |||
topMostTrigger = config.topMost; | |||
wrapTextTrigger = config.wrapText; | |||
toolbarTrigger = config.toolbarShown; | |||
LogMessageTextBox.BackColor = config.GetBackgroundColor(); | |||
LogMessageTextBox.ForeColor = config.GetTextColor(); | |||
LogMessageTextBox.Font = config.GetFont(); | |||
} | |||
topMostTrigger = config.topMost; | |||
wrapTextTrigger = config.wrapText; | |||
toolbarTrigger = config.toolbarShown; | |||
LogMessageTextBox.BackColor = config.GetBackgroundColor(); | |||
LogMessageTextBox.ForeColor = config.GetTextColor(); | |||
LogMessageTextBox.Font = config.GetFont(); | |||
controller.TrafficChanged += controller_TrafficChanged; | |||
@@ -59,7 +66,7 @@ namespace Shadowsocks.View | |||
List<float> outboundPoints = new List<float>(); | |||
TextAnnotation inboundAnnotation = new TextAnnotation(); | |||
TextAnnotation outboundAnnotation = new TextAnnotation(); | |||
Tuple<float, string, long> bandwidthScale; | |||
BandwidthScaleInfo bandwidthScale; | |||
const long minScale = 50; | |||
long maxSpeed = 0; | |||
long lastInbound, lastOutbound; | |||
@@ -70,12 +77,12 @@ namespace Shadowsocks.View | |||
return; | |||
foreach (var trafficPerSecond in traffic) | |||
{ | |||
inboundPoints.Add(trafficPerSecond.Item1); | |||
outboundPoints.Add(trafficPerSecond.Item2); | |||
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.Item1, trafficPerSecond.Item2)); | |||
inboundPoints.Add(trafficPerSecond.inbound); | |||
outboundPoints.Add(trafficPerSecond.outbound); | |||
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.inbound, trafficPerSecond.outbound)); | |||
} | |||
lastInbound = traffic.Last().Item1; | |||
lastOutbound = traffic.Last().Item2; | |||
lastInbound = traffic.Last().inbound; | |||
lastOutbound = traffic.Last().outbound; | |||
} | |||
if (maxSpeed > 0) | |||
@@ -92,15 +99,15 @@ namespace Shadowsocks.View | |||
bandwidthScale = Utils.GetBandwidthScale(maxSpeed); | |||
//rescale the original data points, since it is List<float>, .ForEach does not work | |||
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList(); | |||
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList(); | |||
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.unit).ToList(); | |||
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.unit).ToList(); | |||
if (trafficChart.IsHandleCreated) | |||
{ | |||
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints); | |||
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints); | |||
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2; | |||
trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.Item1; | |||
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.unit_name; | |||
trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.value; | |||
inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last(); | |||
inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound); | |||
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last(); | |||
@@ -115,10 +122,10 @@ namespace Shadowsocks.View | |||
{ | |||
lock (this) | |||
{ | |||
traffic = new ShadowsocksController.QueueLast<Tuple<long, long>>(); | |||
traffic = new ShadowsocksController.QueueLast<TrafficInfo>(); | |||
foreach (var trafficPerSecond in controller.traffic) | |||
{ | |||
traffic.Enqueue(new Tuple<long, long>(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement)); | |||
traffic.Enqueue(new TrafficInfo(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement)); | |||
} | |||
} | |||
} | |||
@@ -162,9 +169,11 @@ namespace Shadowsocks.View | |||
} | |||
string line = ""; | |||
StringBuilder appendText = new StringBuilder(1024); | |||
while ((line = reader.ReadLine()) != null) | |||
LogMessageTextBox.AppendText(line + Environment.NewLine); | |||
appendText.Append(line + Environment.NewLine); | |||
LogMessageTextBox.AppendText(appendText.ToString()); | |||
LogMessageTextBox.ScrollToCaret(); | |||
lastOffset = reader.BaseStream.Position; | |||
@@ -182,14 +191,16 @@ namespace Shadowsocks.View | |||
string line = ""; | |||
bool changed = false; | |||
StringBuilder appendText = new StringBuilder(128); | |||
while ((line = reader.ReadLine()) != null) | |||
{ | |||
changed = true; | |||
LogMessageTextBox.AppendText(line + Environment.NewLine); | |||
appendText.Append(line + Environment.NewLine); | |||
} | |||
if (changed) | |||
{ | |||
LogMessageTextBox.AppendText(appendText.ToString()); | |||
LogMessageTextBox.ScrollToCaret(); | |||
} | |||
@@ -214,8 +225,7 @@ namespace Shadowsocks.View | |||
timer.Start(); | |||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||
if (config == null) | |||
config = new LogViewerConfig(); | |||
Height = config.height; | |||
Width = config.width; | |||
Top = config.GetBestTop(); | |||
@@ -241,8 +251,7 @@ namespace Shadowsocks.View | |||
timer.Stop(); | |||
controller.TrafficChanged -= controller_TrafficChanged; | |||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||
if (config == null) | |||
config = new LogViewerConfig(); | |||
config.topMost = topMostTrigger; | |||
config.wrapText = wrapTextTrigger; | |||
config.toolbarShown = toolbarTrigger; | |||
@@ -49,11 +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 List<LogForm> logForms = new List<LogForm>(); | |||
private bool logFormsVisible = false; | |||
private LogForm logForm; | |||
private HotkeySettingsForm hotkeySettingsForm; | |||
private string _urlToOpen; | |||
public MenuViewController(ShadowsocksController controller) | |||
@@ -133,6 +134,8 @@ namespace Shadowsocks.View | |||
MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message)); | |||
} | |||
#region Tray Icon | |||
private void UpdateTrayIcon() | |||
{ | |||
int dpi; | |||
@@ -177,10 +180,10 @@ namespace Shadowsocks.View | |||
} | |||
// we want to show more details but notify icon title is limited to 63 characters | |||
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" + | |||
(enabled ? | |||
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) : | |||
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running | |||
+ "\n" + serverInfo; | |||
(enabled ? | |||
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) : | |||
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running | |||
+ "\n" + serverInfo; | |||
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length)); | |||
} | |||
@@ -201,7 +204,7 @@ namespace Shadowsocks.View | |||
else if (global) | |||
{ | |||
Color flyBlue = Color.FromArgb(25, 125, 191); | |||
// Muliply with flyBlue | |||
// Multiply with flyBlue | |||
int red = color.R * flyBlue.R / 255; | |||
int green = color.G * flyBlue.G / 255; | |||
int blue = color.B * flyBlue.B / 255; | |||
@@ -230,6 +233,10 @@ namespace Shadowsocks.View | |||
return bitmap; | |||
} | |||
#endregion | |||
#region MenuItems and MenuGroups | |||
private MenuItem CreateMenuItem(string text, EventHandler click) | |||
{ | |||
return new MenuItem(I18N.GetString(text), click); | |||
@@ -271,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("-"), | |||
@@ -282,6 +290,8 @@ namespace Shadowsocks.View | |||
}); | |||
} | |||
#endregion | |||
private void controller_ConfigChanged(object sender, EventArgs e) | |||
{ | |||
LoadCurrentConfiguration(); | |||
@@ -406,7 +416,7 @@ namespace Shadowsocks.View | |||
i++; | |||
} | |||
// user want a seperator item between strategy and servers menugroup | |||
// user wants a seperator item between strategy and servers menugroup | |||
items.Add( i++, new MenuItem("-") ); | |||
int strategyCount = i; | |||
@@ -459,31 +469,40 @@ namespace Shadowsocks.View | |||
} | |||
} | |||
private void ShowLogForms() | |||
private void ShowHotKeySettingsForm() | |||
{ | |||
if (logForms.Count == 0) | |||
if (hotkeySettingsForm != null) | |||
{ | |||
hotkeySettingsForm.Activate(); | |||
} | |||
else | |||
{ | |||
LogForm f = new LogForm(controller, Logging.LogFilePath); | |||
f.Show(); | |||
f.Activate(); | |||
f.FormClosed += logForm_FormClosed; | |||
hotkeySettingsForm = new HotkeySettingsForm(controller); | |||
hotkeySettingsForm.Show(); | |||
hotkeySettingsForm.Activate(); | |||
hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed; | |||
} | |||
} | |||
logForms.Add(f); | |||
logFormsVisible = true; | |||
private void ShowLogForm() | |||
{ | |||
if (logForm != null) | |||
{ | |||
logForm.Activate(); | |||
} | |||
else | |||
{ | |||
logFormsVisible = !logFormsVisible; | |||
foreach (LogForm f in logForms) | |||
{ | |||
f.Visible = logFormsVisible; | |||
} | |||
logForm = new LogForm(controller, Logging.LogFilePath); | |||
logForm.Show(); | |||
logForm.Activate(); | |||
logForm.FormClosed += logForm_FormClosed; | |||
} | |||
} | |||
void logForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
logForms.Remove((LogForm)sender); | |||
logForm = null; | |||
Utils.ReleaseMemory(true); | |||
} | |||
void configForm_FormClosed(object sender, FormClosedEventArgs e) | |||
@@ -504,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(); | |||
@@ -519,11 +544,9 @@ namespace Shadowsocks.View | |||
private void CheckUpdateForFirstRun() | |||
{ | |||
Configuration config = controller.GetConfigurationCopy(); | |||
if (!config.isDefault) | |||
{ | |||
_isStartupChecking = true; | |||
updateChecker.CheckUpdate(config, 3000); | |||
} | |||
if (config.isDefault) return; | |||
_isStartupChecking = true; | |||
updateChecker.CheckUpdate(config, 3000); | |||
} | |||
private void ShowFirstTimeBalloon() | |||
@@ -547,7 +570,7 @@ namespace Shadowsocks.View | |||
} | |||
else if (e.Button == MouseButtons.Middle) | |||
{ | |||
ShowLogForms(); | |||
ShowLogForm(); | |||
} | |||
} | |||
@@ -607,15 +630,6 @@ namespace Shadowsocks.View | |||
controller.SelectStrategy((string)item.Tag); | |||
} | |||
private void ShowLogItem_Click(object sender, EventArgs e) | |||
{ | |||
LogForm f = new LogForm(controller, Logging.LogFilePath); | |||
f.Show(); | |||
f.FormClosed += logForm_FormClosed; | |||
logForms.Add(f); | |||
} | |||
private void VerboseLoggingToggleItem_Click( object sender, EventArgs e ) { | |||
VerboseLoggingToggleItem.Checked = ! VerboseLoggingToggleItem.Checked; | |||
controller.ToggleVerboseLogging( VerboseLoggingToggleItem.Checked ); | |||
@@ -820,5 +834,20 @@ 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(); | |||
} | |||
} | |||
} |
@@ -1,116 +1,125 @@ | |||
using System; | |||
using System.Drawing; | |||
using System.Windows.Forms; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
namespace Shadowsocks.View | |||
{ | |||
public partial class ProxyForm : Form | |||
{ | |||
private ShadowsocksController controller; | |||
// this is a copy of configuration that we are working on | |||
private Configuration _modifiedConfiguration; | |||
public ProxyForm(ShadowsocksController controller) | |||
{ | |||
this.Font = System.Drawing.SystemFonts.MessageBoxFont; | |||
InitializeComponent(); | |||
UpdateTexts(); | |||
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||
this.controller = controller; | |||
controller.ConfigChanged += controller_ConfigChanged; | |||
UpdateEnabled(); | |||
LoadCurrentConfiguration(); | |||
} | |||
private void UpdateTexts() | |||
{ | |||
UseProxyCheckBox.Text = I18N.GetString("Use Proxy"); | |||
ProxyAddrLabel.Text = I18N.GetString("Proxy Addr"); | |||
ProxyPortLable.Text = I18N.GetString("Proxy Port"); | |||
OKButton.Text = I18N.GetString("OK"); | |||
MyCancelButton.Text = I18N.GetString("Cancel"); | |||
this.Text = I18N.GetString("Edit Proxy"); | |||
} | |||
private void controller_ConfigChanged(object sender, EventArgs e) | |||
{ | |||
LoadCurrentConfiguration(); | |||
} | |||
private void LoadCurrentConfiguration() | |||
{ | |||
_modifiedConfiguration = controller.GetConfigurationCopy(); | |||
UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy; | |||
ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer; | |||
ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString(); | |||
} | |||
private void OKButton_Click(object sender, EventArgs e) | |||
{ | |||
if (UseProxyCheckBox.Checked) | |||
{ | |||
try | |||
{ | |||
var proxy = ProxyServerTextBox.Text; | |||
var port = int.Parse(ProxyPortTextBox.Text); | |||
Configuration.CheckServer(proxy); | |||
Configuration.CheckPort(port); | |||
controller.EnableProxy(proxy, port); | |||
} | |||
catch (FormatException) | |||
{ | |||
MessageBox.Show(I18N.GetString("Illegal port number format")); | |||
return; | |||
} | |||
catch (Exception ex) | |||
{ | |||
MessageBox.Show(ex.Message); | |||
return; | |||
} | |||
} | |||
else | |||
{ | |||
controller.DisableProxy(); | |||
} | |||
this.Close(); | |||
} | |||
private void CancelButton_Click(object sender, EventArgs e) | |||
{ | |||
this.Close(); | |||
} | |||
private void ProxyForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
controller.ConfigChanged -= controller_ConfigChanged; | |||
} | |||
private void UseProxyCheckBox_CheckedChanged(object sender, EventArgs e) | |||
{ | |||
UpdateEnabled(); | |||
} | |||
private void UpdateEnabled() | |||
{ | |||
if (UseProxyCheckBox.Checked) | |||
{ | |||
ProxyServerTextBox.Enabled = true; | |||
ProxyPortTextBox.Enabled = true; | |||
} | |||
else | |||
{ | |||
ProxyServerTextBox.Enabled = false; | |||
ProxyPortTextBox.Enabled = false; | |||
} | |||
} | |||
} | |||
} | |||
using System; | |||
using System.Drawing; | |||
using System.Windows.Forms; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
namespace Shadowsocks.View | |||
{ | |||
public partial class ProxyForm : Form | |||
{ | |||
private ShadowsocksController controller; | |||
// this is a copy of configuration that we are working on | |||
private ProxyConfig _modifiedConfiguration; | |||
public ProxyForm(ShadowsocksController controller) | |||
{ | |||
this.Font = System.Drawing.SystemFonts.MessageBoxFont; | |||
InitializeComponent(); | |||
UpdateTexts(); | |||
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||
this.controller = controller; | |||
controller.ConfigChanged += controller_ConfigChanged; | |||
UpdateEnabled(); | |||
LoadCurrentConfiguration(); | |||
} | |||
private void UpdateTexts() | |||
{ | |||
UseProxyCheckBox.Text = I18N.GetString("Use Proxy"); | |||
ProxyAddrLabel.Text = I18N.GetString("Proxy Addr"); | |||
ProxyPortLable.Text = I18N.GetString("Proxy Port"); | |||
OKButton.Text = I18N.GetString("OK"); | |||
MyCancelButton.Text = I18N.GetString("Cancel"); | |||
this.Text = I18N.GetString("Edit Proxy"); | |||
} | |||
private void controller_ConfigChanged(object sender, EventArgs e) | |||
{ | |||
LoadCurrentConfiguration(); | |||
} | |||
private void LoadCurrentConfiguration() | |||
{ | |||
_modifiedConfiguration = controller.GetConfigurationCopy().proxy; | |||
UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy; | |||
ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer; | |||
ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString(); | |||
} | |||
private void OKButton_Click(object sender, EventArgs e) | |||
{ | |||
if (UseProxyCheckBox.Checked) | |||
{ | |||
try | |||
{ | |||
var proxy = ProxyServerTextBox.Text; | |||
var port = int.Parse(ProxyPortTextBox.Text); | |||
Configuration.CheckServer(proxy); | |||
Configuration.CheckPort(port); | |||
controller.EnableProxy(proxy, port); | |||
} | |||
catch (FormatException) | |||
{ | |||
MessageBox.Show(I18N.GetString("Illegal port number format")); | |||
return; | |||
} | |||
catch (Exception ex) | |||
{ | |||
MessageBox.Show(ex.Message); | |||
return; | |||
} | |||
} | |||
else | |||
{ | |||
controller.DisableProxy(); | |||
} | |||
_modifiedConfiguration.useProxy = UseProxyCheckBox.Checked; | |||
_modifiedConfiguration.proxyServer = ProxyServerTextBox.Text; | |||
var tmpProxyPort = 0; | |||
int.TryParse(ProxyPortTextBox.Text, out tmpProxyPort); | |||
_modifiedConfiguration.proxyPort = tmpProxyPort; | |||
controller.SaveProxyConfig(_modifiedConfiguration); | |||
this.Close(); | |||
} | |||
private void CancelButton_Click(object sender, EventArgs e) | |||
{ | |||
this.Close(); | |||
} | |||
private void ProxyForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
controller.ConfigChanged -= controller_ConfigChanged; | |||
} | |||
private void UseProxyCheckBox_CheckedChanged(object sender, EventArgs e) | |||
{ | |||
UpdateEnabled(); | |||
} | |||
private void UpdateEnabled() | |||
{ | |||
if (UseProxyCheckBox.Checked) | |||
{ | |||
ProxyServerTextBox.Enabled = true; | |||
ProxyPortTextBox.Enabled = true; | |||
} | |||
else | |||
{ | |||
ProxyServerTextBox.Clear(); | |||
ProxyPortTextBox.Clear(); | |||
ProxyServerTextBox.Enabled = false; | |||
ProxyPortTextBox.Enabled = false; | |||
} | |||
} | |||
} | |||
} |
@@ -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,8 @@ | |||
<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" /> | |||
<Compile Include="Controller\Service\AvailabilityStatistics.cs" /> | |||
@@ -177,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\Sockets\SocketUtil.cs" /> | |||
@@ -200,6 +207,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 +253,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'"> | |||