@@ -6,10 +6,11 @@ using System.Net; | |||||
using SimpleJson; | using SimpleJson; | ||||
using System.Net.Http; | using System.Net.Http; | ||||
using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||
using System.Net.Sockets; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using SimpleJson = SimpleJson.SimpleJson; | |||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using Timer = System.Threading.Timer; | using Timer = System.Threading.Timer; | ||||
@@ -20,35 +21,43 @@ namespace Shadowsocks.Controller | |||||
internal class AvailabilityStatistics | internal class AvailabilityStatistics | ||||
{ | { | ||||
public static readonly string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; | |||||
private const string StatisticsFilesName = "shadowsocks.availability.csv"; | private const string StatisticsFilesName = "shadowsocks.availability.csv"; | ||||
private const string Delimiter = ","; | private const string Delimiter = ","; | ||||
private const int Timeout = 500; | private const int Timeout = 500; | ||||
private const int Repeat = 4; //repeat times every evaluation | |||||
private const int Interval = 10*60*1000; //evaluate proxies every 15 minutes | |||||
private const int delayBeforeStart = 1*1000; //delay 1 second before start | |||||
private const int DelayBeforeStart = 1000; | |||||
private int _repeat => _config.RepeatTimesNum; | |||||
private int _interval => (int) TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds; | |||||
private Timer _timer; | private Timer _timer; | ||||
private State _state; | private State _state; | ||||
private List<Server> _servers; | private List<Server> _servers; | ||||
private StatisticsStrategyConfiguration _config; | |||||
public static string AvailabilityStatisticsFile; | public static string AvailabilityStatisticsFile; | ||||
//static constructor to initialize every public static fields before refereced | //static constructor to initialize every public static fields before refereced | ||||
static AvailabilityStatistics() | static AvailabilityStatistics() | ||||
{ | { | ||||
string temppath = Utils.GetTempPath(); | |||||
var temppath = Utils.GetTempPath(); | |||||
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); | AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); | ||||
} | } | ||||
public bool Set(bool enabled) | |||||
public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig) | |||||
{ | { | ||||
UpdateConfiguration(config, statisticsConfig); | |||||
} | |||||
public bool Set(StatisticsStrategyConfiguration config) | |||||
{ | |||||
_config = config; | |||||
try | try | ||||
{ | { | ||||
if (enabled) | |||||
if (config.StatisticsEnabled) | |||||
{ | { | ||||
if (_timer?.Change(0, Interval) == null) | |||||
if (_timer?.Change(DelayBeforeStart, _interval) == null) | |||||
{ | { | ||||
_state = new State(); | _state = new State(); | ||||
_timer = new Timer(Evaluate, _state, 0, Interval); | |||||
_timer = new Timer(Evaluate, _state, DelayBeforeStart, _interval); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
@@ -66,7 +75,7 @@ namespace Shadowsocks.Controller | |||||
//hardcode | //hardcode | ||||
//TODO: backup reliable isp&geolocation provider or a local database is required | //TODO: backup reliable isp&geolocation provider or a local database is required | ||||
private static async Task<DataList> getGeolocationAndISP() | |||||
public static async Task<DataList> GetGeolocationAndIsp() | |||||
{ | { | ||||
Logging.Debug("Retrive information of geolocation and isp"); | Logging.Debug("Retrive information of geolocation and isp"); | ||||
const string api = "http://ip-api.com/json"; | const string api = "http://ip-api.com/json"; | ||||
@@ -92,24 +101,25 @@ namespace Shadowsocks.Controller | |||||
string isp = obj["isp"]; | string isp = obj["isp"]; | ||||
string regionName = obj["regionName"]; | string regionName = obj["regionName"]; | ||||
if (country == null || city == null || isp == null || regionName == null) return ret; | if (country == null || city == null || isp == null || regionName == null) return ret; | ||||
ret[0] = new DataUnit(State.Geolocation, $"{country} {regionName} {city}"); | |||||
ret[0] = new DataUnit(State.Geolocation, $"{country} | {regionName} | {city}"); | |||||
ret[1] = new DataUnit(State.ISP, isp); | ret[1] = new DataUnit(State.ISP, isp); | ||||
return ret; | return ret; | ||||
} | } | ||||
private static async Task<List<DataList>> ICMPTest(Server server) | |||||
private async Task<List<DataList>> ICMPTest(Server server) | |||||
{ | { | ||||
Logging.Debug("eveluating " + server.FriendlyName()); | |||||
Logging.Debug("Ping " + server.FriendlyName()); | |||||
if (server.server == "") return null; | if (server.server == "") return null; | ||||
var IP = Dns.GetHostAddresses(server.server).First(ip => ip.AddressFamily == AddressFamily.InterNetwork); | |||||
var ping = new Ping(); | var ping = new Ping(); | ||||
var ret = new List<DataList>(); | var ret = new List<DataList>(); | ||||
foreach ( | foreach ( | ||||
var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))) | |||||
var timestamp in Enumerable.Range(0, _repeat).Select(_ => DateTime.Now.ToString(DateTimePattern))) | |||||
{ | { | ||||
//ICMP echo. we can also set options and special bytes | //ICMP echo. we can also set options and special bytes | ||||
try | try | ||||
{ | { | ||||
var reply = await ping.SendTaskAsync(server.server, Timeout); | |||||
var reply = ping.Send(IP, Timeout); | |||||
ret.Add(new List<KeyValuePair<string, string>> | ret.Add(new List<KeyValuePair<string, string>> | ||||
{ | { | ||||
new KeyValuePair<string, string>("Timestamp", timestamp), | new KeyValuePair<string, string>("Timestamp", timestamp), | ||||
@@ -118,6 +128,8 @@ namespace Shadowsocks.Controller | |||||
new KeyValuePair<string, string>("RoundtripTime", reply?.RoundtripTime.ToString()) | new KeyValuePair<string, string>("RoundtripTime", reply?.RoundtripTime.ToString()) | ||||
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply | //new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply | ||||
}); | }); | ||||
Thread.Sleep(new Random().Next() % Timeout); | |||||
//Do ICMPTest in a random frequency | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -130,7 +142,7 @@ namespace Shadowsocks.Controller | |||||
private async void Evaluate(object obj) | private async void Evaluate(object obj) | ||||
{ | { | ||||
var geolocationAndIsp = getGeolocationAndISP(); | |||||
var geolocationAndIsp = GetGeolocationAndIsp(); | |||||
foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) | foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) | ||||
{ | { | ||||
if (dataLists == null) continue; | if (dataLists == null) continue; | ||||
@@ -156,16 +168,23 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
lines = new[] {dataLine}; | lines = new[] {dataLine}; | ||||
} | } | ||||
File.AppendAllLines(AvailabilityStatisticsFile, lines); | |||||
try | |||||
{ | |||||
File.AppendAllLines(AvailabilityStatisticsFile, lines); | |||||
} | |||||
catch (IOException e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | } | ||||
internal void UpdateConfiguration(Configuration config) | |||||
internal void UpdateConfiguration(Configuration config, StatisticsStrategyConfiguration statisticsConfig) | |||||
{ | { | ||||
Set(config.availabilityStatistics); | |||||
Set(statisticsConfig); | |||||
_servers = config.configs; | _servers = config.configs; | ||||
} | } | ||||
private class State | |||||
public class State | |||||
{ | { | ||||
public DataList dataList = new DataList(); | public DataList dataList = new DataList(); | ||||
public const string Geolocation = "Geolocation"; | public const string Geolocation = "Geolocation"; | ||||
@@ -258,14 +258,12 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
public void ToggleAvailabilityStatistics(bool enabled) | |||||
public void UpdateStatisticsConfiguration(bool enabled) | |||||
{ | { | ||||
if (_availabilityStatics != null) | |||||
{ | |||||
_availabilityStatics.Set(enabled); | |||||
_config.availabilityStatistics = enabled; | |||||
SaveConfig(_config); | |||||
} | |||||
if (_availabilityStatics == null) return; | |||||
_availabilityStatics.UpdateConfiguration(_config, StatisticsConfiguration); | |||||
_config.availabilityStatistics = enabled; | |||||
SaveConfig(_config); | |||||
} | } | ||||
public void SavePACUrl(string pacUrl) | public void SavePACUrl(string pacUrl) | ||||
@@ -315,9 +313,9 @@ namespace Shadowsocks.Controller | |||||
if (_availabilityStatics == null) | if (_availabilityStatics == null) | ||||
{ | { | ||||
_availabilityStatics = new AvailabilityStatistics(); | |||||
_availabilityStatics = new AvailabilityStatistics(_config, StatisticsConfiguration); | |||||
} | } | ||||
_availabilityStatics.UpdateConfiguration(_config); | |||||
_availabilityStatics.UpdateConfiguration(_config, StatisticsConfiguration); | |||||
if (_listener != null) | if (_listener != null) | ||||
{ | { | ||||
@@ -1,5 +1,6 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Globalization; | |||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | using System.Net; | ||||
@@ -7,19 +8,25 @@ using System.Text; | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using System.IO; | using System.IO; | ||||
using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||
using System.Threading; | |||||
using System.Windows.Forms; | |||||
using Newtonsoft.Json; | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Timer = System.Threading.Timer; | |||||
namespace Shadowsocks.Controller.Strategy | namespace Shadowsocks.Controller.Strategy | ||||
{ | { | ||||
using DataUnit = KeyValuePair<string, string>; | |||||
using DataList = List<KeyValuePair<string, string>>; | |||||
class StatisticsStrategy : IStrategy | class StatisticsStrategy : IStrategy | ||||
{ | { | ||||
private readonly ShadowsocksController _controller; | private readonly ShadowsocksController _controller; | ||||
private Server _currentServer; | private Server _currentServer; | ||||
private readonly Timer _timer; | private readonly Timer _timer; | ||||
private Dictionary<string, StatisticsData> _statistics; | |||||
private const int CachedInterval = 30*60*1000; //choose a new server every 30 minutes | |||||
private const int RetryInterval = 2*60*1000; //choose a new server every 30 minutes | |||||
private Dictionary<string, List<StatisticsRawData>> _rawStatistics; | |||||
private int ChoiceKeptMilliseconds | |||||
=> (int) TimeSpan.FromMinutes(_controller.StatisticsConfiguration.ChoiceKeptMinutes).TotalMilliseconds; | |||||
private const int RetryInterval = 2*60*1000; //retry 2 minutes after failed | |||||
public StatisticsStrategy(ShadowsocksController controller) | public StatisticsStrategy(ShadowsocksController controller) | ||||
{ | { | ||||
@@ -38,13 +45,6 @@ namespace Shadowsocks.Controller.Strategy | |||||
ChooseNewServer(servers); | ChooseNewServer(servers); | ||||
} | } | ||||
/* | |||||
return a dict: | |||||
{ | |||||
'ServerFriendlyName1':StatisticsData, | |||||
'ServerFriendlyName2':... | |||||
} | |||||
*/ | |||||
private void LoadStatistics() | private void LoadStatistics() | ||||
{ | { | ||||
try | try | ||||
@@ -53,32 +53,30 @@ namespace Shadowsocks.Controller.Strategy | |||||
Logging.Debug($"loading statistics from {path}"); | Logging.Debug($"loading statistics from {path}"); | ||||
if (!File.Exists(path)) | if (!File.Exists(path)) | ||||
{ | { | ||||
LogWhenEnabled($"statistics file does not exist, try to reload {RetryInterval} minutes later"); | |||||
_timer.Change(RetryInterval, CachedInterval); | |||||
LogWhenEnabled($"statistics file does not exist, try to reload {RetryInterval/60/1000} minutes later"); | |||||
_timer.Change(RetryInterval, ChoiceKeptMilliseconds); | |||||
return; | return; | ||||
} | } | ||||
_statistics = (from l in File.ReadAllLines(path) | |||||
.Skip(1) | |||||
let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) | |||||
let rawData = new | |||||
{ | |||||
ServerName = strings[1], | |||||
IPStatus = strings[2], | |||||
RoundtripTime = int.Parse(strings[3]) | |||||
} | |||||
group rawData by rawData.ServerName into server | |||||
select new | |||||
{ | |||||
ServerName = server.Key, | |||||
data = new StatisticsData | |||||
{ | |||||
SuccessTimes = server.Count(data => IPStatus.Success.ToString().Equals(data.IPStatus)), | |||||
TimedOutTimes = server.Count(data => IPStatus.TimedOut.ToString().Equals(data.IPStatus)), | |||||
AverageResponse = Convert.ToInt32(server.Average(data => data.RoundtripTime)), | |||||
MinResponse = server.Min(data => data.RoundtripTime), | |||||
MaxResponse = server.Max(data => data.RoundtripTime) | |||||
} | |||||
}).ToDictionary(server => server.ServerName, server => server.data); | |||||
_rawStatistics = (from l in File.ReadAllLines(path) | |||||
.Skip(1) | |||||
let strings = l.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries) | |||||
let rawData = new StatisticsRawData | |||||
{ | |||||
Timestamp = strings[0], | |||||
ServerName = strings[1], | |||||
ICMPStatus = strings[2], | |||||
RoundtripTime = int.Parse(strings[3]), | |||||
Geolocation = 5 > strings.Length ? | |||||
null | |||||
: strings[4], | |||||
ISP = 6 > strings.Length ? null : strings[5] | |||||
} | |||||
group rawData by rawData.ServerName into server | |||||
select new | |||||
{ | |||||
ServerName = server.Key, | |||||
data = server.ToList() | |||||
}).ToDictionary(server => server.ServerName, server=> server.data); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -88,15 +86,67 @@ namespace Shadowsocks.Controller.Strategy | |||||
//return the score by data | //return the score by data | ||||
//server with highest score will be choosen | //server with highest score will be choosen | ||||
private static double GetScore(StatisticsData data) | |||||
private float GetScore(IEnumerable<StatisticsRawData> rawDataList) | |||||
{ | |||||
var config = _controller.StatisticsConfiguration; | |||||
if (config.ByIsp) | |||||
{ | |||||
var current = AvailabilityStatistics.GetGeolocationAndIsp().Result; | |||||
rawDataList = rawDataList.Where(data => data.Geolocation == current[0].Value || data.Geolocation == AvailabilityStatistics.State.Unknown); | |||||
rawDataList = rawDataList.Where(data => data.ISP == current[1].Value || data.ISP == AvailabilityStatistics.State.Unknown); | |||||
if (rawDataList.LongCount() == 0) return 0; | |||||
} | |||||
if (config.ByHourOfDay) | |||||
{ | |||||
var currentHour = DateTime.Now.Hour; | |||||
rawDataList = rawDataList.Where(data => | |||||
{ | |||||
DateTime dateTime; | |||||
DateTime.TryParseExact(data.Timestamp, AvailabilityStatistics.DateTimePattern, null, | |||||
DateTimeStyles.None, out dateTime); | |||||
var result = dateTime.Hour.Equals(currentHour); | |||||
return result; | |||||
}); | |||||
if (rawDataList.LongCount() == 0) return 0; | |||||
} | |||||
var dataList = rawDataList as IList<StatisticsRawData> ?? rawDataList.ToList(); | |||||
var serverName = dataList[0]?.ServerName; | |||||
var SuccessTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.Success.ToString())); | |||||
var TimedOutTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.TimedOut.ToString())); | |||||
var statisticsData = new StatisticsData() | |||||
{ | |||||
PackageLoss = TimedOutTimes / (SuccessTimes + TimedOutTimes) * 100, | |||||
AverageResponse = Convert.ToInt32(dataList.Average(data => data.RoundtripTime)), | |||||
MinResponse = dataList.Min(data => data.RoundtripTime), | |||||
MaxResponse = dataList.Max(data => data.RoundtripTime) | |||||
}; | |||||
float factor; | |||||
float score = 0; | |||||
if (!config.Calculations.TryGetValue("PackageLoss", out factor)) factor = 0; | |||||
score += statisticsData.PackageLoss*factor; | |||||
if (!config.Calculations.TryGetValue("AverageResponse", out factor)) factor = 0; | |||||
score += statisticsData.AverageResponse*factor; | |||||
if (!config.Calculations.TryGetValue("MinResponse", out factor)) factor = 0; | |||||
score += statisticsData.MinResponse*factor; | |||||
if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0; | |||||
score += statisticsData.MaxResponse*factor; | |||||
Logging.Debug($"{serverName} {JsonConvert.SerializeObject(statisticsData)}"); | |||||
return score; | |||||
} | |||||
class StatisticsRawData | |||||
{ | { | ||||
return (double)data.SuccessTimes / (data.SuccessTimes + data.TimedOutTimes); //simply choose min package loss | |||||
public string Timestamp; | |||||
public string ServerName; | |||||
public string ICMPStatus; | |||||
public int RoundtripTime; | |||||
public string Geolocation; | |||||
public string ISP ; | |||||
} | } | ||||
public class StatisticsData | public class StatisticsData | ||||
{ | { | ||||
public int SuccessTimes; | |||||
public int TimedOutTimes; | |||||
public float PackageLoss; | |||||
public int AverageResponse; | public int AverageResponse; | ||||
public int MinResponse; | public int MinResponse; | ||||
public int MaxResponse; | public int MaxResponse; | ||||
@@ -104,7 +154,7 @@ namespace Shadowsocks.Controller.Strategy | |||||
private void ChooseNewServer(List<Server> servers) | private void ChooseNewServer(List<Server> servers) | ||||
{ | { | ||||
if (_statistics == null || servers.Count == 0) | |||||
if (_rawStatistics == null || servers.Count == 0) | |||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
@@ -112,17 +162,17 @@ namespace Shadowsocks.Controller.Strategy | |||||
{ | { | ||||
var bestResult = (from server in servers | var bestResult = (from server in servers | ||||
let name = server.FriendlyName() | let name = server.FriendlyName() | ||||
where _statistics.ContainsKey(name) | |||||
where _rawStatistics.ContainsKey(name) | |||||
select new | select new | ||||
{ | { | ||||
server, | server, | ||||
score = GetScore(_statistics[name]) | |||||
score = GetScore(_rawStatistics[name]) | |||||
} | } | ||||
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); | ).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); | ||||
if (!_currentServer.Equals(bestResult.server)) //output when enabled | if (!_currentServer.Equals(bestResult.server)) //output when enabled | ||||
{ | { | ||||
LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by package loss:{1 - bestResult.score}"); | |||||
LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by statistics: score {bestResult.score}"); | |||||
} | } | ||||
_currentServer = bestResult.server; | _currentServer = bestResult.server; | ||||
} | } | ||||
@@ -160,7 +210,7 @@ namespace Shadowsocks.Controller.Strategy | |||||
public void ReloadServers() | public void ReloadServers() | ||||
{ | { | ||||
ChooseNewServer(_controller.GetCurrentConfiguration().configs); | ChooseNewServer(_controller.GetCurrentConfiguration().configs); | ||||
_timer?.Change(0, CachedInterval); | |||||
_timer?.Change(0, ChoiceKeptMilliseconds); | |||||
} | } | ||||
public void SetFailure(Server server) | public void SetFailure(Server server) | ||||
@@ -14,12 +14,12 @@ namespace Shadowsocks.Model | |||||
public class StatisticsStrategyConfiguration | public class StatisticsStrategyConfiguration | ||||
{ | { | ||||
public static readonly string ID = "com.shadowsocks.strategy.statistics"; | public static readonly string ID = "com.shadowsocks.strategy.statistics"; | ||||
private bool _statisticsEnabled; | |||||
private bool _byIsp; | |||||
private bool _byHourOfDay; | |||||
private int _choiceKeptMinutes; | |||||
private int _dataCollectionMinutes; | |||||
private int _repeatTimesNum; | |||||
private bool _statisticsEnabled = true; | |||||
private bool _byIsp = false; | |||||
private bool _byHourOfDay = false; | |||||
private int _choiceKeptMinutes = 10; | |||||
private int _dataCollectionMinutes = 10; | |||||
private int _repeatTimesNum = 4; | |||||
private const string ConfigFile = "statistics-config.json"; | private const string ConfigFile = "statistics-config.json"; | ||||
@@ -34,7 +34,9 @@ namespace Shadowsocks.Model | |||||
} | } | ||||
catch (FileNotFoundException e) | catch (FileNotFoundException e) | ||||
{ | { | ||||
return new StatisticsStrategyConfiguration(); | |||||
var configuration = new StatisticsStrategyConfiguration(); | |||||
Save(configuration); | |||||
return configuration; | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -37,6 +37,7 @@ | |||||
// | // | ||||
// factorNum | // factorNum | ||||
// | // | ||||
this.factorNum.DecimalPlaces = 2; | |||||
this.factorNum.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | this.factorNum.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | ||||
this.factorNum.Increment = new decimal(new int[] { | this.factorNum.Increment = new decimal(new int[] { | ||||
1, | 1, | ||||
@@ -3,6 +3,7 @@ using System.Collections.Generic; | |||||
using System.ComponentModel; | using System.ComponentModel; | ||||
using System.Drawing; | using System.Drawing; | ||||
using System.Data; | using System.Data; | ||||
using System.Globalization; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Text; | using System.Text; | ||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
@@ -11,13 +12,14 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
public partial class CalculationControl : UserControl | public partial class CalculationControl : UserControl | ||||
{ | { | ||||
public CalculationControl(string value) | |||||
public CalculationControl(string text, float value) | |||||
{ | { | ||||
InitializeComponent(); | InitializeComponent(); | ||||
valueLabel.Text = value; | |||||
valueLabel.Text = text; | |||||
factorNum.Value = (decimal) value; | |||||
} | } | ||||
public string Value => valueLabel.Text; | public string Value => valueLabel.Text; | ||||
public float Factor => float.Parse(factorNum.Text); | |||||
public float Factor => (float) factorNum.Value; | |||||
} | } | ||||
} | } |
@@ -29,7 +29,6 @@ namespace Shadowsocks.View | |||||
private MenuItem enableItem; | private MenuItem enableItem; | ||||
private MenuItem modeItem; | private MenuItem modeItem; | ||||
private MenuItem AutoStartupItem; | private MenuItem AutoStartupItem; | ||||
private MenuItem AvailabilityStatistics; | |||||
private MenuItem ShareOverLANItem; | private MenuItem ShareOverLANItem; | ||||
private MenuItem SeperatorItem; | private MenuItem SeperatorItem; | ||||
private MenuItem ConfigItem; | private MenuItem ConfigItem; | ||||
@@ -179,7 +178,6 @@ namespace Shadowsocks.View | |||||
}), | }), | ||||
new MenuItem("-"), | new MenuItem("-"), | ||||
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), | this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), | ||||
this.AvailabilityStatistics = CreateMenuItem("Availability Statistics", new EventHandler(this.AvailabilityStatisticsItem_Click)), | |||||
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), | this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), | ||||
new MenuItem("-"), | new MenuItem("-"), | ||||
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | ||||
@@ -264,7 +262,6 @@ namespace Shadowsocks.View | |||||
PACModeItem.Checked = !config.global; | PACModeItem.Checked = !config.global; | ||||
ShareOverLANItem.Checked = config.shareOverLan; | ShareOverLANItem.Checked = config.shareOverLan; | ||||
AutoStartupItem.Checked = AutoStartup.Check(); | AutoStartupItem.Checked = AutoStartup.Check(); | ||||
AvailabilityStatistics.Checked = config.availabilityStatistics; | |||||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | ||||
localPACItem.Checked = !onlinePACItem.Checked; | localPACItem.Checked = !onlinePACItem.Checked; | ||||
UpdatePACItemsEnabledStatus(); | UpdatePACItemsEnabledStatus(); | ||||
@@ -535,11 +532,6 @@ namespace Shadowsocks.View | |||||
} | } | ||||
} | } | ||||
private void AvailabilityStatisticsItem_Click(object sender, EventArgs e) { | |||||
AvailabilityStatistics.Checked = !AvailabilityStatistics.Checked; | |||||
controller.ToggleAvailabilityStatistics(AvailabilityStatistics.Checked); | |||||
} | |||||
private void LocalPACItem_Click(object sender, EventArgs e) | private void LocalPACItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
if (!localPACItem.Checked) | if (!localPACItem.Checked) | ||||
@@ -33,9 +33,9 @@ | |||||
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); | System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); | ||||
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); | System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); | ||||
System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); | System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); | ||||
System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series(); | |||||
this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); | this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); | ||||
this.byISPCheckBox = new System.Windows.Forms.CheckBox(); | this.byISPCheckBox = new System.Windows.Forms.CheckBox(); | ||||
this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); | |||||
this.label2 = new System.Windows.Forms.Label(); | this.label2 = new System.Windows.Forms.Label(); | ||||
this.label3 = new System.Windows.Forms.Label(); | this.label3 = new System.Windows.Forms.Label(); | ||||
this.label4 = new System.Windows.Forms.Label(); | this.label4 = new System.Windows.Forms.Label(); | ||||
@@ -57,8 +57,8 @@ | |||||
this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel(); | this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel(); | ||||
this.CancelButton = new System.Windows.Forms.Button(); | this.CancelButton = new System.Windows.Forms.Button(); | ||||
this.OKButton = new System.Windows.Forms.Button(); | this.OKButton = new System.Windows.Forms.Button(); | ||||
this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); | |||||
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); | ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); | ||||
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); | |||||
this.groupBox1.SuspendLayout(); | this.groupBox1.SuspendLayout(); | ||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); | ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); | ||||
this.splitContainer1.Panel1.SuspendLayout(); | this.splitContainer1.Panel1.SuspendLayout(); | ||||
@@ -75,7 +75,6 @@ | |||||
this.splitContainer3.Panel1.SuspendLayout(); | this.splitContainer3.Panel1.SuspendLayout(); | ||||
this.splitContainer3.Panel2.SuspendLayout(); | this.splitContainer3.Panel2.SuspendLayout(); | ||||
this.splitContainer3.SuspendLayout(); | this.splitContainer3.SuspendLayout(); | ||||
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); | |||||
this.SuspendLayout(); | this.SuspendLayout(); | ||||
// | // | ||||
// StatisticsChart | // StatisticsChart | ||||
@@ -96,26 +95,20 @@ | |||||
this.StatisticsChart.Name = "StatisticsChart"; | this.StatisticsChart.Name = "StatisticsChart"; | ||||
this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; | this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; | ||||
series1.ChartArea = "ChartArea"; | series1.ChartArea = "ChartArea"; | ||||
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Area; | |||||
series1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(204)))), ((int)(((byte)(204)))), ((int)(((byte)(204))))); | |||||
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; | |||||
series1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(221)))), ((int)(((byte)(88)))), ((int)(((byte)(0))))); | |||||
series1.Legend = "ChartLegend"; | series1.Legend = "ChartLegend"; | ||||
series1.Name = "Data Transferred"; | |||||
series1.Name = "Package Loss"; | |||||
series1.YValuesPerPoint = 4; | |||||
series2.BorderWidth = 4; | |||||
series2.ChartArea = "ChartArea"; | series2.ChartArea = "ChartArea"; | ||||
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; | |||||
series2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(221)))), ((int)(((byte)(88)))), ((int)(((byte)(0))))); | |||||
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; | |||||
series2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(155)))), ((int)(((byte)(77)))), ((int)(((byte)(150))))); | |||||
series2.Legend = "ChartLegend"; | series2.Legend = "ChartLegend"; | ||||
series2.Name = "Package Loss"; | |||||
series2.YValuesPerPoint = 4; | |||||
series3.BorderWidth = 4; | |||||
series3.ChartArea = "ChartArea"; | |||||
series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; | |||||
series3.Color = System.Drawing.Color.FromArgb(((int)(((byte)(155)))), ((int)(((byte)(77)))), ((int)(((byte)(150))))); | |||||
series3.Legend = "ChartLegend"; | |||||
series3.Name = "Ping"; | |||||
series2.Name = "Ping"; | |||||
this.StatisticsChart.Series.Add(series1); | this.StatisticsChart.Series.Add(series1); | ||||
this.StatisticsChart.Series.Add(series2); | this.StatisticsChart.Series.Add(series2); | ||||
this.StatisticsChart.Series.Add(series3); | |||||
this.StatisticsChart.Size = new System.Drawing.Size(951, 222); | |||||
this.StatisticsChart.Size = new System.Drawing.Size(1061, 314); | |||||
this.StatisticsChart.TabIndex = 2; | this.StatisticsChart.TabIndex = 2; | ||||
// | // | ||||
// byISPCheckBox | // byISPCheckBox | ||||
@@ -130,6 +123,10 @@ | |||||
this.byISPCheckBox.Text = "By ISP/geolocation"; | this.byISPCheckBox.Text = "By ISP/geolocation"; | ||||
this.byISPCheckBox.UseVisualStyleBackColor = true; | this.byISPCheckBox.UseVisualStyleBackColor = true; | ||||
// | // | ||||
// bindingConfiguration | |||||
// | |||||
this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); | |||||
// | |||||
// label2 | // label2 | ||||
// | // | ||||
this.label2.AutoSize = true; | this.label2.AutoSize = true; | ||||
@@ -153,7 +150,7 @@ | |||||
// label4 | // label4 | ||||
// | // | ||||
this.label4.AutoSize = true; | this.label4.AutoSize = true; | ||||
this.label4.Location = new System.Drawing.Point(12, 226); | |||||
this.label4.Location = new System.Drawing.Point(7, 226); | |||||
this.label4.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | this.label4.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | ||||
this.label4.Name = "label4"; | this.label4.Name = "label4"; | ||||
this.label4.Size = new System.Drawing.Size(51, 28); | this.label4.Size = new System.Drawing.Size(51, 28); | ||||
@@ -165,7 +162,7 @@ | |||||
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | ||||
this.groupBox1.Controls.Add(this.radioButton2); | this.groupBox1.Controls.Add(this.radioButton2); | ||||
this.groupBox1.Controls.Add(this.radioButton1); | this.groupBox1.Controls.Add(this.radioButton1); | ||||
this.groupBox1.Location = new System.Drawing.Point(698, 7); | |||||
this.groupBox1.Location = new System.Drawing.Point(808, 81); | |||||
this.groupBox1.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | this.groupBox1.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | ||||
this.groupBox1.Name = "groupBox1"; | this.groupBox1.Name = "groupBox1"; | ||||
this.groupBox1.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); | this.groupBox1.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); | ||||
@@ -177,6 +174,7 @@ | |||||
// radioButton2 | // radioButton2 | ||||
// | // | ||||
this.radioButton2.AutoSize = true; | this.radioButton2.AutoSize = true; | ||||
this.radioButton2.Checked = true; | |||||
this.radioButton2.Location = new System.Drawing.Point(10, 63); | this.radioButton2.Location = new System.Drawing.Point(10, 63); | ||||
this.radioButton2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | this.radioButton2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | ||||
this.radioButton2.Name = "radioButton2"; | this.radioButton2.Name = "radioButton2"; | ||||
@@ -194,7 +192,6 @@ | |||||
this.radioButton1.Name = "radioButton1"; | this.radioButton1.Name = "radioButton1"; | ||||
this.radioButton1.Size = new System.Drawing.Size(70, 32); | this.radioButton1.Size = new System.Drawing.Size(70, 32); | ||||
this.radioButton1.TabIndex = 0; | this.radioButton1.TabIndex = 0; | ||||
this.radioButton1.TabStop = true; | |||||
this.radioButton1.Text = "24h"; | this.radioButton1.Text = "24h"; | ||||
this.radioButton1.UseVisualStyleBackColor = true; | this.radioButton1.UseVisualStyleBackColor = true; | ||||
// | // | ||||
@@ -217,8 +214,8 @@ | |||||
this.splitContainer1.Panel2.Controls.Add(this.OKButton); | this.splitContainer1.Panel2.Controls.Add(this.OKButton); | ||||
this.splitContainer1.Panel2.Controls.Add(this.groupBox1); | this.splitContainer1.Panel2.Controls.Add(this.groupBox1); | ||||
this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); | this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); | ||||
this.splitContainer1.Size = new System.Drawing.Size(951, 458); | |||||
this.splitContainer1.SplitterDistance = 226; | |||||
this.splitContainer1.Size = new System.Drawing.Size(1061, 637); | |||||
this.splitContainer1.SplitterDistance = 313; | |||||
this.splitContainer1.SplitterWidth = 10; | this.splitContainer1.SplitterWidth = 10; | ||||
this.splitContainer1.TabIndex = 12; | this.splitContainer1.TabIndex = 12; | ||||
// | // | ||||
@@ -249,7 +246,7 @@ | |||||
// splitContainer2.Panel2 | // splitContainer2.Panel2 | ||||
// | // | ||||
this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); | this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); | ||||
this.splitContainer2.Size = new System.Drawing.Size(951, 226); | |||||
this.splitContainer2.Size = new System.Drawing.Size(1061, 313); | |||||
this.splitContainer2.SplitterDistance = 365; | this.splitContainer2.SplitterDistance = 365; | ||||
this.splitContainer2.SplitterWidth = 5; | this.splitContainer2.SplitterWidth = 5; | ||||
this.splitContainer2.TabIndex = 7; | this.splitContainer2.TabIndex = 7; | ||||
@@ -282,7 +279,7 @@ | |||||
0, | 0, | ||||
0, | 0, | ||||
0}); | 0}); | ||||
this.dataCollectionMinutesNum.Location = new System.Drawing.Point(161, 188); | |||||
this.dataCollectionMinutesNum.Location = new System.Drawing.Point(161, 179); | |||||
this.dataCollectionMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | this.dataCollectionMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | ||||
this.dataCollectionMinutesNum.Maximum = new decimal(new int[] { | this.dataCollectionMinutesNum.Maximum = new decimal(new int[] { | ||||
120, | 120, | ||||
@@ -298,7 +295,7 @@ | |||||
this.dataCollectionMinutesNum.Size = new System.Drawing.Size(92, 34); | this.dataCollectionMinutesNum.Size = new System.Drawing.Size(92, 34); | ||||
this.dataCollectionMinutesNum.TabIndex = 18; | this.dataCollectionMinutesNum.TabIndex = 18; | ||||
this.dataCollectionMinutesNum.Value = new decimal(new int[] { | this.dataCollectionMinutesNum.Value = new decimal(new int[] { | ||||
5, | |||||
10, | |||||
0, | 0, | ||||
0, | 0, | ||||
0}); | 0}); | ||||
@@ -339,7 +336,7 @@ | |||||
this.choiceKeptMinutesNum.Size = new System.Drawing.Size(92, 34); | this.choiceKeptMinutesNum.Size = new System.Drawing.Size(92, 34); | ||||
this.choiceKeptMinutesNum.TabIndex = 16; | this.choiceKeptMinutesNum.TabIndex = 16; | ||||
this.choiceKeptMinutesNum.Value = new decimal(new int[] { | this.choiceKeptMinutesNum.Value = new decimal(new int[] { | ||||
5, | |||||
10, | |||||
0, | 0, | ||||
0, | 0, | ||||
0}); | 0}); | ||||
@@ -359,7 +356,7 @@ | |||||
// repeatTimesNum | // repeatTimesNum | ||||
// | // | ||||
this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true)); | this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true)); | ||||
this.repeatTimesNum.Location = new System.Drawing.Point(72, 223); | |||||
this.repeatTimesNum.Location = new System.Drawing.Point(66, 224); | |||||
this.repeatTimesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | this.repeatTimesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | ||||
this.repeatTimesNum.Maximum = new decimal(new int[] { | this.repeatTimesNum.Maximum = new decimal(new int[] { | ||||
10, | 10, | ||||
@@ -378,7 +375,7 @@ | |||||
// label6 | // label6 | ||||
// | // | ||||
this.label6.AutoSize = true; | this.label6.AutoSize = true; | ||||
this.label6.Location = new System.Drawing.Point(171, 226); | |||||
this.label6.Location = new System.Drawing.Point(163, 226); | |||||
this.label6.Name = "label6"; | this.label6.Name = "label6"; | ||||
this.label6.Size = new System.Drawing.Size(184, 28); | this.label6.Size = new System.Drawing.Size(184, 28); | ||||
this.label6.TabIndex = 13; | this.label6.TabIndex = 13; | ||||
@@ -401,7 +398,7 @@ | |||||
// splitContainer3.Panel2 | // splitContainer3.Panel2 | ||||
// | // | ||||
this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); | this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); | ||||
this.splitContainer3.Size = new System.Drawing.Size(581, 226); | |||||
this.splitContainer3.Size = new System.Drawing.Size(691, 313); | |||||
this.splitContainer3.SplitterDistance = 46; | this.splitContainer3.SplitterDistance = 46; | ||||
this.splitContainer3.SplitterWidth = 10; | this.splitContainer3.SplitterWidth = 10; | ||||
this.splitContainer3.TabIndex = 6; | this.splitContainer3.TabIndex = 6; | ||||
@@ -423,13 +420,13 @@ | |||||
this.calculationContainer.Location = new System.Drawing.Point(0, 0); | this.calculationContainer.Location = new System.Drawing.Point(0, 0); | ||||
this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | ||||
this.calculationContainer.Name = "calculationContainer"; | this.calculationContainer.Name = "calculationContainer"; | ||||
this.calculationContainer.Size = new System.Drawing.Size(581, 170); | |||||
this.calculationContainer.Size = new System.Drawing.Size(691, 257); | |||||
this.calculationContainer.TabIndex = 1; | this.calculationContainer.TabIndex = 1; | ||||
// | // | ||||
// CancelButton | // CancelButton | ||||
// | // | ||||
this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | ||||
this.CancelButton.Location = new System.Drawing.Point(844, 166); | |||||
this.CancelButton.Location = new System.Drawing.Point(954, 240); | |||||
this.CancelButton.Name = "CancelButton"; | this.CancelButton.Name = "CancelButton"; | ||||
this.CancelButton.Size = new System.Drawing.Size(93, 43); | this.CancelButton.Size = new System.Drawing.Size(93, 43); | ||||
this.CancelButton.TabIndex = 5; | this.CancelButton.TabIndex = 5; | ||||
@@ -440,7 +437,7 @@ | |||||
// OKButton | // OKButton | ||||
// | // | ||||
this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | ||||
this.OKButton.Location = new System.Drawing.Point(745, 166); | |||||
this.OKButton.Location = new System.Drawing.Point(855, 240); | |||||
this.OKButton.Name = "OKButton"; | this.OKButton.Name = "OKButton"; | ||||
this.OKButton.Size = new System.Drawing.Size(93, 43); | this.OKButton.Size = new System.Drawing.Size(93, 43); | ||||
this.OKButton.TabIndex = 4; | this.OKButton.TabIndex = 4; | ||||
@@ -448,16 +445,12 @@ | |||||
this.OKButton.UseVisualStyleBackColor = true; | this.OKButton.UseVisualStyleBackColor = true; | ||||
this.OKButton.Click += new System.EventHandler(this.OKButton_Click); | this.OKButton.Click += new System.EventHandler(this.OKButton_Click); | ||||
// | // | ||||
// bindingConfiguration | |||||
// | |||||
this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); | |||||
// | |||||
// StatisticsStrategyConfigurationForm | // StatisticsStrategyConfigurationForm | ||||
// | // | ||||
this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 28F); | this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 28F); | ||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||
this.AutoSize = true; | this.AutoSize = true; | ||||
this.ClientSize = new System.Drawing.Size(951, 458); | |||||
this.ClientSize = new System.Drawing.Size(1061, 637); | |||||
this.Controls.Add(this.splitContainer1); | this.Controls.Add(this.splitContainer1); | ||||
this.Font = new System.Drawing.Font("Segoe UI", 10F); | this.Font = new System.Drawing.Font("Segoe UI", 10F); | ||||
this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | ||||
@@ -465,6 +458,7 @@ | |||||
this.Name = "StatisticsStrategyConfigurationForm"; | this.Name = "StatisticsStrategyConfigurationForm"; | ||||
this.Text = "StatisticsStrategyConfigurationForm"; | this.Text = "StatisticsStrategyConfigurationForm"; | ||||
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); | ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); | ||||
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); | |||||
this.groupBox1.ResumeLayout(false); | this.groupBox1.ResumeLayout(false); | ||||
this.groupBox1.PerformLayout(); | this.groupBox1.PerformLayout(); | ||||
this.splitContainer1.Panel1.ResumeLayout(false); | this.splitContainer1.Panel1.ResumeLayout(false); | ||||
@@ -484,7 +478,6 @@ | |||||
this.splitContainer3.Panel2.ResumeLayout(false); | this.splitContainer3.Panel2.ResumeLayout(false); | ||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); | ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); | ||||
this.splitContainer3.ResumeLayout(false); | this.splitContainer3.ResumeLayout(false); | ||||
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); | |||||
this.ResumeLayout(false); | this.ResumeLayout(false); | ||||
} | } | ||||
@@ -37,7 +37,7 @@ namespace Shadowsocks.View | |||||
bindingConfiguration.Add(_configuration); | bindingConfiguration.Add(_configuration); | ||||
foreach (var kv in _configuration.Calculations) | foreach (var kv in _configuration.Calculations) | ||||
{ | { | ||||
var calculation = new CalculationControl(kv.Key); | |||||
var calculation = new CalculationControl(kv.Key, kv.Value); | |||||
calculationContainer.Controls.Add(calculation); | calculationContainer.Controls.Add(calculation); | ||||
} | } | ||||
} | } | ||||
@@ -54,6 +54,7 @@ namespace Shadowsocks.View | |||||
_configuration.Calculations[calculation.Value] = calculation.Factor; | _configuration.Calculations[calculation.Value] = calculation.Factor; | ||||
} | } | ||||
_controller?.SaveStrategyConfigurations(_configuration); | _controller?.SaveStrategyConfigurations(_configuration); | ||||
_controller?.UpdateStatisticsConfiguration(StatisticsEnabledCheckBox.Checked); | |||||
Close(); | Close(); | ||||
} | } | ||||
} | } | ||||
@@ -120,4 +120,7 @@ | |||||
<metadata name="bindingConfiguration.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | <metadata name="bindingConfiguration.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | ||||
<value>1, 30</value> | <value>1, 30</value> | ||||
</metadata> | </metadata> | ||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>63</value> | |||||
</metadata> | |||||
</root> | </root> |