Browse Source

Merge pull request #2 from shadowsocks/master

合并主分支
pull/298/head^2
zhxxcq 9 years ago
parent
commit
608e0c9359
16 changed files with 297 additions and 90 deletions
  1. +24
    -6
      README.md
  2. +15
    -8
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  3. +1
    -1
      shadowsocks-csharp/Controller/Service/PACServer.cs
  4. +5
    -2
      shadowsocks-csharp/Controller/Service/PolipoRunner.cs
  5. +46
    -2
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  6. +2
    -2
      shadowsocks-csharp/Controller/Service/UpdateChecker.cs
  7. +2
    -2
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  8. +2
    -6
      shadowsocks-csharp/Controller/System/SystemProxy.cs
  9. +7
    -1
      shadowsocks-csharp/Data/cn.txt
  10. +44
    -42
      shadowsocks-csharp/Encryption/SodiumEncryptor.cs
  11. +5
    -3
      shadowsocks-csharp/Program.cs
  12. +24
    -3
      shadowsocks-csharp/Util/Util.cs
  13. +53
    -1
      shadowsocks-csharp/View/ConfigForm.Designer.cs
  14. +64
    -8
      shadowsocks-csharp/View/ConfigForm.cs
  15. +2
    -2
      shadowsocks-csharp/View/ConfigForm.resx
  16. +1
    -1
      shadowsocks-csharp/View/MenuViewController.cs

+ 24
- 6
README.md View File

@@ -3,34 +3,36 @@ Shadowsocks for Windows

[![Build Status]][Appveyor]

[中文说明]

#### Features

1. System proxy configuration
2. PAC mode and global mode
3. GFWList and user rules
3. [GFWList] and user rules
4. Supports HTTP proxy
5. Supports server auto switching
6. Supports UDP relay (see Usage)

#### Download

Download a [latest release].
Download the [latest release].

#### Basic

1. Find Shadowsocks icon in the notification tray
2. You can add multiple servers in servers menu
3. Select Enable System Proxy menu to enable system proxy. Please disable other
3. Select `Enable System Proxy` menu to enable system proxy. Please disable other
proxy addons in your browser, or set them to use system proxy
4. You can also configure your browser proxy manually if you don't want to enable
system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this
port in Server -> Edit Servers
port in `Servers -> Edit Servers`

#### PAC

1. You can change PAC rules by editing the PAC file. When you save the PAC file
with any editor, Shadowsocks will notify browsers about the change automatically
2. You can also update PAC file from GFWList (maintained by 3rd party)
2. You can also update PAC file from [GFWList] (maintained by 3rd party)
3. You can also use online PAC URL

#### Server Auto Switching
@@ -44,7 +46,20 @@ with any editor, Shadowsocks will notify browsers about the change automatically
#### UDP

For UDP, you need to use SocksCap or ProxyCap to force programs you want
to proxy to tunnel over Shadowsocks
to be proxied to tunnel over Shadowsocks

#### Multiple Instances

If you want to manage multiple servers using other tools like SwitchyOmega,
you can start multiple Shadowsocks instances. To avoid configuration conflicts,
copy Shadowsocks to a new directory and choose a different local port.

Also, make sure to use `SOCKS5` proxy in SwitchyOmega, since we have only
one HTTP proxy instance.

#### Server Configuration

Please visit [Servers] for more information.

#### Develop

@@ -58,3 +73,6 @@ GPLv3
[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side
[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E

+ 15
- 8
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -67,14 +67,21 @@ namespace Shadowsocks.Controller
string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
//ICMP echo. we can also set options and special bytes
//seems no need to use SendPingAsync
PingReply reply = ping.Send(server.server, Timeout);
state.data = new List<KeyValuePair<string, string>>();
state.data.Add(new KeyValuePair<string, string>("Timestamp", timestamp));
state.data.Add(new KeyValuePair<string, string>("Server", server.FriendlyName()));
state.data.Add(new KeyValuePair<string, string>("Status", reply.Status.ToString()));
state.data.Add(new KeyValuePair<string, string>("RoundtripTime", reply.RoundtripTime.ToString()));
//state.data.Add(new KeyValuePair<string, string>("data", reply.Buffer.ToString())); // The data of reply
Append(state.data);
try
{
PingReply reply = ping.Send(server.server, Timeout);
state.data = new List<KeyValuePair<string, string>>();
state.data.Add(new KeyValuePair<string, string>("Timestamp", timestamp));
state.data.Add(new KeyValuePair<string, string>("Server", server.FriendlyName()));
state.data.Add(new KeyValuePair<string, string>("Status", reply.Status.ToString()));
state.data.Add(new KeyValuePair<string, string>("RoundtripTime", reply.RoundtripTime.ToString()));
//state.data.Add(new KeyValuePair<string, string>("data", reply.Buffer.ToString())); // The data of reply
Append(state.data);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
}
}


+ 1
- 1
shadowsocks-csharp/Controller/Service/PACServer.cs View File

@@ -146,7 +146,7 @@ Connection: Close
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket);
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
}
catch (Exception e)
{


+ 5
- 2
shadowsocks-csharp/Controller/Service/PolipoRunner.cs View File

@@ -65,10 +65,13 @@ namespace Shadowsocks.Controller
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig));
if (!(temppath.EndsWith("\\") || temppath.EndsWith("/"))) {
temppath = temppath + "\\";
}
_process = new Process();
// Configure the process using the StartInfo properties.
_process.StartInfo.FileName = temppath + "/ss_privoxy.exe";
_process.StartInfo.Arguments = " \"" + temppath + "/privoxy.conf\"";
_process.StartInfo.FileName = temppath + "ss_privoxy.exe";
_process.StartInfo.Arguments = " \"" + temppath + "privoxy.conf\"";
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_process.StartInfo.UseShellExecute = true;
_process.StartInfo.CreateNoWindow = true;


+ 46
- 2
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -14,9 +14,18 @@ namespace Shadowsocks.Controller
class TCPRelay : Listener.Service
{
private ShadowsocksController _controller;
private DateTime _lastSweepTime;
public ISet<Handler> Handlers
{
get; set;
}
public TCPRelay(ShadowsocksController controller)
{
this._controller = controller;
this.Handlers = new HashSet<Handler>();
this._lastSweepTime = DateTime.Now;
}
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
@@ -33,9 +42,33 @@ namespace Shadowsocks.Controller
Handler handler = new Handler();
handler.connection = socket;
handler.controller = _controller;
handler.relay = this;
handler.Start(firstPacket, length);
return true;
IList<Handler> handlersToClose = new List<Handler>();
lock (this.Handlers)
{
this.Handlers.Add(handler);
Logging.Debug($"connections: {Handlers.Count}");
DateTime now = DateTime.Now;
if (now - _lastSweepTime > TimeSpan.FromSeconds(1))
{
_lastSweepTime = now;
foreach (Handler handler1 in this.Handlers)
{
if (now - handler1.lastActivity > TimeSpan.FromSeconds(1800))
{
handlersToClose.Add(handler1);
}
}
}
}
foreach (Handler handler1 in handlersToClose)
{
Logging.Debug("Closing timed out connection");
handler1.Close();
}
return true;
}
}
@@ -48,6 +81,10 @@ namespace Shadowsocks.Controller
public Socket remote;
public Socket connection;
public ShadowsocksController controller;
public TCPRelay relay;
public DateTime lastActivity;
private int retryCount = 0;
private bool connected;
@@ -55,7 +92,7 @@ namespace Shadowsocks.Controller
private byte[] _firstPacket;
private int _firstPacketLength;
// Size of receive buffer.
public const int RecvSize = 16384;
public const int RecvSize = 8192;
public const int BufferSize = RecvSize + 32;
private int totalRead = 0;
@@ -96,6 +133,7 @@ namespace Shadowsocks.Controller
this._firstPacket = firstPacket;
this._firstPacketLength = length;
this.HandshakeReceive();
this.lastActivity = DateTime.Now;
}
private void CheckClose()
@@ -108,6 +146,11 @@ namespace Shadowsocks.Controller
public void Close()
{
lock (relay.Handlers)
{
Logging.Debug($"connections: {relay.Handlers.Count}");
relay.Handlers.Remove(this);
}
lock (this)
{
if (closed)
@@ -485,6 +528,7 @@ namespace Shadowsocks.Controller
if (bytesRead > 0)
{
this.lastActivity = DateTime.Now;
int bytesToSend;
lock (decryptionLock)
{


+ 2
- 2
shadowsocks-csharp/Controller/Service/UpdateChecker.cs View File

@@ -12,13 +12,13 @@ namespace Shadowsocks.Controller
{
public class UpdateChecker
{
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-csharp/releases";
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases";
public string LatestVersionNumber;
public string LatestVersionURL;
public event EventHandler NewVersionFound;
public const string Version = "2.5.4";
public const string Version = "2.5.5";
public void CheckUpdate(Configuration config)
{


+ 2
- 2
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -360,7 +360,7 @@ namespace Shadowsocks.Controller
}
UpdateSystemProxy();
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
}
@@ -417,7 +417,7 @@ namespace Shadowsocks.Controller
{
while (true)
{
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(false);
Thread.Sleep(30 * 1000);
}
}


+ 2
- 6
shadowsocks-csharp/Controller/System/SystemProxy.cs View File

@@ -56,18 +56,14 @@ namespace Shadowsocks.Controller
pacUrl = "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now);
registry.SetValue("ProxyEnable", 0);
var readProxyServer = registry.GetValue("ProxyServer");
if (readProxyServer != null && readProxyServer.Equals("127.0.0.1:" + config.localPort.ToString()))
registry.SetValue("ProxyServer", "");
registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", pacUrl);
}
}
else
{
registry.SetValue("ProxyEnable", 0);
if (global)
{
registry.SetValue("ProxyServer", "");
}
registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", "");
}
//Set AutoDetectProxy Off


+ 7
- 1
shadowsocks-csharp/Data/cn.txt View File

@@ -19,6 +19,7 @@ Update Local PAC from GFWList=从 GFWList 更新本地 PAC
Edit User Rule for GFWList...=编辑 GFWList 的用户规则...
Show QRCode...=显示二维码...
Scan QRCode from Screen...=扫描屏幕上的二维码...
Availability Statistics=统计可用性
Show Logs...=显示日志...
About...=关于...
Quit=退出
@@ -41,6 +42,8 @@ Remarks=备注
OK=确定
Cancel=取消
New server=未配置的服务器
Move &Up=上移
Move D&own=下移

# QRCode Form

@@ -72,7 +75,10 @@ Failed to update PAC file =更新 PAC 文件失败
PAC updated=更新 PAC 成功
No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。
No QRCode found. Try to zoom in or move it to the center of the screen.=未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置
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.=如果想同时启动多个,可以另外复制一份到别的目录。
Failed to decode QRCode=无法解析二维码
Failed to update registry=无法修改注册表
System Proxy On: =系统代理已启用:
Running: Port {0}=正在运行:端口 {0}
Running: Port {0}=正在运行:端口 {0}

+ 44
- 42
shadowsocks-csharp/Encryption/SodiumEncryptor.cs View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Shadowsocks.Encryption
{
@@ -12,19 +13,17 @@ namespace Shadowsocks.Encryption
const int SODIUM_BLOCK_SIZE = 64;
static byte[] sodiumBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
protected int _encryptBytesRemaining;
protected int _decryptBytesRemaining;
protected ulong _encryptIC;
protected ulong _decryptIC;
protected byte[] _encryptBuf;
protected byte[] _decryptBuf;
public SodiumEncryptor(string method, string password)
: base(method, password)
{
InitKey(method, password);
_encryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
_decryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
}
private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> {
@@ -47,48 +46,51 @@ namespace Shadowsocks.Encryption
// TODO write a unidirection cipher so we don't have to if if if
int bytesRemaining;
ulong ic;
byte[] sodiumBuf;
byte[] iv;
if (isCipher)
{
bytesRemaining = _encryptBytesRemaining;
ic = _encryptIC;
sodiumBuf = _encryptBuf;
iv = _encryptIV;
}
else
{
bytesRemaining = _decryptBytesRemaining;
ic = _decryptIC;
sodiumBuf = _decryptBuf;
iv = _decryptIV;
}
int padding = bytesRemaining;
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length);
switch (_cipher)
// I'm tired. just add a big lock
// let's optimize for RAM instead of CPU
lock(sodiumBuf)
{
case CIPHER_SALSA20:
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
case CIPHER_CHACHA20:
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
}
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
padding += length;
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
if (isCipher)
{
bytesRemaining = _encryptBytesRemaining;
ic = _encryptIC;
iv = _encryptIV;
}
else
{
bytesRemaining = _decryptBytesRemaining;
ic = _decryptIC;
iv = _decryptIV;
}
int padding = bytesRemaining;
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length);
if (isCipher)
{
_encryptBytesRemaining = bytesRemaining;
_encryptIC = ic;
}
else
{
_decryptBytesRemaining = bytesRemaining;
_decryptIC = ic;
switch (_cipher)
{
case CIPHER_SALSA20:
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
case CIPHER_CHACHA20:
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
}
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
padding += length;
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
if (isCipher)
{
_encryptBytesRemaining = bytesRemaining;
_encryptIC = ic;
}
else
{
_decryptBytesRemaining = bytesRemaining;
_decryptIC = ic;
}
}
}


+ 5
- 3
shadowsocks-csharp/Program.cs View File

@@ -18,8 +18,8 @@ namespace Shadowsocks
[STAThread]
static void Main()
{
Util.Utils.ReleaseMemory();
using (Mutex mutex = new Mutex(false, "Global\\" + "71981632-A427-497F-AB91-241CD227EC1F"))
Util.Utils.ReleaseMemory(true);
using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode()))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
@@ -31,7 +31,9 @@ namespace Shadowsocks
{
Process oldProcess = oldProcesses[0];
}
MessageBox.Show("Shadowsocks is already running.\n\nFind Shadowsocks icon in your notify tray.");
MessageBox.Show(I18N.GetString("Find Shadowsocks icon in your notify tray.") + "\n" +
I18N.GetString("If you want to start multiple Shadowsocks, make a copy in another directory."),
I18N.GetString("Shadowsocks is already running."));
return;
}
Directory.SetCurrentDirectory(Application.StartupPath);


+ 24
- 3
shadowsocks-csharp/Util/Util.cs View File

@@ -10,7 +10,7 @@ namespace Shadowsocks.Util
{
public class Utils
{
public static void ReleaseMemory()
public static void ReleaseMemory(bool removePages)
{
// release any unused pages
// making the numbers look good in task manager
@@ -20,8 +20,29 @@ namespace Shadowsocks.Util
// which is part of user experience
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
(UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
if (removePages)
{
// as some users have pointed out
// removing pages from working set will cause some IO
// which lowered user experience for another group of users
//
// so we do 2 more things here to satisfy them:
// 1. only remove pages once when configuration is changed
// 2. add more comments here to tell users that calling
// this function will not be more frequent than
// IM apps writing chat logs, or web browsers writing cache files
// if they're so concerned about their disk, they should
// uninstall all IM apps and web browsers
//
// please open an issue if you're worried about anything else in your computer
// no matter it's GPU performance, monitor contrast, audio fidelity
// or anything else in the task manager
// we'll do as much as we can to help you
//
// just kidding
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
(UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
}
}
public static string UnGzip(byte[] buf)


+ 53
- 1
shadowsocks-csharp/View/ConfigForm.Designer.cs View File

@@ -47,6 +47,9 @@
this.ServerGroupBox = new System.Windows.Forms.GroupBox();
this.ServersListBox = new System.Windows.Forms.ListBox();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel();
this.MoveDownButton = new System.Windows.Forms.Button();
this.MoveUpButton = new System.Windows.Forms.Button();
this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel();
this.ProxyPortTextBox = new System.Windows.Forms.TextBox();
this.ProxyPortLabel = new System.Windows.Forms.Label();
@@ -55,6 +58,7 @@
this.tableLayoutPanel1.SuspendLayout();
this.ServerGroupBox.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.tableLayoutPanel6.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout();
this.tableLayoutPanel3.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
@@ -184,7 +188,7 @@
//
// EncryptionSelect
//
this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.EncryptionSelect.FormattingEnabled = true;
@@ -281,6 +285,7 @@
//
this.ServersListBox.FormattingEnabled = true;
this.ServersListBox.IntegralHeight = false;
this.ServersListBox.ItemHeight = 12;
this.ServersListBox.Location = new System.Drawing.Point(0, 0);
this.ServersListBox.Margin = new System.Windows.Forms.Padding(0);
this.ServersListBox.Name = "ServersListBox";
@@ -295,6 +300,7 @@
this.tableLayoutPanel2.ColumnCount = 2;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel6, 0, 2);
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel5, 1, 1);
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2);
this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0);
@@ -310,6 +316,48 @@
this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238);
this.tableLayoutPanel2.TabIndex = 7;
//
// tableLayoutPanel6
//
this.tableLayoutPanel6.AutoSize = true;
this.tableLayoutPanel6.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.tableLayoutPanel6.ColumnCount = 2;
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.Controls.Add(this.MoveDownButton, 1, 0);
this.tableLayoutPanel6.Controls.Add(this.MoveUpButton, 0, 0);
this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Top;
this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 211);
this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel6.Name = "tableLayoutPanel6";
this.tableLayoutPanel6.RowCount = 1;
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.Size = new System.Drawing.Size(166, 32);
this.tableLayoutPanel6.TabIndex = 10;
//
// MoveDownButton
//
this.MoveDownButton.Dock = System.Windows.Forms.DockStyle.Right;
this.MoveDownButton.Location = new System.Drawing.Point(86, 6);
this.MoveDownButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3);
this.MoveDownButton.Name = "MoveDownButton";
this.MoveDownButton.Size = new System.Drawing.Size(80, 23);
this.MoveDownButton.TabIndex = 7;
this.MoveDownButton.Text = "Move D&own";
this.MoveDownButton.UseVisualStyleBackColor = true;
this.MoveDownButton.Click += new System.EventHandler(this.MoveDownButton_Click);
//
// MoveUpButton
//
this.MoveUpButton.Dock = System.Windows.Forms.DockStyle.Left;
this.MoveUpButton.Location = new System.Drawing.Point(0, 6);
this.MoveUpButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3);
this.MoveUpButton.Name = "MoveUpButton";
this.MoveUpButton.Size = new System.Drawing.Size(80, 23);
this.MoveUpButton.TabIndex = 6;
this.MoveUpButton.Text = "Move &Up";
this.MoveUpButton.UseVisualStyleBackColor = true;
this.MoveUpButton.Click += new System.EventHandler(this.MoveUpButton_Click);
//
// tableLayoutPanel5
//
this.tableLayoutPanel5.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
@@ -418,6 +466,7 @@
this.ServerGroupBox.PerformLayout();
this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.tableLayoutPanel6.ResumeLayout(false);
this.tableLayoutPanel5.ResumeLayout(false);
this.tableLayoutPanel5.PerformLayout();
this.tableLayoutPanel3.ResumeLayout(false);
@@ -453,6 +502,9 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5;
private System.Windows.Forms.TextBox ProxyPortTextBox;
private System.Windows.Forms.Label ProxyPortLabel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6;
private System.Windows.Forms.Button MoveDownButton;
private System.Windows.Forms.Button MoveUpButton;
}
}

+ 64
- 8
shadowsocks-csharp/View/ConfigForm.cs View File

@@ -51,6 +51,8 @@ namespace Shadowsocks.View
ServerGroupBox.Text = I18N.GetString("Server");
OKButton.Text = I18N.GetString("OK");
MyCancelButton.Text = I18N.GetString("Cancel");
MoveUpButton.Text = I18N.GetString("Move &Up");
MoveDownButton.Text = I18N.GetString("Move D&own");
this.Text = I18N.GetString("Edit Servers");
}
@@ -58,7 +60,7 @@ namespace Shadowsocks.View
{
LoadCurrentConfiguration();
}
private void ShowWindow()
{
this.Opacity = 1;
@@ -87,7 +89,7 @@ namespace Shadowsocks.View
Configuration.CheckLocalPort(localPort);
_modifiedConfiguration.configs[_oldSelectedIndex] = server;
_modifiedConfiguration.localPort = localPort;
return true;
}
catch (FormatException)
@@ -113,12 +115,6 @@ namespace Shadowsocks.View
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString();
EncryptionSelect.Text = server.method ?? "aes-256-cfb";
RemarksTextBox.Text = server.remarks;
ServerGroupBox.Visible = true;
//IPTextBox.Focus();
}
else
{
ServerGroupBox.Visible = false;
}
}
@@ -141,6 +137,7 @@ namespace Shadowsocks.View
_oldSelectedIndex = 0;
}
ServersListBox.SelectedIndex = _oldSelectedIndex;
UpdateMoveUpAndDownButton();
LoadSelectedServer();
}
@@ -162,6 +159,7 @@ namespace Shadowsocks.View
ServersListBox.SelectedIndex = _oldSelectedIndex;
return;
}
UpdateMoveUpAndDownButton();
LoadSelectedServer();
_oldSelectedIndex = ServersListBox.SelectedIndex;
}
@@ -208,7 +206,9 @@ namespace Shadowsocks.View
MessageBox.Show(I18N.GetString("Please add at least one server"));
return;
}
int index = _modifiedConfiguration.index;
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort);
controller.SelectServerIndex(index);
this.Close();
}
@@ -227,5 +227,61 @@ namespace Shadowsocks.View
controller.ConfigChanged -= controller_ConfigChanged;
}
private void MoveConfigItem(int step)
{
int index = ServersListBox.SelectedIndex;
Server server = _modifiedConfiguration.configs[index];
object item = ServersListBox.SelectedItem;
_modifiedConfiguration.configs.Remove(server);
_modifiedConfiguration.configs.Insert(index + step, server);
_modifiedConfiguration.index += step;
ServersListBox.BeginUpdate();
_oldSelectedIndex = index + step;
ServersListBox.Items.Remove(item);
ServersListBox.Items.Insert(index + step, item);
ServersListBox.SelectedIndex = index + step;
ServersListBox.EndUpdate();
UpdateMoveUpAndDownButton();
}
private void UpdateMoveUpAndDownButton()
{
if (ServersListBox.SelectedIndex == 0)
{
MoveUpButton.Enabled = false;
}
else
{
MoveUpButton.Enabled = true;
}
if (ServersListBox.SelectedIndex == ServersListBox.Items.Count - 1)
{
MoveDownButton.Enabled = false;
}
else
{
MoveDownButton.Enabled = true;
}
}
private void MoveUpButton_Click(object sender, EventArgs e)
{
if (ServersListBox.SelectedIndex > 0)
{
MoveConfigItem(-1); // -1 means move backward
}
}
private void MoveDownButton_Click(object sender, EventArgs e)
{
if (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1)
{
MoveConfigItem(+1); // +1 means move forward
}
}
}
}

+ 2
- 2
shadowsocks-csharp/View/ConfigForm.resx View File

@@ -112,9 +112,9 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

+ 1
- 1
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -322,7 +322,7 @@ namespace Shadowsocks.View
void configForm_FormClosed(object sender, FormClosedEventArgs e)
{
configForm = null;
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
ShowFirstTimeBalloon();
}


Loading…
Cancel
Save