@@ -1,3 +1,15 @@ | |||
4.3.0.0 2020-10-19 | |||
- Cleanup and update dependencies (#2983) | |||
- Geosite group validation + PAC regeneration on version update (#2988) | |||
- PAC: add options for direct and proxied groups (#2990) | |||
- Transition to WPF: ForwardProxyView + HotkeysView + OnlineConfigView + VersionUpdatePromptView (#2991) | |||
4.2.1.0 2020-10-12 | |||
- SIP008 support (#2942) | |||
- Exclude @cn from PAC proxied list (#2982) | |||
- Transition to WPF: ReactiveUI and ServerSharingView (#2959) | |||
- User-Agent for OnlineConfigResolver and GeositeUpdater (#2978) | |||
4.2.0.1 2020-09-26 | |||
- Fix domain rule handling in PAC script (#2956) | |||
@@ -5,7 +5,7 @@ | |||
[中文说明] | |||
#### Features | |||
## Features | |||
1. System proxy configuration | |||
2. PAC mode and global mode | |||
@@ -15,15 +15,15 @@ | |||
6. Supports UDP relay (see Usage) | |||
7. Supports plugins | |||
#### Download | |||
## Downloads | |||
Download the latest release from [release page]. | |||
#### Requirements | |||
## Requirements | |||
Microsoft [.NET Framework 4.7.2] or higher, Microsoft [Visual C++ 2015 Redistributable] (x86) . | |||
#### Basic | |||
## Basics | |||
1. Find Shadowsocks icon in the notification tray | |||
2. You can add multiple servers in servers menu | |||
@@ -33,19 +33,32 @@ proxy addons in your browser, or set them to use system proxy | |||
system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this | |||
port in `Servers -> Edit Servers` | |||
#### PAC | |||
## 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 [GeoSite] \(maintained by 3rd party) | |||
3. You can also use online PAC URL | |||
- The PAC rules are generated from the geosite database in [v2fly/domain-list-community](https://github.com/v2fly/domain-list-community). | |||
- Generation modes: whitelist mode and blacklist mode. | |||
- Domain groups: `geositeDirectGroups` and `geositeProxiedGroups`. | |||
- `geositeDirectGroups` is initialized with `cn` and `geolocation-!cn@cn`. | |||
- `geositeProxiedGroups` is initialized with `geolocation-!cn`. | |||
- To switch between different modes, modify the `geositePreferDirect` property in `gui-config.json` | |||
- When `geositePreferDirect` is false (default), PAC works in whitelist mode. Exception rules are generated from `geositeDirectGroups`. Unmatched domains goes through the proxy. | |||
- When `geositePreferDirect` is true, PAC works in blacklist mode. Blocking rules are generated from `geositeProxiedGroups`. Exception rules are generated from `geositeDirectGroups`. Unmatched domains are connected to directly. | |||
- Starting from 4.3.0.0, shadowsocks-windows defaults to whitelist mode with Chinese domains excluded from connecting via the proxy. | |||
- The new default values make sure that: | |||
- When in whitelist mode, Chinese domains, including non-Chinese companies' Chinese CDNs, are connected to directly. | |||
- When in blacklist mode, only non-Chinese domains goes through the proxy. Chinese domains, as well as non-Chinese companies' Chinese CDNs, are connected to directly. | |||
### User-defined rules | |||
- To define your own PAC rules, it's recommended to use the `user-rule.txt` file. | |||
- You can also modify `pac.txt` directly. But your modifications won't persist after updating geosite from the upstream. | |||
For Windows10 Store and related applications, please execute the following command under Admin privilege: | |||
``` | |||
netsh winhttp import proxy source=ie | |||
``` | |||
#### Server Auto Switching | |||
## Server Auto Switching | |||
1. Load balance: choosing server randomly | |||
2. High availability: choosing the best server (low latency and packet loss) | |||
@@ -53,18 +66,18 @@ netsh winhttp import proxy source=ie | |||
`Availability Statistics` in the menu if you want to use this | |||
4. Write your own strategy by implement IStrategy interface and send us a pull request! | |||
#### UDP | |||
## UDP | |||
For UDP, you need to use SocksCap or ProxyCap to force programs you want | |||
to be proxied to tunnel over Shadowsocks | |||
#### Multiple Instances | |||
## 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. | |||
#### Plugins | |||
## Plugins | |||
If you would like to connect to server via a plugin, please set the plugin's | |||
path (relative or absolute) on Edit Servers form. | |||
@@ -73,70 +86,72 @@ _Note_: Forward Proxy will not be used while a plugin is enabled. | |||
Details: | |||
[Working with non SIP003 standard Plugin]. | |||
#### Global hotkeys | |||
## Global hotkeys | |||
Hotkeys could be registered automatically on startup. | |||
If you are using multiple instances of Shadowsocks, | |||
you must set different key combination for each instance. | |||
##### How to input? | |||
### How to input? | |||
1. Put focus in the corresponding textbox. | |||
2. Press the key combination that you want to use. | |||
3. Release all keys when you think it is ready. | |||
4. Your input appears in the textbox. | |||
##### How to change? | |||
### How to change? | |||
1. Put focus in the corresponding textbox. | |||
2. Press BackSpace key to clear content. | |||
3. Re-input new key combination. | |||
##### How to deactivate? | |||
### How to deactivate? | |||
1. Clear content in the textbox that you want to deactivate, | |||
if you want to deactivate all, please clear all textboxes. | |||
2. Press OK button to confirm. | |||
##### Meaning of label color | |||
### Meaning of label color | |||
- Green: This key combination is not occupied by other programs and register successfully. | |||
- Yellow: This key combination is occupied by other programs and you have to change to another one. | |||
- Transparent without color: The initial status. | |||
#### Server Configuration | |||
## Server Configuration | |||
Please visit [Servers] for more information. | |||
#### Experimental | |||
## Experimental | |||
[Experimental Features] | |||
#### Development | |||
## Development | |||
1. [Visual Studio 2017] & [.NET Framework 4.7.2 Developer Pack] are required. | |||
1. [Visual Studio 2019] & [.NET Framework 4.7.2 Developer Pack] are required. | |||
2. It is recommended to share your idea on the Issue Board before you start to work, | |||
especially for feature development. | |||
#### License | |||
## License | |||
[GPLv3] | |||
#### Open Source Components / Libraries | |||
## Open Source Components / Libraries | |||
``` | |||
Caseless.Fody (MIT) https://github.com/Fody/Caseless | |||
Costura.Fody (MIT) https://github.com/Fody/Costura | |||
Fody (MIT) https://github.com/Fody/Fody | |||
GlobalHotKey (GPLv3) https://github.com/kirmir/GlobalHotKey | |||
Newtonsoft.Json (MIT) https://www.newtonsoft.com/json | |||
StringEx.CS () https://github.com/LazyMode/StringEx | |||
ZXing.Net (Apache 2.0) https://github.com/micjahn/ZXing.Net | |||
BouncyCastle.NetCore (MIT) https://github.com/chrishaly/bc-csharp | |||
NaCl.Core (MIT) https://github.com/idaviddesmet/NaCl.Core | |||
Privoxy (GPLv2) https://www.privoxy.org | |||
Sysproxy () https://github.com/Noisyfox/sysproxy | |||
BouncyCastle.NetCore (MIT) https://github.com/chrishaly/bc-csharp | |||
Caseless.Fody (MIT) https://github.com/Fody/Caseless | |||
Costura.Fody (MIT) https://github.com/Fody/Costura | |||
Fody (MIT) https://github.com/Fody/Fody | |||
GlobalHotKey (GPLv3) https://github.com/kirmir/GlobalHotKey | |||
MdXaml (MIT) https://github.com/whistyun/MdXaml | |||
Newtonsoft.Json (MIT) https://www.newtonsoft.com/json | |||
Privoxy (GPLv2) https://www.privoxy.org | |||
ReactiveUI.WPF (MIT) https://github.com/reactiveui/ReactiveUI | |||
ReactiveUI.Events.WPF (MIT) https://github.com/reactiveui/ReactiveUI | |||
ReactiveUI.Fody (MIT) https://github.com/reactiveui/ReactiveUI | |||
ReactiveUI.Validation (MIT) https://github.com/reactiveui/ReactiveUI.Validation | |||
WPFLocalizationExtension (MS-PL) https://github.com/XAMLMarkupExtensions/WPFLocalizationExtension/ | |||
ZXing.Net (Apache 2.0) https://github.com/micjahn/ZXing.Net | |||
``` | |||
@@ -153,4 +168,4 @@ Sysproxy () https://github.com/Noisyfox/sysproxy | |||
[Visual C++ 2015 Redistributable]: https://www.microsoft.com/en-us/download/details.aspx?id=53840 | |||
[GPLv3]: https://github.com/shadowsocks/shadowsocks-windows/blob/master/LICENSE.txt | |||
[Working with non SIP003 standard Plugin]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Working-with-non-SIP003-standard-Plugin | |||
[Experimental Features]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Experimental | |||
[Experimental Features]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Experimental |
@@ -12,7 +12,7 @@ namespace Shadowsocks.Controller | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
public static void RegAllHotkeys() | |||
{ | |||
var hotkeyConfig = Configuration.Load().hotkey; | |||
var hotkeyConfig = Program.MainController.GetCurrentConfiguration().hotkey; | |||
if (hotkeyConfig == null || !hotkeyConfig.RegHotkeysAtStartup) | |||
return; | |||
@@ -50,7 +50,7 @@ namespace Shadowsocks.Controller | |||
var callback = _callback as HotKeys.HotKeyCallBackHandler; | |||
if (hotkeyStr.IsNullOrEmpty()) | |||
if (string.IsNullOrEmpty(hotkeyStr)) | |||
{ | |||
HotKeys.UnregExistingHotkey(callback); | |||
onComplete?.Invoke(RegResult.UnregSuccess); | |||
@@ -1,534 +0,0 @@ | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.NetworkInformation; | |||
using System.Net.Sockets; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using Newtonsoft.Json; | |||
using NLog; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Util; | |||
namespace Shadowsocks.Controller | |||
{ | |||
using Statistics = Dictionary<string, List<StatisticsRecord>>; | |||
public sealed class AvailabilityStatistics : IDisposable | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; | |||
private const string StatisticsFilesName = "shadowsocks.availability.json"; | |||
public static string AvailabilityStatisticsFile; | |||
//static constructor to initialize every public static fields before refereced | |||
static AvailabilityStatistics() | |||
{ | |||
AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); | |||
} | |||
//arguments for ICMP tests | |||
private int Repeat => Config.RepeatTimesNum; | |||
public const int TimeoutMilliseconds = 500; | |||
//records cache for current server in {_monitorInterval} minutes | |||
private readonly ConcurrentDictionary<string, List<int>> _latencyRecords = new ConcurrentDictionary<string, List<int>>(); | |||
//speed in KiB/s | |||
private readonly ConcurrentDictionary<string, List<int>> _inboundSpeedRecords = new ConcurrentDictionary<string, List<int>>(); | |||
private readonly ConcurrentDictionary<string, List<int>> _outboundSpeedRecords = new ConcurrentDictionary<string, List<int>>(); | |||
private readonly ConcurrentDictionary<string, InOutBoundRecord> _inOutBoundRecords = new ConcurrentDictionary<string, InOutBoundRecord>(); | |||
private class InOutBoundRecord | |||
{ | |||
private long _inbound; | |||
private long _lastInbound; | |||
private long _outbound; | |||
private long _lastOutbound; | |||
public void UpdateInbound(long delta) | |||
{ | |||
Interlocked.Add(ref _inbound, delta); | |||
} | |||
public void UpdateOutbound(long delta) | |||
{ | |||
Interlocked.Add(ref _outbound, delta); | |||
} | |||
public void GetDelta(out long inboundDelta, out long outboundDelta) | |||
{ | |||
var i = Interlocked.Read(ref _inbound); | |||
var il = Interlocked.Exchange(ref _lastInbound, i); | |||
inboundDelta = i - il; | |||
var o = Interlocked.Read(ref _outbound); | |||
var ol = Interlocked.Exchange(ref _lastOutbound, o); | |||
outboundDelta = o - ol; | |||
} | |||
} | |||
//tasks | |||
private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1); | |||
private readonly TimeSpan _retryInterval = TimeSpan.FromMinutes(2); | |||
private TimeSpan RecordingInterval => TimeSpan.FromMinutes(Config.DataCollectionMinutes); | |||
private Timer _perSecondTimer; //analyze and save cached records to RawStatistics and filter records | |||
private readonly TimeSpan _monitorInterval = TimeSpan.FromSeconds(1); | |||
//private Timer _writer; //write RawStatistics to file | |||
//private readonly TimeSpan _writingInterval = TimeSpan.FromMinutes(1); | |||
private ShadowsocksController _controller; | |||
private StatisticsStrategyConfiguration Config => _controller.StatisticsConfiguration; | |||
// Static Singleton Initialization | |||
public static AvailabilityStatistics Instance { get; } = new AvailabilityStatistics(); | |||
public Statistics RawStatistics { get; private set; } | |||
public Statistics FilteredStatistics { get; private set; } | |||
private AvailabilityStatistics() | |||
{ | |||
RawStatistics = new Statistics(); | |||
} | |||
internal void UpdateConfiguration(ShadowsocksController controller) | |||
{ | |||
_controller = controller; | |||
Reset(); | |||
try | |||
{ | |||
if (Config.StatisticsEnabled) | |||
{ | |||
LoadRawStatistics(); | |||
if (_perSecondTimer == null) | |||
{ | |||
_perSecondTimer = new Timer(OperationsPerSecond, new Counter(), _delayBeforeStart, TimeSpan.FromSeconds(1)); | |||
} | |||
} | |||
else | |||
{ | |||
_perSecondTimer?.Dispose(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
} | |||
} | |||
private void OperationsPerSecond(object state) | |||
{ | |||
lock(state) | |||
{ | |||
var counter = state as Counter; | |||
if (counter.count % _monitorInterval.TotalSeconds == 0) | |||
{ | |||
UpdateSpeed(); | |||
} | |||
if (counter.count % RecordingInterval.TotalSeconds == 0) | |||
{ | |||
Run(); | |||
} | |||
counter.count++; | |||
} | |||
} | |||
private void UpdateSpeed() | |||
{ | |||
foreach (var kv in _inOutBoundRecords) | |||
{ | |||
var id = kv.Key; | |||
var record = kv.Value; | |||
long inboundDelta, outboundDelta; | |||
record.GetDelta(out inboundDelta, out outboundDelta); | |||
var inboundSpeed = GetSpeedInKiBPerSecond(inboundDelta, _monitorInterval.TotalSeconds); | |||
var outboundSpeed = GetSpeedInKiBPerSecond(outboundDelta, _monitorInterval.TotalSeconds); | |||
// not thread safe | |||
var inR = _inboundSpeedRecords.GetOrAdd(id, (k) => new List<int>()); | |||
var outR = _outboundSpeedRecords.GetOrAdd(id, (k) => new List<int>()); | |||
inR.Add(inboundSpeed); | |||
outR.Add(outboundSpeed); | |||
logger.Debug( | |||
$"{id}: current/max inbound {inboundSpeed}/{inR.Max()} KiB/s, current/max outbound {outboundSpeed}/{outR.Max()} KiB/s"); | |||
} | |||
} | |||
private void Reset() | |||
{ | |||
_inboundSpeedRecords.Clear(); | |||
_outboundSpeedRecords.Clear(); | |||
_latencyRecords.Clear(); | |||
} | |||
private void Run() | |||
{ | |||
UpdateRecords(); | |||
Reset(); | |||
} | |||
private void UpdateRecords() | |||
{ | |||
var records = new Dictionary<string, StatisticsRecord>(); | |||
UpdateRecordsState state = new UpdateRecordsState(); | |||
int serverCount = _controller.GetCurrentConfiguration().configs.Count; | |||
state.counter = serverCount; | |||
bool isPing = Config.Ping; | |||
for (int i = 0; i < serverCount; i++) | |||
{ | |||
try | |||
{ | |||
var server = _controller.GetCurrentConfiguration().configs[i]; | |||
var id = server.Identifier(); | |||
List<int> inboundSpeedRecords = null; | |||
List<int> outboundSpeedRecords = null; | |||
List<int> latencyRecords = null; | |||
_inboundSpeedRecords.TryGetValue(id, out inboundSpeedRecords); | |||
_outboundSpeedRecords.TryGetValue(id, out outboundSpeedRecords); | |||
_latencyRecords.TryGetValue(id, out latencyRecords); | |||
StatisticsRecord record = new StatisticsRecord(id, inboundSpeedRecords, outboundSpeedRecords, latencyRecords); | |||
/* duplicate server identifier */ | |||
if (records.ContainsKey(id)) | |||
records[id] = record; | |||
else | |||
records.Add(id, record); | |||
if (isPing) | |||
{ | |||
// FIXME: on ping completed, every thing could be asynchrously changed. | |||
// focus on: Config/ RawStatistics | |||
MyPing ping = new MyPing(server, Repeat); | |||
ping.Completed += ping_Completed; | |||
ping.Start(new PingState { state = state, record = record }); | |||
} | |||
else if (!record.IsEmptyData()) | |||
{ | |||
AppendRecord(id, record); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.Debug("config changed asynchrously, just ignore this server"); | |||
} | |||
} | |||
if (!isPing) | |||
{ | |||
Save(); | |||
FilterRawStatistics(); | |||
} | |||
} | |||
private void ping_Completed(object sender, MyPing.CompletedEventArgs e) | |||
{ | |||
PingState pingState = (PingState)e.UserState; | |||
UpdateRecordsState state = pingState.state; | |||
Server server = e.Server; | |||
StatisticsRecord record = pingState.record; | |||
record.SetResponse(e.RoundtripTime); | |||
if (!record.IsEmptyData()) | |||
{ | |||
AppendRecord(server.Identifier(), record); | |||
} | |||
logger.Debug($"Ping {server} {e.RoundtripTime.Count} times, {(100 - record.PackageLoss * 100)}% packages loss, min {record.MinResponse} ms, max {record.MaxResponse} ms, avg {record.AverageResponse} ms"); | |||
if (Interlocked.Decrement(ref state.counter) == 0) | |||
{ | |||
Save(); | |||
FilterRawStatistics(); | |||
} | |||
} | |||
private void AppendRecord(string serverIdentifier, StatisticsRecord record) | |||
{ | |||
try | |||
{ | |||
List<StatisticsRecord> records; | |||
lock (RawStatistics) | |||
{ | |||
if (!RawStatistics.TryGetValue(serverIdentifier, out records)) | |||
{ | |||
records = new List<StatisticsRecord>(); | |||
RawStatistics[serverIdentifier] = records; | |||
} | |||
} | |||
records.Add(record); | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
} | |||
} | |||
private void Save() | |||
{ | |||
logger.Debug($"save statistics to {AvailabilityStatisticsFile}"); | |||
if (RawStatistics.Count == 0) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
string content; | |||
#if DEBUG | |||
content = JsonConvert.SerializeObject(RawStatistics, Formatting.Indented); | |||
#else | |||
content = JsonConvert.SerializeObject(RawStatistics, Formatting.None); | |||
#endif | |||
File.WriteAllText(AvailabilityStatisticsFile, content); | |||
} | |||
catch (IOException e) | |||
{ | |||
logger.LogUsefulException(e); | |||
} | |||
} | |||
private bool IsValidRecord(StatisticsRecord record) | |||
{ | |||
if (Config.ByHourOfDay) | |||
{ | |||
if (!record.Timestamp.Hour.Equals(DateTime.Now.Hour)) return false; | |||
} | |||
return true; | |||
} | |||
private void FilterRawStatistics() | |||
{ | |||
try | |||
{ | |||
logger.Debug("filter raw statistics"); | |||
if (RawStatistics == null) return; | |||
var filteredStatistics = new Statistics(); | |||
foreach (var serverAndRecords in RawStatistics) | |||
{ | |||
var server = serverAndRecords.Key; | |||
var filteredRecords = serverAndRecords.Value.FindAll(IsValidRecord); | |||
filteredStatistics[server] = filteredRecords; | |||
} | |||
FilteredStatistics = filteredStatistics; | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
} | |||
} | |||
private void LoadRawStatistics() | |||
{ | |||
try | |||
{ | |||
var path = AvailabilityStatisticsFile; | |||
logger.Debug($"loading statistics from {path}"); | |||
if (!File.Exists(path)) | |||
{ | |||
using (File.Create(path)) | |||
{ | |||
//do nothing | |||
} | |||
} | |||
var content = File.ReadAllText(path); | |||
RawStatistics = JsonConvert.DeserializeObject<Statistics>(content) ?? RawStatistics; | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
Console.WriteLine($"failed to load statistics; use runtime statistics, some data may be lost"); | |||
} | |||
} | |||
private static int GetSpeedInKiBPerSecond(long bytes, double seconds) | |||
{ | |||
var result = (int)(bytes / seconds) / 1024; | |||
return result; | |||
} | |||
public void Dispose() | |||
{ | |||
_perSecondTimer.Dispose(); | |||
} | |||
public void UpdateLatency(Server server, int latency) | |||
{ | |||
_latencyRecords.GetOrAdd(server.Identifier(), (k) => | |||
{ | |||
List<int> records = new List<int>(); | |||
records.Add(latency); | |||
return records; | |||
}); | |||
} | |||
public void UpdateInboundCounter(Server server, long n) | |||
{ | |||
_inOutBoundRecords.AddOrUpdate(server.Identifier(), (k) => | |||
{ | |||
var r = new InOutBoundRecord(); | |||
r.UpdateInbound(n); | |||
return r; | |||
}, (k, v) => | |||
{ | |||
v.UpdateInbound(n); | |||
return v; | |||
}); | |||
} | |||
public void UpdateOutboundCounter(Server server, long n) | |||
{ | |||
_inOutBoundRecords.AddOrUpdate(server.Identifier(), (k) => | |||
{ | |||
var r = new InOutBoundRecord(); | |||
r.UpdateOutbound(n); | |||
return r; | |||
}, (k, v) => | |||
{ | |||
v.UpdateOutbound(n); | |||
return v; | |||
}); | |||
} | |||
private class Counter | |||
{ | |||
public int count = 0; | |||
} | |||
class UpdateRecordsState | |||
{ | |||
public int counter; | |||
} | |||
class PingState | |||
{ | |||
public UpdateRecordsState state; | |||
public StatisticsRecord record; | |||
} | |||
class MyPing | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
//arguments for ICMP tests | |||
public const int TimeoutMilliseconds = 500; | |||
public EventHandler<CompletedEventArgs> Completed; | |||
private Server server; | |||
private int repeat; | |||
private IPAddress ip; | |||
private Ping ping; | |||
private List<int?> RoundtripTime; | |||
public MyPing(Server server, int repeat) | |||
{ | |||
this.server = server; | |||
this.repeat = repeat; | |||
RoundtripTime = new List<int?>(repeat); | |||
ping = new Ping(); | |||
ping.PingCompleted += Ping_PingCompleted; | |||
} | |||
public void Start(object userstate) | |||
{ | |||
if (server.server == "") | |||
{ | |||
FireCompleted(new Exception("Invalid Server"), userstate); | |||
return; | |||
} | |||
new Task(() => ICMPTest(0, userstate)).Start(); | |||
} | |||
private void ICMPTest(int delay, object userstate) | |||
{ | |||
try | |||
{ | |||
logger.Debug($"Ping {server}"); | |||
if (ip == null) | |||
{ | |||
ip = Dns.GetHostAddresses(server.server) | |||
.First( | |||
ip => | |||
ip.AddressFamily == AddressFamily.InterNetwork || | |||
ip.AddressFamily == AddressFamily.InterNetworkV6); | |||
} | |||
repeat--; | |||
if (delay > 0) | |||
Thread.Sleep(delay); | |||
ping.SendAsync(ip, TimeoutMilliseconds, userstate); | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.Error($"An exception occured while eveluating {server}"); | |||
logger.LogUsefulException(e); | |||
FireCompleted(e, userstate); | |||
} | |||
} | |||
private void Ping_PingCompleted(object sender, PingCompletedEventArgs e) | |||
{ | |||
try | |||
{ | |||
if (e.Reply.Status == IPStatus.Success) | |||
{ | |||
logger.Debug($"Ping {server} {e.Reply.RoundtripTime} ms"); | |||
RoundtripTime.Add((int?)e.Reply.RoundtripTime); | |||
} | |||
else | |||
{ | |||
logger.Debug($"Ping {server} timeout"); | |||
RoundtripTime.Add(null); | |||
} | |||
TestNext(e.UserState); | |||
} | |||
catch (Exception ex) | |||
{ | |||
logger.Error($"An exception occured while eveluating {server}"); | |||
logger.LogUsefulException(ex); | |||
FireCompleted(ex, e.UserState); | |||
} | |||
} | |||
private void TestNext(object userstate) | |||
{ | |||
if (repeat > 0) | |||
{ | |||
//Do ICMPTest in a random frequency | |||
int delay = TimeoutMilliseconds + new Random().Next() % TimeoutMilliseconds; | |||
new Task(() => ICMPTest(delay, userstate)).Start(); | |||
} | |||
else | |||
{ | |||
FireCompleted(null, userstate); | |||
} | |||
} | |||
private void FireCompleted(Exception error, object userstate) | |||
{ | |||
Completed?.Invoke(this, new CompletedEventArgs | |||
{ | |||
Error = error, | |||
Server = server, | |||
RoundtripTime = RoundtripTime, | |||
UserState = userstate | |||
}); | |||
} | |||
public class CompletedEventArgs : EventArgs | |||
{ | |||
public Exception Error; | |||
public Server Server; | |||
public List<int?> RoundtripTime; | |||
public object UserState; | |||
} | |||
} | |||
} | |||
} |
@@ -35,8 +35,6 @@ namespace Shadowsocks.Controller | |||
private static readonly string DATABASE_PATH = Utils.GetTempPath("dlc.dat"); | |||
private static SocketsHttpHandler socketsHttpHandler; | |||
private static HttpClient httpClient; | |||
private static readonly string GEOSITE_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat"; | |||
private static readonly string GEOSITE_SHA256SUM_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat.sha256sum"; | |||
private static byte[] geositeDB; | |||
@@ -45,9 +43,6 @@ namespace Shadowsocks.Controller | |||
static GeositeUpdater() | |||
{ | |||
//socketsHttpHandler = new SocketsHttpHandler(); | |||
//httpClient = new HttpClient(socketsHttpHandler); | |||
if (File.Exists(DATABASE_PATH) && new FileInfo(DATABASE_PATH).Length > 0) | |||
{ | |||
geositeDB = File.ReadAllBytes(DATABASE_PATH); | |||
@@ -68,7 +63,7 @@ namespace Shadowsocks.Controller | |||
var list = GeositeList.Parser.ParseFrom(geositeDB); | |||
foreach (var item in list.Entries) | |||
{ | |||
Geosites[item.GroupName.ToLower()] = item.Domains; | |||
Geosites[item.GroupName.ToLowerInvariant()] = item.Domains; | |||
} | |||
} | |||
@@ -84,9 +79,9 @@ namespace Shadowsocks.Controller | |||
string geositeSha256sumUrl = GEOSITE_SHA256SUM_URL; | |||
SHA256 mySHA256 = SHA256.Create(); | |||
var config = Program.MainController.GetCurrentConfiguration(); | |||
string group = config.geositeGroup; | |||
bool blacklist = config.geositeBlacklistMode; | |||
bool blacklist = config.geositePreferDirect; | |||
var httpClient = Program.MainController.GetHttpClient(); | |||
if (!string.IsNullOrWhiteSpace(config.geositeUrl)) | |||
{ | |||
logger.Info("Found custom Geosite URL in config file"); | |||
@@ -94,21 +89,6 @@ namespace Shadowsocks.Controller | |||
} | |||
logger.Info($"Checking Geosite from {geositeUrl}"); | |||
// use System.Net.Http.HttpClient to download GeoSite db. | |||
// NASTY workaround: new HttpClient every update | |||
// because we can't change proxy on existing socketsHttpHandler instance | |||
socketsHttpHandler = new SocketsHttpHandler(); | |||
httpClient = new HttpClient(socketsHttpHandler); | |||
if (config.enabled) | |||
{ | |||
socketsHttpHandler.UseProxy = true; | |||
socketsHttpHandler.Proxy = new WebProxy( | |||
config.isIPv6Enabled | |||
? $"[{IPAddress.IPv6Loopback}]" | |||
: IPAddress.Loopback.ToString(), | |||
config.localPort); | |||
} | |||
try | |||
{ | |||
// download checksum first | |||
@@ -150,32 +130,26 @@ namespace Shadowsocks.Controller | |||
// update stuff | |||
geositeDB = downloadedBytes; | |||
LoadGeositeList(); | |||
bool pacFileChanged = MergeAndWritePACFile(group, blacklist); | |||
bool pacFileChanged = MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, blacklist); | |||
UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged)); | |||
} | |||
catch (Exception ex) | |||
{ | |||
Error?.Invoke(null, new ErrorEventArgs(ex)); | |||
} | |||
finally | |||
{ | |||
if (socketsHttpHandler != null) | |||
{ | |||
socketsHttpHandler.Dispose(); | |||
socketsHttpHandler = null; | |||
} | |||
if (httpClient != null) | |||
{ | |||
httpClient.Dispose(); | |||
httpClient = null; | |||
} | |||
} | |||
} | |||
public static bool MergeAndWritePACFile(string group, bool blacklist) | |||
/// <summary> | |||
/// Merge and write pac.txt from geosite. | |||
/// Used at multiple places. | |||
/// </summary> | |||
/// <param name="directGroups">A list of geosite groups configured for direct connection.</param> | |||
/// <param name="proxiedGroups">A list of geosite groups configured for proxied connection.</param> | |||
/// <param name="blacklist">Whether to use blacklist mode. False for whitelist.</param> | |||
/// <returns></returns> | |||
public static bool MergeAndWritePACFile(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
{ | |||
IList<DomainObject> domains = Geosites[group]; | |||
string abpContent = MergePACFile(domains, blacklist); | |||
string abpContent = MergePACFile(directGroups, proxiedGroups, blacklist); | |||
if (File.Exists(PACDaemon.PAC_FILE)) | |||
{ | |||
string original = FileManager.NonExclusiveReadAllText(PACDaemon.PAC_FILE, Encoding.UTF8); | |||
@@ -188,7 +162,44 @@ namespace Shadowsocks.Controller | |||
return true; | |||
} | |||
private static string MergePACFile(IList<DomainObject> domains, bool blacklist) | |||
/// <summary> | |||
/// Checks if the specified group exists in GeoSite database. | |||
/// </summary> | |||
/// <param name="group">The group name to check for.</param> | |||
/// <returns>True if the group exists. False if the group doesn't exist.</returns> | |||
public static bool CheckGeositeGroup(string group) => SeparateAttributeFromGroupName(group, out string groupName, out _) && Geosites.ContainsKey(groupName); | |||
/// <summary> | |||
/// Separates the attribute (e.g. @cn) from a group name. | |||
/// No checks are performed. | |||
/// </summary> | |||
/// <param name="group">A group name potentially with a trailing attribute.</param> | |||
/// <param name="groupName">The group name with the attribute stripped.</param> | |||
/// <param name="attribute">The attribute.</param> | |||
/// <returns>True for success. False for more than one '@'.</returns> | |||
private static bool SeparateAttributeFromGroupName(string group, out string groupName, out string attribute) | |||
{ | |||
var splitGroupAttributeList = group.Split('@'); | |||
if (splitGroupAttributeList.Length == 1) // no attribute | |||
{ | |||
groupName = splitGroupAttributeList[0]; | |||
attribute = ""; | |||
} | |||
else if (splitGroupAttributeList.Length == 2) // has attribute | |||
{ | |||
groupName = splitGroupAttributeList[0]; | |||
attribute = splitGroupAttributeList[1]; | |||
} | |||
else | |||
{ | |||
groupName = ""; | |||
attribute = ""; | |||
return false; | |||
} | |||
return true; | |||
} | |||
private static string MergePACFile(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
{ | |||
string abpContent; | |||
if (File.Exists(PACDaemon.USER_ABP_FILE)) | |||
@@ -204,27 +215,25 @@ namespace Shadowsocks.Controller | |||
if (File.Exists(PACDaemon.USER_RULE_FILE)) | |||
{ | |||
string userrulesString = FileManager.NonExclusiveReadAllText(PACDaemon.USER_RULE_FILE, Encoding.UTF8); | |||
userruleLines = PreProcessGFWList(userrulesString); | |||
userruleLines = ProcessUserRules(userrulesString); | |||
} | |||
List<string> gfwLines = GeositeToGFWList(domains, blacklist); | |||
List<string> ruleLines = GenerateRules(directGroups, proxiedGroups, blacklist); | |||
abpContent = | |||
$@"var __USERRULES__ = {JsonConvert.SerializeObject(userruleLines, Formatting.Indented)}; | |||
var __RULES__ = {JsonConvert.SerializeObject(gfwLines, Formatting.Indented)}; | |||
var __RULES__ = {JsonConvert.SerializeObject(ruleLines, Formatting.Indented)}; | |||
{abpContent}"; | |||
return abpContent; | |||
} | |||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | |||
private static List<string> PreProcessGFWList(string content) | |||
private static List<string> ProcessUserRules(string content) | |||
{ | |||
List<string> valid_lines = new List<string>(); | |||
using (var sr = new StringReader(content)) | |||
using (var stringReader = new StringReader(content)) | |||
{ | |||
foreach (var line in sr.NonWhiteSpaceLines()) | |||
for (string line = stringReader.ReadLine(); line != null; line = stringReader.ReadLine()) | |||
{ | |||
if (line.BeginWithAny(IgnoredLineBegins)) | |||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("!") || line.StartsWith("[")) | |||
continue; | |||
valid_lines.Add(line); | |||
} | |||
@@ -232,44 +241,105 @@ var __RULES__ = {JsonConvert.SerializeObject(gfwLines, Formatting.Indented)}; | |||
return valid_lines; | |||
} | |||
private static List<string> GeositeToGFWList(IList<DomainObject> domains, bool blacklist) | |||
/// <summary> | |||
/// Generates rule lines based on user preference. | |||
/// </summary> | |||
/// <param name="directGroups">A list of geosite groups configured for direct connection.</param> | |||
/// <param name="proxiedGroups">A list of geosite groups configured for proxied connection.</param> | |||
/// <param name="blacklist">Whether to use blacklist mode. False for whitelist.</param> | |||
/// <returns>A list of rule lines.</returns> | |||
private static List<string> GenerateRules(List<string> directGroups, List<string> proxiedGroups, bool blacklist) | |||
{ | |||
return blacklist ? GeositeToGFWListBlack(domains) : GeositeToGFWListWhite(domains); | |||
List<string> ruleLines; | |||
if (blacklist) // blocking + exception rules | |||
{ | |||
ruleLines = GenerateBlockingRules(proxiedGroups); | |||
ruleLines.AddRange(GenerateExceptionRules(directGroups)); | |||
} | |||
else // proxy all + exception rules | |||
{ | |||
ruleLines = new List<string>() | |||
{ | |||
"/.*/" // block/proxy all unmatched domains | |||
}; | |||
ruleLines.AddRange(GenerateExceptionRules(directGroups)); | |||
} | |||
return ruleLines; | |||
} | |||
private static List<string> GeositeToGFWListBlack(IList<DomainObject> domains) | |||
/// <summary> | |||
/// Generates rules that match domains that should be proxied. | |||
/// </summary> | |||
/// <param name="groups">A list of source groups.</param> | |||
/// <returns>A list of rule lines.</returns> | |||
private static List<string> GenerateBlockingRules(List<string> groups) | |||
{ | |||
List<string> ret = new List<string>(domains.Count + 100);// 100 overhead | |||
foreach (var d in domains) | |||
List<string> ruleLines = new List<string>(); | |||
foreach (var group in groups) | |||
{ | |||
string domain = d.Value; | |||
switch (d.Type) | |||
// separate group name and attribute | |||
SeparateAttributeFromGroupName(group, out string groupName, out string attribute); | |||
var domainObjects = Geosites[groupName]; | |||
if (!string.IsNullOrEmpty(attribute)) // has attribute | |||
{ | |||
case DomainObject.Types.Type.Plain: | |||
ret.Add(domain); | |||
break; | |||
case DomainObject.Types.Type.Regex: | |||
ret.Add($"/{domain}/"); | |||
break; | |||
case DomainObject.Types.Type.Domain: | |||
ret.Add($"||{domain}"); | |||
break; | |||
case DomainObject.Types.Type.Full: | |||
ret.Add($"|http://{domain}"); | |||
ret.Add($"|https://{domain}"); | |||
break; | |||
var attributeObject = new DomainObject.Types.Attribute | |||
{ | |||
Key = attribute, | |||
BoolValue = true | |||
}; | |||
foreach (var domainObject in domainObjects) | |||
{ | |||
if (domainObject.Attribute.Contains(attributeObject)) | |||
switch (domainObject.Type) | |||
{ | |||
case DomainObject.Types.Type.Plain: | |||
ruleLines.Add(domainObject.Value); | |||
break; | |||
case DomainObject.Types.Type.Regex: | |||
ruleLines.Add($"/{domainObject.Value}/"); | |||
break; | |||
case DomainObject.Types.Type.Domain: | |||
ruleLines.Add($"||{domainObject.Value}"); | |||
break; | |||
case DomainObject.Types.Type.Full: | |||
ruleLines.Add($"|http://{domainObject.Value}"); | |||
ruleLines.Add($"|https://{domainObject.Value}"); | |||
break; | |||
} | |||
} | |||
} | |||
else // no attribute | |||
foreach (var domainObject in domainObjects) | |||
{ | |||
switch (domainObject.Type) | |||
{ | |||
case DomainObject.Types.Type.Plain: | |||
ruleLines.Add(domainObject.Value); | |||
break; | |||
case DomainObject.Types.Type.Regex: | |||
ruleLines.Add($"/{domainObject.Value}/"); | |||
break; | |||
case DomainObject.Types.Type.Domain: | |||
ruleLines.Add($"||{domainObject.Value}"); | |||
break; | |||
case DomainObject.Types.Type.Full: | |||
ruleLines.Add($"|http://{domainObject.Value}"); | |||
ruleLines.Add($"|https://{domainObject.Value}"); | |||
break; | |||
} | |||
} | |||
} | |||
return ret; | |||
return ruleLines; | |||
} | |||
private static List<string> GeositeToGFWListWhite(IList<DomainObject> domains) | |||
{ | |||
return GeositeToGFWListBlack(domains) | |||
.Select(r => $"@@{r}") // convert to whitelist | |||
.Prepend("/.*/") // blacklist all other site | |||
/// <summary> | |||
/// Generates rules that match domains that should be connected directly without a proxy. | |||
/// </summary> | |||
/// <param name="groups">A list of source groups.</param> | |||
/// <returns>A list of rule lines.</returns> | |||
private static List<string> GenerateExceptionRules(List<string> groups) | |||
=> GenerateBlockingRules(groups) | |||
.Select(r => $"@@{r}") // convert blocking rules to exception rules | |||
.ToList(); | |||
} | |||
} | |||
} |
@@ -11,27 +11,15 @@ namespace Shadowsocks.Controller.Service | |||
{ | |||
public class OnlineConfigResolver | |||
{ | |||
public static async Task<List<Server>> GetOnline(string url, IWebProxy proxy = null) | |||
public static async Task<List<Server>> GetOnline(string url) | |||
{ | |||
var socketsHttpHandler = new SocketsHttpHandler() | |||
{ | |||
UseProxy = proxy != null, | |||
Proxy = proxy | |||
}; | |||
var httpClient = new HttpClient(socketsHttpHandler) | |||
{ | |||
Timeout = TimeSpan.FromSeconds(15) | |||
}; | |||
var httpClient = Program.MainController.GetHttpClient(); | |||
string server_json = await httpClient.GetStringAsync(url); | |||
var servers = server_json.GetServers(); | |||
foreach (var server in servers) | |||
{ | |||
server.group = url; | |||
} | |||
return servers.ToList(); | |||
} | |||
} | |||
@@ -45,7 +45,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
if (!File.Exists(PAC_FILE)) | |||
{ | |||
GeositeUpdater.MergeAndWritePACFile(config.geositeGroup, config.geositeBlacklistMode); | |||
GeositeUpdater.MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, config.geositePreferDirect); | |||
} | |||
return PAC_FILE; | |||
} | |||
@@ -63,7 +63,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
if (!File.Exists(PAC_FILE)) | |||
{ | |||
GeositeUpdater.MergeAndWritePACFile(config.geositeGroup, config.geositeBlacklistMode); | |||
GeositeUpdater.MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, config.geositePreferDirect); | |||
} | |||
return File.ReadAllText(PAC_FILE, Encoding.UTF8); | |||
} | |||
@@ -43,7 +43,7 @@ namespace Shadowsocks.Controller | |||
_config = config; | |||
string usedSecret = _config.secureLocalPac ? $"&secret={PacSecret}" : ""; | |||
string contentHash = GetHash(_pacDaemon.GetPACContent()); | |||
PacUrl = $"http://{config.localHost}:{config.localPort}/{RESOURCE_NAME}?hash={contentHash}{usedSecret}"; | |||
PacUrl = $"http://{config.LocalHost}:{config.localPort}/{RESOURCE_NAME}?hash={contentHash}{usedSecret}"; | |||
logger.Debug("Set PAC URL:" + PacUrl); | |||
} | |||
@@ -122,12 +122,13 @@ namespace Shadowsocks.Controller.Service | |||
public string ExpandEnvironmentVariables(string name, StringDictionary environmentVariables = null) | |||
{ | |||
name = name.ToLower(); | |||
// Expand the environment variables from the new process itself | |||
if (environmentVariables != null) | |||
{ | |||
foreach(string key in environmentVariables.Keys) | |||
{ | |||
name = name.Replace($"%{key}%", environmentVariables[key], StringComparison.OrdinalIgnoreCase); | |||
name = name.Replace($"%{key.ToLower()}%", environmentVariables[key]); | |||
} | |||
} | |||
// Also expand the environment variables from current main process (system) | |||
@@ -239,7 +239,7 @@ namespace Shadowsocks.Controller | |||
public DateTime lastActivity; | |||
private readonly ShadowsocksController _controller; | |||
private readonly ProxyConfig _config; | |||
private readonly ForwardProxyConfig _config; | |||
private readonly Socket _connection; | |||
private IEncryptor encryptor; | |||
@@ -708,10 +708,10 @@ namespace Shadowsocks.Controller | |||
{ | |||
switch (_config.proxyType) | |||
{ | |||
case ProxyConfig.PROXY_SOCKS5: | |||
case ForwardProxyConfig.PROXY_SOCKS5: | |||
remote = new Socks5Proxy(); | |||
break; | |||
case ProxyConfig.PROXY_HTTP: | |||
case ForwardProxyConfig.PROXY_HTTP: | |||
remote = new HttpProxy(); | |||
break; | |||
default: | |||
@@ -1,55 +1,36 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Net; | |||
using System.Net.Http; | |||
using System.Reflection; | |||
using System.Text.RegularExpressions; | |||
using System.Threading.Tasks; | |||
using System.Windows; | |||
using Newtonsoft.Json.Linq; | |||
using NLog; | |||
using Shadowsocks.Localization; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Util; | |||
using Shadowsocks.Views; | |||
namespace Shadowsocks.Controller | |||
{ | |||
public class UpdateChecker | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
private readonly Logger logger; | |||
private readonly HttpClient httpClient; | |||
// https://developer.github.com/v3/repos/releases/ | |||
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases"; | |||
private const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"; | |||
private Configuration config; | |||
public bool NewVersionFound; | |||
public string LatestVersionNumber; | |||
public string LatestVersionSuffix; | |||
public string LatestVersionName; | |||
public string LatestVersionURL; | |||
public string LatestVersionLocalName; | |||
public event EventHandler CheckUpdateCompleted; | |||
public static readonly string Version = Assembly.GetEntryAssembly().GetName().Version.ToString(); | |||
private class CheckUpdateTimer : System.Timers.Timer | |||
{ | |||
public Configuration config; | |||
public CheckUpdateTimer(int p) : base(p) | |||
{ | |||
} | |||
} | |||
private Configuration _config; | |||
private Window versionUpdatePromptWindow; | |||
private JToken _releaseObject; | |||
public void CheckUpdate(Configuration config, int delay) | |||
{ | |||
#if DEBUG | |||
return; | |||
#pragma warning disable CS0162 // 检测到无法访问的代码 | |||
#endif | |||
CheckUpdateTimer timer = new CheckUpdateTimer(delay); | |||
timer.AutoReset = false; | |||
timer.Elapsed += Timer_Elapsed; | |||
timer.config = config; | |||
timer.Enabled = true; | |||
} | |||
public string NewReleaseVersion { get; private set; } | |||
public string NewReleaseZipFilename { get; private set; } | |||
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) | |||
{ | |||
@@ -60,204 +41,146 @@ namespace Shadowsocks.Controller | |||
timer.Dispose(); | |||
CheckUpdate(config); | |||
} | |||
public event EventHandler CheckUpdateCompleted; | |||
public void CheckUpdate(Configuration config) | |||
{ | |||
#if DEBUG | |||
return; | |||
#endif | |||
this.config = config; | |||
public static readonly string Version = Assembly.GetEntryAssembly().GetName().Version.ToString(); | |||
private readonly Version _version; | |||
try | |||
{ | |||
logger.Info("Checking updates..."); | |||
WebClient http = CreateWebClient(); | |||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||
http.DownloadStringAsync(new Uri(UpdateURL)); | |||
} | |||
catch (Exception ex) | |||
{ | |||
logger.LogUsefulException(ex); | |||
} | |||
public UpdateChecker() | |||
{ | |||
logger = LogManager.GetCurrentClassLogger(); | |||
httpClient = Program.MainController.GetHttpClient(); | |||
_version = new Version(Version); | |||
_config = Program.MainController.GetCurrentConfiguration(); | |||
} | |||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | |||
/// <summary> | |||
/// Checks for updates and asks the user if updates are found. | |||
/// </summary> | |||
/// <param name="millisecondsDelay">A delay in milliseconds before checking.</param> | |||
/// <returns></returns> | |||
public async Task CheckForVersionUpdate(int millisecondsDelay = 0) | |||
{ | |||
// delay | |||
logger.Info($"Waiting for {millisecondsDelay}ms before checking for version update."); | |||
await Task.Delay(millisecondsDelay); | |||
// update _config so we would know if the user checked or unchecked pre-release checks | |||
_config = Program.MainController.GetCurrentConfiguration(); | |||
// start | |||
logger.Info($"Checking for version update."); | |||
try | |||
{ | |||
string response = e.Result; | |||
JArray result = JArray.Parse(response); | |||
List<Asset> asserts = new List<Asset>(); | |||
if (result != null) | |||
// list releases via API | |||
var releasesListJsonString = await httpClient.GetStringAsync(UpdateURL); | |||
// parse | |||
var releasesJArray = JArray.Parse(releasesListJsonString); | |||
foreach (var releaseObject in releasesJArray) | |||
{ | |||
foreach (JObject release in result) | |||
var releaseTagName = (string)releaseObject["tag_name"]; | |||
var releaseVersion = new Version(releaseTagName); | |||
if (releaseTagName == _config.skippedUpdateVersion) // finished checking | |||
break; | |||
if (releaseVersion.CompareTo(_version) > 0 && | |||
(!(bool)releaseObject["prerelease"] || _config.checkPreRelease && (bool)releaseObject["prerelease"])) // selected | |||
{ | |||
var isPreRelease = (bool)release["prerelease"]; | |||
if (isPreRelease && !config.checkPreRelease) | |||
{ | |||
continue; | |||
} | |||
foreach (JObject asset in (JArray)release["assets"]) | |||
{ | |||
Asset ass = Asset.ParseAsset(asset); | |||
if (ass != null) | |||
{ | |||
ass.prerelease = isPreRelease; | |||
if (ass.IsNewVersion(Version, config.checkPreRelease)) | |||
{ | |||
asserts.Add(ass); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
if (asserts.Count != 0) | |||
{ | |||
SortByVersions(asserts); | |||
Asset asset = asserts[asserts.Count - 1]; | |||
NewVersionFound = true; | |||
LatestVersionURL = asset.browser_download_url; | |||
LatestVersionNumber = asset.version; | |||
LatestVersionName = asset.name; | |||
LatestVersionSuffix = asset.suffix == null ? "" : $"-{asset.suffix}"; | |||
startDownload(); | |||
} | |||
else | |||
{ | |||
logger.Info("No update is available"); | |||
if (CheckUpdateCompleted != null) | |||
{ | |||
CheckUpdateCompleted(this, new EventArgs()); | |||
logger.Info($"Found new version {releaseTagName}."); | |||
_releaseObject = releaseObject; | |||
NewReleaseVersion = releaseTagName; | |||
AskToUpdate(releaseObject); | |||
return; | |||
} | |||
} | |||
logger.Info($"No new versions found."); | |||
CheckUpdateCompleted?.Invoke(this, new EventArgs()); | |||
} | |||
catch (Exception ex) | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(ex); | |||
logger.LogUsefulException(e); | |||
} | |||
} | |||
private void startDownload() | |||
/// <summary> | |||
/// Opens a window to show the update's information. | |||
/// </summary> | |||
/// <param name="releaseObject">The update release object.</param> | |||
private void AskToUpdate(JToken releaseObject) | |||
{ | |||
try | |||
{ | |||
LatestVersionLocalName = Utils.GetTempPath(LatestVersionName); | |||
WebClient http = CreateWebClient(); | |||
http.DownloadFileCompleted += Http_DownloadFileCompleted; | |||
http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName); | |||
} | |||
catch (Exception ex) | |||
if (versionUpdatePromptWindow == null) | |||
{ | |||
logger.LogUsefulException(ex); | |||
versionUpdatePromptWindow = new Window() | |||
{ | |||
Title = LocalizationProvider.GetLocalizedValue<string>("VersionUpdate"), | |||
Height = 480, | |||
Width = 640, | |||
MinHeight = 480, | |||
MinWidth = 640, | |||
Content = new VersionUpdatePromptView(releaseObject) | |||
}; | |||
versionUpdatePromptWindow.Closed += VersionUpdatePromptWindow_Closed; | |||
versionUpdatePromptWindow.Show(); | |||
} | |||
versionUpdatePromptWindow.Activate(); | |||
} | |||
private void Http_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) | |||
private void VersionUpdatePromptWindow_Closed(object sender, EventArgs e) | |||
{ | |||
versionUpdatePromptWindow = null; | |||
} | |||
/// <summary> | |||
/// Downloads the selected update and notifies the user. | |||
/// </summary> | |||
/// <returns></returns> | |||
public async Task DoUpdate() | |||
{ | |||
try | |||
{ | |||
if (e.Error != null) | |||
{ | |||
logger.LogUsefulException(e.Error); | |||
return; | |||
} | |||
logger.Info($"New version {LatestVersionNumber}{LatestVersionSuffix} found: {LatestVersionLocalName}"); | |||
if (CheckUpdateCompleted != null) | |||
var assets = (JArray)_releaseObject["assets"]; | |||
// download all assets | |||
foreach (JObject asset in assets) | |||
{ | |||
CheckUpdateCompleted(this, new EventArgs()); | |||
var filename = (string)asset["name"]; | |||
var browser_download_url = (string)asset["browser_download_url"]; | |||
var response = await httpClient.GetAsync(browser_download_url); | |||
using (var downloadedFileStream = File.Create(Utils.GetTempPath(filename))) | |||
await response.Content.CopyToAsync(downloadedFileStream); | |||
logger.Info($"Downloaded {filename}."); | |||
// store .zip filename | |||
if (filename.EndsWith(".zip")) | |||
NewReleaseZipFilename = filename; | |||
} | |||
logger.Info("Finished downloading."); | |||
// notify user | |||
CloseVersionUpdatePromptWindow(); | |||
Process.Start("explorer.exe", $"/select, \"{Utils.GetTempPath(NewReleaseZipFilename)}\""); | |||
} | |||
catch (Exception ex) | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(ex); | |||
logger.LogUsefulException(e); | |||
} | |||
} | |||
private WebClient CreateWebClient() | |||
/// <summary> | |||
/// Saves the skipped update version. | |||
/// </summary> | |||
public void SkipUpdate() | |||
{ | |||
WebClient http = new WebClient(); | |||
http.Headers.Add("User-Agent", UserAgent); | |||
http.Proxy = new WebProxy(config.localHost, config.localPort); | |||
return http; | |||
var version = (string)_releaseObject["tag_name"] ?? ""; | |||
_config.skippedUpdateVersion = version; | |||
Program.MainController.SaveSkippedUpdateVerion(version); | |||
logger.Info($"The update {version} has been skipped and will be ignored next time."); | |||
CloseVersionUpdatePromptWindow(); | |||
} | |||
private void SortByVersions(List<Asset> asserts) | |||
/// <summary> | |||
/// Closes the update prompt window. | |||
/// </summary> | |||
public void CloseVersionUpdatePromptWindow() | |||
{ | |||
asserts.Sort(); | |||
} | |||
public class Asset : IComparable<Asset> | |||
{ | |||
public bool prerelease; | |||
public string name; | |||
public string version; | |||
public string browser_download_url; | |||
public string suffix; | |||
public static Asset ParseAsset(JObject assertJObject) | |||
{ | |||
var name = (string)assertJObject["name"]; | |||
Match match = Regex.Match(name, @"^Shadowsocks-(?<version>\d+(?:\.\d+)*)(?:|-(?<suffix>.+))\.\w+$", | |||
RegexOptions.IgnoreCase); | |||
if (match.Success) | |||
{ | |||
string version = match.Groups["version"].Value; | |||
var asset = new Asset | |||
{ | |||
browser_download_url = (string)assertJObject["browser_download_url"], | |||
name = name, | |||
version = version | |||
}; | |||
if (match.Groups["suffix"].Success) | |||
{ | |||
asset.suffix = match.Groups["suffix"].Value; | |||
} | |||
return asset; | |||
} | |||
return null; | |||
} | |||
public bool IsNewVersion(string currentVersion, bool checkPreRelease) | |||
{ | |||
if (prerelease && !checkPreRelease) | |||
{ | |||
return false; | |||
} | |||
if (version == null) | |||
{ | |||
return false; | |||
} | |||
var cmp = CompareVersion(version, currentVersion); | |||
return cmp > 0; | |||
} | |||
public static int CompareVersion(string l, string r) | |||
{ | |||
var ls = l.Split('.'); | |||
var rs = r.Split('.'); | |||
for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++) | |||
{ | |||
int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0; | |||
int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0; | |||
if (lp != rp) | |||
{ | |||
return lp - rp; | |||
} | |||
} | |||
return 0; | |||
} | |||
public int CompareTo(Asset other) | |||
if (versionUpdatePromptWindow != null) | |||
{ | |||
return CompareVersion(version, other.version); | |||
versionUpdatePromptWindow.Close(); | |||
versionUpdatePromptWindow = null; | |||
} | |||
} | |||
} | |||
@@ -4,6 +4,7 @@ using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Http; | |||
using System.Net.Sockets; | |||
using System.Text; | |||
using System.Threading; | |||
@@ -15,12 +16,14 @@ using Shadowsocks.Controller.Service; | |||
using Shadowsocks.Controller.Strategy; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Util; | |||
using WPFLocalizeExtension.Engine; | |||
namespace Shadowsocks.Controller | |||
{ | |||
public class ShadowsocksController | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
private readonly Logger logger; | |||
private readonly HttpClient httpClient; | |||
// controller: | |||
// handle user actions | |||
@@ -38,9 +41,6 @@ namespace Shadowsocks.Controller | |||
private PrivoxyRunner privoxyRunner; | |||
private readonly ConcurrentDictionary<Server, Sip003Plugin> _pluginsByServer; | |||
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; | |||
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } | |||
private long _inboundCounter = 0; | |||
private long _outboundCounter = 0; | |||
public long InboundCounter => Interlocked.Read(ref _inboundCounter); | |||
@@ -92,8 +92,10 @@ namespace Shadowsocks.Controller | |||
public ShadowsocksController() | |||
{ | |||
logger = LogManager.GetCurrentClassLogger(); | |||
httpClient = new HttpClient(); | |||
_config = Configuration.Load(); | |||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | |||
Configuration.Process(ref _config); | |||
_strategyManager = new StrategyManager(this); | |||
_pluginsByServer = new ConcurrentDictionary<Server, Sip003Plugin>(); | |||
StartTrafficStatistics(61); | |||
@@ -106,23 +108,34 @@ namespace Shadowsocks.Controller | |||
#region Basic | |||
public void Start(bool regHotkeys = true) | |||
public void Start(bool systemWakeUp = false) | |||
{ | |||
if (_config.updated && regHotkeys) | |||
if (_config.firstRunOnNewVersion && !systemWakeUp) | |||
{ | |||
_config.updated = false; | |||
ProgramUpdated.Invoke(this, new UpdatedEventArgs() | |||
{ | |||
OldVersion = _config.version, | |||
NewVersion = UpdateChecker.Version, | |||
}); | |||
// delete pac.txt when regeneratePacOnUpdate is true | |||
if (_config.regeneratePacOnUpdate) | |||
try | |||
{ | |||
File.Delete(PACDaemon.PAC_FILE); | |||
logger.Info("Deleted pac.txt from previous version."); | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
} | |||
// finish up first run of new version | |||
_config.firstRunOnNewVersion = false; | |||
_config.version = UpdateChecker.Version; | |||
Configuration.Save(_config); | |||
} | |||
Reload(); | |||
if (regHotkeys) | |||
{ | |||
if (!systemWakeUp) | |||
HotkeyReg.RegAllHotkeys(); | |||
} | |||
} | |||
public void Stop() | |||
@@ -150,10 +163,24 @@ namespace Shadowsocks.Controller | |||
Encryption.RNG.Reload(); | |||
// some logic in configuration updated the config when saving, we need to read it again | |||
_config = Configuration.Load(); | |||
Configuration.Process(ref _config); | |||
NLogConfig.LoadConfiguration(); | |||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | |||
logger.Info($"WPF Localization Extension|Current culture: {LocalizeDictionary.CurrentCulture}"); | |||
// set User-Agent for httpClient | |||
try | |||
{ | |||
if (!string.IsNullOrWhiteSpace(_config.userAgentString)) | |||
httpClient.DefaultRequestHeaders.Add("User-Agent", _config.userAgentString); | |||
} | |||
catch | |||
{ | |||
// reset userAgent to default and reapply | |||
Configuration.ResetUserAgent(_config); | |||
httpClient.DefaultRequestHeaders.Add("User-Agent", _config.userAgentString); | |||
} | |||
privoxyRunner = privoxyRunner ?? new PrivoxyRunner(); | |||
@@ -167,7 +194,6 @@ namespace Shadowsocks.Controller | |||
GeositeUpdater.UpdateCompleted += PacServer_PACUpdateCompleted; | |||
GeositeUpdater.Error += PacServer_PACUpdateError; | |||
availabilityStatistics.UpdateConfiguration(this); | |||
_tcpListener?.Stop(); | |||
_udpListener?.Stop(); | |||
StopPlugins(); | |||
@@ -186,7 +212,6 @@ namespace Shadowsocks.Controller | |||
privoxyRunner.Start(_config); | |||
TCPRelay tcpRelay = new TCPRelay(this, _config); | |||
tcpRelay.OnConnected += UpdateLatency; | |||
tcpRelay.OnInbound += UpdateInboundCounter; | |||
tcpRelay.OnOutbound += UpdateOutboundCounter; | |||
tcpRelay.OnFailed += (o, e) => GetCurrentStrategy()?.SetFailure(e.server); | |||
@@ -239,22 +264,9 @@ namespace Shadowsocks.Controller | |||
Errored?.Invoke(this, new ErrorEventArgs(e)); | |||
} | |||
public Server GetCurrentServer() | |||
{ | |||
return _config.GetCurrentServer(); | |||
} | |||
// always return copy | |||
public Configuration GetConfigurationCopy() | |||
{ | |||
return Configuration.Load(); | |||
} | |||
// always return current instance | |||
public Configuration GetCurrentConfiguration() | |||
{ | |||
return _config; | |||
} | |||
public HttpClient GetHttpClient() => httpClient; | |||
public Server GetCurrentServer() => _config.GetCurrentServer(); | |||
public Configuration GetCurrentConfiguration() => _config; | |||
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint) | |||
{ | |||
@@ -270,13 +282,6 @@ namespace Shadowsocks.Controller | |||
return GetCurrentServer(); | |||
} | |||
public void SelectServerIndex(int index) | |||
{ | |||
_config.index = index; | |||
_config.strategy = null; | |||
SaveConfig(_config); | |||
} | |||
public void SaveServers(List<Server> servers, int localPort, bool portableMode) | |||
{ | |||
_config.configs = servers; | |||
@@ -285,6 +290,13 @@ namespace Shadowsocks.Controller | |||
Configuration.Save(_config); | |||
} | |||
public void SelectServerIndex(int index) | |||
{ | |||
_config.index = index; | |||
_config.strategy = null; | |||
SaveConfig(_config); | |||
} | |||
public void ToggleShareOverLAN(bool enabled) | |||
{ | |||
_config.shareOverLan = enabled; | |||
@@ -313,7 +325,7 @@ namespace Shadowsocks.Controller | |||
EnableGlobalChanged?.Invoke(this, new EventArgs()); | |||
} | |||
public void SaveProxy(ProxyConfig proxyConfig) | |||
public void SaveProxy(ForwardProxyConfig proxyConfig) | |||
{ | |||
_config.proxy = proxyConfig; | |||
SaveConfig(_config); | |||
@@ -346,7 +358,7 @@ namespace Shadowsocks.Controller | |||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | |||
private void PacDaemon_UserRuleFileChanged(object sender, EventArgs e) | |||
{ | |||
GeositeUpdater.MergeAndWritePACFile(_config.geositeGroup, _config.geositeBlacklistMode); | |||
GeositeUpdater.MergeAndWritePACFile(_config.geositeDirectGroups, _config.geositeProxiedGroups, _config.geositePreferDirect); | |||
UpdateSystemProxy(); | |||
} | |||
@@ -393,6 +405,13 @@ namespace Shadowsocks.Controller | |||
ConfigChanged?.Invoke(this, new EventArgs()); | |||
} | |||
public void ToggleRegeneratePacOnUpdate(bool enabled) | |||
{ | |||
_config.regeneratePacOnUpdate = enabled; | |||
SaveConfig(_config); | |||
ConfigChanged?.Invoke(this, new EventArgs()); | |||
} | |||
#endregion | |||
#region SIP002 | |||
@@ -419,7 +438,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
if (ssURL.IsNullOrEmpty() || ssURL.IsWhiteSpace()) | |||
if (string.IsNullOrWhiteSpace(ssURL)) | |||
return false; | |||
var servers = Server.GetServers(ssURL); | |||
@@ -474,6 +493,12 @@ namespace Shadowsocks.Controller | |||
ConfigChanged?.Invoke(this, new EventArgs()); | |||
} | |||
public void SaveSkippedUpdateVerion(string version) | |||
{ | |||
_config.skippedUpdateVersion = version; | |||
Configuration.Save(_config); | |||
} | |||
public void SaveLogViewerConfig(LogViewerConfig newConfig) | |||
{ | |||
_config.logViewer = newConfig; | |||
@@ -493,7 +518,7 @@ namespace Shadowsocks.Controller | |||
#endregion | |||
#region Statistic | |||
#region Strategy | |||
public void SelectStrategy(string strategyID) | |||
{ | |||
@@ -502,12 +527,6 @@ namespace Shadowsocks.Controller | |||
SaveConfig(_config); | |||
} | |||
public void SaveStrategyConfigurations(StatisticsStrategyConfiguration configuration) | |||
{ | |||
StatisticsConfiguration = configuration; | |||
StatisticsStrategyConfiguration.Save(configuration); | |||
} | |||
public IList<IStrategy> GetStrategies() | |||
{ | |||
return _strategyManager.GetStrategies(); | |||
@@ -525,43 +544,16 @@ namespace Shadowsocks.Controller | |||
return null; | |||
} | |||
public void UpdateStatisticsConfiguration(bool enabled) | |||
{ | |||
if (availabilityStatistics != null) | |||
{ | |||
availabilityStatistics.UpdateConfiguration(this); | |||
_config.availabilityStatistics = enabled; | |||
SaveConfig(_config); | |||
} | |||
} | |||
public void UpdateLatency(object sender, SSTCPConnectedEventArgs args) | |||
{ | |||
GetCurrentStrategy()?.UpdateLatency(args.server, args.latency); | |||
if (_config.availabilityStatistics) | |||
{ | |||
availabilityStatistics.UpdateLatency(args.server, (int)args.latency.TotalMilliseconds); | |||
} | |||
} | |||
public void UpdateInboundCounter(object sender, SSTransmitEventArgs args) | |||
{ | |||
GetCurrentStrategy()?.UpdateLastRead(args.server); | |||
Interlocked.Add(ref _inboundCounter, args.length); | |||
if (_config.availabilityStatistics) | |||
{ | |||
availabilityStatistics.UpdateInboundCounter(args.server, args.length); | |||
} | |||
} | |||
public void UpdateOutboundCounter(object sender, SSTransmitEventArgs args) | |||
{ | |||
GetCurrentStrategy()?.UpdateLastWrite(args.server); | |||
Interlocked.Add(ref _outboundCounter, args.length); | |||
if (_config.availabilityStatistics) | |||
{ | |||
availabilityStatistics.UpdateOutboundCounter(args.server, args.length); | |||
} | |||
} | |||
#endregion | |||
@@ -620,7 +612,7 @@ namespace Shadowsocks.Controller | |||
} | |||
#endregion | |||
#region Traffic Statistics | |||
private void StartTrafficStatistics(int queueMaxSize) | |||
@@ -668,7 +660,7 @@ namespace Shadowsocks.Controller | |||
public async Task<int> UpdateOnlineConfigInternal(string url) | |||
{ | |||
var onlineServer = await OnlineConfigResolver.GetOnline(url, _config.WebProxy); | |||
var onlineServer = await OnlineConfigResolver.GetOnline(url); | |||
_config.configs = Configuration.SortByOnlineConfig( | |||
_config.configs | |||
.Where(c => c.group != url) | |||
@@ -695,10 +687,10 @@ namespace Shadowsocks.Controller | |||
return true; | |||
} | |||
public async Task<int> UpdateAllOnlineConfig() | |||
public async Task<List<string>> UpdateAllOnlineConfig() | |||
{ | |||
var selected = GetCurrentServer(); | |||
int failCount = 0; | |||
var failedUrls = new List<string>(); | |||
foreach (var url in _config.onlineConfigSource) | |||
{ | |||
try | |||
@@ -708,18 +700,18 @@ namespace Shadowsocks.Controller | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
failCount++; | |||
failedUrls.Add(url); | |||
} | |||
} | |||
_config.index = _config.configs.IndexOf(selected); | |||
SaveConfig(_config); | |||
return failCount; | |||
return failedUrls; | |||
} | |||
public void SaveOnlineConfigSource(IEnumerable<string> vs) | |||
public void SaveOnlineConfigSource(List<string> sources) | |||
{ | |||
_config.onlineConfigSource = vs.ToList(); | |||
_config.onlineConfigSource = sources; | |||
SaveConfig(_config); | |||
} | |||
@@ -1,170 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Threading; | |||
using Newtonsoft.Json; | |||
using NLog; | |||
using Shadowsocks.Model; | |||
namespace Shadowsocks.Controller.Strategy | |||
{ | |||
using Statistics = Dictionary<string, List<StatisticsRecord>>; | |||
internal class StatisticsStrategy : IStrategy, IDisposable | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
private readonly ShadowsocksController _controller; | |||
private Server _currentServer; | |||
private readonly Timer _timer; | |||
private Statistics _filteredStatistics; | |||
private AvailabilityStatistics Service => _controller.availabilityStatistics; | |||
private int ChoiceKeptMilliseconds | |||
=> (int)TimeSpan.FromMinutes(_controller.StatisticsConfiguration.ChoiceKeptMinutes).TotalMilliseconds; | |||
public StatisticsStrategy(ShadowsocksController controller) | |||
{ | |||
_controller = controller; | |||
var servers = controller.GetCurrentConfiguration().configs; | |||
var randomIndex = new Random().Next() % servers.Count; | |||
_currentServer = servers[randomIndex]; //choose a server randomly at first | |||
// FIXME: consider Statistics and Config changing asynchrously. | |||
_timer = new Timer(ReloadStatisticsAndChooseAServer); | |||
} | |||
private void ReloadStatisticsAndChooseAServer(object obj) | |||
{ | |||
logger.Debug("Reloading statistics and choose a new server...."); | |||
var servers = _controller.GetCurrentConfiguration().configs; | |||
LoadStatistics(); | |||
ChooseNewServer(servers); | |||
} | |||
private void LoadStatistics() | |||
{ | |||
_filteredStatistics = | |||
Service.FilteredStatistics ?? | |||
Service.RawStatistics ?? | |||
_filteredStatistics; | |||
} | |||
//return the score by data | |||
//server with highest score will be choosen | |||
private float? GetScore(string identifier, List<StatisticsRecord> records) | |||
{ | |||
var config = _controller.StatisticsConfiguration; | |||
float? score = null; | |||
var averageRecord = new StatisticsRecord(identifier, | |||
records.Where(record => record.MaxInboundSpeed != null).Select(record => record.MaxInboundSpeed.Value).ToList(), | |||
records.Where(record => record.MaxOutboundSpeed != null).Select(record => record.MaxOutboundSpeed.Value).ToList(), | |||
records.Where(record => record.AverageLatency != null).Select(record => record.AverageLatency.Value).ToList()); | |||
averageRecord.SetResponse(records.Select(record => record.AverageResponse).ToList()); | |||
foreach (var calculation in config.Calculations) | |||
{ | |||
var name = calculation.Key; | |||
var field = typeof (StatisticsRecord).GetField(name); | |||
dynamic value = field?.GetValue(averageRecord); | |||
var factor = calculation.Value; | |||
if (value == null || factor.Equals(0)) continue; | |||
score = score ?? 0; | |||
score += value * factor; | |||
} | |||
if (score != null) | |||
{ | |||
logger.Debug($"Highest score: {score} {JsonConvert.SerializeObject(averageRecord, Formatting.Indented)}"); | |||
} | |||
return score; | |||
} | |||
private void ChooseNewServer(List<Server> servers) | |||
{ | |||
if (_filteredStatistics == null || servers.Count == 0) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
var serversWithStatistics = (from server in servers | |||
let id = server.Identifier() | |||
where _filteredStatistics.ContainsKey(id) | |||
let score = GetScore(id, _filteredStatistics[id]) | |||
where score != null | |||
select new | |||
{ | |||
server, | |||
score | |||
}).ToArray(); | |||
if (serversWithStatistics.Length < 2) | |||
{ | |||
LogWhenEnabled("no enough statistics data or all factors in calculations are 0"); | |||
return; | |||
} | |||
var bestResult = serversWithStatistics | |||
.Aggregate((server1, server2) => server1.score > server2.score ? server1 : server2); | |||
LogWhenEnabled($"Switch to server: {bestResult.server.ToString()} by statistics: score {bestResult.score}"); | |||
_currentServer = bestResult.server; | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
} | |||
} | |||
private void LogWhenEnabled(string log) | |||
{ | |||
if (_controller.GetCurrentStrategy()?.ID == ID) //output when enabled | |||
{ | |||
Console.WriteLine(log); | |||
} | |||
} | |||
public string ID => "com.shadowsocks.strategy.scbs"; | |||
public string Name => I18N.GetString("Choose by statistics"); | |||
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint) | |||
{ | |||
if (_currentServer == null) | |||
{ | |||
ChooseNewServer(_controller.GetCurrentConfiguration().configs); | |||
} | |||
return _currentServer; //current server cached for CachedInterval | |||
} | |||
public void ReloadServers() | |||
{ | |||
ChooseNewServer(_controller.GetCurrentConfiguration().configs); | |||
_timer?.Change(0, ChoiceKeptMilliseconds); | |||
} | |||
public void SetFailure(Server server) | |||
{ | |||
logger.Debug($"failure: {server.ToString()}"); | |||
} | |||
public void UpdateLastRead(Server server) | |||
{ | |||
} | |||
public void UpdateLastWrite(Server server) | |||
{ | |||
} | |||
public void UpdateLatency(Server server, TimeSpan latency) | |||
{ | |||
} | |||
public void Dispose() | |||
{ | |||
_timer.Dispose(); | |||
} | |||
} | |||
} |
@@ -13,7 +13,6 @@ namespace Shadowsocks.Controller.Strategy | |||
_strategies = new List<IStrategy>(); | |||
_strategies.Add(new BalancingStrategy(controller)); | |||
_strategies.Add(new HighAvailabilityStrategy(controller)); | |||
_strategies.Add(new StatisticsStrategy(controller)); | |||
// TODO: load DLL plugins | |||
} | |||
public IList<IStrategy> GetStrategies() | |||
@@ -23,7 +23,7 @@ namespace Shadowsocks.Controller.Hotkeys | |||
/// <returns></returns> | |||
public static Delegate GetCallback(string methodname) | |||
{ | |||
if (methodname.IsNullOrEmpty()) throw new ArgumentException(nameof(methodname)); | |||
if (string.IsNullOrEmpty(methodname)) throw new ArgumentException(nameof(methodname)); | |||
MethodInfo dynMethod = typeof(HotkeyCallbacks).GetMethod(methodname, | |||
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase); | |||
return dynMethod == null ? null : Delegate.CreateDelegate(typeof(HotKeys.HotKeyCallBackHandler), Instance, dynMethod); | |||
@@ -46,21 +46,20 @@ namespace Shadowsocks.Controller.Hotkeys | |||
private void SwitchSystemProxyCallback() | |||
{ | |||
bool enabled = _controller.GetConfigurationCopy().enabled; | |||
bool enabled = _controller.GetCurrentConfiguration().enabled; | |||
_controller.ToggleEnable(!enabled); | |||
} | |||
private void SwitchSystemProxyModeCallback() | |||
{ | |||
var config = _controller.GetConfigurationCopy(); | |||
if (config.enabled == false) return; | |||
var currStatus = config.global; | |||
_controller.ToggleGlobal(!currStatus); | |||
var config = _controller.GetCurrentConfiguration(); | |||
if (config.enabled) | |||
_controller.ToggleGlobal(!config.global); | |||
} | |||
private void SwitchAllowLanCallback() | |||
{ | |||
var status = _controller.GetConfigurationCopy().shareOverLan; | |||
var status = _controller.GetCurrentConfiguration().shareOverLan; | |||
_controller.ToggleShareOverLAN(!status); | |||
} | |||
@@ -110,7 +110,7 @@ namespace Shadowsocks.Controller.Hotkeys | |||
{ | |||
try | |||
{ | |||
if (s.IsNullOrEmpty()) return null; | |||
if (string.IsNullOrEmpty(s)) return null; | |||
int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase); | |||
if (offset <= 0) return null; | |||
string modifierStr = s.Substring(0, offset).Trim(); | |||
@@ -31,7 +31,7 @@ namespace Shadowsocks.Controller | |||
else | |||
{ | |||
string pacUrl; | |||
if (config.useOnlinePac && !config.pacUrl.IsNullOrEmpty()) | |||
if (config.useOnlinePac && !string.IsNullOrEmpty(config.pacUrl)) | |||
{ | |||
pacUrl = config.pacUrl; | |||
} | |||
@@ -16,7 +16,6 @@ PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PACモード | |||
Global,Для всей системы,全局模式,全局模式,グローバルプロキシ,전역,Global | |||
Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs | |||
Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… | |||
Statistics Config...,Настройки статистики…,统计配置...,統計設定檔...,統計情報の設定...,통계 설정,Configuration des statistiques… | |||
Online Config...,,在线配置...,線上配置...,,, | |||
Start on Boot,Автозагрузка,开机启动,開機啟動,システム起動時に実行,시스템 시작 시에 시작하기,Démarrage automatique | |||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,ss:// リンクの関連付け,ss:// 링크 연결, | |||
@@ -28,6 +27,7 @@ Edit Local PAC File...,Редактировать локальный PAC…,编 | |||
Update Local PAC from Geosite,Обновить локальный PAC из Geosite,从 Geosite 更新本地 PAC,從 Geosite 更新本機 PAC,Geosite からローカル PAC を更新,Geosite에서 로컬 프록시 자동 구성 파일 업데이트,Mettre à jour le PAC local à partir de Geosite | |||
Edit User Rule for Geosite...,Редактировать свои правила для Geosite,编辑 Geosite 的用户规则...,編輯 Geosite 的使用者規則...,ユーザールールの編集...,Geosite 사용자 수정,Modifier la règle utilisateur pour Geosite ... | |||
Secure Local PAC,Безопасный URL локального PAC,保护本地 PAC,安全本機 PAC,ローカル PAC を保護,로컬 프록시 자동 구성 파일 암호화,Sécuriser PAC local | |||
Regenerate local PAC on version update,,版本更新后重新生成本地 PAC,版本更新後重新生成本地 PAC,,, | |||
Copy Local PAC URL,Копировать URL локального PAC,复制本地 PAC 网址,複製本機 PAC 網址,ローカル PAC URL をコピー,로컬 프록시 자동 구성 파일 URL 복사,Copier l'URL du PAC local | |||
Share Server Config...,Поделиться конфигурацией сервера…,分享服务器配置...,分享伺服器設定檔...,サーバーの設定を共有...,서버 설정 공유,Partager la configuration du serveur ... | |||
Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... | |||
@@ -46,7 +46,6 @@ Quit,Выход,退出,結束,終了,종료,Quitter | |||
Edit Servers,Редактирование серверов,编辑服务器,編輯伺服器,サーバーの編集,서버 수정,Éditer serveurs | |||
Load Balance,Балансировка нагрузки,负载均衡,負載平衡,サーバーロードバランス,로드밸런싱,Répartition de charge | |||
High Availability,Высокая доступность,高可用,高可用性,高可用性,고가용성,Haute disponibilité | |||
Choose by statistics,На основе статистики,根据统计,根據統計,統計で選ぶ,통계 기반,Choisissez par statistiques | |||
Show Plugin Output,События плагинов в журнале,显示插件输出,,プラグインの出力情報を表示,플러그인 출력 보이기,Afficher la sortie du plugin | |||
Write translation template,Создать шаблон для перевода,写入翻译模板,,翻訳テンプレートファイルを書き込む,번역 템플릿 쓰기,Écrire un modèle de traduction | |||
,,,,,, | |||
@@ -80,55 +79,6 @@ Move D&own,Ниже,下移(&O),下移 (&O),下に移動 (&O),아래로 (&O),Desc | |||
deprecated,Устаревшее,不推荐,不推薦,非推奨,더 이상 사용되지 않음,Obsolète | |||
"Encryption method {0} not exist, will replace with {1}",,加密方法{0}不存在,将使用{1}代替,,暗号化方式{0}が存在しません,{1}に置換します,{0} 암호화 방식이 존재하지 않으므로 {1}로 대체될 것입니다.,"Méthode de chiffrement {0} n'existe pas, sera remplacée par {1}" | |||
,,,,,, | |||
# Online Config Form,,,,,, | |||
,,,,,, | |||
Online config,,在线配置,線上配置,,, | |||
Online config URL,,在线配置链接,線上配置鏈接,,, | |||
&Update,,更新,更新,,, | |||
U&pdate All,,全部更新,全部更新,,, | |||
,,,,,, | |||
#Statistics Config,,,,,, | |||
,,,,,, | |||
Enable Statistics,Включить сбор статистики,启用统计,,統計を有効にする,통계 활성화,Activer statistiques | |||
Ping Test,Проверка связи (Ping),Ping测试,,Ping測定,Ping 테스트,Test ping | |||
packages everytime,пакета на проверку,个包/次,,パケット/回,시간 당 패킷,Packages à chaque fois | |||
By hour of day,Ежечасно,按照每天的小时数统计,,毎日の時間数で統計,매 시간,Par heure du jour | |||
Collect data per,Собирать данные за,收集数据每,,ごとにデータを収集,데이터 수집 기간,Recueillir des données par | |||
Keep choice for,Хранить отбор данных за,保持选择每,,,설정 유지하기,Gardez le choix pour | |||
minutes,мин.,分钟,,分,분,Minutes | |||
Final Score:,Финальная оценка:,总分:,,,최종 점수:,Score final: | |||
AverageLatency,СредЗадержка,平均延迟,,平均遅延時間,평균 지연시간,LatenceMoyenne | |||
MinLatency,МинЗадержка,最小延迟,,最小遅延時間,최소 지연시간,MinLatence | |||
MaxLatency,МаксЗадержка,最大延迟,,最大遅延時間,최대 지연시간,MaxLatence | |||
AverageInboundSpeed,СредВходСкорость,平均入站速度,,平均インバウンド速度,평균 인바운드 속도,VitesseEntranteMoyenne | |||
MinInboundSpeed,МинВходСкорость,最小入站速度,,最小インバウンド速度,최소 인바운드 속도,MinVitesseEntrante | |||
MaxInboundSpeed,СредВходСкорость,最大入站速度,,最大インバウンド速度,최대 인바운드 속도,MaxVitesseEntrante | |||
AverageOutboundSpeed,СредИсхСкорость,平均出站速度,,平均アウトバウンド速度,평균 아웃바운드 속도,VitesseSortanteMoyenne | |||
MinOutboundSpeed,МинИсхСкорость,最小出站速度,,最小アウトバウンド速度,최소 아웃바운드 속도,MinVitesseSortante | |||
MaxOutboundSpeed,МаксИсхСкорость,最大出站速度,,最大アウトバウンド速度,최대 아웃바운드 속도,MaxVitesseSortante | |||
AverageResponse,СредВремяОтвета,平均响应速度,,平均レスポンス速度,평균 응답,RéponseMoyenne | |||
MinResponse,МинВремяОтвета,最小响应速度,,最小レスポンス速度,최소 응답,MinRéponse | |||
MaxResponse,МаксВремяОтвета,最大响应速度,,最大レスポンス速度,최대 응답,MaxRéponse | |||
PackageLoss,ПотериПакетов,丢包率,,パケットロス率,패킷 손실,Perte de paquets | |||
Speed,Скорость,速度,,速度,속도,Vitesse | |||
Package Loss,Потери пакетов,丢包率,,パケットロス率,패킷 손실,Perte de paquets | |||
Ping,Ping,网络延迟,,Ping,Ping,Ping | |||
Chart Mode,График,图表模式,,図表モード,차트 모드,Mode graphique | |||
24h,24ч,24小时,,24時間,24시간,24h | |||
all,За все время,全部,,すべて,전체,tout | |||
,,,,,, | |||
# Proxy Form,,,,,, | |||
,,,,,, | |||
Edit Proxy,Редактирование прокси,代理设置,編輯 Proxy,プロキシの編集,프록시 수정,Modifier le proxy | |||
Use Proxy,Использовать прокси,使用代理,使用 Proxy,プロキシを使用する,프록시 사용,Utiliser un proxy | |||
Proxy Type,Тип прокси,代理类型,Proxy 類型,プロキシタイプ,프록시 종류,Type de proxy | |||
Proxy Addr,Адрес прокси,代理地址,Proxy 位址,プロキシアドレス,프록시 주소,Adresse de proxy | |||
Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port de proxy | |||
"If server has a plugin, proxy will not be used","Если сервер использует плагины, прокси НЕ будет использоваться",若服务器含有插件,代理将不被使用,若伺服器含有外掛程式,Proxy 將不被使用,サーバーにプラグインがある場合、プロキシは使用されません,서버에 플러그인이 설치 되어 있는 경우 프록시를 사용할 수 없습니다.,"Si le serveur a un plugin, le proxy ne sera pas utilisé" | |||
Use Auth,Требуется авторизация,使用认证,使用認證,認証を使用する,서버 인증 사용,Utiliser l'authentification | |||
User Name,Пользователь,用户名,認證用戶,ユーザ名,사용자 이름,Nom d'utilisateur | |||
Auth Pwd,Пароль,认证密码,認證口令,パスワード,비밀번호,Mot de passe d'authentification | |||
,,,,,, | |||
# Log Form,,,,,, | |||
,,,,,, | |||
&File,Файл,文件(&F),檔案 (&F),ファイル (&F),파일 (&F),Fichier | |||
@@ -154,17 +104,6 @@ Edit Online PAC URL,Изменение URL удаленного PAC,编辑在线 | |||
Edit Online PAC URL...,Редактировать URL удаленного PAC…,编辑在线 PAC 网址...,編輯線上 PAC 網址...,オンライン PAC URL の編集...,온라인 프록시 자동 구성 URL 수정…,Modifier l'URL du PAC en ligne ... | |||
Please input PAC Url,Введите URL адрес для PAC-файла,请输入 PAC 网址,請輸入 PAC 網址,PAC URLを入力して下さい,프록시 자동 구성 URL을 입력하세요,Veuillez saisir l'URL PAC | |||
,,,,,, | |||
# HotkeySettings Form,,,,,, | |||
,,,,,, | |||
Switch system proxy,ВКЛ/ВЫКЛ системный прокси-сервер,切换系统代理状态,切換系統 Proxy 狀態,システム プロキシの状態を切り替える,시스템 프록시 전환,Changer l'état de proxy système | |||
Switch system proxy mode,Переключение режима прокси-сервера,切换系统代理模式,切換系統 Proxy 模式,プロキシモードを切り替える,시스템 프록시 모드 전환,Changer le mode de proxy système | |||
Allow Clients from LAN,Общий доступ к подключению,切换局域网共享,切換區域網路共用,LAN からのアクセスの許可を切り替える,LAN으로부터 클라이언트 허용,Autoriser les clients du LAN | |||
Show Logs...,Просмотр журналов,显示日志,顯示記錄檔,ログの表示,로그 보기…,Afficher les journaux | |||
Switch to previous server,Переключить на пред. сервер,切换上个服务器,切換上一個伺服器,前のサーバーに切り替える,이전 서버로 전환,Passer au serveur précédent | |||
Switch to next server,Переключить на след. сервер,切换下个服务器,切換下一個伺服器,次のサーバーに切り替える,다음 서버로 전환,Passer au serveur suivant | |||
Reg All,Применить все,注册全部快捷键,註冊所有快速鍵,全部登録する,모두 등록,Enregistrer tout | |||
Reg Hotkeys At Startup,Применять при запуске программы,启动时注册快捷键,啟動時註冊快速鍵,起動時にホットキーを登録する,시스템 시작 시 단축키 등록,Enregistrer tout au démarrage | |||
,,,,,, | |||
# Messages,,,,,, | |||
,,,,,, | |||
Shadowsocks Error: {0},Ошибка Shadowsocks: {0},Shadowsocks 错误: {0},Shadowsocks 錯誤: {0},Shadowsocks エラー: {0},Shadowsocks 오류: {0},Erreur shadowsocks: {0} | |||
@@ -177,9 +116,7 @@ Server IP can not be blank,IP-адрес сервера не может быть | |||
Password can not be blank,Пароль не может быть пустым,密码不能为空,密碼不能為空,パスワードが指定されていません。,비밀번호는 비어있으면 안됩니다.,Le mot de passe ne peut pas être vide | |||
Port out of range,Порт выходит за допустимый диапазон,端口超出范围,連接埠號碼超出範圍,ポート番号は範囲外です。,올바른 포트 범위가 아닙니다.,Port hors de portée | |||
Port can't be 8123,Адрес порта 8123 не может быть использован,端口不能为 8123,連接埠號碼不能為 8123,8123 番以外のポート番号を指定して下さい。,8123번 포트는 사용할 수 없습니다.,Le port ne peut pas être 8123 | |||
Shadowsocks {0} Update Found,Обнаружена новая версия Shadowsocks: {0},Shadowsocks {0} 更新,Shadowsocks {0} 更新,Shadowsocks バージョン {0} は利用できます。,Shadowsocks {0} 업데이트가 있습니다.,Shadowsocks {0} Mise à jour trouvée | |||
No update is available,Обновлений не обнаружено,没有可用的更新,沒有可用的更新,お使いのバージョンは最新です。,사용 가능한 업데이트가 없습니다.,Aucune mise à jour n'est disponible | |||
Click here to update,Нажмите сюда для обновления,点击这里升级,點按此處升級,クリックしてアップデートします。,여기를 클릭하여 업데이트,Cliquez ici pour mettre à jour | |||
Shadowsocks is here,Shadowsocks находится здесь,Shadowsocks 在这里,Shadowsocks 在這裡,Shadowsocks はここです。,Shadowsocks는 여기에 있습니다,Veuillez trouver Shadowsocks ici | |||
You can turn on/off Shadowsocks in the context menu,Вы можете управлять Shadowsocks из контекстного меню,可以在右键菜单中开关 Shadowsocks,可以在右鍵選項單中開關 Shadowsocks,コンテキストメニューを使って、Shadowsocks を有効または無効にすることができます。,프로그램 메뉴에서 Shadowsocks를 끄고 켤 수 있습니다.,Vous pouvez activer / désactiver Shadowsocks dans le menu contextuel | |||
System Proxy Enabled,Системный прокси включен,系统代理已启用,系統 Proxy 已啟用,システム プロキシが有効です。,시스템 프록시가 활성화되었습니다.,Proxy système activé | |||
@@ -191,7 +128,7 @@ No QRCode found. Try to zoom in or move it to the center of the screen.,QRCode | |||
Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution. | |||
Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно найти в области уведомлений.,请在任务栏里寻找 Shadowsocks 图标。,請在工作列裡尋找 Shadowsocks 圖示。,通知領域には Shadowsocks のアイコンがあります。,트레이에서 Shadowsocks를 찾아주세요.,Trouvez l'icône Shadowsocks dans votre barre de notification. | |||
"If you want to start multiple Shadowsocks, make a copy in another directory.","Если вы хотите запустить несколько копий Shadowsocks одновременно, создайте отдельную папку на каждую копию.",如果想同时启动多个,可以另外复制一份到别的目录。,如果想同時啟動多個,可以另外複製一份至別的目錄。,複数起動したい場合は、プログラムファイルを別のフォルダーにコピーしてから、もう一度実行して下さい。,"만약 여러 개의 Shadowsocks를 사용하고 싶으시다면, 파일을 다른 곳에 복사해주세요.","Si vous souhaitez démarrer plusieurs Shadowsocks, faites une copie dans un autre répertoire." | |||
Failed to decode QRCode,Не удалось распознать QRCode,无法解析二维码,QR 碼解碼失敗,QR コードの読み取りに失敗しました。,QR코드를 해석하는데에 실패했습니다.,Impossible de décoder le QRCode | |||
Invalid QR Code content: {0},,无效二维码内容: {0},無效二維碼內容: {0},,, | |||
Failed to update registry,Не удалось обновить запись в реестре,无法修改注册表,無法修改登錄檔,レジストリの更新に失敗しました。,레지스트리를 업데이트하는데에 실패했습니다.,Impossible de mettre à jour de la base de registre | |||
Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,{0}:このURLからインポートしますか?,, | |||
Successfully imported from {0},Успешно импортировано из {0},导入成功:{0},導入成功:{0},{0}:インポートしました。,, | |||
@@ -218,5 +155,3 @@ Whether to discard unconfigured servers,Внесенные изменения б | |||
,,,,タイムアウト値のフォーマットが無効なため、オートセーブできません。変更を破棄しますか,, | |||
"Error occured when process proxy setting, do you want reset current setting and retry?",Произошла ошибка при обработке настроек. Хотите сбросить текущие настройки и попробовать снова?,处理代理设置时发生错误,是否重置当前代理设置并重试?,,プロキシ設定の処理にエラーが発生しました、現在のプロキシ設定をリセットし、再試行してもいいですか,프록시 설정을 처리하는데에 오류가 발생했습니다. 현재 설정을 폐기하고 다시 시도하시겠습니까?,Une erreur s'est produite lors du processus de configuration du proxy. Voulez-vous réinitialiser le paramètre actuel et réessayer? | |||
"Unrecoverable proxy setting error occured, see log for detail","Произошла серьезная ошибка, подробности можно узнать в журналах",发生不可恢复的代理设置错误,查看日志以取得详情,,プロキシ設定に回復不能なエラーが発生しました、ログで詳細をご確認ください,복구 불가능한 프록시 설정 오류가 발생했습니다. 자세한 정보는 로그를 참조하세요.,"Une erreur de paramètre de proxy irrécupérable s'est produite, consultez le journal pour plus de détails" | |||
Auth user can not be blank,Пользователь не может быть пустым,认证用户不能为空,認證用戶不能為空,認証ユーザが指定されていません。,인증 정보의 사용자 이름은 비어있을 수 없습니다.,L'utilisateur d'authentification ne peut pas être vide | |||
Auth pwd can not be blank,Пароль не может быть пустым,认证密码不能为空,認證口令不能為空,認証パスワードが指定されていません。,인증 정보의 비밀번호는 비어있을 수 없습니다.,Le mot de passe d'authentification ne peut pas être vide |
@@ -71,7 +71,7 @@ namespace Shadowsocks.Encryption | |||
public static IEncryptor GetEncryptor(string method, string password) | |||
{ | |||
if (method.IsNullOrEmpty()) | |||
if (string.IsNullOrEmpty(method)) | |||
{ | |||
method = Model.Server.DefaultMethod; | |||
} | |||
@@ -2,4 +2,5 @@ | |||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | |||
<Caseless StringComparison="Ordinal" /> | |||
<Costura /> | |||
<ReactiveUI /> | |||
</Weavers> |
@@ -4,6 +4,7 @@ | |||
<xs:element name="Weavers"> | |||
<xs:complexType> | |||
<xs:all> | |||
<xs:element name="ReactiveUI" minOccurs="0" maxOccurs="1" type="xs:anyType" /> | |||
<xs:element name="Costura" minOccurs="0" maxOccurs="1"> | |||
<xs:complexType> | |||
<xs:all> | |||
@@ -0,0 +1,13 @@ | |||
using System.Reflection; | |||
using WPFLocalizeExtension.Extensions; | |||
namespace Shadowsocks.Localization | |||
{ | |||
public static class LocalizationProvider | |||
{ | |||
public static T GetLocalizedValue<T>(string key) | |||
{ | |||
return LocExtension.GetLocalizedValue<T>(Assembly.GetCallingAssembly().GetName().Name + ":Strings:" + key); | |||
} | |||
} | |||
} |
@@ -0,0 +1,432 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.42000 | |||
// | |||
// Changes to this file may cause incorrect behavior and will be lost if | |||
// the code is regenerated. | |||
// </auto-generated> | |||
//------------------------------------------------------------------------------ | |||
namespace Shadowsocks.Localization { | |||
using System; | |||
/// <summary> | |||
/// A strongly-typed resource class, for looking up localized strings, etc. | |||
/// </summary> | |||
// This class was auto-generated by the StronglyTypedResourceBuilder | |||
// class via a tool like ResGen or Visual Studio. | |||
// To add or remove a member, edit your .ResX file then rerun ResGen | |||
// with the /str option, or rebuild your VS project. | |||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] | |||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | |||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | |||
internal class Strings { | |||
private static global::System.Resources.ResourceManager resourceMan; | |||
private static global::System.Globalization.CultureInfo resourceCulture; | |||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] | |||
internal Strings() { | |||
} | |||
/// <summary> | |||
/// Returns the cached ResourceManager instance used by this class. | |||
/// </summary> | |||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | |||
internal static global::System.Resources.ResourceManager ResourceManager { | |||
get { | |||
if (object.ReferenceEquals(resourceMan, null)) { | |||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.Localization.Strings", typeof(Strings).Assembly); | |||
resourceMan = temp; | |||
} | |||
return resourceMan; | |||
} | |||
} | |||
/// <summary> | |||
/// Overrides the current thread's CurrentUICulture property for all | |||
/// resource lookups using this strongly typed resource class. | |||
/// </summary> | |||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | |||
internal static global::System.Globalization.CultureInfo Culture { | |||
get { | |||
return resourceCulture; | |||
} | |||
set { | |||
resourceCulture = value; | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Add. | |||
/// </summary> | |||
internal static string addButton_Content { | |||
get { | |||
return ResourceManager.GetString("addButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Address. | |||
/// </summary> | |||
internal static string Address { | |||
get { | |||
return ResourceManager.GetString("Address", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Allow clients from LAN. | |||
/// </summary> | |||
internal static string AllowClientsFromLAN { | |||
get { | |||
return ResourceManager.GetString("AllowClientsFromLAN", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Cancel. | |||
/// </summary> | |||
internal static string cancelButton_Content { | |||
get { | |||
return ResourceManager.GetString("cancelButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Copy. | |||
/// </summary> | |||
internal static string Copy { | |||
get { | |||
return ResourceManager.GetString("Copy", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Copy link. | |||
/// </summary> | |||
internal static string copyLinkButton_Content { | |||
get { | |||
return ResourceManager.GetString("copyLinkButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Credentials (optional). | |||
/// </summary> | |||
internal static string CredentialsOptional { | |||
get { | |||
return ResourceManager.GetString("CredentialsOptional", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Details. | |||
/// </summary> | |||
internal static string Details { | |||
get { | |||
return ResourceManager.GetString("Details", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Forward Proxy. | |||
/// </summary> | |||
internal static string ForwardProxy { | |||
get { | |||
return ResourceManager.GetString("ForwardProxy", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Hotkeys. | |||
/// </summary> | |||
internal static string Hotkeys { | |||
get { | |||
return ResourceManager.GetString("Hotkeys", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to HTTP. | |||
/// </summary> | |||
internal static string HTTP { | |||
get { | |||
return ResourceManager.GetString("HTTP", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to No proxy. | |||
/// </summary> | |||
internal static string NoProxy { | |||
get { | |||
return ResourceManager.GetString("NoProxy", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Not now. | |||
/// </summary> | |||
internal static string notNowButton_Content { | |||
get { | |||
return ResourceManager.GetString("notNowButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _OK. | |||
/// </summary> | |||
internal static string okButton_Content { | |||
get { | |||
return ResourceManager.GetString("okButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Online Configuration Delivery. | |||
/// </summary> | |||
internal static string OnlineConfigDelivery { | |||
get { | |||
return ResourceManager.GetString("OnlineConfigDelivery", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Open logs window. | |||
/// </summary> | |||
internal static string OpenLogsWindow { | |||
get { | |||
return ResourceManager.GetString("OpenLogsWindow", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Password. | |||
/// </summary> | |||
internal static string Password { | |||
get { | |||
return ResourceManager.GetString("Password", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Port. | |||
/// </summary> | |||
internal static string Port { | |||
get { | |||
return ResourceManager.GetString("Port", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Register all. | |||
/// </summary> | |||
internal static string registerAllButton_Content { | |||
get { | |||
return ResourceManager.GetString("registerAllButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Register hotkeys at startup. | |||
/// </summary> | |||
internal static string RegisterHotkeysAtStartup { | |||
get { | |||
return ResourceManager.GetString("RegisterHotkeysAtStartup", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Remove. | |||
/// </summary> | |||
internal static string removeButton_Content { | |||
get { | |||
return ResourceManager.GetString("removeButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Save. | |||
/// </summary> | |||
internal static string saveButton_Content { | |||
get { | |||
return ResourceManager.GetString("saveButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Server Sharing. | |||
/// </summary> | |||
internal static string ServerSharing { | |||
get { | |||
return ResourceManager.GetString("ServerSharing", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to The following sources failed to update:\n\n. | |||
/// </summary> | |||
internal static string sip008UpdateAllFailure { | |||
get { | |||
return ResourceManager.GetString("sip008UpdateAllFailure", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Successfully updated all sources!. | |||
/// </summary> | |||
internal static string sip008UpdateAllSuccess { | |||
get { | |||
return ResourceManager.GetString("sip008UpdateAllSuccess", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Update failed. See the logs for more information.. | |||
/// </summary> | |||
internal static string sip008UpdateFailure { | |||
get { | |||
return ResourceManager.GetString("sip008UpdateFailure", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Successfully updated the selected source!. | |||
/// </summary> | |||
internal static string sip008UpdateSuccess { | |||
get { | |||
return ResourceManager.GetString("sip008UpdateSuccess", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Skip version. | |||
/// </summary> | |||
internal static string skipVersionButton_Content { | |||
get { | |||
return ResourceManager.GetString("skipVersionButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to SOCKS5. | |||
/// </summary> | |||
internal static string SOCKS5 { | |||
get { | |||
return ResourceManager.GetString("SOCKS5", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Switch to next server. | |||
/// </summary> | |||
internal static string SwitchToNextServer { | |||
get { | |||
return ResourceManager.GetString("SwitchToNextServer", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Switch to previous server. | |||
/// </summary> | |||
internal static string SwitchToPreviousServer { | |||
get { | |||
return ResourceManager.GetString("SwitchToPreviousServer", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Timeout (sec). | |||
/// </summary> | |||
internal static string Timeout { | |||
get { | |||
return ResourceManager.GetString("Timeout", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Toggle proxy mode. | |||
/// </summary> | |||
internal static string ToggleProxyMode { | |||
get { | |||
return ResourceManager.GetString("ToggleProxyMode", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Toggle system proxy. | |||
/// </summary> | |||
internal static string ToggleSystemProxy { | |||
get { | |||
return ResourceManager.GetString("ToggleSystemProxy", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Type. | |||
/// </summary> | |||
internal static string Type { | |||
get { | |||
return ResourceManager.GetString("Type", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Update all. | |||
/// </summary> | |||
internal static string updateAllButton_Content { | |||
get { | |||
return ResourceManager.GetString("updateAllButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to _Update. | |||
/// </summary> | |||
internal static string updateButton_Content { | |||
get { | |||
return ResourceManager.GetString("updateButton_Content", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Please read the release notes carefully. Then decide whether to update.. | |||
/// </summary> | |||
internal static string updatePromptBody { | |||
get { | |||
return ResourceManager.GetString("updatePromptBody", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to An update is available.. | |||
/// </summary> | |||
internal static string updatePromptTitle { | |||
get { | |||
return ResourceManager.GetString("updatePromptTitle", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Username. | |||
/// </summary> | |||
internal static string Username { | |||
get { | |||
return ResourceManager.GetString("Username", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to VersionUpdate. | |||
/// </summary> | |||
internal static string VersionUpdate { | |||
get { | |||
return ResourceManager.GetString("VersionUpdate", resourceCulture); | |||
} | |||
} | |||
} | |||
} |
@@ -1,120 +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> | |||
<?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> | |||
<data name="Password" xml:space="preserve"> | |||
<value>Mot de passe</value> | |||
</data> | |||
<data name="Port" xml:space="preserve"> | |||
<value>Port</value> | |||
</data> | |||
<data name="Type" xml:space="preserve"> | |||
<value>Type</value> | |||
</data> | |||
<data name="Address" xml:space="preserve"> | |||
<value>adresse</value> | |||
</data> | |||
<data name="Timeout" xml:space="preserve"> | |||
<value>Délai d'attente (sec)</value> | |||
</data> | |||
<data name="cancelButton_Content" xml:space="preserve"> | |||
<value>Annuler</value> | |||
</data> | |||
<data name="okButton_Content" xml:space="preserve"> | |||
<value>OK</value> | |||
</data> | |||
<data name="ToggleSystemProxy" xml:space="preserve"> | |||
<value>Changer l'état de proxy système</value> | |||
</data> | |||
<data name="ToggleProxyMode" xml:space="preserve"> | |||
<value>Changer le mode de proxy système</value> | |||
</data> | |||
<data name="AllowClientsFromLAN" xml:space="preserve"> | |||
<value>Autoriser les clients du LAN</value> | |||
</data> | |||
<data name="OpenLogsWindow" xml:space="preserve"> | |||
<value>Afficher les journaux</value> | |||
</data> | |||
<data name="SwitchToPreviousServer" xml:space="preserve"> | |||
<value>Passer au serveur précédent</value> | |||
</data> | |||
<data name="SwitchToNextServer" xml:space="preserve"> | |||
<value>Passer au serveur suivant</value> | |||
</data> | |||
<data name="RegisterHotkeysAtStartup" xml:space="preserve"> | |||
<value>Enregistrer tout</value> | |||
</data> | |||
<data name="registerAllButton_Content" xml:space="preserve"> | |||
<value>Enregistrer tout au démarrage</value> | |||
</data> | |||
<data name="addButton_Content" xml:space="preserve"> | |||
<value>Ajouter</value> | |||
</data> | |||
<data name="skipVersionButton_Content" xml:space="preserve"> | |||
<value>sauter la version</value> | |||
</data> | |||
<data name="copyLinkButton_Content" xml:space="preserve"> | |||
<value>Copier le lien</value> | |||
</data> | |||
<data name="removeButton_Content" xml:space="preserve"> | |||
<value>retirer</value> | |||
</data> | |||
<data name="updateButton_Content" xml:space="preserve"> | |||
<value>mise à jour</value> | |||
</data> | |||
<data name="Copy" xml:space="preserve"> | |||
<value>copie</value> | |||
</data> | |||
</root> |
@@ -1,120 +1,180 @@ | |||
<?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> | |||
<?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> | |||
<data name="Password" xml:space="preserve"> | |||
<value>パスワード</value> | |||
</data> | |||
<data name="Type" xml:space="preserve"> | |||
<value>タイプ</value> | |||
</data> | |||
<data name="Address" xml:space="preserve"> | |||
<value>アドレス</value> | |||
</data> | |||
<data name="Port" xml:space="preserve"> | |||
<value>ポート</value> | |||
</data> | |||
<data name="Timeout" xml:space="preserve"> | |||
<value>タイムアウト (秒)</value> | |||
</data> | |||
<data name="cancelButton_Content" xml:space="preserve"> | |||
<value>キャンセル</value> | |||
</data> | |||
<data name="okButton_Content" xml:space="preserve"> | |||
<value>OK</value> | |||
</data> | |||
<data name="ToggleSystemProxy" xml:space="preserve"> | |||
<value>システム プロキシの状態を切り替える</value> | |||
</data> | |||
<data name="ToggleProxyMode" xml:space="preserve"> | |||
<value>プロキシモードを切り替える</value> | |||
</data> | |||
<data name="AllowClientsFromLAN" xml:space="preserve"> | |||
<value>LAN からのアクセスの許可を切り替える</value> | |||
</data> | |||
<data name="OpenLogsWindow" xml:space="preserve"> | |||
<value>ログの表示</value> | |||
</data> | |||
<data name="SwitchToPreviousServer" xml:space="preserve"> | |||
<value>前のサーバーに切り替える</value> | |||
</data> | |||
<data name="SwitchToNextServer" xml:space="preserve"> | |||
<value>次のサーバーに切り替える</value> | |||
</data> | |||
<data name="RegisterHotkeysAtStartup" xml:space="preserve"> | |||
<value>全部登録する</value> | |||
</data> | |||
<data name="registerAllButton_Content" xml:space="preserve"> | |||
<value>起動時にホットキーを登録する</value> | |||
</data> | |||
<data name="addButton_Content" xml:space="preserve"> | |||
<value>新規</value> | |||
</data> | |||
<data name="skipVersionButton_Content" xml:space="preserve"> | |||
<value>バージョンをスキップ</value> | |||
</data> | |||
<data name="copyLinkButton_Content" xml:space="preserve"> | |||
<value>リンクをコピーする</value> | |||
</data> | |||
<data name="updateButton_Content" xml:space="preserve"> | |||
<value>更新</value> | |||
</data> | |||
<data name="Copy" xml:space="preserve"> | |||
<value>コピー</value> | |||
</data> | |||
</root> |
@@ -117,13 +117,64 @@ | |||
<resheader name="writer"> | |||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
<metadata name="bindingConfiguration.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | |||
<value>4, 5</value> | |||
</metadata> | |||
<metadata name="CalculatinTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | |||
<value>238, 6</value> | |||
</metadata> | |||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||
<value>191</value> | |||
</metadata> | |||
<data name="Password" xml:space="preserve"> | |||
<value>비밀번호</value> | |||
</data> | |||
<data name="Type" xml:space="preserve"> | |||
<value>유형</value> | |||
</data> | |||
<data name="Address" xml:space="preserve"> | |||
<value>주소</value> | |||
</data> | |||
<data name="Port" xml:space="preserve"> | |||
<value>포트</value> | |||
</data> | |||
<data name="Timeout" xml:space="preserve"> | |||
<value>시간 초과 (초)</value> | |||
</data> | |||
<data name="cancelButton_Content" xml:space="preserve"> | |||
<value>취소</value> | |||
</data> | |||
<data name="okButton_Content" xml:space="preserve"> | |||
<value>확인</value> | |||
</data> | |||
<data name="ToggleSystemProxy" xml:space="preserve"> | |||
<value>시스템 프록시 전환</value> | |||
</data> | |||
<data name="ToggleProxyMode" xml:space="preserve"> | |||
<value>시스템 프록시 모드 전환</value> | |||
</data> | |||
<data name="AllowClientsFromLAN" xml:space="preserve"> | |||
<value>LAN으로부터 클라이언트 허용</value> | |||
</data> | |||
<data name="OpenLogsWindow" xml:space="preserve"> | |||
<value>로그 보기…</value> | |||
</data> | |||
<data name="SwitchToPreviousServer" xml:space="preserve"> | |||
<value>이전 서버로 전환</value> | |||
</data> | |||
<data name="SwitchToNextServer" xml:space="preserve"> | |||
<value>다음 서버로 전환</value> | |||
</data> | |||
<data name="RegisterHotkeysAtStartup" xml:space="preserve"> | |||
<value>모두 등록</value> | |||
</data> | |||
<data name="registerAllButton_Content" xml:space="preserve"> | |||
<value>시스템 시작 시 단축키 등록</value> | |||
</data> | |||
<data name="addButton_Content" xml:space="preserve"> | |||
<value>추가</value> | |||
</data> | |||
<data name="skipVersionButton_Content" xml:space="preserve"> | |||
<value>버전 건너 뛰기</value> | |||
</data> | |||
<data name="copyLinkButton_Content" xml:space="preserve"> | |||
<value>링크 복사</value> | |||
</data> | |||
<data name="updateButton_Content" xml:space="preserve"> | |||
<value>새롭게 함</value> | |||
</data> | |||
<data name="Copy" xml:space="preserve"> | |||
<value>부</value> | |||
</data> | |||
</root> |
@@ -0,0 +1,243 @@ | |||
<?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> | |||
<data name="Type" xml:space="preserve"> | |||
<value>Type</value> | |||
</data> | |||
<data name="NoProxy" xml:space="preserve"> | |||
<value>No proxy</value> | |||
</data> | |||
<data name="SOCKS5" xml:space="preserve"> | |||
<value>SOCKS5</value> | |||
</data> | |||
<data name="HTTP" xml:space="preserve"> | |||
<value>HTTP</value> | |||
</data> | |||
<data name="Details" xml:space="preserve"> | |||
<value>Details</value> | |||
</data> | |||
<data name="Address" xml:space="preserve"> | |||
<value>Address</value> | |||
</data> | |||
<data name="Port" xml:space="preserve"> | |||
<value>Port</value> | |||
</data> | |||
<data name="Timeout" xml:space="preserve"> | |||
<value>Timeout (sec)</value> | |||
</data> | |||
<data name="CredentialsOptional" xml:space="preserve"> | |||
<value>Credentials (optional)</value> | |||
</data> | |||
<data name="Username" xml:space="preserve"> | |||
<value>Username</value> | |||
</data> | |||
<data name="Password" xml:space="preserve"> | |||
<value>Password</value> | |||
</data> | |||
<data name="saveButton_Content" xml:space="preserve"> | |||
<value>_Save</value> | |||
</data> | |||
<data name="cancelButton_Content" xml:space="preserve"> | |||
<value>_Cancel</value> | |||
</data> | |||
<data name="okButton_Content" xml:space="preserve"> | |||
<value>_OK</value> | |||
</data> | |||
<data name="ToggleSystemProxy" xml:space="preserve"> | |||
<value>Toggle system proxy</value> | |||
</data> | |||
<data name="ToggleProxyMode" xml:space="preserve"> | |||
<value>Toggle proxy mode</value> | |||
</data> | |||
<data name="AllowClientsFromLAN" xml:space="preserve"> | |||
<value>Allow clients from LAN</value> | |||
</data> | |||
<data name="OpenLogsWindow" xml:space="preserve"> | |||
<value>Open logs window</value> | |||
</data> | |||
<data name="SwitchToPreviousServer" xml:space="preserve"> | |||
<value>Switch to previous server</value> | |||
</data> | |||
<data name="SwitchToNextServer" xml:space="preserve"> | |||
<value>Switch to next server</value> | |||
</data> | |||
<data name="RegisterHotkeysAtStartup" xml:space="preserve"> | |||
<value>Register hotkeys at startup</value> | |||
</data> | |||
<data name="registerAllButton_Content" xml:space="preserve"> | |||
<value>_Register all</value> | |||
</data> | |||
<data name="updateButton_Content" xml:space="preserve"> | |||
<value>_Update</value> | |||
</data> | |||
<data name="updateAllButton_Content" xml:space="preserve"> | |||
<value>Update all</value> | |||
</data> | |||
<data name="copyLinkButton_Content" xml:space="preserve"> | |||
<value>_Copy link</value> | |||
</data> | |||
<data name="removeButton_Content" xml:space="preserve"> | |||
<value>Remove</value> | |||
</data> | |||
<data name="addButton_Content" xml:space="preserve"> | |||
<value>_Add</value> | |||
</data> | |||
<data name="updatePromptTitle" xml:space="preserve"> | |||
<value>An update is available.</value> | |||
</data> | |||
<data name="updatePromptBody" xml:space="preserve"> | |||
<value>Please read the release notes carefully. Then decide whether to update.</value> | |||
</data> | |||
<data name="skipVersionButton_Content" xml:space="preserve"> | |||
<value>_Skip version</value> | |||
</data> | |||
<data name="notNowButton_Content" xml:space="preserve"> | |||
<value>_Not now</value> | |||
</data> | |||
<data name="Copy" xml:space="preserve"> | |||
<value>_Copy</value> | |||
</data> | |||
<data name="ForwardProxy" xml:space="preserve"> | |||
<value>Forward Proxy</value> | |||
</data> | |||
<data name="ServerSharing" xml:space="preserve"> | |||
<value>Server Sharing</value> | |||
</data> | |||
<data name="Hotkeys" xml:space="preserve"> | |||
<value>Hotkeys</value> | |||
</data> | |||
<data name="OnlineConfigDelivery" xml:space="preserve"> | |||
<value>Online Configuration Delivery</value> | |||
</data> | |||
<data name="VersionUpdate" xml:space="preserve"> | |||
<value>VersionUpdate</value> | |||
</data> | |||
<data name="sip008UpdateSuccess" xml:space="preserve"> | |||
<value>Successfully updated the selected source!</value> | |||
</data> | |||
<data name="sip008UpdateFailure" xml:space="preserve"> | |||
<value>Update failed. See the logs for more information.</value> | |||
</data> | |||
<data name="sip008UpdateAllSuccess" xml:space="preserve"> | |||
<value>Successfully updated all sources!</value> | |||
</data> | |||
<data name="sip008UpdateAllFailure" xml:space="preserve"> | |||
<value>The following sources failed to update:\n\n</value> | |||
</data> | |||
</root> |
@@ -1,120 +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> | |||
<?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> | |||
<data name="Password" xml:space="preserve"> | |||
<value>Пароль</value> | |||
</data> | |||
<data name="Type" xml:space="preserve"> | |||
<value>тип</value> | |||
</data> | |||
<data name="Address" xml:space="preserve"> | |||
<value>адрес</value> | |||
</data> | |||
<data name="Port" xml:space="preserve"> | |||
<value>порт</value> | |||
</data> | |||
<data name="Timeout" xml:space="preserve"> | |||
<value>Таймаут (сек)</value> | |||
</data> | |||
<data name="cancelButton_Content" xml:space="preserve"> | |||
<value>Отмена</value> | |||
</data> | |||
<data name="okButton_Content" xml:space="preserve"> | |||
<value>ОК</value> | |||
</data> | |||
<data name="ToggleSystemProxy" xml:space="preserve"> | |||
<value>ВКЛ/ВЫКЛ системный прокси-сервер</value> | |||
</data> | |||
<data name="ToggleProxyMode" xml:space="preserve"> | |||
<value>Переключение режима прокси-сервера</value> | |||
</data> | |||
<data name="AllowClientsFromLAN" xml:space="preserve"> | |||
<value>Общий доступ к подключению</value> | |||
</data> | |||
<data name="OpenLogsWindow" xml:space="preserve"> | |||
<value>Просмотр журналов</value> | |||
</data> | |||
<data name="SwitchToPreviousServer" xml:space="preserve"> | |||
<value>Переключить на пред. сервер</value> | |||
</data> | |||
<data name="SwitchToNextServer" xml:space="preserve"> | |||
<value>Переключить на след. сервер</value> | |||
</data> | |||
<data name="RegisterHotkeysAtStartup" xml:space="preserve"> | |||
<value>Применить все</value> | |||
</data> | |||
<data name="registerAllButton_Content" xml:space="preserve"> | |||
<value>Применять при запуске программы</value> | |||
</data> | |||
<data name="addButton_Content" xml:space="preserve"> | |||
<value>Добавить</value> | |||
</data> | |||
<data name="skipVersionButton_Content" xml:space="preserve"> | |||
<value>пропустить версию</value> | |||
</data> | |||
<data name="copyLinkButton_Content" xml:space="preserve"> | |||
<value>копировать ссылку</value> | |||
</data> | |||
<data name="removeButton_Content" xml:space="preserve"> | |||
<value>Удалить</value> | |||
</data> | |||
<data name="updateButton_Content" xml:space="preserve"> | |||
<value>Обновить</value> | |||
</data> | |||
<data name="Copy" xml:space="preserve"> | |||
<value>копировать</value> | |||
</data> | |||
</root> |
@@ -0,0 +1,237 @@ | |||
<?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> | |||
<data name="NoProxy" xml:space="preserve"> | |||
<value>不使用代理</value> | |||
</data> | |||
<data name="Type" xml:space="preserve"> | |||
<value>类型</value> | |||
</data> | |||
<data name="CredentialsOptional" xml:space="preserve"> | |||
<value>认证 (可选)</value> | |||
</data> | |||
<data name="Details" xml:space="preserve"> | |||
<value>详细设置</value> | |||
</data> | |||
<data name="Address" xml:space="preserve"> | |||
<value>地址</value> | |||
</data> | |||
<data name="saveButton_Content" xml:space="preserve"> | |||
<value>保存</value> | |||
</data> | |||
<data name="cancelButton_Content" xml:space="preserve"> | |||
<value>取消</value> | |||
</data> | |||
<data name="Username" xml:space="preserve"> | |||
<value>用户名</value> | |||
</data> | |||
<data name="Password" xml:space="preserve"> | |||
<value>密码</value> | |||
</data> | |||
<data name="Port" xml:space="preserve"> | |||
<value>端口</value> | |||
</data> | |||
<data name="Timeout" xml:space="preserve"> | |||
<value>超时 (秒)</value> | |||
</data> | |||
<data name="okButton_Content" xml:space="preserve"> | |||
<value>确定</value> | |||
</data> | |||
<data name="registerAllButton_Content" xml:space="preserve"> | |||
<value>全部注册</value> | |||
</data> | |||
<data name="ToggleSystemProxy" xml:space="preserve"> | |||
<value>切换系统代理</value> | |||
</data> | |||
<data name="ToggleProxyMode" xml:space="preserve"> | |||
<value>切换代理模式</value> | |||
</data> | |||
<data name="AllowClientsFromLAN" xml:space="preserve"> | |||
<value>允许内网客户端连接</value> | |||
</data> | |||
<data name="OpenLogsWindow" xml:space="preserve"> | |||
<value>打开日志窗口</value> | |||
</data> | |||
<data name="SwitchToPreviousServer" xml:space="preserve"> | |||
<value>切换到上一个服务器</value> | |||
</data> | |||
<data name="SwitchToNextServer" xml:space="preserve"> | |||
<value>切换到下一个服务器</value> | |||
</data> | |||
<data name="RegisterHotkeysAtStartup" xml:space="preserve"> | |||
<value>启动时注册快捷键</value> | |||
</data> | |||
<data name="updateButton_Content" xml:space="preserve"> | |||
<value>更新</value> | |||
</data> | |||
<data name="updateAllButton_Content" xml:space="preserve"> | |||
<value>全部更新</value> | |||
</data> | |||
<data name="copyLinkButton_Content" xml:space="preserve"> | |||
<value>复制链接</value> | |||
</data> | |||
<data name="removeButton_Content" xml:space="preserve"> | |||
<value>移除</value> | |||
</data> | |||
<data name="addButton_Content" xml:space="preserve"> | |||
<value>添加</value> | |||
</data> | |||
<data name="skipVersionButton_Content" xml:space="preserve"> | |||
<value>跳过版本</value> | |||
</data> | |||
<data name="notNowButton_Content" xml:space="preserve"> | |||
<value>暂不更新</value> | |||
</data> | |||
<data name="updatePromptTitle" xml:space="preserve"> | |||
<value>发现可用更新</value> | |||
</data> | |||
<data name="updatePromptBody" xml:space="preserve"> | |||
<value>请仔细阅读发布信息,然后决定是否更新。</value> | |||
</data> | |||
<data name="Copy" xml:space="preserve"> | |||
<value>复制</value> | |||
</data> | |||
<data name="ForwardProxy" xml:space="preserve"> | |||
<value>前置代理</value> | |||
</data> | |||
<data name="OnlineConfigDelivery" xml:space="preserve"> | |||
<value>在线配置下发</value> | |||
</data> | |||
<data name="Hotkeys" xml:space="preserve"> | |||
<value>热键</value> | |||
</data> | |||
<data name="ServerSharing" xml:space="preserve"> | |||
<value>服务器分享</value> | |||
</data> | |||
<data name="VersionUpdate" xml:space="preserve"> | |||
<value>版本更新</value> | |||
</data> | |||
<data name="sip008UpdateSuccess" xml:space="preserve"> | |||
<value>成功更新选定来源!</value> | |||
</data> | |||
<data name="sip008UpdateFailure" xml:space="preserve"> | |||
<value>更新失败,请查看日志获取更多信息。</value> | |||
</data> | |||
<data name="sip008UpdateAllSuccess" xml:space="preserve"> | |||
<value>成功更新所有来源!</value> | |||
</data> | |||
<data name="sip008UpdateAllFailure" xml:space="preserve"> | |||
<value>下列来源更新失败:\n\n</value> | |||
</data> | |||
</root> |
@@ -0,0 +1,189 @@ | |||
<?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> | |||
<data name="Password" xml:space="preserve"> | |||
<value>密碼</value> | |||
</data> | |||
<data name="Port" xml:space="preserve"> | |||
<value>Port</value> | |||
</data> | |||
<data name="Type" xml:space="preserve"> | |||
<value>類型</value> | |||
</data> | |||
<data name="Address" xml:space="preserve"> | |||
<value>位址</value> | |||
</data> | |||
<data name="Timeout" xml:space="preserve"> | |||
<value>逾時 (秒)</value> | |||
</data> | |||
<data name="cancelButton_Content" xml:space="preserve"> | |||
<value>取消</value> | |||
</data> | |||
<data name="okButton_Content" xml:space="preserve"> | |||
<value>確定</value> | |||
</data> | |||
<data name="ToggleSystemProxy" xml:space="preserve"> | |||
<value>切換系統 Proxy 狀態</value> | |||
</data> | |||
<data name="ToggleProxyMode" xml:space="preserve"> | |||
<value>切換系統 Proxy 模式</value> | |||
</data> | |||
<data name="AllowClientsFromLAN" xml:space="preserve"> | |||
<value>切換區域網路共用</value> | |||
</data> | |||
<data name="OpenLogsWindow" xml:space="preserve"> | |||
<value>顯示記錄檔</value> | |||
</data> | |||
<data name="SwitchToPreviousServer" xml:space="preserve"> | |||
<value>切換上一個伺服器</value> | |||
</data> | |||
<data name="SwitchToNextServer" xml:space="preserve"> | |||
<value>切換下一個伺服器</value> | |||
</data> | |||
<data name="RegisterHotkeysAtStartup" xml:space="preserve"> | |||
<value>註冊所有快速鍵</value> | |||
</data> | |||
<data name="registerAllButton_Content" xml:space="preserve"> | |||
<value>啟動時註冊快速鍵</value> | |||
</data> | |||
<data name="updateButton_Content" xml:space="preserve"> | |||
<value>更新</value> | |||
</data> | |||
<data name="updateAllButton_Content" xml:space="preserve"> | |||
<value>全部更新</value> | |||
</data> | |||
<data name="addButton_Content" xml:space="preserve"> | |||
<value>新增</value> | |||
</data> | |||
<data name="skipVersionButton_Content" xml:space="preserve"> | |||
<value>跳過版本</value> | |||
</data> | |||
<data name="notNowButton_Content" xml:space="preserve"> | |||
<value>暫不更新</value> | |||
</data> | |||
<data name="updatePromptTitle" xml:space="preserve"> | |||
<value>發現可用更新</value> | |||
</data> | |||
<data name="copyLinkButton_Content" xml:space="preserve"> | |||
<value>複製鏈接</value> | |||
</data> | |||
<data name="Copy" xml:space="preserve"> | |||
<value>複製</value> | |||
</data> | |||
</root> |
@@ -3,6 +3,7 @@ using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Windows; | |||
using Newtonsoft.Json; | |||
using NLog; | |||
using Shadowsocks.Controller; | |||
@@ -27,33 +28,36 @@ namespace Shadowsocks.Model | |||
public bool global; | |||
public bool enabled; | |||
public bool shareOverLan; | |||
public bool isDefault; | |||
public bool firstRun; | |||
public int localPort; | |||
public bool portableMode; | |||
public bool showPluginOutput; | |||
public string pacUrl; | |||
public bool useOnlinePac; | |||
public bool secureLocalPac; | |||
public bool availabilityStatistics; | |||
public bool secureLocalPac; // enable secret for PAC server | |||
public bool regeneratePacOnUpdate; // regenerate pac.txt on version update | |||
public bool autoCheckUpdate; | |||
public bool checkPreRelease; | |||
public string skippedUpdateVersion; // skip the update with this version number | |||
public bool isVerboseLogging; | |||
// hidden options | |||
public bool isIPv6Enabled; // for experimental ipv6 support | |||
public bool generateLegacyUrl = false; // for pre-sip002 url compatibility | |||
public bool generateLegacyUrl; // for pre-sip002 url compatibility | |||
public string geositeUrl; // for custom geosite source (and rule group) | |||
public string geositeGroup; | |||
public bool geositeBlacklistMode; | |||
public List<string> geositeDirectGroups; // groups of domains that we connect without the proxy | |||
public List<string> geositeProxiedGroups; // groups of domains that we connect via the proxy | |||
public bool geositePreferDirect; // a.k.a blacklist mode | |||
public string userAgent; | |||
//public NLogConfig.LogLevel logLevel; | |||
public LogViewerConfig logViewer; | |||
public ProxyConfig proxy; | |||
public ForwardProxyConfig proxy; | |||
public HotkeyConfig hotkey; | |||
[JsonIgnore] | |||
public bool updated; | |||
public bool firstRunOnNewVersion; | |||
public Configuration() | |||
{ | |||
@@ -63,35 +67,48 @@ namespace Shadowsocks.Model | |||
global = false; | |||
enabled = false; | |||
shareOverLan = false; | |||
isDefault = true; | |||
firstRun = true; | |||
localPort = 1080; | |||
portableMode = true; | |||
showPluginOutput = false; | |||
pacUrl = ""; | |||
useOnlinePac = false; | |||
secureLocalPac = true; | |||
availabilityStatistics = false; | |||
regeneratePacOnUpdate = true; | |||
autoCheckUpdate = false; | |||
checkPreRelease = false; | |||
skippedUpdateVersion = ""; | |||
isVerboseLogging = false; | |||
// hidden options | |||
isIPv6Enabled = false; | |||
generateLegacyUrl = false; | |||
geositeUrl = ""; | |||
geositeGroup = "geolocation-!cn"; | |||
geositeBlacklistMode = true; | |||
geositeDirectGroups = new List<string>() | |||
{ | |||
"cn", | |||
"geolocation-!cn@cn" | |||
}; | |||
geositeProxiedGroups = new List<string>() | |||
{ | |||
"geolocation-!cn" | |||
}; | |||
geositePreferDirect = false; | |||
userAgent = "ShadowsocksWindows/$version"; | |||
logViewer = new LogViewerConfig(); | |||
proxy = new ProxyConfig(); | |||
proxy = new ForwardProxyConfig(); | |||
hotkey = new HotkeyConfig(); | |||
updated = false; | |||
firstRunOnNewVersion = false; | |||
configs = new List<Server>(); | |||
onlineConfigSource = new List<string>(); | |||
} | |||
[JsonIgnore] | |||
public string userAgentString; // $version substituted with numeral version in it | |||
[JsonIgnore] | |||
NLogConfig nLogConfig; | |||
@@ -103,11 +120,8 @@ namespace Shadowsocks.Model | |||
#endif | |||
[JsonIgnore] | |||
public string localHost => GetLocalHost(); | |||
private string GetLocalHost() | |||
{ | |||
return isIPv6Enabled ? "[::1]" : "127.0.0.1"; | |||
} | |||
public string LocalHost => isIPv6Enabled ? "[::1]" : "127.0.0.1"; | |||
public Server GetCurrentServer() | |||
{ | |||
if (index >= 0 && index < configs.Count) | |||
@@ -124,6 +138,11 @@ namespace Shadowsocks.Model | |||
localPort) | |||
: null; | |||
/// <summary> | |||
/// Used by multiple forms to validate a server. | |||
/// Communication is done by throwing exceptions. | |||
/// </summary> | |||
/// <param name="server"></param> | |||
public static void CheckServer(Server server) | |||
{ | |||
CheckServer(server.server); | |||
@@ -132,52 +151,70 @@ namespace Shadowsocks.Model | |||
CheckTimeout(server.timeout, Server.MaxServerTimeoutSec); | |||
} | |||
public static bool ChecksServer(Server server) | |||
{ | |||
try | |||
{ | |||
CheckServer(server); | |||
return true; | |||
} | |||
catch (Exception) | |||
{ | |||
return false; | |||
} | |||
} | |||
/// <summary> | |||
/// Loads the configuration from file. | |||
/// </summary> | |||
/// <returns>An Configuration object.</returns> | |||
public static Configuration Load() | |||
{ | |||
Configuration config; | |||
try | |||
if (File.Exists(CONFIG_FILE)) | |||
{ | |||
string configContent = File.ReadAllText(CONFIG_FILE); | |||
config = JsonConvert.DeserializeObject<Configuration>(configContent); | |||
config.isDefault = false; | |||
if (UpdateChecker.Asset.CompareVersion(UpdateChecker.Version, config.version ?? "0") > 0) | |||
try | |||
{ | |||
config.updated = true; | |||
string configContent = File.ReadAllText(CONFIG_FILE); | |||
config = JsonConvert.DeserializeObject<Configuration>(configContent, new JsonSerializerSettings() | |||
{ | |||
ObjectCreationHandling = ObjectCreationHandling.Replace | |||
}); | |||
return config; | |||
} | |||
if (config.configs.Count == 0) | |||
config.configs.Add(GetDefaultServer()); | |||
if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) | |||
config.index = 0; | |||
if (!System.Net.Sockets.Socket.OSSupportsIPv6) | |||
catch (Exception e) | |||
{ | |||
config.isIPv6Enabled = false; // disable IPv6 if os not support | |||
if (!(e is FileNotFoundException)) | |||
logger.LogUsefulException(e); | |||
} | |||
//TODO if remote host(server) do not support IPv6 (or DNS resolve AAAA TYPE record) disable IPv6? | |||
config.proxy.CheckConfig(); | |||
} | |||
catch (Exception e) | |||
config = new Configuration(); | |||
return config; | |||
} | |||
/// <summary> | |||
/// Process the loaded configurations and set up things. | |||
/// </summary> | |||
/// <param name="config">A reference of Configuration object.</param> | |||
public static void Process(ref Configuration config) | |||
{ | |||
// Verify if the configured geosite groups exist. | |||
// Reset to default if ANY one of the configured group doesn't exist. | |||
if (!ValidateGeositeGroupList(config.geositeDirectGroups)) | |||
ResetGeositeDirectGroup(ref config.geositeDirectGroups); | |||
if (!ValidateGeositeGroupList(config.geositeProxiedGroups)) | |||
ResetGeositeProxiedGroup(ref config.geositeProxiedGroups); | |||
// Mark the first run of a new version. | |||
var appVersion = new Version(UpdateChecker.Version); | |||
var configVersion = new Version(config.version); | |||
if (appVersion.CompareTo(configVersion) > 0) | |||
{ | |||
if (!(e is FileNotFoundException)) | |||
logger.LogUsefulException(e); | |||
config = new Configuration(); | |||
config.configs.Add(GetDefaultServer()); | |||
config.firstRunOnNewVersion = true; | |||
} | |||
// Add an empty server configuration | |||
if (config.configs.Count == 0) | |||
config.configs.Add(GetDefaultServer()); | |||
// Selected server | |||
if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) | |||
config.index = 0; | |||
if (config.index >= config.configs.Count) | |||
config.index = config.configs.Count - 1; | |||
// Check OS IPv6 support | |||
if (!System.Net.Sockets.Socket.OSSupportsIPv6) | |||
config.isIPv6Enabled = false; | |||
config.proxy.CheckConfig(); | |||
// Replace $version with the version number. | |||
config.userAgentString = config.userAgent.Replace("$version", config.version); | |||
// NLog log level | |||
try | |||
{ | |||
config.nLogConfig = NLogConfig.LoadXML(); | |||
@@ -197,46 +234,42 @@ namespace Shadowsocks.Model | |||
} | |||
catch (Exception e) | |||
{ | |||
// todo: route the error to UI since there is no log file in this scenario | |||
logger.Error(e, "Cannot get the log level from NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); | |||
MessageBox.Show($"Cannot get the log level from NLog config file. Please check if the nlog config file exists with corresponding XML nodes.\n{e.Message}"); | |||
} | |||
return config; | |||
} | |||
/// <summary> | |||
/// Saves the Configuration object to file. | |||
/// </summary> | |||
/// <param name="config">A Configuration object.</param> | |||
public static void Save(Configuration config) | |||
{ | |||
config.configs = SortByOnlineConfig(config.configs); | |||
if (config.index >= config.configs.Count) | |||
config.index = config.configs.Count - 1; | |||
if (config.index < -1) | |||
config.index = -1; | |||
if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) | |||
config.index = 0; | |||
config.isDefault = false; | |||
FileStream configFileStream = null; | |||
StreamWriter configStreamWriter = null; | |||
try | |||
{ | |||
using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create))) | |||
{ | |||
string jsonString = JsonConvert.SerializeObject(config, Formatting.Indented); | |||
sw.Write(jsonString); | |||
sw.Flush(); | |||
} | |||
try | |||
{ | |||
// apply changes to NLog.config | |||
config.nLogConfig.SetLogLevel(config.isVerboseLogging ? verboseLogLevel : NLogConfig.LogLevel.Info); | |||
NLogConfig.SaveXML(config.nLogConfig); | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.Error(e, "Cannot set the log level to NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); | |||
} | |||
configFileStream = File.Open(CONFIG_FILE, FileMode.Create); | |||
configStreamWriter = new StreamWriter(configFileStream); | |||
var jsonString = JsonConvert.SerializeObject(config, Formatting.Indented); | |||
configStreamWriter.Write(jsonString); | |||
configStreamWriter.Flush(); | |||
// NLog | |||
config.nLogConfig.SetLogLevel(config.isVerboseLogging ? verboseLogLevel : NLogConfig.LogLevel.Info); | |||
NLogConfig.SaveXML(config.nLogConfig); | |||
} | |||
catch (IOException e) | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
} | |||
finally | |||
{ | |||
if (configStreamWriter != null) | |||
configStreamWriter.Dispose(); | |||
if (configFileStream != null) | |||
configFileStream.Dispose(); | |||
} | |||
} | |||
public static List<Server> SortByOnlineConfig(IEnumerable<Server> servers) | |||
@@ -248,6 +281,49 @@ namespace Shadowsocks.Model | |||
return ret; | |||
} | |||
/// <summary> | |||
/// Validates if the groups in the list are all valid. | |||
/// </summary> | |||
/// <param name="groups">The list of groups to validate.</param> | |||
/// <returns> | |||
/// True if all groups are valid. | |||
/// False if any one of them is invalid. | |||
/// </returns> | |||
public static bool ValidateGeositeGroupList(List<string> groups) | |||
{ | |||
foreach (var geositeGroup in groups) | |||
if (!GeositeUpdater.CheckGeositeGroup(geositeGroup)) // found invalid group | |||
{ | |||
#if DEBUG | |||
logger.Debug($"Available groups:"); | |||
foreach (var group in GeositeUpdater.Geosites.Keys) | |||
logger.Debug($"{group}"); | |||
#endif | |||
logger.Warn($"The Geosite group {geositeGroup} doesn't exist. Resetting to default groups."); | |||
return false; | |||
} | |||
return true; | |||
} | |||
public static void ResetGeositeDirectGroup(ref List<string> geositeDirectGroups) | |||
{ | |||
geositeDirectGroups.Clear(); | |||
geositeDirectGroups.Add("cn"); | |||
geositeDirectGroups.Add("geolocation-!cn@cn"); | |||
} | |||
public static void ResetGeositeProxiedGroup(ref List<string> geositeProxiedGroups) | |||
{ | |||
geositeProxiedGroups.Clear(); | |||
geositeProxiedGroups.Add("geolocation-!cn"); | |||
} | |||
public static void ResetUserAgent(Configuration config) | |||
{ | |||
config.userAgent = "ShadowsocksWindows/$version"; | |||
config.userAgentString = config.userAgent.Replace("$version", config.version); | |||
} | |||
public static Server AddDefaultServerOrServer(Configuration config, Server server = null, int? index = null) | |||
{ | |||
if (config?.configs != null) | |||
@@ -284,13 +360,13 @@ namespace Shadowsocks.Model | |||
private static void CheckPassword(string password) | |||
{ | |||
if (password.IsNullOrEmpty()) | |||
if (string.IsNullOrEmpty(password)) | |||
throw new ArgumentException(I18N.GetString("Password can not be blank")); | |||
} | |||
public static void CheckServer(string server) | |||
{ | |||
if (server.IsNullOrEmpty()) | |||
if (string.IsNullOrEmpty(server)) | |||
throw new ArgumentException(I18N.GetString("Server IP can not be blank")); | |||
} | |||
@@ -300,17 +376,5 @@ namespace Shadowsocks.Model | |||
throw new ArgumentException( | |||
I18N.GetString("Timeout is invalid, it should not exceed {0}", maxTimeout)); | |||
} | |||
public static void CheckProxyAuthUser(string user) | |||
{ | |||
if (user.IsNullOrEmpty()) | |||
throw new ArgumentException(I18N.GetString("Auth user can not be blank")); | |||
} | |||
public static void CheckProxyAuthPwd(string pwd) | |||
{ | |||
if (pwd.IsNullOrEmpty()) | |||
throw new ArgumentException(I18N.GetString("Auth pwd can not be blank")); | |||
} | |||
} | |||
} |
@@ -1,43 +1,43 @@ | |||
using System; | |||
namespace Shadowsocks.Model | |||
{ | |||
[Serializable] | |||
public class ProxyConfig | |||
{ | |||
public const int PROXY_SOCKS5 = 0; | |||
public const int PROXY_HTTP = 1; | |||
public const int MaxProxyTimeoutSec = 10; | |||
private const int DefaultProxyTimeoutSec = 3; | |||
public bool useProxy; | |||
public int proxyType; | |||
public string proxyServer; | |||
public int proxyPort; | |||
public int proxyTimeout; | |||
public bool useAuth; | |||
public string authUser; | |||
public string authPwd; | |||
public ProxyConfig() | |||
{ | |||
useProxy = false; | |||
proxyType = PROXY_SOCKS5; | |||
proxyServer = ""; | |||
proxyPort = 0; | |||
proxyTimeout = DefaultProxyTimeoutSec; | |||
useAuth = false; | |||
authUser = ""; | |||
authPwd = ""; | |||
} | |||
public void CheckConfig() | |||
{ | |||
if (proxyType < PROXY_SOCKS5 || proxyType > PROXY_HTTP) | |||
{ | |||
proxyType = PROXY_SOCKS5; | |||
} | |||
} | |||
} | |||
} | |||
using System; | |||
namespace Shadowsocks.Model | |||
{ | |||
[Serializable] | |||
public class ForwardProxyConfig | |||
{ | |||
public const int PROXY_SOCKS5 = 0; | |||
public const int PROXY_HTTP = 1; | |||
public const int MaxProxyTimeoutSec = 10; | |||
private const int DefaultProxyTimeoutSec = 3; | |||
public bool useProxy; | |||
public int proxyType; | |||
public string proxyServer; | |||
public int proxyPort; | |||
public int proxyTimeout; | |||
public bool useAuth; | |||
public string authUser; | |||
public string authPwd; | |||
public ForwardProxyConfig() | |||
{ | |||
useProxy = false; | |||
proxyType = PROXY_SOCKS5; | |||
proxyServer = ""; | |||
proxyPort = 0; | |||
proxyTimeout = DefaultProxyTimeoutSec; | |||
useAuth = false; | |||
authUser = ""; | |||
authPwd = ""; | |||
} | |||
public void CheckConfig() | |||
{ | |||
if (proxyType < PROXY_SOCKS5 || proxyType > PROXY_HTTP) | |||
{ | |||
proxyType = PROXY_SOCKS5; | |||
} | |||
} | |||
} | |||
} |
@@ -55,21 +55,17 @@ namespace Shadowsocks.Model | |||
return server.GetHashCode() ^ server_port; | |||
} | |||
public override bool Equals(object obj) | |||
{ | |||
Server o2 = (Server)obj; | |||
return server == o2.server && server_port == o2.server_port; | |||
} | |||
public override bool Equals(object obj) => obj is Server o2 && server == o2.server && server_port == o2.server_port; | |||
public override string ToString() | |||
{ | |||
if (server.IsNullOrEmpty()) | |||
if (string.IsNullOrEmpty(server)) | |||
{ | |||
return I18N.GetString("New server"); | |||
} | |||
string serverStr = $"{FormalHostName}:{server_port}"; | |||
return remarks.IsNullOrEmpty() | |||
return string.IsNullOrEmpty(remarks) | |||
? serverStr | |||
: $"{remarks} ({serverStr})"; | |||
} | |||
@@ -93,7 +89,7 @@ namespace Shadowsocks.Model | |||
u.Port = server_port; | |||
u.Fragment = HttpUtility.UrlEncode(remarks, Encoding.UTF8); | |||
if (!plugin.IsNullOrWhiteSpace()) | |||
if (!string.IsNullOrWhiteSpace(plugin)) | |||
{ | |||
NameValueCollection param = HttpUtility.ParseQueryString(""); | |||
@@ -147,7 +143,7 @@ namespace Shadowsocks.Model | |||
Server server = new Server(); | |||
var base64 = match.Groups["base64"].Value.TrimEnd('/'); | |||
var tag = match.Groups["tag"].Value; | |||
if (!tag.IsNullOrEmpty()) | |||
if (!string.IsNullOrEmpty(tag)) | |||
{ | |||
server.remarks = HttpUtility.UrlDecode(tag, Encoding.UTF8); | |||
} | |||
@@ -173,7 +169,7 @@ namespace Shadowsocks.Model | |||
public static Server ParseURL(string serverUrl) | |||
{ | |||
string _serverUrl = serverUrl.Trim(); | |||
if (!_serverUrl.BeginWith("ss://", StringComparison.InvariantCultureIgnoreCase)) | |||
if (!_serverUrl.StartsWith("ss://", StringComparison.InvariantCultureIgnoreCase)) | |||
{ | |||
return null; | |||
} | |||
@@ -1,95 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
namespace Shadowsocks.Model | |||
{ | |||
// Simple processed records for a short period of time | |||
public class StatisticsRecord | |||
{ | |||
public DateTime Timestamp { get; set; } = DateTime.Now; | |||
public string ServerIdentifier { get; set; } | |||
// in ping-only records, these fields would be null | |||
public int? AverageLatency; | |||
public int? MinLatency; | |||
public int? MaxLatency; | |||
private bool EmptyLatencyData => (AverageLatency == null) && (MinLatency == null) && (MaxLatency == null); | |||
public int? AverageInboundSpeed; | |||
public int? MinInboundSpeed; | |||
public int? MaxInboundSpeed; | |||
private bool EmptyInboundSpeedData | |||
=> (AverageInboundSpeed == null) && (MinInboundSpeed == null) && (MaxInboundSpeed == null); | |||
public int? AverageOutboundSpeed; | |||
public int? MinOutboundSpeed; | |||
public int? MaxOutboundSpeed; | |||
private bool EmptyOutboundSpeedData | |||
=> (AverageOutboundSpeed == null) && (MinOutboundSpeed == null) && (MaxOutboundSpeed == null); | |||
// if user disabled ping test, response would be null | |||
public int? AverageResponse; | |||
public int? MinResponse; | |||
public int? MaxResponse; | |||
public float? PackageLoss; | |||
private bool EmptyResponseData | |||
=> (AverageResponse == null) && (MinResponse == null) && (MaxResponse == null) && (PackageLoss == null); | |||
public bool IsEmptyData() { | |||
return EmptyInboundSpeedData && EmptyOutboundSpeedData && EmptyResponseData && EmptyLatencyData; | |||
} | |||
public StatisticsRecord() | |||
{ | |||
} | |||
public StatisticsRecord(string identifier, ICollection<int> inboundSpeedRecords, ICollection<int> outboundSpeedRecords, ICollection<int> latencyRecords) | |||
{ | |||
ServerIdentifier = identifier; | |||
var inbound = inboundSpeedRecords?.Where(s => s > 0).ToList(); | |||
if (inbound != null && inbound.Any()) | |||
{ | |||
AverageInboundSpeed = (int) inbound.Average(); | |||
MinInboundSpeed = inbound.Min(); | |||
MaxInboundSpeed = inbound.Max(); | |||
} | |||
var outbound = outboundSpeedRecords?.Where(s => s > 0).ToList(); | |||
if (outbound!= null && outbound.Any()) | |||
{ | |||
AverageOutboundSpeed = (int) outbound.Average(); | |||
MinOutboundSpeed = outbound.Min(); | |||
MaxOutboundSpeed = outbound.Max(); | |||
} | |||
var latency = latencyRecords?.Where(s => s > 0).ToList(); | |||
if (latency!= null && latency.Any()) | |||
{ | |||
AverageLatency = (int) latency.Average(); | |||
MinLatency = latency.Min(); | |||
MaxLatency = latency.Max(); | |||
} | |||
} | |||
public StatisticsRecord(string identifier, ICollection<int?> responseRecords) | |||
{ | |||
ServerIdentifier = identifier; | |||
SetResponse(responseRecords); | |||
} | |||
public void SetResponse(ICollection<int?> responseRecords) | |||
{ | |||
if (responseRecords == null) return; | |||
var records = responseRecords.Where(response => response != null).Select(response => response.Value).ToList(); | |||
if (!records.Any()) return; | |||
AverageResponse = (int?) records.Average(); | |||
MinResponse = records.Min(); | |||
MaxResponse = records.Max(); | |||
PackageLoss = responseRecords.Count(response => response != null)/(float) responseRecords.Count; | |||
} | |||
} | |||
} |
@@ -1,69 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Reflection; | |||
using Newtonsoft.Json; | |||
using NLog; | |||
namespace Shadowsocks.Model | |||
{ | |||
[Serializable] | |||
public class StatisticsStrategyConfiguration | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
public static readonly string ID = "com.shadowsocks.strategy.statistics"; | |||
public bool StatisticsEnabled { get; set; } = false; | |||
public bool ByHourOfDay { get; set; } = true; | |||
public bool Ping { get; set; } | |||
public int ChoiceKeptMinutes { get; set; } = 10; | |||
public int DataCollectionMinutes { get; set; } = 10; | |||
public int RepeatTimesNum { get; set; } = 4; | |||
private const string ConfigFile = "statistics-config.json"; | |||
public static StatisticsStrategyConfiguration Load() | |||
{ | |||
try | |||
{ | |||
var content = File.ReadAllText(ConfigFile); | |||
var configuration = JsonConvert.DeserializeObject<StatisticsStrategyConfiguration>(content); | |||
return configuration; | |||
} | |||
catch (FileNotFoundException) | |||
{ | |||
var configuration = new StatisticsStrategyConfiguration(); | |||
Save(configuration); | |||
return configuration; | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
return new StatisticsStrategyConfiguration(); | |||
} | |||
} | |||
public static void Save(StatisticsStrategyConfiguration configuration) | |||
{ | |||
try | |||
{ | |||
var content = JsonConvert.SerializeObject(configuration, Formatting.Indented); | |||
File.WriteAllText(ConfigFile, content); | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
} | |||
} | |||
public Dictionary<string, float> Calculations; | |||
public StatisticsStrategyConfiguration() | |||
{ | |||
var properties = typeof(StatisticsRecord).GetFields(BindingFlags.Instance | BindingFlags.Public); | |||
Calculations = properties.ToDictionary(p => p.Name, _ => (float)0); | |||
} | |||
} | |||
} |
@@ -3,6 +3,7 @@ using System.Diagnostics; | |||
using System.IO; | |||
using System.IO.Pipes; | |||
using System.Net; | |||
using System.Reflection; | |||
using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
@@ -10,10 +11,13 @@ using System.Windows.Forms; | |||
using CommandLine; | |||
using Microsoft.Win32; | |||
using NLog; | |||
using ReactiveUI; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Controller.Hotkeys; | |||
using Shadowsocks.Util; | |||
using Shadowsocks.View; | |||
using Splat; | |||
using WPFLocalizeExtension.Engine; | |||
namespace Shadowsocks | |||
{ | |||
@@ -89,6 +93,16 @@ namespace Shadowsocks | |||
// we have to do this for self-contained executables | |||
Directory.SetCurrentDirectory(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)); | |||
// We would use this in v5. | |||
// Parameters would have to be dropped from views' constructors (VersionUpdatePromptView) | |||
//Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly()); | |||
// Workaround for hosting WPF controls in a WinForms app. | |||
// We have to manually set the culture for the LocalizeDictionary instance. | |||
// https://stackoverflow.com/questions/374518/localizing-a-winforms-application-with-embedded-wpf-user-controls | |||
// https://stackoverflow.com/questions/14668640/wpf-localize-extension-translate-window-at-run-time | |||
LocalizeDictionary.Instance.Culture = Thread.CurrentThread.CurrentCulture; | |||
#if DEBUG | |||
// truncate privoxy log file while debugging | |||
string privoxyLogFilename = Utils.GetTempPath("privoxy.log"); | |||
@@ -163,7 +177,7 @@ namespace Shadowsocks | |||
Thread.Sleep(10 * 1000); | |||
try | |||
{ | |||
MainController.Start(false); | |||
MainController.Start(true); | |||
logger.Info("controller started"); | |||
} | |||
catch (Exception ex) | |||
@@ -1,10 +0,0 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<!-- | |||
This file is automatically generated by Visual Studio .Net. It is | |||
used to store generic object data source configuration information. | |||
Renaming the file extension or editing the content of this file may | |||
cause the file to be unrecognizable by the program. | |||
--> | |||
<GenericObjectDataSource DisplayName="StatisticsStrategyConfiguration" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource"> | |||
<TypeInfo>Shadowsocks.Model.StatisticsStrategyConfiguration, Shadowsocks, Version=2.5.2.0, Culture=neutral, PublicKeyToken=null</TypeInfo> | |||
</GenericObjectDataSource> |
@@ -1,10 +1,10 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// 此代码由工具生成。 | |||
// 运行时版本:4.0.30319.42000 | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.42000 | |||
// | |||
// 对此文件的更改可能会导致不正确的行为,并且如果 | |||
// 重新生成代码,这些更改将会丢失。 | |||
// Changes to this file may cause incorrect behavior and will be lost if | |||
// the code is regenerated. | |||
// </auto-generated> | |||
//------------------------------------------------------------------------------ | |||
@@ -13,16 +13,16 @@ namespace Shadowsocks.Properties { | |||
/// <summary> | |||
/// 一个强类型的资源类,用于查找本地化的字符串等。 | |||
/// A strongly-typed resource class, for looking up localized strings, etc. | |||
/// </summary> | |||
// 此类是由 StronglyTypedResourceBuilder | |||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 | |||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen | |||
// (以 /str 作为命令选项),或重新生成 VS 项目。 | |||
// This class was auto-generated by the StronglyTypedResourceBuilder | |||
// class via a tool like ResGen or Visual Studio. | |||
// To add or remove a member, edit your .ResX file then rerun ResGen | |||
// with the /str option, or rebuild your VS project. | |||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] | |||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | |||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | |||
internal class Resources { | |||
public class Resources { | |||
private static global::System.Resources.ResourceManager resourceMan; | |||
@@ -33,10 +33,10 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 返回此类使用的缓存的 ResourceManager 实例。 | |||
/// Returns the cached ResourceManager instance used by this class. | |||
/// </summary> | |||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | |||
internal static global::System.Resources.ResourceManager ResourceManager { | |||
public static global::System.Resources.ResourceManager ResourceManager { | |||
get { | |||
if (object.ReferenceEquals(resourceMan, null)) { | |||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.Properties.Resources", typeof(Resources).Assembly); | |||
@@ -47,11 +47,11 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 重写当前线程的 CurrentUICulture 属性 | |||
/// 重写当前线程的 CurrentUICulture 属性。 | |||
/// Overrides the current thread's CurrentUICulture property for all | |||
/// resource lookups using this strongly typed resource class. | |||
/// </summary> | |||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | |||
internal static global::System.Globalization.CultureInfo Culture { | |||
public static global::System.Globalization.CultureInfo Culture { | |||
get { | |||
return resourceCulture; | |||
} | |||
@@ -61,7 +61,7 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找类似 /* eslint-disable */ | |||
/// Looks up a localized string similar to /* eslint-disable */ | |||
///// Was generated by gfwlist2pac in precise mode | |||
///// https://github.com/clowwindy/gfwlist2pac | |||
/// | |||
@@ -69,25 +69,28 @@ namespace Shadowsocks.Properties { | |||
///// 2019-02-08: Updated to support shadowsocks-windows user rules. | |||
/// | |||
///var proxy = __PROXY__; | |||
///var userrules = __USERRULES__; | |||
///var rules = __RULES__; | |||
///var userrules = []; | |||
///var rules = []; | |||
/// | |||
////* | |||
///* This file is part of Adblock Plus <http://adblockplus.org/>, | |||
///* Copyright (C) 2006-2014 Eyeo GmbH | |||
///* | |||
///* Adblock Plus is free software: you can redistribute it and/or [字符串的其余部分被截断]"; 的本地化字符串。 | |||
///// convert to abp grammar | |||
///for (var i = 0; i < __RULES__.length; i++) { | |||
/// var s = __RULES__[i]; | |||
/// if (s.substring(0, 2) == "||") s += "^"; | |||
/// rules.push(s); | |||
///} | |||
/// | |||
///for (var i = 0; i < [rest of string was truncated]";. | |||
/// </summary> | |||
internal static string abp_js { | |||
public static string abp_js { | |||
get { | |||
return ResourceManager.GetString("abp_js", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// 查找 System.Byte[] 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] dlc_dat { | |||
public static byte[] dlc_dat { | |||
get { | |||
object obj = ResourceManager.GetObject("dlc_dat", resourceCulture); | |||
return ((byte[])(obj)); | |||
@@ -95,7 +98,7 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找类似 en,ru-RU,zh-CN,zh-TW,ja,ko,fr | |||
/// Looks up a localized string similar to en,ru-RU,zh-CN,zh-TW,ja,ko,fr | |||
///#Restart program to apply translation,,,,,, | |||
///#This is comment line,,,,,, | |||
///#Always keep language name at head of file,,,,,, | |||
@@ -107,31 +110,31 @@ namespace Shadowsocks.Properties { | |||
///,,,,,, | |||
///#Menu,,,,,, | |||
///,,,,,, | |||
///System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,P [字符串的其余部分被截断]"; 的本地化字符串。 | |||
///System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,P [rest of string was truncated]";. | |||
/// </summary> | |||
internal static string i18n_csv { | |||
public static string i18n_csv { | |||
get { | |||
return ResourceManager.GetString("i18n_csv", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// 查找类似 <?xml version="1.0" encoding="utf-8" ?> | |||
/// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> | |||
///<!-- Warning: Configuration may reset after shadowsocks upgrade. --> | |||
///<!-- If you messed it up, delete this file and Shadowsocks will create a new one. --> | |||
///<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
/// <targets> | |||
/// <!-- This line is managed by Shadowsocks. Do not modify it unless you know what you are doing.--> | |||
/// <target name="file" xsi:type="File" fileName="ss_win_temp\shadowsocks.log" writ [字符串的其余部分被截断]"; 的本地化字符串。 | |||
/// <target name="file" xsi:type="File" fileName="ss_win_temp\shadowsocks.log" writ [rest of string was truncated]";. | |||
/// </summary> | |||
internal static string NLog_config { | |||
public static string NLog_config { | |||
get { | |||
return ResourceManager.GetString("NLog_config", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// 查找类似 listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ | |||
/// Looks up a localized string similar to listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ | |||
///toggle 0 | |||
///logfile ss_privoxy.log | |||
///show-on-task-bar 0 | |||
@@ -139,18 +142,18 @@ namespace Shadowsocks.Properties { | |||
///forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . | |||
///max-client-connections 2048 | |||
///hide-console | |||
/// 的本地化字符串。 | |||
///. | |||
/// </summary> | |||
internal static string privoxy_conf { | |||
public static string privoxy_conf { | |||
get { | |||
return ResourceManager.GetString("privoxy_conf", resourceCulture); | |||
} | |||
} | |||
/// <summary> | |||
/// 查找 System.Byte[] 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] privoxy_exe { | |||
public static byte[] privoxy_exe { | |||
get { | |||
object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture); | |||
return ((byte[])(obj)); | |||
@@ -158,9 +161,9 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||
/// </summary> | |||
internal static System.Drawing.Bitmap ss32Fill { | |||
public static System.Drawing.Bitmap ss32Fill { | |||
get { | |||
object obj = ResourceManager.GetObject("ss32Fill", resourceCulture); | |||
return ((System.Drawing.Bitmap)(obj)); | |||
@@ -168,9 +171,9 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||
/// </summary> | |||
internal static System.Drawing.Bitmap ss32In { | |||
public static System.Drawing.Bitmap ss32In { | |||
get { | |||
object obj = ResourceManager.GetObject("ss32In", resourceCulture); | |||
return ((System.Drawing.Bitmap)(obj)); | |||
@@ -178,9 +181,9 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||
/// </summary> | |||
internal static System.Drawing.Bitmap ss32Out { | |||
public static System.Drawing.Bitmap ss32Out { | |||
get { | |||
object obj = ResourceManager.GetObject("ss32Out", resourceCulture); | |||
return ((System.Drawing.Bitmap)(obj)); | |||
@@ -188,9 +191,9 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||
/// </summary> | |||
internal static System.Drawing.Bitmap ss32Outline { | |||
public static System.Drawing.Bitmap ss32Outline { | |||
get { | |||
object obj = ResourceManager.GetObject("ss32Outline", resourceCulture); | |||
return ((System.Drawing.Bitmap)(obj)); | |||
@@ -198,9 +201,9 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||
/// </summary> | |||
internal static System.Drawing.Bitmap ssw128 { | |||
public static System.Drawing.Bitmap ssw128 { | |||
get { | |||
object obj = ResourceManager.GetObject("ssw128", resourceCulture); | |||
return ((System.Drawing.Bitmap)(obj)); | |||
@@ -208,9 +211,9 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找 System.Byte[] 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] sysproxy_exe { | |||
public static byte[] sysproxy_exe { | |||
get { | |||
object obj = ResourceManager.GetObject("sysproxy_exe", resourceCulture); | |||
return ((byte[])(obj)); | |||
@@ -218,9 +221,9 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找 System.Byte[] 类型的本地化资源。 | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] sysproxy64_exe { | |||
public static byte[] sysproxy64_exe { | |||
get { | |||
object obj = ResourceManager.GetObject("sysproxy64_exe", resourceCulture); | |||
return ((byte[])(obj)); | |||
@@ -228,11 +231,11 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// 查找类似 ! Put user rules line by line in this file. | |||
/// Looks up a localized string similar to ! Put user rules line by line in this file. | |||
///! See https://adblockplus.org/en/filter-cheatsheet | |||
/// 的本地化字符串。 | |||
///. | |||
/// </summary> | |||
internal static string user_rule { | |||
public static string user_rule { | |||
get { | |||
return ResourceManager.GetString("user_rule", resourceCulture); | |||
} | |||
@@ -39,8 +39,6 @@ namespace Shadowsocks.Proxy | |||
public object AsyncState { get; set; } | |||
public int BytesToRead; | |||
public Exception ex { get; set; } | |||
} | |||
@@ -199,7 +197,7 @@ namespace Shadowsocks.Proxy | |||
} | |||
else | |||
{ | |||
if (line.IsNullOrEmpty()) | |||
if (string.IsNullOrEmpty(line)) | |||
{ | |||
return true; | |||
} | |||
@@ -1,314 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Globalization; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
#if EXPOSE_EVERYTHING || EXPOSE_STRINGEX | |||
public | |||
#endif | |||
static partial class StringEx | |||
{ | |||
#pragma warning disable 1591 | |||
public static StringComparison GlobalDefaultComparison { get; set; } = StringComparison.Ordinal; | |||
[ThreadStatic] | |||
private static StringComparison? _DefaultComparison; | |||
public static StringComparison DefaultComparison | |||
{ | |||
get { return _DefaultComparison ?? GlobalDefaultComparison; } | |||
set { _DefaultComparison = value; } | |||
} | |||
#region basic String methods | |||
public static bool IsNullOrEmpty(this string value) | |||
=> string.IsNullOrEmpty(value); | |||
public static bool IsNullOrWhiteSpace(this string value) | |||
=> string.IsNullOrWhiteSpace(value); | |||
public static bool IsWhiteSpace(this string value) | |||
{ | |||
foreach (var c in value) | |||
{ | |||
if (char.IsWhiteSpace(c)) continue; | |||
return false; | |||
} | |||
return true; | |||
} | |||
#if !PCL | |||
public static string IsInterned(this string value) | |||
{ | |||
if (value == null) | |||
throw new ArgumentNullException(nameof(value)); | |||
return string.IsInterned(value); | |||
} | |||
public static string Intern(this string value) | |||
{ | |||
if (value == null) | |||
throw new ArgumentNullException(nameof(value)); | |||
return string.Intern(value); | |||
} | |||
#endif | |||
#if UNSAFE | |||
public static unsafe string ToLowerForASCII(this string value) | |||
{ | |||
if (value.IsNullOrWhiteSpace()) | |||
return value; | |||
value = string.Copy(value); | |||
fixed (char* low = value) | |||
{ | |||
var end = low + value.Length; | |||
for (var p = low; p < end; p++) | |||
{ | |||
var c = *p; | |||
if (c < 'A' || c > 'Z') | |||
continue; | |||
*p = (char)(c + 0x20); | |||
} | |||
} | |||
return value; | |||
} | |||
public static unsafe string ToUpperForASCII(this string value) | |||
{ | |||
if (value.IsNullOrWhiteSpace()) | |||
return value; | |||
value = string.Copy(value); | |||
fixed (char* low = value) | |||
{ | |||
var end = low + value.Length; | |||
for (var p = low; p < end; p++) | |||
{ | |||
var c = *p; | |||
if (c < 'a' || c > 'z') | |||
continue; | |||
*p = (char)(c - 0x20); | |||
} | |||
} | |||
return value; | |||
} | |||
#else | |||
public static string ToLowerForASCII(this string value) | |||
{ | |||
if (value.IsNullOrWhiteSpace()) | |||
return value; | |||
var sb = new StringBuilder(value.Length); | |||
foreach (var c in value) | |||
{ | |||
if (c < 'A' || c > 'Z') | |||
sb.Append(c); | |||
else | |||
sb.Append((char)(c + 0x20)); | |||
} | |||
return sb.ToString(); | |||
} | |||
public static string ToUpperForASCII(this string value) | |||
{ | |||
if (value.IsNullOrWhiteSpace()) | |||
return value; | |||
var sb = new StringBuilder(value.Length); | |||
foreach (var c in value) | |||
{ | |||
if (c < 'a' || c > 'z') | |||
sb.Append(c); | |||
else | |||
sb.Append((char)(c - 0x20)); | |||
} | |||
return sb.ToString(); | |||
} | |||
#endif | |||
#endregion | |||
#region comparing | |||
#region Is | |||
public static bool Is(this string a, string b) | |||
=> string.Equals(a, b, DefaultComparison); | |||
public static bool Is(this string a, string b, StringComparison comparisonType) | |||
=> string.Equals(a, b, comparisonType); | |||
#endregion | |||
#region BeginWith | |||
public static bool BeginWith(this string s, char c) | |||
{ | |||
if (s.IsNullOrEmpty()) return false; | |||
return s[0] == c; | |||
} | |||
public static bool BeginWithAny(this string s, IEnumerable<char> chars) | |||
{ | |||
if (s.IsNullOrEmpty()) return false; | |||
return chars.Contains(s[0]); | |||
} | |||
public static bool BeginWithAny(this string s, params char[] chars) | |||
=> s.BeginWithAny(chars.AsEnumerable()); | |||
public static bool BeginWith(this string a, string b) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.StartsWith(b, DefaultComparison); | |||
} | |||
public static bool BeginWith(this string a, string b, StringComparison comparisonType) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.StartsWith(b, comparisonType); | |||
} | |||
#if !PCL | |||
public static bool BeginWith(this string a, string b, bool ignoreCase, CultureInfo culture) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.StartsWith(b, ignoreCase, culture); | |||
} | |||
#endif | |||
#endregion | |||
#region FinishWith | |||
public static bool FinishWith(this string s, char c) | |||
{ | |||
if (s.IsNullOrEmpty()) return false; | |||
return s.Last() == c; | |||
} | |||
public static bool FinishWithAny(this string s, IEnumerable<char> chars) | |||
{ | |||
if (s.IsNullOrEmpty()) return false; | |||
return chars.Contains(s.Last()); | |||
} | |||
public static bool FinishWithAny(this string s, params char[] chars) | |||
=> s.FinishWithAny(chars.AsEnumerable()); | |||
public static bool FinishWith(this string a, string b) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.EndsWith(b, DefaultComparison); | |||
} | |||
public static bool FinishWith(this string a, string b, StringComparison comparisonType) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.EndsWith(b, comparisonType); | |||
} | |||
#if !PCL | |||
public static bool FinishWith(this string a, string b, bool ignoreCase, CultureInfo culture) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.EndsWith(b, ignoreCase, culture); | |||
} | |||
#endif | |||
#endregion | |||
#endregion | |||
#region ToLines | |||
public static IEnumerable<string> ToLines(this TextReader reader) | |||
{ | |||
string line; | |||
while ((line = reader.ReadLine()) != null) | |||
yield return line; | |||
} | |||
public static IEnumerable<string> NonEmptyLines(this TextReader reader) | |||
{ | |||
string line; | |||
while ((line = reader.ReadLine()) != null) | |||
{ | |||
if (line == "") continue; | |||
yield return line; | |||
} | |||
} | |||
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader) | |||
{ | |||
string line; | |||
while ((line = reader.ReadLine()) != null) | |||
{ | |||
if (line.IsWhiteSpace()) continue; | |||
yield return line; | |||
} | |||
} | |||
#endregion | |||
#region others | |||
private static readonly char[][] Quotes = new[] | |||
{ | |||
"\"\"", | |||
"''", | |||
"“”", | |||
"‘’", | |||
"『』", | |||
"「」", | |||
"〖〗", | |||
"【】", | |||
}.Select(s => s.ToCharArray()).ToArray(); | |||
public static string Enquote(this string value) | |||
{ | |||
if (value == null) | |||
return "(null)"; | |||
foreach (var pair in Quotes) | |||
{ | |||
if (value.IndexOfAny(pair) < 0) | |||
return pair[0] + value + pair[1]; | |||
} | |||
return '"' + value.Replace("\\", @"\\").Replace("\"", @"\""") + '"'; | |||
} | |||
public static string Replace(this string value, string find, string rep, StringComparison comparsionType) | |||
{ | |||
if (find.IsNullOrEmpty()) | |||
throw new ArgumentException(null, nameof(find)); | |||
if (rep == null) | |||
rep = ""; | |||
if (value.IsNullOrEmpty()) | |||
return value; | |||
var sb = new StringBuilder(value.Length); | |||
var last = 0; | |||
var len = find.Length; | |||
var idx = value.IndexOf(find, DefaultComparison); | |||
while (idx != -1) | |||
{ | |||
sb.Append(value.Substring(last, idx - last)); | |||
sb.Append(rep); | |||
idx += len; | |||
last = idx; | |||
idx = value.IndexOf(find, idx, comparsionType); | |||
} | |||
sb.Append(value.Substring(last)); | |||
return sb.ToString(); | |||
} | |||
public static string ReplaceEx(this string value, string find, string rep) | |||
=> value.Replace(find, rep, DefaultComparison); | |||
#endregion | |||
} |
@@ -8,6 +8,10 @@ using System.Windows.Forms; | |||
using Microsoft.Win32; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Model; | |||
using System.Drawing; | |||
using ZXing; | |||
using ZXing.QrCode; | |||
using ZXing.Common; | |||
namespace Shadowsocks.Util | |||
{ | |||
@@ -183,7 +187,7 @@ namespace Shadowsocks.Util | |||
// we are building x86 binary for both x86 and x64, which will | |||
// cause problem when opening registry key | |||
// detect operating system instead of CPU | |||
if (name.IsNullOrEmpty()) throw new ArgumentException(nameof(name)); | |||
if (string.IsNullOrEmpty(name)) throw new ArgumentException(nameof(name)); | |||
try | |||
{ | |||
RegistryKey userKey = RegistryKey.OpenBaseKey(hive, | |||
@@ -208,10 +212,47 @@ namespace Shadowsocks.Util | |||
return Environment.OSVersion.Version.Major > 5; | |||
} | |||
[DllImport("kernel32.dll")] | |||
[return: MarshalAs(UnmanagedType.Bool)] | |||
private static extern bool SetProcessWorkingSetSize(IntPtr process, | |||
UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize); | |||
public static string ScanQRCodeFromScreen() | |||
{ | |||
foreach (Screen screen in Screen.AllScreens) | |||
{ | |||
using (Bitmap fullImage = new Bitmap(screen.Bounds.Width, | |||
screen.Bounds.Height)) | |||
{ | |||
using (Graphics g = Graphics.FromImage(fullImage)) | |||
{ | |||
g.CopyFromScreen(screen.Bounds.X, | |||
screen.Bounds.Y, | |||
0, 0, | |||
fullImage.Size, | |||
CopyPixelOperation.SourceCopy); | |||
} | |||
int maxTry = 10; | |||
for (int i = 0; i < maxTry; i++) | |||
{ | |||
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry); | |||
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry); | |||
Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2); | |||
Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height); | |||
double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width; | |||
using (Graphics g = Graphics.FromImage(target)) | |||
{ | |||
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height), | |||
cropRect, | |||
GraphicsUnit.Pixel); | |||
} | |||
var source = new BitmapLuminanceSource(target); | |||
var bitmap = new BinaryBitmap(new HybridBinarizer(source)); | |||
QRCodeReader reader = new QRCodeReader(); | |||
var result = reader.decode(bitmap); | |||
if (result != null) | |||
return result.Text; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
public static void OpenInBrowser(string url) | |||
{ | |||
@@ -1,114 +0,0 @@ | |||
namespace Shadowsocks.View | |||
{ | |||
partial class CalculationControl | |||
{ | |||
/// <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 Component 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() | |||
{ | |||
this.factorNum = new System.Windows.Forms.NumericUpDown(); | |||
this.multiply = new System.Windows.Forms.Label(); | |||
this.plus = new System.Windows.Forms.Label(); | |||
this.valueLabel = new System.Windows.Forms.Label(); | |||
((System.ComponentModel.ISupportInitialize)(this.factorNum)).BeginInit(); | |||
this.SuspendLayout(); | |||
// | |||
// factorNum | |||
// | |||
this.factorNum.DecimalPlaces = 2; | |||
this.factorNum.Dock = System.Windows.Forms.DockStyle.Right; | |||
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[] { | |||
1, | |||
0, | |||
0, | |||
131072}); | |||
this.factorNum.Location = new System.Drawing.Point(210, 0); | |||
this.factorNum.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); | |||
this.factorNum.Minimum = new decimal(new int[] { | |||
1000, | |||
0, | |||
0, | |||
-2147418112}); | |||
this.factorNum.Name = "factorNum"; | |||
this.factorNum.Size = new System.Drawing.Size(76, 30); | |||
this.factorNum.TabIndex = 6; | |||
// | |||
// multiply | |||
// | |||
this.multiply.AutoSize = true; | |||
this.multiply.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||
this.multiply.Location = new System.Drawing.Point(180, 2); | |||
this.multiply.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||
this.multiply.Name = "multiply"; | |||
this.multiply.Size = new System.Drawing.Size(22, 23); | |||
this.multiply.TabIndex = 2; | |||
this.multiply.Text = "×"; | |||
// | |||
// plus | |||
// | |||
this.plus.AutoSize = true; | |||
this.plus.Font = new System.Drawing.Font("Segoe UI", 10F); | |||
this.plus.Location = new System.Drawing.Point(4, 2); | |||
this.plus.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||
this.plus.Name = "plus"; | |||
this.plus.Size = new System.Drawing.Size(22, 23); | |||
this.plus.TabIndex = 3; | |||
this.plus.Text = "+"; | |||
// | |||
// valueLabel | |||
// | |||
this.valueLabel.AutoSize = true; | |||
this.valueLabel.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||
this.valueLabel.Location = new System.Drawing.Point(35, 4); | |||
this.valueLabel.Name = "valueLabel"; | |||
this.valueLabel.Size = new System.Drawing.Size(101, 20); | |||
this.valueLabel.TabIndex = 7; | |||
this.valueLabel.Text = "PackageLoss"; | |||
// | |||
// CalculationControl | |||
// | |||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); | |||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |||
this.Controls.Add(this.valueLabel); | |||
this.Controls.Add(this.factorNum); | |||
this.Controls.Add(this.multiply); | |||
this.Controls.Add(this.plus); | |||
this.Margin = new System.Windows.Forms.Padding(0); | |||
this.Name = "CalculationControl"; | |||
this.Size = new System.Drawing.Size(286, 26); | |||
((System.ComponentModel.ISupportInitialize)(this.factorNum)).EndInit(); | |||
this.ResumeLayout(false); | |||
this.PerformLayout(); | |||
} | |||
#endregion | |||
private System.Windows.Forms.NumericUpDown factorNum; | |||
private System.Windows.Forms.Label multiply; | |||
private System.Windows.Forms.Label plus; | |||
private System.Windows.Forms.Label valueLabel; | |||
} | |||
} |
@@ -1,25 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.ComponentModel; | |||
using System.Drawing; | |||
using System.Data; | |||
using System.Globalization; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Windows.Forms; | |||
namespace Shadowsocks.View | |||
{ | |||
public partial class CalculationControl : UserControl | |||
{ | |||
public CalculationControl(string text, float value) | |||
{ | |||
InitializeComponent(); | |||
valueLabel.Text = text; | |||
factorNum.Value = (decimal) value; | |||
} | |||
public string Value => valueLabel.Text; | |||
public float Factor => (float) factorNum.Value; | |||
} | |||
} |
@@ -229,7 +229,7 @@ namespace Shadowsocks.View | |||
{ | |||
password = null; | |||
string outPassword; | |||
if ((outPassword = PasswordTextBox.Text).IsNullOrWhiteSpace()) | |||
if (string.IsNullOrWhiteSpace(outPassword = PasswordTextBox.Text)) | |||
{ | |||
if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) | |||
{ | |||
@@ -352,7 +352,7 @@ namespace Shadowsocks.View | |||
private void LoadCurrentConfiguration() | |||
{ | |||
_modifiedConfiguration = controller.GetConfigurationCopy(); | |||
_modifiedConfiguration = controller.GetCurrentConfiguration(); | |||
LoadServerNameListToUI(_modifiedConfiguration); | |||
_lastSelectedIndex = _modifiedConfiguration.index; | |||
@@ -1,190 +0,0 @@ | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
using System; | |||
using System.Drawing; | |||
using System.Text; | |||
using System.Windows.Forms; | |||
using static Shadowsocks.Controller.HotkeyReg; | |||
namespace Shadowsocks.View | |||
{ | |||
public partial class HotkeySettingsForm : Form | |||
{ | |||
private readonly ShadowsocksController _controller; | |||
// this is a copy of hotkey configuration that we are working on | |||
private HotkeyConfig _modifiedHotkeyConfig; | |||
public HotkeySettingsForm(ShadowsocksController controller) | |||
{ | |||
InitializeComponent(); | |||
UpdateTexts(); | |||
Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||
_controller = controller; | |||
_controller.ConfigChanged += controller_ConfigChanged; | |||
LoadCurrentConfiguration(); | |||
} | |||
private void UpdateTexts() | |||
{ | |||
I18N.TranslateForm(this); | |||
} | |||
private void controller_ConfigChanged(object sender, EventArgs e) | |||
{ | |||
LoadCurrentConfiguration(); | |||
} | |||
private void LoadCurrentConfiguration() | |||
{ | |||
_modifiedHotkeyConfig = _controller.GetConfigurationCopy().hotkey; | |||
SetConfigToUI(_modifiedHotkeyConfig); | |||
} | |||
private void SetConfigToUI(HotkeyConfig config) | |||
{ | |||
SwitchSystemProxyTextBox.Text = config.SwitchSystemProxy; | |||
SwitchProxyModeTextBox.Text = config.SwitchSystemProxyMode; | |||
SwitchAllowLanTextBox.Text = config.SwitchAllowLan; | |||
ShowLogsTextBox.Text = config.ShowLogs; | |||
ServerMoveUpTextBox.Text = config.ServerMoveUp; | |||
ServerMoveDownTextBox.Text = config.ServerMoveDown; | |||
RegHotkeysAtStartupCheckBox.Checked = config.RegHotkeysAtStartup; | |||
} | |||
private void SaveConfig() | |||
{ | |||
_controller.SaveHotkeyConfig(_modifiedHotkeyConfig); | |||
} | |||
private HotkeyConfig GetConfigFromUI() | |||
{ | |||
return new HotkeyConfig | |||
{ | |||
SwitchSystemProxy = SwitchSystemProxyTextBox.Text, | |||
SwitchSystemProxyMode = SwitchProxyModeTextBox.Text, | |||
SwitchAllowLan = SwitchAllowLanTextBox.Text, | |||
ShowLogs = ShowLogsTextBox.Text, | |||
ServerMoveUp = ServerMoveUpTextBox.Text, | |||
ServerMoveDown = ServerMoveDownTextBox.Text, | |||
RegHotkeysAtStartup = RegHotkeysAtStartupCheckBox.Checked | |||
}; | |||
} | |||
/// <summary> | |||
/// Capture hotkey - Press key | |||
/// </summary> | |||
private void HotkeyDown(object sender, KeyEventArgs e) | |||
{ | |||
StringBuilder sb = new StringBuilder(); | |||
//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) | |||
{ | |||
var tb = (TextBox)sender; | |||
var content = tb.Text.TrimEnd(); | |||
if (content.Length >= 1 && content[content.Length - 1] == '+') | |||
{ | |||
tb.Text = ""; | |||
} | |||
} | |||
private void CancelButton_Click(object sender, EventArgs e) | |||
{ | |||
this.Close(); | |||
} | |||
private void OKButton_Click(object sender, EventArgs e) | |||
{ | |||
_modifiedHotkeyConfig = GetConfigFromUI(); | |||
// try to register, notify to change settings if failed | |||
if (!RegisterAllHotkeys(_modifiedHotkeyConfig)) | |||
{ | |||
MessageBox.Show(I18N.GetString("Register hotkey failed")); | |||
} | |||
// All check passed, saving | |||
SaveConfig(); | |||
this.Close(); | |||
} | |||
private void RegisterAllButton_Click(object sender, EventArgs e) | |||
{ | |||
_modifiedHotkeyConfig = GetConfigFromUI(); | |||
RegisterAllHotkeys(_modifiedHotkeyConfig); | |||
} | |||
private bool RegisterAllHotkeys(HotkeyConfig hotkeyConfig) | |||
{ | |||
return | |||
RegHotkeyFromString(hotkeyConfig.SwitchSystemProxy, "SwitchSystemProxyCallback", result => HandleRegResult(hotkeyConfig.SwitchSystemProxy, SwitchSystemProxyLabel, result)) | |||
&& RegHotkeyFromString(hotkeyConfig.SwitchSystemProxyMode, "SwitchSystemProxyModeCallback", result => HandleRegResult(hotkeyConfig.SwitchSystemProxyMode, SwitchProxyModeLabel, result)) | |||
&& RegHotkeyFromString(hotkeyConfig.SwitchAllowLan, "SwitchAllowLanCallback", result => HandleRegResult(hotkeyConfig.SwitchAllowLan, SwitchAllowLanLabel, result)) | |||
&& RegHotkeyFromString(hotkeyConfig.ShowLogs, "ShowLogsCallback", result => HandleRegResult(hotkeyConfig.ShowLogs, ShowLogsLabel, result)) | |||
&& RegHotkeyFromString(hotkeyConfig.ServerMoveUp, "ServerMoveUpCallback", result => HandleRegResult(hotkeyConfig.ServerMoveUp, ServerMoveUpLabel, result)) | |||
&& RegHotkeyFromString(hotkeyConfig.ServerMoveDown, "ServerMoveDownCallback", result => HandleRegResult(hotkeyConfig.ServerMoveDown, ServerMoveDownLabel, result)); | |||
} | |||
private void HandleRegResult(string hotkeyStr, Label label, RegResult result) | |||
{ | |||
switch (result) | |||
{ | |||
case RegResult.ParseError: | |||
MessageBox.Show(I18N.GetString("Cannot parse hotkey: {0}", hotkeyStr)); | |||
break; | |||
case RegResult.UnregSuccess: | |||
label.ResetBackColor(); | |||
break; | |||
case RegResult.RegSuccess: | |||
label.BackColor = Color.Green; | |||
break; | |||
case RegResult.RegFailure: | |||
label.BackColor = Color.Red; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
} | |||
} |
@@ -1,346 +0,0 @@ | |||
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.RegHotkeysAtStartupLabel = new System.Windows.Forms.Label(); | |||
this.SwitchSystemProxyLabel = new System.Windows.Forms.Label(); | |||
this.SwitchProxyModeLabel = 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.SwitchProxyModeTextBox = 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(); | |||
this.RegHotkeysAtStartupCheckBox = new System.Windows.Forms.CheckBox(); | |||
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, 248); | |||
flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); | |||
flowLayoutPanel1.Name = "flowLayoutPanel1"; | |||
flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 0, 20, 0); | |||
flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes; | |||
flowLayoutPanel1.Size = new System.Drawing.Size(594, 52); | |||
flowLayoutPanel1.TabIndex = 6; | |||
// | |||
// btnOK | |||
// | |||
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; | |||
this.btnOK.Location = new System.Drawing.Point(416, 9); | |||
this.btnOK.Margin = new System.Windows.Forms.Padding(4); | |||
this.btnOK.Name = "btnOK"; | |||
this.btnOK.Size = new System.Drawing.Size(154, 39); | |||
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(254, 9); | |||
this.btnCancel.Margin = new System.Windows.Forms.Padding(4); | |||
this.btnCancel.Name = "btnCancel"; | |||
this.btnCancel.Size = new System.Drawing.Size(154, 39); | |||
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(92, 9); | |||
this.btnRegisterAll.Margin = new System.Windows.Forms.Padding(4); | |||
this.btnRegisterAll.Name = "btnRegisterAll"; | |||
this.btnRegisterAll.Size = new System.Drawing.Size(154, 39); | |||
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.RegHotkeysAtStartupLabel, 0, 6); | |||
this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyLabel, 0, 0); | |||
this.tableLayoutPanel1.Controls.Add(this.SwitchProxyModeLabel, 0, 1); | |||
this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanLabel, 0, 2); | |||
this.tableLayoutPanel1.Controls.Add(this.ShowLogsLabel, 0, 3); | |||
this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpLabel, 0, 4); | |||
this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownLabel, 0, 5); | |||
this.tableLayoutPanel1.Controls.Add(flowLayoutPanel1, 0, 7); | |||
this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyTextBox, 1, 0); | |||
this.tableLayoutPanel1.Controls.Add(this.SwitchProxyModeTextBox, 1, 1); | |||
this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanTextBox, 1, 2); | |||
this.tableLayoutPanel1.Controls.Add(this.ShowLogsTextBox, 1, 3); | |||
this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpTextBox, 1, 4); | |||
this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownTextBox, 1, 5); | |||
this.tableLayoutPanel1.Controls.Add(this.RegHotkeysAtStartupCheckBox, 1, 6); | |||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; | |||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); | |||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4); | |||
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | |||
this.tableLayoutPanel1.RowCount = 8; | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16726F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16726F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16726F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16726F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.7784F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.38949F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16309F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 50F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F)); | |||
this.tableLayoutPanel1.Size = new System.Drawing.Size(582, 303); | |||
this.tableLayoutPanel1.TabIndex = 0; | |||
// | |||
// RegHotkeysAtStartupLabel | |||
// | |||
this.RegHotkeysAtStartupLabel.AutoSize = true; | |||
this.RegHotkeysAtStartupLabel.Dock = System.Windows.Forms.DockStyle.Right; | |||
this.RegHotkeysAtStartupLabel.Location = new System.Drawing.Point(40, 213); | |||
this.RegHotkeysAtStartupLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); | |||
this.RegHotkeysAtStartupLabel.Name = "RegHotkeysAtStartupLabel"; | |||
this.RegHotkeysAtStartupLabel.Size = new System.Drawing.Size(199, 35); | |||
this.RegHotkeysAtStartupLabel.TabIndex = 16; | |||
this.RegHotkeysAtStartupLabel.Text = "Reg Hotkeys At Startup"; | |||
this.RegHotkeysAtStartupLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; | |||
// | |||
// SwitchSystemProxyLabel | |||
// | |||
this.SwitchSystemProxyLabel.AutoSize = true; | |||
this.SwitchSystemProxyLabel.Dock = System.Windows.Forms.DockStyle.Right; | |||
this.SwitchSystemProxyLabel.Location = new System.Drawing.Point(63, 0); | |||
this.SwitchSystemProxyLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); | |||
this.SwitchSystemProxyLabel.Name = "SwitchSystemProxyLabel"; | |||
this.SwitchSystemProxyLabel.Size = new System.Drawing.Size(176, 35); | |||
this.SwitchSystemProxyLabel.TabIndex = 0; | |||
this.SwitchSystemProxyLabel.Text = "Switch system proxy"; | |||
this.SwitchSystemProxyLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; | |||
// | |||
// SwitchProxyModeLabel | |||
// | |||
this.SwitchProxyModeLabel.AutoSize = true; | |||
this.SwitchProxyModeLabel.Dock = System.Windows.Forms.DockStyle.Right; | |||
this.SwitchProxyModeLabel.Location = new System.Drawing.Point(10, 35); | |||
this.SwitchProxyModeLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); | |||
this.SwitchProxyModeLabel.Name = "SwitchProxyModeLabel"; | |||
this.SwitchProxyModeLabel.Size = new System.Drawing.Size(229, 35); | |||
this.SwitchProxyModeLabel.TabIndex = 1; | |||
this.SwitchProxyModeLabel.Text = "Switch system proxy mode"; | |||
this.SwitchProxyModeLabel.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(39, 70); | |||
this.SwitchAllowLanLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); | |||
this.SwitchAllowLanLabel.Name = "SwitchAllowLanLabel"; | |||
this.SwitchAllowLanLabel.Size = new System.Drawing.Size(200, 35); | |||
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(129, 105); | |||
this.ShowLogsLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); | |||
this.ShowLogsLabel.Name = "ShowLogsLabel"; | |||
this.ShowLogsLabel.Size = new System.Drawing.Size(110, 35); | |||
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(25, 140); | |||
this.ServerMoveUpLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); | |||
this.ServerMoveUpLabel.Name = "ServerMoveUpLabel"; | |||
this.ServerMoveUpLabel.Size = new System.Drawing.Size(214, 37); | |||
this.ServerMoveUpLabel.TabIndex = 4; | |||
this.ServerMoveUpLabel.Text = "Switch to previous server"; | |||
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(60, 177); | |||
this.ServerMoveDownLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); | |||
this.ServerMoveDownLabel.Name = "ServerMoveDownLabel"; | |||
this.ServerMoveDownLabel.Size = new System.Drawing.Size(179, 36); | |||
this.ServerMoveDownLabel.TabIndex = 4; | |||
this.ServerMoveDownLabel.Text = "Switch to next server"; | |||
this.ServerMoveDownLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; | |||
// | |||
// SwitchSystemProxyTextBox | |||
// | |||
this.SwitchSystemProxyTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||
this.SwitchSystemProxyTextBox.Location = new System.Drawing.Point(252, 3); | |||
this.SwitchSystemProxyTextBox.Name = "SwitchSystemProxyTextBox"; | |||
this.SwitchSystemProxyTextBox.ReadOnly = true; | |||
this.SwitchSystemProxyTextBox.Size = new System.Drawing.Size(339, 29); | |||
this.SwitchSystemProxyTextBox.TabIndex = 7; | |||
this.SwitchSystemProxyTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); | |||
this.SwitchSystemProxyTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); | |||
// | |||
// SwitchProxyModeTextBox | |||
// | |||
this.SwitchProxyModeTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||
this.SwitchProxyModeTextBox.Location = new System.Drawing.Point(252, 38); | |||
this.SwitchProxyModeTextBox.Name = "SwitchProxyModeTextBox"; | |||
this.SwitchProxyModeTextBox.ReadOnly = true; | |||
this.SwitchProxyModeTextBox.Size = new System.Drawing.Size(339, 29); | |||
this.SwitchProxyModeTextBox.TabIndex = 8; | |||
this.SwitchProxyModeTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); | |||
this.SwitchProxyModeTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); | |||
// | |||
// SwitchAllowLanTextBox | |||
// | |||
this.SwitchAllowLanTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||
this.SwitchAllowLanTextBox.Location = new System.Drawing.Point(252, 73); | |||
this.SwitchAllowLanTextBox.Name = "SwitchAllowLanTextBox"; | |||
this.SwitchAllowLanTextBox.ReadOnly = true; | |||
this.SwitchAllowLanTextBox.Size = new System.Drawing.Size(339, 29); | |||
this.SwitchAllowLanTextBox.TabIndex = 10; | |||
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(252, 108); | |||
this.ShowLogsTextBox.Name = "ShowLogsTextBox"; | |||
this.ShowLogsTextBox.ReadOnly = true; | |||
this.ShowLogsTextBox.Size = new System.Drawing.Size(339, 29); | |||
this.ShowLogsTextBox.TabIndex = 11; | |||
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(252, 143); | |||
this.ServerMoveUpTextBox.Name = "ServerMoveUpTextBox"; | |||
this.ServerMoveUpTextBox.ReadOnly = true; | |||
this.ServerMoveUpTextBox.Size = new System.Drawing.Size(339, 29); | |||
this.ServerMoveUpTextBox.TabIndex = 12; | |||
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(252, 180); | |||
this.ServerMoveDownTextBox.Name = "ServerMoveDownTextBox"; | |||
this.ServerMoveDownTextBox.ReadOnly = true; | |||
this.ServerMoveDownTextBox.Size = new System.Drawing.Size(339, 29); | |||
this.ServerMoveDownTextBox.TabIndex = 13; | |||
this.ServerMoveDownTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); | |||
this.ServerMoveDownTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); | |||
// | |||
// RegHotkeysAtStartupCheckBox | |||
// | |||
this.RegHotkeysAtStartupCheckBox.AutoSize = true; | |||
this.RegHotkeysAtStartupCheckBox.Dock = System.Windows.Forms.DockStyle.Left; | |||
this.RegHotkeysAtStartupCheckBox.Location = new System.Drawing.Point(252, 216); | |||
this.RegHotkeysAtStartupCheckBox.Name = "RegHotkeysAtStartupCheckBox"; | |||
this.RegHotkeysAtStartupCheckBox.Size = new System.Drawing.Size(18, 29); | |||
this.RegHotkeysAtStartupCheckBox.TabIndex = 17; | |||
this.RegHotkeysAtStartupCheckBox.UseVisualStyleBackColor = true; | |||
// | |||
// HotkeySettingsForm | |||
// | |||
this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); | |||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; | |||
this.ClientSize = new System.Drawing.Size(582, 303); | |||
this.Controls.Add(this.tableLayoutPanel1); | |||
this.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); | |||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | |||
this.Margin = new System.Windows.Forms.Padding(5, 6, 5, 6); | |||
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 SwitchProxyModeLabel; | |||
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 SwitchProxyModeTextBox; | |||
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; | |||
private System.Windows.Forms.Label RegHotkeysAtStartupLabel; | |||
private System.Windows.Forms.CheckBox RegHotkeysAtStartupCheckBox; | |||
} | |||
} |
@@ -1,183 +0,0 @@ | |||
<?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="RegHotkeysAtStartupLabel.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="SwitchProxyModeLabel.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="SwitchProxyModeTextBox.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="RegHotkeysAtStartupCheckBox.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> |
@@ -62,7 +62,7 @@ namespace Shadowsocks.View | |||
LogMessageTextBox.AppendText("Cannot get the log file name from NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); | |||
} | |||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||
LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; | |||
topMostTrigger = config.topMost; | |||
wrapTextTrigger = config.wrapText; | |||
@@ -244,7 +244,7 @@ namespace Shadowsocks.View | |||
timer.Tick += Timer_Tick; | |||
timer.Start(); | |||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||
LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; | |||
Height = config.Height; | |||
Width = config.Width; | |||
@@ -270,7 +270,7 @@ namespace Shadowsocks.View | |||
{ | |||
timer.Stop(); | |||
controller.TrafficChanged -= controller_TrafficChanged; | |||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||
LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; | |||
config.topMost = topMostTrigger; | |||
config.wrapText = wrapTextTrigger; | |||
@@ -1,9 +1,11 @@ | |||
using NLog; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Localization; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
using Shadowsocks.Util; | |||
using Shadowsocks.Util.SystemProxy; | |||
using Shadowsocks.Views; | |||
using System; | |||
using System.Diagnostics; | |||
using System.Drawing; | |||
@@ -11,7 +13,8 @@ using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Windows.Forms; | |||
using System.Windows.Media.Imaging; | |||
using System.Windows.Forms.Integration; | |||
using System.Windows.Threading; | |||
using ZXing; | |||
using ZXing.Common; | |||
using ZXing.QrCode; | |||
@@ -20,19 +23,15 @@ namespace Shadowsocks.View | |||
{ | |||
public class MenuViewController | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
// yes this is just a menu view controller | |||
// when config form is closed, it moves away from RAM | |||
// and it should just do anything related to the config form | |||
private readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||
private ShadowsocksController controller; | |||
private UpdateChecker updateChecker; | |||
public UpdateChecker updateChecker; | |||
private NotifyIcon _notifyIcon; | |||
private Icon icon, icon_in, icon_out, icon_both, previousIcon; | |||
private bool _isFirstRun; | |||
private bool _isStartupChecking; | |||
private bool _isStartupCheck; | |||
private string _urlToOpen; | |||
private ContextMenuStrip contextMenu1; | |||
@@ -52,6 +51,7 @@ namespace Shadowsocks.View | |||
private ToolStripMenuItem editGFWUserRuleItem; | |||
private ToolStripMenuItem editOnlinePACItem; | |||
private ToolStripMenuItem secureLocalPacUrlToggleItem; | |||
private ToolStripMenuItem regenerateLocalPacOnUpdateItem; | |||
private ToolStripMenuItem autoCheckUpdatesToggleItem; | |||
private ToolStripMenuItem checkPreReleaseToggleItem; | |||
private ToolStripMenuItem proxyItem; | |||
@@ -62,12 +62,12 @@ namespace Shadowsocks.View | |||
private ToolStripMenuItem onlineConfigItem; | |||
private ConfigForm configForm; | |||
private ProxyForm proxyForm; | |||
private LogForm logForm; | |||
private HotkeySettingsForm hotkeySettingsForm; | |||
private OnlineConfigForm onlineConfigForm; | |||
private System.Windows.Window serverSharingWindow; | |||
private System.Windows.Window hotkeysWindow; | |||
private System.Windows.Window forwardProxyWindow; | |||
private System.Windows.Window onlineConfigWindow; | |||
// color definition for icon color transformation | |||
private readonly Color colorMaskBlue = Color.FromArgb(255, 25, 125, 191); | |||
@@ -103,22 +103,21 @@ namespace Shadowsocks.View | |||
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; | |||
controller.TrafficChanged += controller_TrafficChanged; | |||
this.updateChecker = new UpdateChecker(); | |||
updateChecker = new UpdateChecker(); | |||
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; | |||
LoadCurrentConfiguration(); | |||
Configuration config = controller.GetConfigurationCopy(); | |||
Configuration config = controller.GetCurrentConfiguration(); | |||
if (config.isDefault) | |||
if (config.firstRun) | |||
{ | |||
_isFirstRun = true; | |||
ShowConfigForm(); | |||
} | |||
else if (config.autoCheckUpdate) | |||
{ | |||
_isStartupChecking = true; | |||
updateChecker.CheckUpdate(config, 3000); | |||
_isStartupCheck = true; | |||
Dispatcher.CurrentDispatcher.Invoke(() => updateChecker.CheckForVersionUpdate(3000)); | |||
} | |||
} | |||
@@ -126,7 +125,7 @@ namespace Shadowsocks.View | |||
private void UpdateTrayIconAndNotifyText() | |||
{ | |||
Configuration config = controller.GetConfigurationCopy(); | |||
Configuration config = controller.GetCurrentConfiguration(); | |||
bool enabled = config.enabled; | |||
bool global = config.global; | |||
@@ -242,8 +241,6 @@ namespace Shadowsocks.View | |||
icon_both = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In, Resources.ss32Out), size.Width, size.Height).GetHicon()); | |||
} | |||
#endregion | |||
#region ToolStripMenuItems and MenuGroups | |||
@@ -270,7 +267,6 @@ namespace Shadowsocks.View | |||
this.ServersItem = CreateMenuGroup("Servers", new ToolStripItem [] { | |||
this.SeperatorItem = new ToolStripSeparator(), | |||
this.ConfigItem = CreateToolStripMenuItem("Edit Servers...", new EventHandler(this.Config_Click)), | |||
CreateToolStripMenuItem("Statistics Config...", StatisticsConfigItem_Click), | |||
new ToolStripSeparator(), | |||
CreateToolStripMenuItem("Share Server Config...", new EventHandler(this.QRCodeItem_Click)), | |||
CreateToolStripMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)), | |||
@@ -284,6 +280,7 @@ namespace Shadowsocks.View | |||
this.updateFromGeositeItem = CreateToolStripMenuItem("Update Local PAC from Geosite", new EventHandler(this.UpdatePACFromGeositeItem_Click)), | |||
this.editGFWUserRuleItem = CreateToolStripMenuItem("Edit User Rule for Geosite...", new EventHandler(this.EditUserRuleFileForGeositeItem_Click)), | |||
this.secureLocalPacUrlToggleItem = CreateToolStripMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)), | |||
this.regenerateLocalPacOnUpdateItem = CreateToolStripMenuItem("Regenerate local PAC on version update", new EventHandler(this.RegenerateLocalPacOnUpdateItem_Click)), | |||
CreateToolStripMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)), | |||
this.editOnlinePACItem = CreateToolStripMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)), | |||
}), | |||
@@ -341,7 +338,7 @@ namespace Shadowsocks.View | |||
} | |||
} | |||
void controller_Errored(object sender, System.IO.ErrorEventArgs e) | |||
void controller_Errored(object sender, ErrorEventArgs e) | |||
{ | |||
MessageBox.Show(e.GetException().ToString(), I18N.GetString("Shadowsocks Error: {0}", e.GetException().Message)); | |||
} | |||
@@ -354,7 +351,7 @@ namespace Shadowsocks.View | |||
private void LoadCurrentConfiguration() | |||
{ | |||
Configuration config = controller.GetConfigurationCopy(); | |||
Configuration config = controller.GetCurrentConfiguration(); | |||
UpdateServersMenu(); | |||
UpdateSystemProxyItemsEnabledStatus(config); | |||
ShareOverLANItem.Checked = config.shareOverLan; | |||
@@ -365,6 +362,7 @@ namespace Shadowsocks.View | |||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | |||
localPACItem.Checked = !onlinePACItem.Checked; | |||
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | |||
regenerateLocalPacOnUpdateItem.Checked = config.regeneratePacOnUpdate; | |||
UpdatePACItemsEnabledStatus(); | |||
UpdateUpdateMenu(); | |||
} | |||
@@ -386,36 +384,6 @@ namespace Shadowsocks.View | |||
} | |||
} | |||
private void ShowProxyForm() | |||
{ | |||
if (proxyForm != null) | |||
{ | |||
proxyForm.Activate(); | |||
} | |||
else | |||
{ | |||
proxyForm = new ProxyForm(controller); | |||
proxyForm.Show(); | |||
proxyForm.Activate(); | |||
proxyForm.FormClosed += proxyForm_FormClosed; | |||
} | |||
} | |||
private void ShowHotKeySettingsForm() | |||
{ | |||
if (hotkeySettingsForm != null) | |||
{ | |||
hotkeySettingsForm.Activate(); | |||
} | |||
else | |||
{ | |||
hotkeySettingsForm = new HotkeySettingsForm(controller); | |||
hotkeySettingsForm.Show(); | |||
hotkeySettingsForm.Activate(); | |||
hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed; | |||
} | |||
} | |||
private void ShowLogForm() | |||
{ | |||
if (logForm != null) | |||
@@ -431,22 +399,6 @@ namespace Shadowsocks.View | |||
} | |||
} | |||
private void ShowOnlineConfigForm() | |||
{ | |||
if (onlineConfigForm != null) | |||
{ | |||
onlineConfigForm.Activate(); | |||
} | |||
else | |||
{ | |||
onlineConfigForm = new OnlineConfigForm(controller); | |||
onlineConfigForm.Show(); | |||
onlineConfigForm.Activate(); | |||
onlineConfigForm.FormClosed += onlineConfigForm_FormClosed; | |||
} | |||
} | |||
void logForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
logForm.Dispose(); | |||
@@ -457,7 +409,8 @@ namespace Shadowsocks.View | |||
{ | |||
configForm.Dispose(); | |||
configForm = null; | |||
if (_isFirstRun) | |||
var config = controller.GetCurrentConfiguration(); | |||
if (config.firstRun) | |||
{ | |||
CheckUpdateForFirstRun(); | |||
ShowBalloonTip( | |||
@@ -466,28 +419,10 @@ namespace Shadowsocks.View | |||
ToolTipIcon.Info, | |||
0 | |||
); | |||
_isFirstRun = false; | |||
config.firstRun = false; | |||
} | |||
} | |||
void proxyForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
proxyForm.Dispose(); | |||
proxyForm = null; | |||
} | |||
void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
hotkeySettingsForm.Dispose(); | |||
hotkeySettingsForm = null; | |||
} | |||
void onlineConfigForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
onlineConfigForm.Dispose(); | |||
onlineConfigForm = null; | |||
} | |||
#endregion | |||
#region Misc | |||
@@ -502,23 +437,10 @@ namespace Shadowsocks.View | |||
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) | |||
{ | |||
if (updateChecker.NewVersionFound) | |||
{ | |||
updateChecker.NewVersionFound = false; /* Reset the flag */ | |||
if (File.Exists(updateChecker.LatestVersionLocalName)) | |||
{ | |||
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\""; | |||
Process.Start("explorer.exe", argument); | |||
} | |||
} | |||
} | |||
private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e) | |||
{ | |||
if (updateChecker.NewVersionFound) | |||
{ | |||
updateChecker.NewVersionFound = false; /* Reset the flag */ | |||
} | |||
} | |||
private void notifyIcon1_Click(object sender, MouseEventArgs e) | |||
@@ -538,12 +460,13 @@ namespace Shadowsocks.View | |||
} | |||
} | |||
private void CheckUpdateForFirstRun() | |||
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) | |||
{ | |||
Configuration config = controller.GetConfigurationCopy(); | |||
if (config.isDefault) return; | |||
_isStartupChecking = true; | |||
updateChecker.CheckUpdate(config, 3000); | |||
Configuration config = controller.GetCurrentConfiguration(); | |||
if (config.firstRun) | |||
return; | |||
_isStartupCheck = true; | |||
Dispatcher.CurrentDispatcher.Invoke(() => updateChecker.CheckForVersionUpdate(3000)); | |||
} | |||
public void ShowLogForm_HotKey() | |||
@@ -555,6 +478,144 @@ namespace Shadowsocks.View | |||
#region Main menu | |||
void controller_ShareOverLANStatusChanged(object sender, EventArgs e) | |||
{ | |||
ShareOverLANItem.Checked = controller.GetCurrentConfiguration().shareOverLan; | |||
} | |||
private void proxyItem_Click(object sender, EventArgs e) | |||
{ | |||
if (forwardProxyWindow == null) | |||
{ | |||
forwardProxyWindow = new System.Windows.Window() | |||
{ | |||
Title = LocalizationProvider.GetLocalizedValue<string>("ForwardProxy"), | |||
Height = 400, | |||
Width = 280, | |||
MinHeight = 400, | |||
MinWidth = 280, | |||
Content = new ForwardProxyView() | |||
}; | |||
forwardProxyWindow.Closed += ForwardProxyWindow_Closed; | |||
ElementHost.EnableModelessKeyboardInterop(forwardProxyWindow); | |||
forwardProxyWindow.Show(); | |||
} | |||
forwardProxyWindow.Activate(); | |||
} | |||
private void ForwardProxyWindow_Closed(object sender, EventArgs e) | |||
{ | |||
forwardProxyWindow = null; | |||
} | |||
public void CloseForwardProxyWindow() => forwardProxyWindow.Close(); | |||
private void OnlineConfig_Click(object sender, EventArgs e) | |||
{ | |||
if (onlineConfigWindow == null) | |||
{ | |||
onlineConfigWindow = new System.Windows.Window() | |||
{ | |||
Title = LocalizationProvider.GetLocalizedValue<string>("OnlineConfigDelivery"), | |||
Height = 510, | |||
Width = 480, | |||
MinHeight = 510, | |||
MinWidth = 480, | |||
Content = new OnlineConfigView() | |||
}; | |||
onlineConfigWindow.Closed += OnlineConfigWindow_Closed; | |||
ElementHost.EnableModelessKeyboardInterop(onlineConfigWindow); | |||
onlineConfigWindow.Show(); | |||
} | |||
onlineConfigWindow.Activate(); | |||
} | |||
private void OnlineConfigWindow_Closed(object sender, EventArgs e) | |||
{ | |||
onlineConfigWindow = null; | |||
} | |||
private void hotKeyItem_Click(object sender, EventArgs e) | |||
{ | |||
if (hotkeysWindow == null) | |||
{ | |||
hotkeysWindow = new System.Windows.Window() | |||
{ | |||
Title = LocalizationProvider.GetLocalizedValue<string>("Hotkeys"), | |||
Height = 260, | |||
Width = 320, | |||
MinHeight = 260, | |||
MinWidth = 320, | |||
Content = new HotkeysView() | |||
}; | |||
hotkeysWindow.Closed += HotkeysWindow_Closed; | |||
ElementHost.EnableModelessKeyboardInterop(hotkeysWindow); | |||
hotkeysWindow.Show(); | |||
} | |||
hotkeysWindow.Activate(); | |||
} | |||
private void HotkeysWindow_Closed(object sender, EventArgs e) | |||
{ | |||
hotkeysWindow = null; | |||
} | |||
public void CloseHotkeysWindow() => hotkeysWindow.Close(); | |||
private void ShareOverLANItem_Click(object sender, EventArgs e) | |||
{ | |||
ShareOverLANItem.Checked = !ShareOverLANItem.Checked; | |||
controller.ToggleShareOverLAN(ShareOverLANItem.Checked); | |||
} | |||
private void AutoStartupItem_Click(object sender, EventArgs e) | |||
{ | |||
AutoStartupItem.Checked = !AutoStartupItem.Checked; | |||
if (!AutoStartup.Set(AutoStartupItem.Checked)) | |||
{ | |||
MessageBox.Show(I18N.GetString("Failed to update registry")); | |||
} | |||
LoadCurrentConfiguration(); | |||
} | |||
private void ProtocolHandlerItem_Click(object sender, EventArgs e) | |||
{ | |||
ProtocolHandlerItem.Checked = !ProtocolHandlerItem.Checked; | |||
if (!ProtocolHandler.Set(ProtocolHandlerItem.Checked)) | |||
{ | |||
MessageBox.Show(I18N.GetString("Failed to update registry")); | |||
} | |||
LoadCurrentConfiguration(); | |||
} | |||
private void Quit_Click(object sender, EventArgs e) | |||
{ | |||
controller.Stop(); | |||
_notifyIcon.Visible = false; | |||
Application.Exit(); | |||
} | |||
#endregion | |||
#region System proxy | |||
private void controller_EnableStatusChanged(object sender, EventArgs e) | |||
{ | |||
disableItem.Checked = !controller.GetCurrentConfiguration().enabled; | |||
} | |||
private void CheckUpdateForFirstRun() | |||
{ | |||
Configuration config = controller.GetConfigurationCopy(); | |||
if (config.isDefault) return; | |||
_isStartupChecking = true; | |||
updateChecker.CheckUpdate(config, 3000); | |||
} | |||
#endregion | |||
#region Main menu | |||
void controller_ShareOverLANStatusChanged(object sender, EventArgs e) | |||
{ | |||
ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; | |||
@@ -620,13 +681,13 @@ namespace Shadowsocks.View | |||
private void EnableItem_Click(object sender, EventArgs e) | |||
{ | |||
controller.ToggleEnable(false); | |||
Configuration config = controller.GetConfigurationCopy(); | |||
Configuration config = controller.GetCurrentConfiguration(); | |||
UpdateSystemProxyItemsEnabledStatus(config); | |||
} | |||
void controller_EnableGlobalChanged(object sender, EventArgs e) | |||
{ | |||
globalModeItem.Checked = controller.GetConfigurationCopy().global; | |||
globalModeItem.Checked = controller.GetCurrentConfiguration().global; | |||
PACModeItem.Checked = !globalModeItem.Checked; | |||
} | |||
@@ -652,7 +713,7 @@ namespace Shadowsocks.View | |||
{ | |||
controller.ToggleEnable(true); | |||
controller.ToggleGlobal(true); | |||
Configuration config = controller.GetConfigurationCopy(); | |||
Configuration config = controller.GetCurrentConfiguration(); | |||
UpdateSystemProxyItemsEnabledStatus(config); | |||
} | |||
@@ -660,7 +721,7 @@ namespace Shadowsocks.View | |||
{ | |||
controller.ToggleEnable(true); | |||
controller.ToggleGlobal(false); | |||
Configuration config = controller.GetConfigurationCopy(); | |||
Configuration config = controller.GetCurrentConfiguration(); | |||
UpdateSystemProxyItemsEnabledStatus(config); | |||
} | |||
@@ -693,7 +754,7 @@ namespace Shadowsocks.View | |||
items.Add(new ToolStripSeparator() { Tag = "-server-" }); | |||
} | |||
int serverCount = 0; | |||
Configuration configuration = controller.GetConfigurationCopy(); | |||
Configuration configuration = controller.GetCurrentConfiguration(); | |||
foreach (var server in configuration.configs) | |||
{ | |||
if (Configuration.ChecksServer(server)) | |||
@@ -746,112 +807,58 @@ namespace Shadowsocks.View | |||
ShowConfigForm(); | |||
} | |||
void splash_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
ShowConfigForm(); | |||
} | |||
void openURLFromQRCode(object sender, FormClosedEventArgs e) | |||
{ | |||
Utils.OpenInBrowser(_urlToOpen); | |||
} | |||
private void StatisticsConfigItem_Click(object sender, EventArgs e) | |||
private void QRCodeItem_Click(object sender, EventArgs e) | |||
{ | |||
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller); | |||
form.Show(); | |||
if (serverSharingWindow == null) | |||
{ | |||
serverSharingWindow = new System.Windows.Window() | |||
{ | |||
Title = LocalizationProvider.GetLocalizedValue<string>("ServerSharing"), | |||
Height = 400, | |||
Width = 660, | |||
MinHeight = 400, | |||
MinWidth = 660, | |||
Content = new ServerSharingView() | |||
}; | |||
serverSharingWindow.Closed += ServerSharingWindow_Closed; | |||
ElementHost.EnableModelessKeyboardInterop(serverSharingWindow); | |||
serverSharingWindow.Show(); | |||
} | |||
serverSharingWindow.Activate(); | |||
} | |||
private void QRCodeItem_Click(object sender, EventArgs e) | |||
private void ServerSharingWindow_Closed(object sender, EventArgs e) | |||
{ | |||
QRCodeForm qrCodeForm = new QRCodeForm(controller.GetServerURLForCurrentServer()); | |||
//qrCodeForm.Icon = this.Icon; | |||
// TODO | |||
qrCodeForm.Show(); | |||
serverSharingWindow = null; | |||
} | |||
private void ScanQRCodeItem_Click(object sender, EventArgs e) | |||
{ | |||
foreach (Screen screen in Screen.AllScreens) | |||
var result = Utils.ScanQRCodeFromScreen(); | |||
if (result != null) | |||
{ | |||
using (Bitmap fullImage = new Bitmap(screen.Bounds.Width, | |||
screen.Bounds.Height)) | |||
if (result.ToLowerInvariant().StartsWith("http://") || result.ToLowerInvariant().StartsWith("https://")) | |||
{ | |||
using (Graphics g = Graphics.FromImage(fullImage)) | |||
{ | |||
g.CopyFromScreen(screen.Bounds.X, | |||
screen.Bounds.Y, | |||
0, 0, | |||
fullImage.Size, | |||
CopyPixelOperation.SourceCopy); | |||
} | |||
int maxTry = 10; | |||
for (int i = 0; i < maxTry; i++) | |||
{ | |||
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry); | |||
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry); | |||
Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2); | |||
Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height); | |||
double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width; | |||
using (Graphics g = Graphics.FromImage(target)) | |||
{ | |||
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height), | |||
cropRect, | |||
GraphicsUnit.Pixel); | |||
} | |||
var source = new BitmapSourceLuminanceSource(null); | |||
var bitmap = new BinaryBitmap(new HybridBinarizer(source)); | |||
QRCodeReader reader = new QRCodeReader(); | |||
var result = reader.decode(bitmap); | |||
if (result != null) | |||
{ | |||
var success = controller.AddServerBySSURL(result.Text); | |||
QRCodeSplashForm splash = new QRCodeSplashForm(); | |||
if (success) | |||
{ | |||
splash.FormClosed += splash_FormClosed; | |||
} | |||
else if (result.Text.ToLower().StartsWith("http://") || result.Text.ToLower().StartsWith("https://")) | |||
{ | |||
_urlToOpen = result.Text; | |||
splash.FormClosed += openURLFromQRCode; | |||
} | |||
else | |||
{ | |||
MessageBox.Show(I18N.GetString("Failed to decode QRCode")); | |||
return; | |||
} | |||
double minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0; | |||
foreach (ResultPoint point in result.ResultPoints) | |||
{ | |||
minX = Math.Min(minX, point.X); | |||
minY = Math.Min(minY, point.Y); | |||
maxX = Math.Max(maxX, point.X); | |||
maxY = Math.Max(maxY, point.Y); | |||
} | |||
minX /= imageScale; | |||
minY /= imageScale; | |||
maxX /= imageScale; | |||
maxY /= imageScale; | |||
// make it 20% larger | |||
double margin = (maxX - minX) * 0.20f; | |||
minX += -margin + marginLeft; | |||
maxX += margin + marginLeft; | |||
minY += -margin + marginTop; | |||
maxY += margin + marginTop; | |||
splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y); | |||
// we need a panel because a window has a minimal size | |||
// TODO: test on high DPI | |||
splash.TargetRect = new Rectangle((int)minX, (int)minY, (int)maxX - (int)minX, (int)maxY - (int)minY); | |||
splash.Size = new Size(fullImage.Width, fullImage.Height); | |||
splash.Show(); | |||
return; | |||
} | |||
} | |||
_urlToOpen = result; | |||
openURLFromQRCode(); | |||
} | |||
else if (controller.AddServerBySSURL(result)) | |||
{ | |||
ShowConfigForm(); | |||
} | |||
else | |||
{ | |||
MessageBox.Show(I18N.GetString("Invalid QR Code content: {0}", result)); | |||
} | |||
return; | |||
} | |||
MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen.")); | |||
else | |||
MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen.")); | |||
} | |||
private void ImportURLItem_Click(object sender, EventArgs e) | |||
@@ -881,11 +888,11 @@ namespace Shadowsocks.View | |||
{ | |||
if (!onlinePACItem.Checked) | |||
{ | |||
if (controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty()) | |||
if (string.IsNullOrEmpty(controller.GetCurrentConfiguration().pacUrl)) | |||
{ | |||
UpdateOnlinePACURLItem_Click(sender, e); | |||
} | |||
if (!controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty()) | |||
if (!string.IsNullOrEmpty(controller.GetCurrentConfiguration().pacUrl)) | |||
{ | |||
localPACItem.Checked = false; | |||
onlinePACItem.Checked = true; | |||
@@ -897,12 +904,12 @@ namespace Shadowsocks.View | |||
private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) | |||
{ | |||
string origPacUrl = controller.GetConfigurationCopy().pacUrl; | |||
string origPacUrl = controller.GetCurrentConfiguration().pacUrl; | |||
string pacUrl = ViewUtils.InputBox( | |||
I18N.GetString("Please input PAC Url"), | |||
I18N.GetString("Edit Online PAC URL"), | |||
origPacUrl, -1, -1); | |||
if (!pacUrl.IsNullOrEmpty() && pacUrl != origPacUrl) | |||
if (!string.IsNullOrEmpty(pacUrl) && pacUrl != origPacUrl) | |||
{ | |||
controller.SavePACUrl(pacUrl); | |||
} | |||
@@ -910,10 +917,16 @@ namespace Shadowsocks.View | |||
private void SecureLocalPacUrlToggleItem_Click(object sender, EventArgs e) | |||
{ | |||
Configuration configuration = controller.GetConfigurationCopy(); | |||
Configuration configuration = controller.GetCurrentConfiguration(); | |||
controller.ToggleSecureLocalPac(!configuration.secureLocalPac); | |||
} | |||
private void RegenerateLocalPacOnUpdateItem_Click(object sender, EventArgs e) | |||
{ | |||
var config = controller.GetCurrentConfiguration(); | |||
controller.ToggleRegeneratePacOnUpdate(!config.regeneratePacOnUpdate); | |||
} | |||
private void CopyLocalPacUrlItem_Click(object sender, EventArgs e) | |||
{ | |||
controller.CopyPacUrl(); | |||
@@ -937,7 +950,6 @@ namespace Shadowsocks.View | |||
} | |||
} | |||
private void EditPACFileItem_Click(object sender, EventArgs e) | |||
{ | |||
controller.TouchPACFile(); | |||
@@ -980,12 +992,12 @@ namespace Shadowsocks.View | |||
void controller_VerboseLoggingStatusChanged(object sender, EventArgs e) | |||
{ | |||
VerboseLoggingToggleItem.Checked = controller.GetConfigurationCopy().isVerboseLogging; | |||
VerboseLoggingToggleItem.Checked = controller.GetCurrentConfiguration().isVerboseLogging; | |||
} | |||
void controller_ShowPluginOutputChanged(object sender, EventArgs e) | |||
{ | |||
ShowPluginOutputToggleItem.Checked = controller.GetConfigurationCopy().showPluginOutput; | |||
ShowPluginOutputToggleItem.Checked = controller.GetCurrentConfiguration().showPluginOutput; | |||
} | |||
private void VerboseLoggingToggleItem_Click(object sender, EventArgs e) | |||
@@ -1016,41 +1028,37 @@ namespace Shadowsocks.View | |||
void updateChecker_CheckUpdateCompleted(object sender, EventArgs e) | |||
{ | |||
if (updateChecker.NewVersionFound) | |||
{ | |||
ShowBalloonTip(I18N.GetString("Shadowsocks {0} Update Found", updateChecker.LatestVersionNumber + updateChecker.LatestVersionSuffix), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000); | |||
} | |||
else if (!_isStartupChecking) | |||
if (!_isStartupCheck && updateChecker.NewReleaseZipFilename == null) | |||
{ | |||
ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000); | |||
} | |||
_isStartupChecking = false; | |||
_isStartupCheck = false; | |||
} | |||
private void UpdateUpdateMenu() | |||
{ | |||
Configuration configuration = controller.GetConfigurationCopy(); | |||
Configuration configuration = controller.GetCurrentConfiguration(); | |||
autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate; | |||
checkPreReleaseToggleItem.Checked = configuration.checkPreRelease; | |||
} | |||
private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e) | |||
{ | |||
Configuration configuration = controller.GetConfigurationCopy(); | |||
Configuration configuration = controller.GetCurrentConfiguration(); | |||
controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate); | |||
UpdateUpdateMenu(); | |||
} | |||
private void checkPreReleaseToggleItem_Click(object sender, EventArgs e) | |||
{ | |||
Configuration configuration = controller.GetConfigurationCopy(); | |||
Configuration configuration = controller.GetCurrentConfiguration(); | |||
controller.ToggleCheckingPreRelease(!configuration.checkPreRelease); | |||
UpdateUpdateMenu(); | |||
} | |||
private void checkUpdatesItem_Click(object sender, EventArgs e) | |||
private async void checkUpdatesItem_Click(object sender, EventArgs e) | |||
{ | |||
updateChecker.CheckUpdate(controller.GetConfigurationCopy()); | |||
await updateChecker.CheckForVersionUpdate(); | |||
} | |||
private void AboutItem_Click(object sender, EventArgs e) | |||
@@ -1,333 +0,0 @@ | |||
namespace Shadowsocks.View | |||
{ | |||
partial class ProxyForm | |||
{ | |||
/// <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() | |||
{ | |||
this.MyCancelButton = new System.Windows.Forms.Button(); | |||
this.OKButton = new System.Windows.Forms.Button(); | |||
this.UseProxyCheckBox = new System.Windows.Forms.CheckBox(); | |||
this.ProxyAddrLabel = new System.Windows.Forms.Label(); | |||
this.ProxyServerTextBox = new System.Windows.Forms.TextBox(); | |||
this.ProxyPortLabel = new System.Windows.Forms.Label(); | |||
this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); | |||
this.ProxyTypeLabel = new System.Windows.Forms.Label(); | |||
this.ProxyTypeComboBox = new System.Windows.Forms.ComboBox(); | |||
this.ProxyTimeoutTextBox = new System.Windows.Forms.TextBox(); | |||
this.ProxyTimeoutLabel = new System.Windows.Forms.Label(); | |||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | |||
this.ProxyNotificationLabel = new System.Windows.Forms.Label(); | |||
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); | |||
this.AuthUserLabel = new System.Windows.Forms.Label(); | |||
this.AuthPwdLabel = new System.Windows.Forms.Label(); | |||
this.UseAuthCheckBox = new System.Windows.Forms.CheckBox(); | |||
this.AuthUserTextBox = new System.Windows.Forms.TextBox(); | |||
this.AuthPwdTextBox = new System.Windows.Forms.TextBox(); | |||
this.tableLayoutPanel1.SuspendLayout(); | |||
this.flowLayoutPanel1.SuspendLayout(); | |||
this.SuspendLayout(); | |||
// | |||
// MyCancelButton | |||
// | |||
this.MyCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; | |||
this.MyCancelButton.Location = new System.Drawing.Point(84, 3); | |||
this.MyCancelButton.Margin = new System.Windows.Forms.Padding(3, 3, 0, 0); | |||
this.MyCancelButton.Name = "MyCancelButton"; | |||
this.MyCancelButton.Size = new System.Drawing.Size(75, 23); | |||
this.MyCancelButton.TabIndex = 13; | |||
this.MyCancelButton.Text = "Cancel"; | |||
this.MyCancelButton.UseVisualStyleBackColor = true; | |||
this.MyCancelButton.Click += new System.EventHandler(this.CancelButton_Click); | |||
// | |||
// OKButton | |||
// | |||
this.OKButton.DialogResult = System.Windows.Forms.DialogResult.OK; | |||
this.OKButton.Location = new System.Drawing.Point(3, 3); | |||
this.OKButton.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); | |||
this.OKButton.Name = "OKButton"; | |||
this.OKButton.Size = new System.Drawing.Size(75, 23); | |||
this.OKButton.TabIndex = 12; | |||
this.OKButton.Text = "OK"; | |||
this.OKButton.UseVisualStyleBackColor = true; | |||
this.OKButton.Click += new System.EventHandler(this.OKButton_Click); | |||
// | |||
// UseProxyCheckBox | |||
// | |||
this.UseProxyCheckBox.Anchor = System.Windows.Forms.AnchorStyles.Left; | |||
this.UseProxyCheckBox.AutoSize = true; | |||
this.tableLayoutPanel1.SetColumnSpan(this.UseProxyCheckBox, 2); | |||
this.UseProxyCheckBox.Location = new System.Drawing.Point(3, 6); | |||
this.UseProxyCheckBox.Name = "UseProxyCheckBox"; | |||
this.UseProxyCheckBox.Size = new System.Drawing.Size(78, 16); | |||
this.UseProxyCheckBox.TabIndex = 0; | |||
this.UseProxyCheckBox.Text = "Use Proxy"; | |||
this.UseProxyCheckBox.UseVisualStyleBackColor = true; | |||
this.UseProxyCheckBox.CheckedChanged += new System.EventHandler(this.UseProxyCheckBox_CheckedChanged); | |||
// | |||
// ProxyAddrLabel | |||
// | |||
this.ProxyAddrLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyAddrLabel.AutoSize = true; | |||
this.ProxyAddrLabel.Location = new System.Drawing.Point(3, 64); | |||
this.ProxyAddrLabel.Name = "ProxyAddrLabel"; | |||
this.ProxyAddrLabel.Size = new System.Drawing.Size(65, 12); | |||
this.ProxyAddrLabel.TabIndex = 0; | |||
this.ProxyAddrLabel.Text = "Proxy Addr"; | |||
// | |||
// ProxyServerTextBox | |||
// | |||
this.ProxyServerTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyServerTextBox.Location = new System.Drawing.Point(74, 59); | |||
this.ProxyServerTextBox.MaxLength = 512; | |||
this.ProxyServerTextBox.Name = "ProxyServerTextBox"; | |||
this.ProxyServerTextBox.Size = new System.Drawing.Size(138, 21); | |||
this.ProxyServerTextBox.TabIndex = 1; | |||
this.ProxyServerTextBox.WordWrap = false; | |||
// | |||
// ProxyPortLabel | |||
// | |||
this.ProxyPortLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyPortLabel.AutoSize = true; | |||
this.ProxyPortLabel.Location = new System.Drawing.Point(218, 64); | |||
this.ProxyPortLabel.Name = "ProxyPortLabel"; | |||
this.ProxyPortLabel.Size = new System.Drawing.Size(77, 12); | |||
this.ProxyPortLabel.TabIndex = 2; | |||
this.ProxyPortLabel.Text = "Proxy Port"; | |||
// | |||
// ProxyPortTextBox | |||
// | |||
this.ProxyPortTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyPortTextBox.Location = new System.Drawing.Point(301, 59); | |||
this.ProxyPortTextBox.MaxLength = 10; | |||
this.ProxyPortTextBox.Name = "ProxyPortTextBox"; | |||
this.ProxyPortTextBox.Size = new System.Drawing.Size(91, 21); | |||
this.ProxyPortTextBox.TabIndex = 3; | |||
this.ProxyPortTextBox.WordWrap = false; | |||
// | |||
// ProxyTypeLabel | |||
// | |||
this.ProxyTypeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyTypeLabel.AutoSize = true; | |||
this.ProxyTypeLabel.Location = new System.Drawing.Point(3, 36); | |||
this.ProxyTypeLabel.Name = "ProxyTypeLabel"; | |||
this.ProxyTypeLabel.Size = new System.Drawing.Size(65, 12); | |||
this.ProxyTypeLabel.TabIndex = 1; | |||
this.ProxyTypeLabel.Text = "Proxy Type"; | |||
// | |||
// ProxyTypeComboBox | |||
// | |||
this.ProxyTypeComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyTypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | |||
this.ProxyTypeComboBox.FormattingEnabled = true; | |||
this.ProxyTypeComboBox.Items.AddRange(new object[] { | |||
"SOCKS5", | |||
"HTTP"}); | |||
this.ProxyTypeComboBox.Location = new System.Drawing.Point(74, 33); | |||
this.ProxyTypeComboBox.Margin = new System.Windows.Forms.Padding(3, 5, 3, 5); | |||
this.ProxyTypeComboBox.Name = "ProxyTypeComboBox"; | |||
this.ProxyTypeComboBox.Size = new System.Drawing.Size(138, 20); | |||
this.ProxyTypeComboBox.TabIndex = 2; | |||
this.ProxyTypeComboBox.SelectedIndexChanged += new System.EventHandler(this.ProxyTypeComboBox_SelectedIndexChanged); | |||
// | |||
// ProxyTimeoutTextBox | |||
// | |||
this.ProxyTimeoutTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyTimeoutTextBox.Location = new System.Drawing.Point(301, 31); | |||
this.ProxyTimeoutTextBox.Name = "ProxyTimeoutTextBox"; | |||
this.ProxyTimeoutTextBox.Size = new System.Drawing.Size(91, 21); | |||
this.ProxyTimeoutTextBox.TabIndex = 3; | |||
// | |||
// ProxyTimeoutLabel | |||
// | |||
this.ProxyTimeoutLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyTimeoutLabel.AutoSize = true; | |||
this.ProxyTimeoutLabel.Location = new System.Drawing.Point(218, 36); | |||
this.ProxyTimeoutLabel.Name = "ProxyTimeoutLabel"; | |||
this.ProxyTimeoutLabel.Size = new System.Drawing.Size(77, 12); | |||
this.ProxyTimeoutLabel.TabIndex = 4; | |||
this.ProxyTimeoutLabel.Text = "Timeout(Sec)"; | |||
// | |||
// tableLayoutPanel1 | |||
// | |||
this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; | |||
this.tableLayoutPanel1.ColumnCount = 4; | |||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | |||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 60F)); | |||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | |||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 40F)); | |||
this.tableLayoutPanel1.Controls.Add(this.UseProxyCheckBox, 0, 0); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyTypeLabel, 0, 1); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyPortTextBox, 3, 2); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyTypeComboBox, 1, 1); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyTimeoutLabel, 2, 1); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyPortLabel, 2, 2); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyTimeoutTextBox, 3, 1); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyServerTextBox, 1, 2); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyAddrLabel, 0, 2); | |||
this.tableLayoutPanel1.Controls.Add(this.ProxyNotificationLabel, 0, 3); | |||
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 0, 6); | |||
this.tableLayoutPanel1.Controls.Add(this.AuthUserLabel, 0, 5); | |||
this.tableLayoutPanel1.Controls.Add(this.AuthPwdLabel, 2, 5); | |||
this.tableLayoutPanel1.Controls.Add(this.UseAuthCheckBox, 0, 4); | |||
this.tableLayoutPanel1.Controls.Add(this.AuthUserTextBox, 1, 5); | |||
this.tableLayoutPanel1.Controls.Add(this.AuthPwdTextBox, 3, 5); | |||
this.tableLayoutPanel1.Location = new System.Drawing.Point(15, 15); | |||
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | |||
this.tableLayoutPanel1.RowCount = 7; | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66713F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66713F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66713F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.6662F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.6662F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.6662F)); | |||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||
this.tableLayoutPanel1.Size = new System.Drawing.Size(395, 204); | |||
this.tableLayoutPanel1.TabIndex = 14; | |||
// | |||
// ProxyNotificationLabel | |||
// | |||
this.ProxyNotificationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.ProxyNotificationLabel.AutoSize = true; | |||
this.tableLayoutPanel1.SetColumnSpan(this.ProxyNotificationLabel, 4); | |||
this.ProxyNotificationLabel.ForeColor = System.Drawing.Color.Red; | |||
this.ProxyNotificationLabel.Location = new System.Drawing.Point(3, 92); | |||
this.ProxyNotificationLabel.Name = "ProxyNotificationLabel"; | |||
this.ProxyNotificationLabel.Size = new System.Drawing.Size(389, 12); | |||
this.ProxyNotificationLabel.TabIndex = 5; | |||
this.ProxyNotificationLabel.Text = "If server has a plugin, proxy will not be used"; | |||
// | |||
// flowLayoutPanel1 | |||
// | |||
this.flowLayoutPanel1.AutoSize = true; | |||
this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; | |||
this.tableLayoutPanel1.SetColumnSpan(this.flowLayoutPanel1, 4); | |||
this.flowLayoutPanel1.Controls.Add(this.MyCancelButton); | |||
this.flowLayoutPanel1.Controls.Add(this.OKButton); | |||
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Right; | |||
this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; | |||
this.flowLayoutPanel1.Location = new System.Drawing.Point(233, 171); | |||
this.flowLayoutPanel1.Name = "flowLayoutPanel1"; | |||
this.flowLayoutPanel1.Size = new System.Drawing.Size(159, 30); | |||
this.flowLayoutPanel1.TabIndex = 6; | |||
// | |||
// AuthUserLabel | |||
// | |||
this.AuthUserLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.AuthUserLabel.AutoSize = true; | |||
this.AuthUserLabel.Location = new System.Drawing.Point(3, 148); | |||
this.AuthUserLabel.Name = "AuthUserLabel"; | |||
this.AuthUserLabel.Size = new System.Drawing.Size(65, 12); | |||
this.AuthUserLabel.TabIndex = 7; | |||
this.AuthUserLabel.Text = "User Name"; | |||
// | |||
// AuthPwdLabel | |||
// | |||
this.AuthPwdLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.AuthPwdLabel.AutoSize = true; | |||
this.AuthPwdLabel.Location = new System.Drawing.Point(218, 148); | |||
this.AuthPwdLabel.Name = "AuthPwdLabel"; | |||
this.AuthPwdLabel.Size = new System.Drawing.Size(77, 12); | |||
this.AuthPwdLabel.TabIndex = 8; | |||
this.AuthPwdLabel.Text = "Password"; | |||
// | |||
// UseAuthCheckBox | |||
// | |||
this.UseAuthCheckBox.Anchor = System.Windows.Forms.AnchorStyles.Left; | |||
this.UseAuthCheckBox.AutoSize = true; | |||
this.tableLayoutPanel1.SetColumnSpan(this.UseAuthCheckBox, 2); | |||
this.UseAuthCheckBox.Location = new System.Drawing.Point(3, 118); | |||
this.UseAuthCheckBox.Name = "UseAuthCheckBox"; | |||
this.UseAuthCheckBox.Size = new System.Drawing.Size(72, 16); | |||
this.UseAuthCheckBox.TabIndex = 9; | |||
this.UseAuthCheckBox.Text = "Use Auth"; | |||
this.UseAuthCheckBox.UseVisualStyleBackColor = true; | |||
this.UseAuthCheckBox.CheckedChanged += new System.EventHandler(this.UseAuthCheckBox_CheckedChanged); | |||
// | |||
// AuthUserTextBox | |||
// | |||
this.AuthUserTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.AuthUserTextBox.Location = new System.Drawing.Point(74, 143); | |||
this.AuthUserTextBox.Name = "AuthUserTextBox"; | |||
this.AuthUserTextBox.Size = new System.Drawing.Size(138, 21); | |||
this.AuthUserTextBox.TabIndex = 10; | |||
// | |||
// AuthPwdTextBox | |||
// | |||
this.AuthPwdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | |||
this.AuthPwdTextBox.Location = new System.Drawing.Point(301, 143); | |||
this.AuthPwdTextBox.Name = "AuthPwdTextBox"; | |||
this.AuthPwdTextBox.PasswordChar = '*'; | |||
this.AuthPwdTextBox.Size = new System.Drawing.Size(91, 21); | |||
this.AuthPwdTextBox.TabIndex = 11; | |||
// | |||
// ProxyForm | |||
// | |||
this.AcceptButton = this.OKButton; | |||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); | |||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; | |||
this.AutoSize = true; | |||
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; | |||
this.CancelButton = this.MyCancelButton; | |||
this.ClientSize = new System.Drawing.Size(448, 231); | |||
this.Controls.Add(this.tableLayoutPanel1); | |||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | |||
this.MaximizeBox = false; | |||
this.MinimizeBox = false; | |||
this.Name = "ProxyForm"; | |||
this.Padding = new System.Windows.Forms.Padding(12, 12, 12, 9); | |||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | |||
this.Text = "Edit Proxy"; | |||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ProxyForm_FormClosed); | |||
this.tableLayoutPanel1.ResumeLayout(false); | |||
this.tableLayoutPanel1.PerformLayout(); | |||
this.flowLayoutPanel1.ResumeLayout(false); | |||
this.ResumeLayout(false); | |||
} | |||
#endregion | |||
private System.Windows.Forms.CheckBox UseProxyCheckBox; | |||
private System.Windows.Forms.Label ProxyAddrLabel; | |||
private System.Windows.Forms.TextBox ProxyServerTextBox; | |||
private System.Windows.Forms.Label ProxyPortLabel; | |||
private System.Windows.Forms.TextBox ProxyPortTextBox; | |||
private System.Windows.Forms.Button MyCancelButton; | |||
private System.Windows.Forms.Button OKButton; | |||
private System.Windows.Forms.Label ProxyTypeLabel; | |||
private System.Windows.Forms.ComboBox ProxyTypeComboBox; | |||
private System.Windows.Forms.TextBox ProxyTimeoutTextBox; | |||
private System.Windows.Forms.Label ProxyTimeoutLabel; | |||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; | |||
private System.Windows.Forms.Label ProxyNotificationLabel; | |||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; | |||
private System.Windows.Forms.Label AuthUserLabel; | |||
private System.Windows.Forms.Label AuthPwdLabel; | |||
private System.Windows.Forms.CheckBox UseAuthCheckBox; | |||
private System.Windows.Forms.TextBox AuthUserTextBox; | |||
private System.Windows.Forms.TextBox AuthPwdTextBox; | |||
} | |||
} |
@@ -1,176 +0,0 @@ | |||
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 _modifiedProxyConfig; | |||
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() | |||
{ | |||
I18N.TranslateForm(this); | |||
} | |||
private void controller_ConfigChanged(object sender, EventArgs e) | |||
{ | |||
LoadCurrentConfiguration(); | |||
} | |||
private void LoadCurrentConfiguration() | |||
{ | |||
_modifiedProxyConfig = controller.GetConfigurationCopy().proxy; | |||
UseProxyCheckBox.Checked = _modifiedProxyConfig.useProxy; | |||
ProxyServerTextBox.Text = _modifiedProxyConfig.proxyServer; | |||
ProxyPortTextBox.Text = _modifiedProxyConfig.proxyPort.ToString(); | |||
ProxyTimeoutTextBox.Text = _modifiedProxyConfig.proxyTimeout.ToString(); | |||
ProxyTypeComboBox.SelectedIndex = _modifiedProxyConfig.proxyType; | |||
UseAuthCheckBox.Checked = _modifiedProxyConfig.useAuth; | |||
AuthUserTextBox.Text = _modifiedProxyConfig.authUser; | |||
AuthPwdTextBox.Text = _modifiedProxyConfig.authPwd; | |||
} | |||
private void OKButton_Click(object sender, EventArgs e) | |||
{ | |||
_modifiedProxyConfig.useProxy = UseProxyCheckBox.Checked; | |||
if (_modifiedProxyConfig.useProxy) | |||
{ | |||
if (!int.TryParse(ProxyPortTextBox.Text, out _modifiedProxyConfig.proxyPort)) | |||
{ | |||
MessageBox.Show(I18N.GetString("Illegal port number format")); | |||
return; | |||
} | |||
if (!int.TryParse(ProxyTimeoutTextBox.Text, out _modifiedProxyConfig.proxyTimeout)) | |||
{ | |||
MessageBox.Show(I18N.GetString("Illegal timeout format")); | |||
return; | |||
} | |||
_modifiedProxyConfig.proxyType = ProxyTypeComboBox.SelectedIndex; | |||
try | |||
{ | |||
Configuration.CheckServer(_modifiedProxyConfig.proxyServer = ProxyServerTextBox.Text); | |||
Configuration.CheckPort(_modifiedProxyConfig.proxyPort); | |||
Configuration.CheckTimeout(_modifiedProxyConfig.proxyTimeout, ProxyConfig.MaxProxyTimeoutSec); | |||
_modifiedProxyConfig.useAuth = UseAuthCheckBox.Checked; | |||
if (_modifiedProxyConfig.useAuth) | |||
{ | |||
Configuration.CheckProxyAuthUser(_modifiedProxyConfig.authUser = AuthUserTextBox.Text); | |||
Configuration.CheckProxyAuthPwd(_modifiedProxyConfig.authPwd = AuthPwdTextBox.Text); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
MessageBox.Show(ex.Message); | |||
return; | |||
} | |||
} | |||
controller.SaveProxy(_modifiedProxyConfig); | |||
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 = | |||
ProxyPortTextBox.Enabled = | |||
ProxyTimeoutTextBox.Enabled = | |||
ProxyTypeComboBox.Enabled = true; | |||
if (ProxyTypeComboBox.SelectedIndex == ProxyConfig.PROXY_HTTP) | |||
{ | |||
UseAuthCheckBox.Enabled = true; | |||
if (UseAuthCheckBox.Checked) | |||
{ | |||
AuthUserTextBox.Enabled = | |||
AuthPwdTextBox.Enabled = true; | |||
} | |||
else | |||
{ | |||
AuthUserTextBox.Enabled = | |||
AuthPwdTextBox.Enabled = false; | |||
} | |||
} | |||
else | |||
{ | |||
// TODO support for SOCK5 auth | |||
UseAuthCheckBox.Enabled = | |||
AuthUserTextBox.Enabled = | |||
AuthPwdTextBox.Enabled = false; | |||
} | |||
} | |||
else | |||
{ | |||
ProxyServerTextBox.Enabled = | |||
ProxyPortTextBox.Enabled = | |||
ProxyTimeoutTextBox.Enabled = | |||
ProxyTypeComboBox.Enabled = | |||
UseAuthCheckBox.Enabled = | |||
AuthUserTextBox.Enabled = | |||
AuthPwdTextBox.Enabled = false; | |||
} | |||
} | |||
private void ProxyTypeComboBox_SelectedIndexChanged(object sender, EventArgs e) | |||
{ | |||
// TODO support for SOCK5 auth | |||
if (ProxyTypeComboBox.SelectedIndex != ProxyConfig.PROXY_HTTP) | |||
{ | |||
UseAuthCheckBox.Checked = false; | |||
AuthUserTextBox.Clear(); | |||
AuthPwdTextBox.Clear(); | |||
} | |||
UpdateEnabled(); | |||
} | |||
private void UseAuthCheckBox_CheckedChanged(object sender, EventArgs e) | |||
{ | |||
UpdateEnabled(); | |||
} | |||
} | |||
} |
@@ -1,100 +0,0 @@ | |||
namespace Shadowsocks.View | |||
{ | |||
partial class QRCodeForm | |||
{ | |||
/// <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() | |||
{ | |||
this.pictureBox1 = new System.Windows.Forms.PictureBox(); | |||
this.listBox1 = new System.Windows.Forms.ListBox(); | |||
this.textBoxURL = new System.Windows.Forms.TextBox(); | |||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); | |||
this.SuspendLayout(); | |||
// | |||
// pictureBox1 | |||
// | |||
this.pictureBox1.Location = new System.Drawing.Point(10, 10); | |||
this.pictureBox1.Margin = new System.Windows.Forms.Padding(0); | |||
this.pictureBox1.Name = "pictureBox1"; | |||
this.pictureBox1.Size = new System.Drawing.Size(210, 210); | |||
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; | |||
this.pictureBox1.TabIndex = 1; | |||
this.pictureBox1.TabStop = false; | |||
// | |||
// listBox1 | |||
// | |||
this.listBox1.DisplayMember = "Value"; | |||
this.listBox1.FormattingEnabled = true; | |||
this.listBox1.ItemHeight = 12; | |||
this.listBox1.Location = new System.Drawing.Point(224, 10); | |||
this.listBox1.Name = "listBox1"; | |||
this.listBox1.ScrollAlwaysVisible = true; | |||
this.listBox1.Size = new System.Drawing.Size(227, 208); | |||
this.listBox1.TabIndex = 2; | |||
this.listBox1.ValueMember = "Key"; | |||
this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged); | |||
// | |||
// textBoxURL | |||
// | |||
this.textBoxURL.Location = new System.Drawing.Point(10, 227); | |||
this.textBoxURL.Name = "textBoxURL"; | |||
this.textBoxURL.ReadOnly = true; | |||
this.textBoxURL.Size = new System.Drawing.Size(441, 21); | |||
this.textBoxURL.TabIndex = 3; | |||
this.textBoxURL.Click += new System.EventHandler(this.textBoxURL_Click); | |||
// | |||
// QRCodeForm | |||
// | |||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); | |||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; | |||
this.AutoSize = true; | |||
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; | |||
this.BackColor = System.Drawing.Color.White; | |||
this.ClientSize = new System.Drawing.Size(457, 257); | |||
this.Controls.Add(this.textBoxURL); | |||
this.Controls.Add(this.listBox1); | |||
this.Controls.Add(this.pictureBox1); | |||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | |||
this.MaximizeBox = false; | |||
this.MinimizeBox = false; | |||
this.Name = "QRCodeForm"; | |||
this.Padding = new System.Windows.Forms.Padding(10); | |||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | |||
this.Text = "QRCode and URL"; | |||
this.Load += new System.EventHandler(this.QRCodeForm_Load); | |||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); | |||
this.ResumeLayout(false); | |||
this.PerformLayout(); | |||
} | |||
#endregion | |||
private System.Windows.Forms.PictureBox pictureBox1; | |||
private System.Windows.Forms.ListBox listBox1; | |||
private System.Windows.Forms.TextBox textBoxURL; | |||
} | |||
} |
@@ -1,90 +0,0 @@ | |||
using ZXing.QrCode.Internal; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Properties; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.ComponentModel; | |||
using System.Data; | |||
using System.Drawing; | |||
using System.IO; | |||
using System.IO.Compression; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Windows.Forms; | |||
using Shadowsocks.Model; | |||
namespace Shadowsocks.View | |||
{ | |||
public partial class QRCodeForm : Form | |||
{ | |||
private string code; | |||
public QRCodeForm(string code) | |||
{ | |||
this.code = code; | |||
InitializeComponent(); | |||
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||
this.Text = I18N.GetString("QRCode and URL"); | |||
} | |||
private void GenQR(string ssconfig) | |||
{ | |||
string qrText = ssconfig; | |||
QRCode code = ZXing.QrCode.Internal.Encoder.encode(qrText, ErrorCorrectionLevel.M); | |||
ByteMatrix m = code.Matrix; | |||
int blockSize = Math.Max(pictureBox1.Height/m.Height, 1); | |||
var qrWidth = m.Width*blockSize; | |||
var qrHeight = m.Height*blockSize; | |||
var dWidth = pictureBox1.Width - qrWidth; | |||
var dHeight = pictureBox1.Height - qrHeight; | |||
var maxD = Math.Max(dWidth, dHeight); | |||
pictureBox1.SizeMode = maxD >= 7*blockSize ? PictureBoxSizeMode.Zoom : PictureBoxSizeMode.CenterImage; | |||
Bitmap drawArea = new Bitmap((m.Width*blockSize), (m.Height*blockSize)); | |||
using (Graphics g = Graphics.FromImage(drawArea)) | |||
{ | |||
g.Clear(Color.White); | |||
using (Brush b = new SolidBrush(Color.Black)) | |||
{ | |||
for (int row = 0; row < m.Width; row++) | |||
{ | |||
for (int col = 0; col < m.Height; col++) | |||
{ | |||
if (m[row, col] != 0) | |||
{ | |||
g.FillRectangle(b, blockSize*row, blockSize*col, blockSize, blockSize); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
pictureBox1.Image = drawArea; | |||
} | |||
private void QRCodeForm_Load(object sender, EventArgs e) | |||
{ | |||
Configuration config = Configuration.Load(); | |||
List<KeyValuePair<string, string>> serverDatas = config.configs.Select( | |||
server => | |||
new KeyValuePair<string, string>(server.GetURL(config.generateLegacyUrl), server.ToString()) | |||
).ToList(); | |||
listBox1.DataSource = serverDatas; | |||
int selectIndex = serverDatas.FindIndex(serverData => serverData.Key.StartsWith(code)); | |||
if (selectIndex >= 0) listBox1.SetSelected(selectIndex, true); | |||
} | |||
private void listBox1_SelectedIndexChanged(object sender, EventArgs e) | |||
{ | |||
var url = (sender as ListBox)?.SelectedValue.ToString(); | |||
GenQR(url); | |||
textBoxURL.Text = url; | |||
} | |||
private void textBoxURL_Click(object sender, EventArgs e) | |||
{ | |||
textBoxURL.SelectAll(); | |||
} | |||
} | |||
} |
@@ -1,281 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.ComponentModel; | |||
using System.Data; | |||
using System.Drawing; | |||
using System.Text; | |||
using System.Windows.Forms; | |||
using System.Drawing.Imaging; | |||
using System.Runtime.InteropServices; | |||
using System.Diagnostics; | |||
namespace Shadowsocks.View | |||
{ | |||
public class QRCodeSplashForm : Form | |||
{ | |||
public Rectangle TargetRect; | |||
public QRCodeSplashForm() | |||
{ | |||
FormBorderStyle = FormBorderStyle.None; | |||
this.Load += QRCodeSplashForm_Load; | |||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; | |||
this.BackColor = System.Drawing.Color.White; | |||
this.ClientSize = new System.Drawing.Size(1, 1); | |||
this.ControlBox = false; | |||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; | |||
this.MaximizeBox = false; | |||
this.MinimizeBox = false; | |||
this.Name = "QRCodeSplashForm"; | |||
this.ShowIcon = false; | |||
this.ShowInTaskbar = false; | |||
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; | |||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; | |||
this.TopMost = true; | |||
} | |||
private Timer timer; | |||
private int flashStep; | |||
private static double FPS = 1.0 / 15 * 1000; // System.Windows.Forms.Timer resolution is 15ms | |||
private static double ANIMATION_TIME = 0.5; | |||
private static int ANIMATION_STEPS = (int)(ANIMATION_TIME * FPS); | |||
Stopwatch sw; | |||
int x; | |||
int y; | |||
int w; | |||
int h; | |||
Bitmap bitmap; | |||
Graphics g; | |||
Pen pen; | |||
SolidBrush brush; | |||
private void QRCodeSplashForm_Load(object sender, EventArgs e) | |||
{ | |||
SetStyle(ControlStyles.SupportsTransparentBackColor, true); | |||
this.BackColor = Color.Transparent; | |||
flashStep = 0; | |||
x = 0; | |||
y = 0; | |||
w = Width; | |||
h = Height; | |||
sw = Stopwatch.StartNew(); | |||
timer = new Timer(); | |||
timer.Interval = (int)(ANIMATION_TIME * 1000 / ANIMATION_STEPS); | |||
timer.Tick += timer_Tick; | |||
timer.Start(); | |||
bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); | |||
g = Graphics.FromImage(bitmap); | |||
pen = new Pen(Color.Red, 3); | |||
brush = new SolidBrush(Color.FromArgb(30, Color.Red)); | |||
} | |||
void timer_Tick(object sender, EventArgs e) | |||
{ | |||
double percent = (double)sw.ElapsedMilliseconds / 1000.0 / (double)ANIMATION_TIME; | |||
if (percent < 1) | |||
{ | |||
// ease out | |||
percent = 1 - Math.Pow((1 - percent), 4); | |||
x = (int)(TargetRect.X * percent); | |||
y = (int)(TargetRect.Y * percent); | |||
w = (int)(TargetRect.Width * percent + this.Size.Width * (1 - percent)); | |||
h = (int)(TargetRect.Height * percent + this.Size.Height * (1 - percent)); | |||
//codeRectView.Location = new Point(x, y); | |||
//codeRectView.Size = new Size(w, h); | |||
pen.Color = Color.FromArgb((int)(255 * percent), Color.Red); | |||
brush.Color = Color.FromArgb((int)(30 * percent), Color.Red); | |||
g.Clear(Color.Transparent); | |||
g.FillRectangle(brush, x, y, w, h); | |||
g.DrawRectangle(pen, x, y, w, h); | |||
SetBitmap(bitmap); | |||
} | |||
else | |||
{ | |||
if (flashStep == 0) | |||
{ | |||
timer.Interval = 100; | |||
g.Clear(Color.Transparent); | |||
SetBitmap(bitmap); | |||
} | |||
else if (flashStep == 1) | |||
{ | |||
timer.Interval = 50; | |||
g.FillRectangle(brush, x, y, w, h); | |||
g.DrawRectangle(pen, x, y, w, h); | |||
SetBitmap(bitmap); | |||
} | |||
else if (flashStep == 2) | |||
{ | |||
g.Clear(Color.Transparent); | |||
SetBitmap(bitmap); | |||
} | |||
else if (flashStep == 3) | |||
{ | |||
g.FillRectangle(brush, x, y, w, h); | |||
g.DrawRectangle(pen, x, y, w, h); | |||
SetBitmap(bitmap); | |||
} | |||
else if (flashStep == 4) | |||
{ | |||
g.Clear(Color.Transparent); | |||
SetBitmap(bitmap); | |||
} | |||
else if (flashStep == 5) | |||
{ | |||
g.FillRectangle(brush, x, y, w, h); | |||
g.DrawRectangle(pen, x, y, w, h); | |||
SetBitmap(bitmap); | |||
} | |||
else | |||
{ | |||
sw.Stop(); | |||
timer.Stop(); | |||
pen.Dispose(); | |||
brush.Dispose(); | |||
bitmap.Dispose(); | |||
this.Close(); | |||
} | |||
flashStep++; | |||
} | |||
} | |||
// PerPixelAlphaForm.cs | |||
// http://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C | |||
// Rui Lopes | |||
protected override CreateParams CreateParams | |||
{ | |||
get | |||
{ | |||
CreateParams cp = base.CreateParams; | |||
cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style | |||
return cp; | |||
} | |||
} | |||
public void SetBitmap(Bitmap bitmap) | |||
{ | |||
SetBitmap(bitmap, 255); | |||
} | |||
/// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para> | |||
public void SetBitmap(Bitmap bitmap, byte opacity) | |||
{ | |||
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) | |||
throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); | |||
// The idea of this is very simple, | |||
// 1. Create a compatible DC with screen; | |||
// 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC; | |||
// 3. Call the UpdateLayeredWindow. | |||
IntPtr screenDc = Win32.GetDC(IntPtr.Zero); | |||
IntPtr memDc = Win32.CreateCompatibleDC(screenDc); | |||
IntPtr hBitmap = IntPtr.Zero; | |||
IntPtr oldBitmap = IntPtr.Zero; | |||
try | |||
{ | |||
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap | |||
oldBitmap = Win32.SelectObject(memDc, hBitmap); | |||
Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height); | |||
Win32.Point pointSource = new Win32.Point(0, 0); | |||
Win32.Point topPos = new Win32.Point(Left, Top); | |||
Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); | |||
blend.BlendOp = Win32.AC_SRC_OVER; | |||
blend.BlendFlags = 0; | |||
blend.SourceConstantAlpha = opacity; | |||
blend.AlphaFormat = Win32.AC_SRC_ALPHA; | |||
Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA); | |||
} | |||
finally | |||
{ | |||
Win32.ReleaseDC(IntPtr.Zero, screenDc); | |||
if (hBitmap != IntPtr.Zero) | |||
{ | |||
Win32.SelectObject(memDc, oldBitmap); | |||
//Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak. | |||
Win32.DeleteObject(hBitmap); | |||
} | |||
Win32.DeleteDC(memDc); | |||
} | |||
} | |||
} | |||
// class that exposes needed win32 gdi functions. | |||
class Win32 | |||
{ | |||
[StructLayout(LayoutKind.Sequential)] | |||
public struct Point | |||
{ | |||
public Int32 x; | |||
public Int32 y; | |||
public Point(Int32 x, Int32 y) { this.x = x; this.y = y; } | |||
} | |||
[StructLayout(LayoutKind.Sequential)] | |||
public struct Size | |||
{ | |||
public Int32 cx; | |||
public Int32 cy; | |||
public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; } | |||
} | |||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |||
struct ARGB | |||
{ | |||
public byte Blue; | |||
public byte Green; | |||
public byte Red; | |||
public byte Alpha; | |||
} | |||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |||
public struct BLENDFUNCTION | |||
{ | |||
public byte BlendOp; | |||
public byte BlendFlags; | |||
public byte SourceConstantAlpha; | |||
public byte AlphaFormat; | |||
} | |||
public const Int32 ULW_COLORKEY = 0x00000001; | |||
public const Int32 ULW_ALPHA = 0x00000002; | |||
public const Int32 ULW_OPAQUE = 0x00000004; | |||
public const byte AC_SRC_OVER = 0x00; | |||
public const byte AC_SRC_ALPHA = 0x01; | |||
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] | |||
public static extern int UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); | |||
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] | |||
public static extern IntPtr GetDC(IntPtr hWnd); | |||
[DllImport("user32.dll", ExactSpelling = true)] | |||
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); | |||
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] | |||
public static extern IntPtr CreateCompatibleDC(IntPtr hDC); | |||
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] | |||
public static extern int DeleteDC(IntPtr hdc); | |||
[DllImport("gdi32.dll", ExactSpelling = true)] | |||
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); | |||
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] | |||
public static extern int DeleteObject(IntPtr hObject); | |||
} | |||
} |
@@ -1,537 +0,0 @@ | |||
namespace Shadowsocks.View | |||
{ | |||
partial class StatisticsStrategyConfigurationForm | |||
{ | |||
/// <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() | |||
{ | |||
this.components = new System.ComponentModel.Container(); | |||
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); | |||
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 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.PingCheckBox = new System.Windows.Forms.CheckBox(); | |||
this.KeepChoiceForLabel = new System.Windows.Forms.Label(); | |||
this.MinutesLabel2 = new System.Windows.Forms.Label(); | |||
this.chartModeSelector = new System.Windows.Forms.GroupBox(); | |||
this.allMode = new System.Windows.Forms.RadioButton(); | |||
this.dayMode = new System.Windows.Forms.RadioButton(); | |||
this.splitContainer1 = new System.Windows.Forms.SplitContainer(); | |||
this.splitContainer2 = new System.Windows.Forms.SplitContainer(); | |||
this.CollectDataPerLabel = new System.Windows.Forms.Label(); | |||
this.MinutesLabel1 = new System.Windows.Forms.Label(); | |||
this.dataCollectionMinutesNum = new System.Windows.Forms.NumericUpDown(); | |||
this.StatisticsEnabledCheckBox = new System.Windows.Forms.CheckBox(); | |||
this.choiceKeptMinutesNum = new System.Windows.Forms.NumericUpDown(); | |||
this.byHourOfDayCheckBox = new System.Windows.Forms.CheckBox(); | |||
this.repeatTimesNum = new System.Windows.Forms.NumericUpDown(); | |||
this.PackagePerPingLabel = new System.Windows.Forms.Label(); | |||
this.splitContainer3 = new System.Windows.Forms.SplitContainer(); | |||
this.FinalScoreLabel = new System.Windows.Forms.Label(); | |||
this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel(); | |||
this.serverSelector = new System.Windows.Forms.ComboBox(); | |||
this.CancelButton = new System.Windows.Forms.Button(); | |||
this.OKButton = new System.Windows.Forms.Button(); | |||
this.CalculatinTip = new System.Windows.Forms.ToolTip(this.components); | |||
this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); | |||
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); | |||
this.chartModeSelector.SuspendLayout(); | |||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); | |||
this.splitContainer1.Panel1.SuspendLayout(); | |||
this.splitContainer1.Panel2.SuspendLayout(); | |||
this.splitContainer1.SuspendLayout(); | |||
((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); | |||
this.splitContainer2.Panel1.SuspendLayout(); | |||
this.splitContainer2.Panel2.SuspendLayout(); | |||
this.splitContainer2.SuspendLayout(); | |||
((System.ComponentModel.ISupportInitialize)(this.dataCollectionMinutesNum)).BeginInit(); | |||
((System.ComponentModel.ISupportInitialize)(this.choiceKeptMinutesNum)).BeginInit(); | |||
((System.ComponentModel.ISupportInitialize)(this.repeatTimesNum)).BeginInit(); | |||
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).BeginInit(); | |||
this.splitContainer3.Panel1.SuspendLayout(); | |||
this.splitContainer3.Panel2.SuspendLayout(); | |||
this.splitContainer3.SuspendLayout(); | |||
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); | |||
this.SuspendLayout(); | |||
// | |||
// StatisticsChart | |||
// | |||
this.StatisticsChart.BackColor = System.Drawing.Color.Transparent; | |||
chartArea1.AxisX.MajorGrid.Enabled = false; | |||
chartArea1.AxisY.MajorGrid.Enabled = false; | |||
chartArea1.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.False; | |||
chartArea1.AxisY2.MajorGrid.Enabled = false; | |||
chartArea1.BackColor = System.Drawing.Color.Transparent; | |||
chartArea1.Name = "DataArea"; | |||
this.StatisticsChart.ChartAreas.Add(chartArea1); | |||
this.StatisticsChart.Dock = System.Windows.Forms.DockStyle.Fill; | |||
legend1.BackColor = System.Drawing.Color.Transparent; | |||
legend1.Name = "ChartLegend"; | |||
this.StatisticsChart.Legends.Add(legend1); | |||
this.StatisticsChart.Location = new System.Drawing.Point(0, 0); | |||
this.StatisticsChart.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.StatisticsChart.Name = "StatisticsChart"; | |||
this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; | |||
series1.ChartArea = "DataArea"; | |||
series1.Color = System.Drawing.Color.DarkGray; | |||
series1.Legend = "ChartLegend"; | |||
series1.Name = "Speed"; | |||
series1.ToolTip = "#VALX\\nMax inbound speed\\n#VAL KiB/s"; | |||
series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; | |||
series2.ChartArea = "DataArea"; | |||
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; | |||
series2.Color = System.Drawing.Color.Crimson; | |||
series2.CustomProperties = "EmptyPointValue=Zero"; | |||
series2.Legend = "ChartLegend"; | |||
series2.Name = "Package Loss"; | |||
series2.ToolTip = "#VALX\\n#VAL%"; | |||
series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; | |||
series2.YAxisType = System.Windows.Forms.DataVisualization.Charting.AxisType.Secondary; | |||
series2.YValuesPerPoint = 2; | |||
series3.BorderWidth = 5; | |||
series3.ChartArea = "DataArea"; | |||
series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; | |||
series3.Color = System.Drawing.Color.DodgerBlue; | |||
series3.Legend = "ChartLegend"; | |||
series3.MarkerSize = 10; | |||
series3.MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Circle; | |||
series3.Name = "Ping"; | |||
series3.ToolTip = "#VALX\\n#VAL ms"; | |||
series3.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; | |||
this.StatisticsChart.Series.Add(series1); | |||
this.StatisticsChart.Series.Add(series2); | |||
this.StatisticsChart.Series.Add(series3); | |||
this.StatisticsChart.Size = new System.Drawing.Size(982, 435); | |||
this.StatisticsChart.TabIndex = 2; | |||
// | |||
// PingCheckBox | |||
// | |||
this.PingCheckBox.AutoSize = true; | |||
this.PingCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "Ping", true)); | |||
this.PingCheckBox.Location = new System.Drawing.Point(13, 54); | |||
this.PingCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.PingCheckBox.Name = "PingCheckBox"; | |||
this.PingCheckBox.Size = new System.Drawing.Size(107, 27); | |||
this.PingCheckBox.TabIndex = 5; | |||
this.PingCheckBox.Text = "Ping Test"; | |||
this.PingCheckBox.UseVisualStyleBackColor = true; | |||
this.PingCheckBox.CheckedChanged += new System.EventHandler(this.PingCheckBox_CheckedChanged); | |||
// | |||
// KeepChoiceForLabel | |||
// | |||
this.KeepChoiceForLabel.AutoSize = true; | |||
this.KeepChoiceForLabel.Location = new System.Drawing.Point(9, 206); | |||
this.KeepChoiceForLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||
this.KeepChoiceForLabel.Name = "KeepChoiceForLabel"; | |||
this.KeepChoiceForLabel.Size = new System.Drawing.Size(139, 23); | |||
this.KeepChoiceForLabel.TabIndex = 8; | |||
this.KeepChoiceForLabel.Text = "Keep choice for"; | |||
// | |||
// MinutesLabel2 | |||
// | |||
this.MinutesLabel2.AutoSize = true; | |||
this.MinutesLabel2.Location = new System.Drawing.Point(286, 206); | |||
this.MinutesLabel2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||
this.MinutesLabel2.Name = "MinutesLabel2"; | |||
this.MinutesLabel2.Size = new System.Drawing.Size(75, 23); | |||
this.MinutesLabel2.TabIndex = 9; | |||
this.MinutesLabel2.Text = "minutes"; | |||
// | |||
// chartModeSelector | |||
// | |||
this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | |||
this.chartModeSelector.Controls.Add(this.allMode); | |||
this.chartModeSelector.Controls.Add(this.dayMode); | |||
this.chartModeSelector.Location = new System.Drawing.Point(733, 182); | |||
this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.chartModeSelector.Name = "chartModeSelector"; | |||
this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.chartModeSelector.Size = new System.Drawing.Size(234, 103); | |||
this.chartModeSelector.TabIndex = 3; | |||
this.chartModeSelector.TabStop = false; | |||
this.chartModeSelector.Text = "Chart Mode"; | |||
// | |||
// allMode | |||
// | |||
this.allMode.AutoSize = true; | |||
this.allMode.Location = new System.Drawing.Point(11, 61); | |||
this.allMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.allMode.Name = "allMode"; | |||
this.allMode.Size = new System.Drawing.Size(50, 27); | |||
this.allMode.TabIndex = 1; | |||
this.allMode.Text = "all"; | |||
this.allMode.UseVisualStyleBackColor = true; | |||
this.allMode.CheckedChanged += new System.EventHandler(this.allMode_CheckedChanged); | |||
// | |||
// dayMode | |||
// | |||
this.dayMode.AutoSize = true; | |||
this.dayMode.Checked = true; | |||
this.dayMode.Location = new System.Drawing.Point(11, 29); | |||
this.dayMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.dayMode.Name = "dayMode"; | |||
this.dayMode.Size = new System.Drawing.Size(61, 27); | |||
this.dayMode.TabIndex = 0; | |||
this.dayMode.TabStop = true; | |||
this.dayMode.Text = "24h"; | |||
this.dayMode.UseVisualStyleBackColor = true; | |||
this.dayMode.CheckedChanged += new System.EventHandler(this.dayMode_CheckedChanged); | |||
// | |||
// splitContainer1 | |||
// | |||
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; | |||
this.splitContainer1.IsSplitterFixed = true; | |||
this.splitContainer1.Location = new System.Drawing.Point(0, 0); | |||
this.splitContainer1.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.splitContainer1.Name = "splitContainer1"; | |||
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; | |||
// | |||
// splitContainer1.Panel1 | |||
// | |||
this.splitContainer1.Panel1.Controls.Add(this.splitContainer2); | |||
// | |||
// splitContainer1.Panel2 | |||
// | |||
this.splitContainer1.Panel2.Controls.Add(this.serverSelector); | |||
this.splitContainer1.Panel2.Controls.Add(this.CancelButton); | |||
this.splitContainer1.Panel2.Controls.Add(this.OKButton); | |||
this.splitContainer1.Panel2.Controls.Add(this.chartModeSelector); | |||
this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); | |||
this.splitContainer1.Size = new System.Drawing.Size(982, 753); | |||
this.splitContainer1.SplitterDistance = 308; | |||
this.splitContainer1.SplitterWidth = 10; | |||
this.splitContainer1.TabIndex = 12; | |||
// | |||
// splitContainer2 | |||
// | |||
this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; | |||
this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; | |||
this.splitContainer2.IsSplitterFixed = true; | |||
this.splitContainer2.Location = new System.Drawing.Point(0, 0); | |||
this.splitContainer2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.splitContainer2.Name = "splitContainer2"; | |||
// | |||
// splitContainer2.Panel1 | |||
// | |||
this.splitContainer2.Panel1.Controls.Add(this.CollectDataPerLabel); | |||
this.splitContainer2.Panel1.Controls.Add(this.MinutesLabel1); | |||
this.splitContainer2.Panel1.Controls.Add(this.dataCollectionMinutesNum); | |||
this.splitContainer2.Panel1.Controls.Add(this.StatisticsEnabledCheckBox); | |||
this.splitContainer2.Panel1.Controls.Add(this.choiceKeptMinutesNum); | |||
this.splitContainer2.Panel1.Controls.Add(this.byHourOfDayCheckBox); | |||
this.splitContainer2.Panel1.Controls.Add(this.repeatTimesNum); | |||
this.splitContainer2.Panel1.Controls.Add(this.PackagePerPingLabel); | |||
this.splitContainer2.Panel1.Controls.Add(this.KeepChoiceForLabel); | |||
this.splitContainer2.Panel1.Controls.Add(this.PingCheckBox); | |||
this.splitContainer2.Panel1.Controls.Add(this.MinutesLabel2); | |||
// | |||
// splitContainer2.Panel2 | |||
// | |||
this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); | |||
this.splitContainer2.Size = new System.Drawing.Size(982, 308); | |||
this.splitContainer2.SplitterDistance = 384; | |||
this.splitContainer2.SplitterWidth = 5; | |||
this.splitContainer2.TabIndex = 7; | |||
// | |||
// CollectDataPerLabel | |||
// | |||
this.CollectDataPerLabel.AutoSize = true; | |||
this.CollectDataPerLabel.Location = new System.Drawing.Point(9, 164); | |||
this.CollectDataPerLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||
this.CollectDataPerLabel.Name = "CollectDataPerLabel"; | |||
this.CollectDataPerLabel.Size = new System.Drawing.Size(139, 23); | |||
this.CollectDataPerLabel.TabIndex = 20; | |||
this.CollectDataPerLabel.Text = "Collect data per"; | |||
// | |||
// MinutesLabel1 | |||
// | |||
this.MinutesLabel1.AutoSize = true; | |||
this.MinutesLabel1.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||
this.MinutesLabel1.Location = new System.Drawing.Point(286, 165); | |||
this.MinutesLabel1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||
this.MinutesLabel1.Name = "MinutesLabel1"; | |||
this.MinutesLabel1.Size = new System.Drawing.Size(75, 23); | |||
this.MinutesLabel1.TabIndex = 19; | |||
this.MinutesLabel1.Text = "minutes"; | |||
// | |||
// dataCollectionMinutesNum | |||
// | |||
this.dataCollectionMinutesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "DataCollectionMinutes", true)); | |||
this.dataCollectionMinutesNum.Increment = new decimal(new int[] { | |||
10, | |||
0, | |||
0, | |||
0}); | |||
this.dataCollectionMinutesNum.Location = new System.Drawing.Point(177, 162); | |||
this.dataCollectionMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | |||
this.dataCollectionMinutesNum.Maximum = new decimal(new int[] { | |||
120, | |||
0, | |||
0, | |||
0}); | |||
this.dataCollectionMinutesNum.Minimum = new decimal(new int[] { | |||
1, | |||
0, | |||
0, | |||
0}); | |||
this.dataCollectionMinutesNum.Name = "dataCollectionMinutesNum"; | |||
this.dataCollectionMinutesNum.Size = new System.Drawing.Size(100, 29); | |||
this.dataCollectionMinutesNum.TabIndex = 18; | |||
this.dataCollectionMinutesNum.Value = new decimal(new int[] { | |||
10, | |||
0, | |||
0, | |||
0}); | |||
// | |||
// StatisticsEnabledCheckBox | |||
// | |||
this.StatisticsEnabledCheckBox.AutoSize = true; | |||
this.StatisticsEnabledCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "StatisticsEnabled", true)); | |||
this.StatisticsEnabledCheckBox.Location = new System.Drawing.Point(13, 12); | |||
this.StatisticsEnabledCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.StatisticsEnabledCheckBox.Name = "StatisticsEnabledCheckBox"; | |||
this.StatisticsEnabledCheckBox.Size = new System.Drawing.Size(163, 27); | |||
this.StatisticsEnabledCheckBox.TabIndex = 17; | |||
this.StatisticsEnabledCheckBox.Text = "Enable Statistics"; | |||
this.StatisticsEnabledCheckBox.UseVisualStyleBackColor = true; | |||
// | |||
// choiceKeptMinutesNum | |||
// | |||
this.choiceKeptMinutesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "ChoiceKeptMinutes", true)); | |||
this.choiceKeptMinutesNum.Increment = new decimal(new int[] { | |||
10, | |||
0, | |||
0, | |||
0}); | |||
this.choiceKeptMinutesNum.Location = new System.Drawing.Point(177, 204); | |||
this.choiceKeptMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | |||
this.choiceKeptMinutesNum.Maximum = new decimal(new int[] { | |||
120, | |||
0, | |||
0, | |||
0}); | |||
this.choiceKeptMinutesNum.Minimum = new decimal(new int[] { | |||
1, | |||
0, | |||
0, | |||
0}); | |||
this.choiceKeptMinutesNum.Name = "choiceKeptMinutesNum"; | |||
this.choiceKeptMinutesNum.Size = new System.Drawing.Size(100, 29); | |||
this.choiceKeptMinutesNum.TabIndex = 16; | |||
this.choiceKeptMinutesNum.Value = new decimal(new int[] { | |||
10, | |||
0, | |||
0, | |||
0}); | |||
// | |||
// byHourOfDayCheckBox | |||
// | |||
this.byHourOfDayCheckBox.AutoSize = true; | |||
this.byHourOfDayCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "ByHourOfDay", true)); | |||
this.byHourOfDayCheckBox.Location = new System.Drawing.Point(13, 127); | |||
this.byHourOfDayCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.byHourOfDayCheckBox.Name = "byHourOfDayCheckBox"; | |||
this.byHourOfDayCheckBox.Size = new System.Drawing.Size(150, 27); | |||
this.byHourOfDayCheckBox.TabIndex = 15; | |||
this.byHourOfDayCheckBox.Text = "By hour of day"; | |||
this.byHourOfDayCheckBox.UseVisualStyleBackColor = true; | |||
// | |||
// repeatTimesNum | |||
// | |||
this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true)); | |||
this.repeatTimesNum.Location = new System.Drawing.Point(34, 84); | |||
this.repeatTimesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | |||
this.repeatTimesNum.Maximum = new decimal(new int[] { | |||
10, | |||
0, | |||
0, | |||
0}); | |||
this.repeatTimesNum.Name = "repeatTimesNum"; | |||
this.repeatTimesNum.Size = new System.Drawing.Size(99, 29); | |||
this.repeatTimesNum.TabIndex = 14; | |||
this.repeatTimesNum.Value = new decimal(new int[] { | |||
4, | |||
0, | |||
0, | |||
0}); | |||
// | |||
// PackagePerPingLabel | |||
// | |||
this.PackagePerPingLabel.AutoSize = true; | |||
this.PackagePerPingLabel.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||
this.PackagePerPingLabel.Location = new System.Drawing.Point(139, 86); | |||
this.PackagePerPingLabel.Name = "PackagePerPingLabel"; | |||
this.PackagePerPingLabel.Size = new System.Drawing.Size(172, 23); | |||
this.PackagePerPingLabel.TabIndex = 13; | |||
this.PackagePerPingLabel.Text = "packages everytime"; | |||
// | |||
// splitContainer3 | |||
// | |||
this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill; | |||
this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; | |||
this.splitContainer3.IsSplitterFixed = true; | |||
this.splitContainer3.Location = new System.Drawing.Point(0, 0); | |||
this.splitContainer3.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.splitContainer3.Name = "splitContainer3"; | |||
this.splitContainer3.Orientation = System.Windows.Forms.Orientation.Horizontal; | |||
// | |||
// splitContainer3.Panel1 | |||
// | |||
this.splitContainer3.Panel1.Controls.Add(this.FinalScoreLabel); | |||
// | |||
// splitContainer3.Panel2 | |||
// | |||
this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); | |||
this.splitContainer3.Size = new System.Drawing.Size(593, 308); | |||
this.splitContainer3.SplitterDistance = 42; | |||
this.splitContainer3.SplitterWidth = 1; | |||
this.splitContainer3.TabIndex = 6; | |||
// | |||
// FinalScoreLabel | |||
// | |||
this.FinalScoreLabel.AutoSize = true; | |||
this.FinalScoreLabel.Location = new System.Drawing.Point(5, 9); | |||
this.FinalScoreLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||
this.FinalScoreLabel.Name = "FinalScoreLabel"; | |||
this.FinalScoreLabel.Size = new System.Drawing.Size(103, 23); | |||
this.FinalScoreLabel.TabIndex = 0; | |||
this.FinalScoreLabel.Text = "Final Score:"; | |||
this.CalculatinTip.SetToolTip(this.FinalScoreLabel, "(The server with the highest score would be choosen)"); | |||
// | |||
// calculationContainer | |||
// | |||
this.calculationContainer.AutoScroll = true; | |||
this.calculationContainer.Dock = System.Windows.Forms.DockStyle.Fill; | |||
this.calculationContainer.Location = new System.Drawing.Point(0, 0); | |||
this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.calculationContainer.Name = "calculationContainer"; | |||
this.calculationContainer.Size = new System.Drawing.Size(593, 265); | |||
this.calculationContainer.TabIndex = 1; | |||
// | |||
// serverSelector | |||
// | |||
this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | |||
this.serverSelector.FormattingEnabled = true; | |||
this.serverSelector.Location = new System.Drawing.Point(733, 145); | |||
this.serverSelector.Name = "serverSelector"; | |||
this.serverSelector.Size = new System.Drawing.Size(233, 31); | |||
this.serverSelector.TabIndex = 6; | |||
this.serverSelector.SelectionChangeCommitted += new System.EventHandler(this.serverSelector_SelectionChangeCommitted); | |||
// | |||
// CancelButton | |||
// | |||
this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | |||
this.CancelButton.Location = new System.Drawing.Point(865, 364); | |||
this.CancelButton.Name = "CancelButton"; | |||
this.CancelButton.Size = new System.Drawing.Size(101, 41); | |||
this.CancelButton.TabIndex = 5; | |||
this.CancelButton.Text = "Cancel"; | |||
this.CancelButton.UseVisualStyleBackColor = true; | |||
this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); | |||
// | |||
// OKButton | |||
// | |||
this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | |||
this.OKButton.Location = new System.Drawing.Point(758, 364); | |||
this.OKButton.Name = "OKButton"; | |||
this.OKButton.Size = new System.Drawing.Size(101, 41); | |||
this.OKButton.TabIndex = 4; | |||
this.OKButton.Text = "OK"; | |||
this.OKButton.UseVisualStyleBackColor = true; | |||
this.OKButton.Click += new System.EventHandler(this.OKButton_Click); | |||
// | |||
// bindingConfiguration | |||
// | |||
this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); | |||
// | |||
// StatisticsStrategyConfigurationForm | |||
// | |||
this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 23F); | |||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |||
this.AutoSize = true; | |||
this.ClientSize = new System.Drawing.Size(982, 753); | |||
this.Controls.Add(this.splitContainer1); | |||
this.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||
this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||
this.MinimumSize = new System.Drawing.Size(1000, 800); | |||
this.Name = "StatisticsStrategyConfigurationForm"; | |||
this.Text = "StatisticsStrategyConfigurationForm"; | |||
this.Text = "Statistics configuration"; | |||
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); | |||
this.chartModeSelector.ResumeLayout(false); | |||
this.chartModeSelector.PerformLayout(); | |||
this.splitContainer1.Panel1.ResumeLayout(false); | |||
this.splitContainer1.Panel2.ResumeLayout(false); | |||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); | |||
this.splitContainer1.ResumeLayout(false); | |||
this.splitContainer2.Panel1.ResumeLayout(false); | |||
this.splitContainer2.Panel1.PerformLayout(); | |||
this.splitContainer2.Panel2.ResumeLayout(false); | |||
((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); | |||
this.splitContainer2.ResumeLayout(false); | |||
((System.ComponentModel.ISupportInitialize)(this.dataCollectionMinutesNum)).EndInit(); | |||
((System.ComponentModel.ISupportInitialize)(this.choiceKeptMinutesNum)).EndInit(); | |||
((System.ComponentModel.ISupportInitialize)(this.repeatTimesNum)).EndInit(); | |||
this.splitContainer3.Panel1.ResumeLayout(false); | |||
this.splitContainer3.Panel1.PerformLayout(); | |||
this.splitContainer3.Panel2.ResumeLayout(false); | |||
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); | |||
this.splitContainer3.ResumeLayout(false); | |||
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); | |||
this.ResumeLayout(false); | |||
} | |||
#endregion | |||
private System.Windows.Forms.DataVisualization.Charting.Chart StatisticsChart; | |||
private System.Windows.Forms.CheckBox PingCheckBox; | |||
private System.Windows.Forms.Label KeepChoiceForLabel; | |||
private System.Windows.Forms.Label MinutesLabel2; | |||
private System.Windows.Forms.GroupBox chartModeSelector; | |||
private System.Windows.Forms.RadioButton allMode; | |||
private System.Windows.Forms.RadioButton dayMode; | |||
private System.Windows.Forms.SplitContainer splitContainer1; | |||
private System.Windows.Forms.Label FinalScoreLabel; | |||
private System.Windows.Forms.SplitContainer splitContainer2; | |||
private System.Windows.Forms.FlowLayoutPanel calculationContainer; | |||
private System.Windows.Forms.SplitContainer splitContainer3; | |||
private System.Windows.Forms.NumericUpDown repeatTimesNum; | |||
private System.Windows.Forms.Label PackagePerPingLabel; | |||
private System.Windows.Forms.CheckBox byHourOfDayCheckBox; | |||
private System.Windows.Forms.NumericUpDown choiceKeptMinutesNum; | |||
private System.Windows.Forms.CheckBox StatisticsEnabledCheckBox; | |||
private System.Windows.Forms.Label CollectDataPerLabel; | |||
private System.Windows.Forms.Label MinutesLabel1; | |||
private System.Windows.Forms.NumericUpDown dataCollectionMinutesNum; | |||
private System.Windows.Forms.BindingSource bindingConfiguration; | |||
private new System.Windows.Forms.Button CancelButton; | |||
private System.Windows.Forms.Button OKButton; | |||
private System.Windows.Forms.ComboBox serverSelector; | |||
private System.Windows.Forms.ToolTip CalculatinTip; | |||
} | |||
} |
@@ -1,170 +0,0 @@ | |||
using System; | |||
using System.Drawing; | |||
using System.Collections.Generic; | |||
using System.Data; | |||
using System.Linq; | |||
using System.Windows.Forms; | |||
using System.Windows.Forms.DataVisualization.Charting; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
namespace Shadowsocks.View | |||
{ | |||
public partial class StatisticsStrategyConfigurationForm : Form | |||
{ | |||
private readonly ShadowsocksController _controller; | |||
private StatisticsStrategyConfiguration _configuration; | |||
private readonly DataTable _dataTable = new DataTable(); | |||
private List<string> _servers; | |||
private readonly Series _speedSeries; | |||
private readonly Series _packageLossSeries; | |||
private readonly Series _pingSeries; | |||
public StatisticsStrategyConfigurationForm(ShadowsocksController controller) | |||
{ | |||
if (controller == null) return; | |||
InitializeComponent(); | |||
Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||
_speedSeries = StatisticsChart.Series["Speed"]; | |||
_packageLossSeries = StatisticsChart.Series["Package Loss"]; | |||
_pingSeries = StatisticsChart.Series["Ping"]; | |||
_controller = controller; | |||
_controller.ConfigChanged += (sender, args) => LoadConfiguration(); | |||
UpdateTexts(); | |||
LoadConfiguration(); | |||
Load += (sender, args) => InitData(); | |||
} | |||
private void UpdateTexts() | |||
{ | |||
I18N.TranslateForm(this); | |||
foreach (var item in StatisticsChart.Series) | |||
{ | |||
item.Name = I18N.GetString(item.Name); | |||
} | |||
} | |||
private void LoadConfiguration() | |||
{ | |||
var configs = _controller.GetCurrentConfiguration().configs; | |||
_servers = configs.Select(server => server.Identifier()).ToList(); | |||
_configuration = _controller.StatisticsConfiguration | |||
?? new StatisticsStrategyConfiguration(); | |||
if (_configuration.Calculations == null) | |||
{ | |||
_configuration = new StatisticsStrategyConfiguration(); | |||
} | |||
} | |||
private void InitData() | |||
{ | |||
bindingConfiguration.Add(_configuration); | |||
foreach (var kv in _configuration.Calculations) | |||
{ | |||
var calculation = new CalculationControl(I18N.GetString(kv.Key), kv.Value); | |||
calculationContainer.Controls.Add(calculation); | |||
} | |||
serverSelector.DataSource = _servers; | |||
_dataTable.Columns.Add("Timestamp", typeof(DateTime)); | |||
_dataTable.Columns.Add("Speed", typeof(int)); | |||
_speedSeries.XValueMember = "Timestamp"; | |||
_speedSeries.YValueMembers = "Speed"; | |||
// might be empty | |||
_dataTable.Columns.Add("Package Loss", typeof(int)); | |||
_dataTable.Columns.Add("Ping", typeof(int)); | |||
_packageLossSeries.XValueMember = "Timestamp"; | |||
_packageLossSeries.YValueMembers = "Package Loss"; | |||
_pingSeries.XValueMember = "Timestamp"; | |||
_pingSeries.YValueMembers = "Ping"; | |||
StatisticsChart.DataSource = _dataTable; | |||
LoadChartData(); | |||
StatisticsChart.DataBind(); | |||
} | |||
private void CancelButton_Click(object sender, EventArgs e) | |||
{ | |||
Close(); | |||
} | |||
private void OKButton_Click(object sender, EventArgs e) | |||
{ | |||
foreach (CalculationControl calculation in calculationContainer.Controls) | |||
{ | |||
_configuration.Calculations[calculation.Value] = calculation.Factor; | |||
} | |||
_controller?.SaveStrategyConfigurations(_configuration); | |||
_controller?.UpdateStatisticsConfiguration(StatisticsEnabledCheckBox.Checked); | |||
Close(); | |||
} | |||
private void LoadChartData() | |||
{ | |||
var serverName = _servers[serverSelector.SelectedIndex]; | |||
_dataTable.Rows.Clear(); | |||
//return directly when no data is usable | |||
if (_controller.availabilityStatistics?.FilteredStatistics == null) return; | |||
List<StatisticsRecord> statistics; | |||
if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return; | |||
IEnumerable<IGrouping<int, StatisticsRecord>> dataGroups; | |||
if (allMode.Checked) | |||
{ | |||
_pingSeries.XValueType = ChartValueType.DateTime; | |||
_packageLossSeries.XValueType = ChartValueType.DateTime; | |||
_speedSeries.XValueType = ChartValueType.DateTime; | |||
dataGroups = statistics.GroupBy(data => data.Timestamp.DayOfYear); | |||
StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "g"; | |||
StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "g"; | |||
} | |||
else | |||
{ | |||
_pingSeries.XValueType = ChartValueType.Time; | |||
_packageLossSeries.XValueType = ChartValueType.Time; | |||
_speedSeries.XValueType = ChartValueType.Time; | |||
dataGroups = statistics.GroupBy(data => data.Timestamp.Hour); | |||
StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "HH:00"; | |||
StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "HH:00"; | |||
} | |||
var finalData = from dataGroup in dataGroups | |||
orderby dataGroup.Key | |||
select new | |||
{ | |||
dataGroup.First().Timestamp, | |||
Speed = dataGroup.Max(data => data.MaxInboundSpeed) ?? 0, | |||
Ping = (int)(dataGroup.Average(data => data.AverageResponse) ?? 0), | |||
PackageLossPercentage = (int)(dataGroup.Average(data => data.PackageLoss) ?? 0) * 100 | |||
}; | |||
foreach (var data in finalData.Where(data => data.Speed != 0 || data.PackageLossPercentage != 0 || data.Ping != 0)) | |||
{ | |||
_dataTable.Rows.Add(data.Timestamp, data.Speed, data.PackageLossPercentage, data.Ping); | |||
} | |||
StatisticsChart.DataBind(); | |||
} | |||
private void serverSelector_SelectionChangeCommitted(object sender, EventArgs e) | |||
{ | |||
LoadChartData(); | |||
} | |||
private void dayMode_CheckedChanged(object sender, EventArgs e) | |||
{ | |||
LoadChartData(); | |||
} | |||
private void allMode_CheckedChanged(object sender, EventArgs e) | |||
{ | |||
LoadChartData(); | |||
} | |||
private void PingCheckBox_CheckedChanged(object sender, EventArgs e) | |||
{ | |||
repeatTimesNum.ReadOnly = !PingCheckBox.Checked; | |||
} | |||
} | |||
} |
@@ -0,0 +1,131 @@ | |||
using ReactiveUI; | |||
using ReactiveUI.Fody.Helpers; | |||
using ReactiveUI.Validation.Extensions; | |||
using ReactiveUI.Validation.Helpers; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.View; | |||
using System.Linq; | |||
using System.Reactive; | |||
using System.Reactive.Linq; | |||
namespace Shadowsocks.ViewModels | |||
{ | |||
public class ForwardProxyViewModel : ReactiveValidationObject | |||
{ | |||
public ForwardProxyViewModel() | |||
{ | |||
_config = Program.MainController.GetCurrentConfiguration(); | |||
_controller = Program.MainController; | |||
_menuViewController = Program.MenuController; | |||
if (!_config.proxy.useProxy) | |||
NoProxy = true; | |||
else if (_config.proxy.proxyType == 0) | |||
UseSocks5Proxy = true; | |||
else | |||
UseHttpProxy = true; | |||
Address = _config.proxy.proxyServer; | |||
Port = _config.proxy.proxyPort; | |||
Timeout = _config.proxy.proxyTimeout; | |||
Username = _config.proxy.authUser; | |||
Password = _config.proxy.authPwd; | |||
this.WhenAnyValue(x => x.NoProxy, x => !x) | |||
.ToPropertyEx(this, x => x.CanModifyDetails); | |||
AddressRule = this.ValidationRule( | |||
viewModel => viewModel.Address, | |||
address => !string.IsNullOrWhiteSpace(address), | |||
"Address can't be empty or whitespaces."); | |||
PortRule = this.ValidationRule( | |||
viewModel => viewModel.Port, | |||
port => port > 0 && port <= 65535, | |||
port => $"{port} is out of range (0, 65535]."); | |||
TimeoutRule = this.ValidationRule( | |||
viewModel => viewModel.Timeout, | |||
timeout => timeout > 0 && timeout <= 10, | |||
timeout => $"{timeout} is out of range (0, 10]."); | |||
var authValid = this | |||
.WhenAnyValue(x => x.Username, x => x.Password, (username, password) => new { Username = username, Password = password }) | |||
.Select(x => string.IsNullOrWhiteSpace(x.Username) == string.IsNullOrWhiteSpace(x.Password)); | |||
AuthRule = this.ValidationRule(authValid, "You must provide both username and password."); | |||
var canSave = this.IsValid(); | |||
Save = ReactiveCommand.Create(() => | |||
{ | |||
_controller.SaveProxy(GetForwardProxyConfig()); | |||
_menuViewController.CloseForwardProxyWindow(); | |||
}, canSave); | |||
Cancel = ReactiveCommand.Create(_menuViewController.CloseForwardProxyWindow); | |||
} | |||
private readonly Configuration _config; | |||
private readonly ShadowsocksController _controller; | |||
private readonly MenuViewController _menuViewController; | |||
public ValidationHelper AddressRule { get; } | |||
public ValidationHelper PortRule { get; } | |||
public ValidationHelper TimeoutRule { get; } | |||
public ValidationHelper AuthRule { get; } | |||
public ReactiveCommand<Unit, Unit> Save { get; } | |||
public ReactiveCommand<Unit, Unit> Cancel { get; } | |||
[ObservableAsProperty] | |||
public bool CanModifyDetails { get; } | |||
[Reactive] | |||
public bool NoProxy { get; set; } | |||
[Reactive] | |||
public bool UseSocks5Proxy { get; set; } | |||
[Reactive] | |||
public bool UseHttpProxy { get; set; } | |||
[Reactive] | |||
public string Address { get; set; } | |||
[Reactive] | |||
public int Port { get; set; } | |||
[Reactive] | |||
public int Timeout { get; set; } | |||
[Reactive] | |||
public string Username { get; set; } | |||
[Reactive] | |||
public string Password { get; set; } | |||
private ForwardProxyConfig GetForwardProxyConfig() | |||
{ | |||
var forwardProxyConfig = new ForwardProxyConfig() | |||
{ | |||
proxyServer = Address, | |||
proxyPort = Port, | |||
proxyTimeout = Timeout, | |||
authUser = Username, | |||
authPwd = Password | |||
}; | |||
if (NoProxy) | |||
forwardProxyConfig.useProxy = false; | |||
else if (UseSocks5Proxy) | |||
{ | |||
forwardProxyConfig.useProxy = true; | |||
forwardProxyConfig.proxyType = 0; | |||
} | |||
else | |||
{ | |||
forwardProxyConfig.useProxy = true; | |||
forwardProxyConfig.proxyType = 1; | |||
} | |||
return forwardProxyConfig; | |||
} | |||
} | |||
} |
@@ -0,0 +1,189 @@ | |||
using ReactiveUI; | |||
using ReactiveUI.Fody.Helpers; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.View; | |||
using System.Reactive; | |||
using System.Text; | |||
using System.Windows.Input; | |||
namespace Shadowsocks.ViewModels | |||
{ | |||
public class HotkeysViewModel : ReactiveObject | |||
{ | |||
public HotkeysViewModel() | |||
{ | |||
_config = Program.MainController.GetCurrentConfiguration(); | |||
_controller = Program.MainController; | |||
_menuViewController = Program.MenuController; | |||
HotkeySystemProxy = _config.hotkey.SwitchSystemProxy; | |||
HotkeyProxyMode = _config.hotkey.SwitchSystemProxyMode; | |||
HotkeyAllowLan = _config.hotkey.SwitchAllowLan; | |||
HotkeyOpenLogs = _config.hotkey.ShowLogs; | |||
HotkeySwitchPrev = _config.hotkey.ServerMoveUp; | |||
HotkeySwitchNext = _config.hotkey.ServerMoveDown; | |||
RegisterAtStartup = _config.hotkey.RegHotkeysAtStartup; | |||
HotkeySystemProxyStatus = "✔"; | |||
HotkeyProxyModeStatus = "✔"; | |||
HotkeyAllowLanStatus = "✔"; | |||
HotkeyOpenLogsStatus = "✔"; | |||
HotkeySwitchPrevStatus = "✔"; | |||
HotkeySwitchNextStatus = "✔"; | |||
RegisterAll = ReactiveCommand.Create(() => RegisterAllAndUpdateStatus()); | |||
Save = ReactiveCommand.Create(() => RegisterAllAndUpdateStatus(true)); | |||
Cancel = ReactiveCommand.Create(_menuViewController.CloseHotkeysWindow); | |||
} | |||
private readonly Configuration _config; | |||
private readonly ShadowsocksController _controller; | |||
private readonly MenuViewController _menuViewController; | |||
public ReactiveCommand<Unit, Unit> RegisterAll { get; } | |||
public ReactiveCommand<Unit, Unit> Save { get; } | |||
public ReactiveCommand<Unit, Unit> Cancel { get; } | |||
[Reactive] | |||
public string HotkeySystemProxy { get; set; } | |||
[Reactive] | |||
public string HotkeyProxyMode { get; set; } | |||
[Reactive] | |||
public string HotkeyAllowLan { get; set; } | |||
[Reactive] | |||
public string HotkeyOpenLogs { get; set; } | |||
[Reactive] | |||
public string HotkeySwitchPrev { get; set; } | |||
[Reactive] | |||
public string HotkeySwitchNext { get; set; } | |||
[Reactive] | |||
public bool RegisterAtStartup { get; set; } | |||
[Reactive] | |||
public string HotkeySystemProxyStatus { get; set; } | |||
[Reactive] | |||
public string HotkeyProxyModeStatus { get; set; } | |||
[Reactive] | |||
public string HotkeyAllowLanStatus { get; set; } | |||
[Reactive] | |||
public string HotkeyOpenLogsStatus { get; set; } | |||
[Reactive] | |||
public string HotkeySwitchPrevStatus { get; set; } | |||
[Reactive] | |||
public string HotkeySwitchNextStatus { get; set; } | |||
public void RecordKeyDown(int hotkeyIndex, KeyEventArgs keyEventArgs) | |||
{ | |||
var recordedKeyStringBuilder = new StringBuilder(); | |||
// record modifiers | |||
if ((Keyboard.Modifiers & ModifierKeys.Control) > 0) | |||
recordedKeyStringBuilder.Append("Ctrl+"); | |||
if ((Keyboard.Modifiers & ModifierKeys.Alt) > 0) | |||
recordedKeyStringBuilder.Append("Alt+"); | |||
if ((Keyboard.Modifiers & ModifierKeys.Shift) > 0) | |||
recordedKeyStringBuilder.Append("Shift+"); | |||
// record other keys when at least one modifier is pressed | |||
if (recordedKeyStringBuilder.Length > 0 && (keyEventArgs.Key < Key.LeftShift || keyEventArgs.Key > Key.RightAlt)) | |||
recordedKeyStringBuilder.Append(keyEventArgs.Key); | |||
switch (hotkeyIndex) | |||
{ | |||
case 0: | |||
HotkeySystemProxy = recordedKeyStringBuilder.ToString(); | |||
break; | |||
case 1: | |||
HotkeyProxyMode = recordedKeyStringBuilder.ToString(); | |||
break; | |||
case 2: | |||
HotkeyAllowLan = recordedKeyStringBuilder.ToString(); | |||
break; | |||
case 3: | |||
HotkeyOpenLogs = recordedKeyStringBuilder.ToString(); | |||
break; | |||
case 4: | |||
HotkeySwitchPrev = recordedKeyStringBuilder.ToString(); | |||
break; | |||
case 5: | |||
HotkeySwitchNext = recordedKeyStringBuilder.ToString(); | |||
break; | |||
} | |||
} | |||
public void FinishOnKeyUp(int hotkeyIndex, KeyEventArgs keyEventArgs) | |||
{ | |||
switch (hotkeyIndex) | |||
{ | |||
case 0: | |||
if (HotkeySystemProxy.EndsWith("+")) | |||
HotkeySystemProxy = ""; | |||
break; | |||
case 1: | |||
if (HotkeyProxyMode.EndsWith("+")) | |||
HotkeyProxyMode = ""; | |||
break; | |||
case 2: | |||
if (HotkeyAllowLan.EndsWith("+")) | |||
HotkeyAllowLan = ""; | |||
break; | |||
case 3: | |||
if (HotkeyOpenLogs.EndsWith("+")) | |||
HotkeyOpenLogs = ""; | |||
break; | |||
case 4: | |||
if (HotkeySwitchPrev.EndsWith("+")) | |||
HotkeySwitchPrev = ""; | |||
break; | |||
case 5: | |||
if (HotkeySwitchNext.EndsWith("+")) | |||
HotkeySwitchNext = ""; | |||
break; | |||
} | |||
} | |||
private void RegisterAllAndUpdateStatus(bool save = false) | |||
{ | |||
HotkeySystemProxyStatus = HotkeyReg.RegHotkeyFromString(HotkeySystemProxy, "SwitchSystemProxyCallback") ? "✔" : "❌"; | |||
HotkeyProxyModeStatus = HotkeyReg.RegHotkeyFromString(HotkeyProxyMode, "SwitchSystemProxyModeCallback") ? "✔" : "❌"; | |||
HotkeyAllowLanStatus = HotkeyReg.RegHotkeyFromString(HotkeyAllowLan, "SwitchAllowLanCallback") ? "✔" : "❌"; | |||
HotkeyOpenLogsStatus = HotkeyReg.RegHotkeyFromString(HotkeyOpenLogs, "ShowLogsCallback") ? "✔" : "❌"; | |||
HotkeySwitchPrevStatus = HotkeyReg.RegHotkeyFromString(HotkeySwitchPrev, "ServerMoveUpCallback") ? "✔" : "❌"; | |||
HotkeySwitchNextStatus = HotkeyReg.RegHotkeyFromString(HotkeySwitchNext, "ServerMoveDownCallback") ? "✔" : "❌"; | |||
if (HotkeySystemProxyStatus == "✔" && | |||
HotkeyProxyModeStatus == "✔" && | |||
HotkeyAllowLanStatus == "✔" && | |||
HotkeyOpenLogsStatus == "✔" && | |||
HotkeySwitchPrevStatus == "✔" && | |||
HotkeySwitchNextStatus == "✔" && save) | |||
{ | |||
_controller.SaveHotkeyConfig(GetHotkeyConfig); | |||
_menuViewController.CloseHotkeysWindow(); | |||
} | |||
} | |||
private HotkeyConfig GetHotkeyConfig => new HotkeyConfig() | |||
{ | |||
SwitchSystemProxy = HotkeySystemProxy, | |||
SwitchSystemProxyMode = HotkeyProxyMode, | |||
SwitchAllowLan = HotkeyAllowLan, | |||
ShowLogs = HotkeyOpenLogs, | |||
ServerMoveUp = HotkeySwitchPrev, | |||
ServerMoveDown = HotkeySwitchNext, | |||
RegHotkeysAtStartup = RegisterAtStartup | |||
}; | |||
} | |||
} |
@@ -0,0 +1,115 @@ | |||
using ReactiveUI; | |||
using ReactiveUI.Fody.Helpers; | |||
using ReactiveUI.Validation.Extensions; | |||
using ReactiveUI.Validation.Helpers; | |||
using Shadowsocks.Controller; | |||
using Shadowsocks.Localization; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.View; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections.ObjectModel; | |||
using System.Linq; | |||
using System.Reactive; | |||
using System.Reactive.Linq; | |||
using System.Text; | |||
using System.Windows; | |||
namespace Shadowsocks.ViewModels | |||
{ | |||
public class OnlineConfigViewModel : ReactiveValidationObject | |||
{ | |||
public OnlineConfigViewModel() | |||
{ | |||
_config = Program.MainController.GetCurrentConfiguration(); | |||
_controller = Program.MainController; | |||
_menuViewController = Program.MenuController; | |||
Sources = new ObservableCollection<string>(_config.onlineConfigSource); | |||
SelectedSource = ""; | |||
Address = ""; | |||
// TODO in v5: if http:// show warning as materialDesign:HintAssist.HelperText | |||
AddressRule = this.ValidationRule( | |||
viewModel => viewModel.Address, | |||
address => address.StartsWith("http://"), | |||
"Warning: getting online configuration from plain HTTP sources is NOT secure!"); | |||
var canUpdateCopyRemove = this.WhenAnyValue( | |||
x => x.SelectedSource, | |||
selectedSource => !string.IsNullOrWhiteSpace(selectedSource)); | |||
var canUpdateAll = this.WhenAnyValue( | |||
x => x.Sources.Count, | |||
count => count > 0); | |||
var canAdd = this.WhenAnyValue( | |||
x => x.Address, | |||
address => Uri.IsWellFormedUriString(address, UriKind.Absolute) && | |||
(address.StartsWith("https://") || address.StartsWith("http://"))); | |||
Update = ReactiveCommand.CreateFromTask(() => _controller.UpdateOnlineConfig(SelectedSource), canUpdateCopyRemove); | |||
UpdateAll = ReactiveCommand.CreateFromTask(_controller.UpdateAllOnlineConfig, canUpdateAll); | |||
CopyLink = ReactiveCommand.Create(() => Clipboard.SetText(SelectedSource), canUpdateCopyRemove); | |||
Remove = ReactiveCommand.Create(() => | |||
{ | |||
bool result; | |||
var urlToRemove = SelectedSource; // save it here because SelectedSource is lost once we remove the selection | |||
do | |||
{ | |||
result = Sources.Remove(urlToRemove); | |||
} while (result); | |||
_controller.RemoveOnlineConfig(urlToRemove); | |||
}, canUpdateCopyRemove); | |||
Add = ReactiveCommand.Create(() => | |||
{ | |||
Sources.Add(Address); | |||
SelectedSource = Address; | |||
_controller.SaveOnlineConfigSource(Sources.ToList()); | |||
Address = ""; | |||
}, canAdd); | |||
// TODO in v5: use MaterialDesignThemes snackbar messages | |||
this.WhenAnyObservable(x => x.Update) | |||
.Subscribe(x => | |||
{ | |||
if (x) | |||
MessageBox.Show(LocalizationProvider.GetLocalizedValue<string>("sip008UpdateSuccess")); | |||
else | |||
MessageBox.Show(LocalizationProvider.GetLocalizedValue<string>("sip008UpdateFailure")); | |||
}); | |||
this.WhenAnyObservable(x => x.UpdateAll) | |||
.Subscribe(x => | |||
{ | |||
if (x.Count == 0) | |||
MessageBox.Show(LocalizationProvider.GetLocalizedValue<string>("sip008UpdateAllSuccess")); | |||
else | |||
{ | |||
var stringBuilder = new StringBuilder(LocalizationProvider.GetLocalizedValue<string>("sip008UpdateAllFailure")); | |||
foreach (var url in x) | |||
stringBuilder.AppendLine(url); | |||
MessageBox.Show(stringBuilder.ToString()); | |||
} | |||
}); | |||
} | |||
private readonly Configuration _config; | |||
private readonly ShadowsocksController _controller; | |||
private readonly MenuViewController _menuViewController; | |||
public ValidationHelper AddressRule { get; } | |||
public ReactiveCommand<Unit, bool> Update { get; } | |||
public ReactiveCommand<Unit, List<string>> UpdateAll { get; } | |||
public ReactiveCommand<Unit, Unit> CopyLink { get; } | |||
public ReactiveCommand<Unit, Unit> Remove { get; } | |||
public ReactiveCommand<Unit, Unit> Add { get; } | |||
[Reactive] | |||
public ObservableCollection<string> Sources { get; private set; } | |||
[Reactive] | |||
public string SelectedSource { get; set; } | |||
[Reactive] | |||
public string Address { get; set; } | |||
} | |||
} |
@@ -0,0 +1,96 @@ | |||
using ReactiveUI; | |||
using ReactiveUI.Fody.Helpers; | |||
using Shadowsocks.Model; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Drawing; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Reactive; | |||
using System.Windows; | |||
using System.Windows.Media.Imaging; | |||
namespace Shadowsocks.ViewModels | |||
{ | |||
public class ServerSharingViewModel : ReactiveObject | |||
{ | |||
/// <summary> | |||
/// The view model class for the server sharing user control. | |||
/// </summary> | |||
public ServerSharingViewModel() | |||
{ | |||
_config = Program.MainController.GetCurrentConfiguration(); | |||
Servers = _config.configs; | |||
SelectedServer = Servers[0]; | |||
this.WhenAnyValue(x => x.SelectedServer) | |||
.Subscribe(_ => UpdateUrlAndImage()); | |||
CopyLink = ReactiveCommand.Create(() => Clipboard.SetText(SelectedServerUrl)); | |||
} | |||
private readonly Configuration _config; | |||
public ReactiveCommand<Unit, Unit> CopyLink { get; } | |||
[Reactive] | |||
public List<Server> Servers { get; private set; } | |||
[Reactive] | |||
public Server SelectedServer { get; set; } | |||
[Reactive] | |||
public string SelectedServerUrl { get; private set; } | |||
[Reactive] | |||
public BitmapImage SelectedServerUrlImage { get; private set; } | |||
/// <summary> | |||
/// Called when SelectedServer changed | |||
/// to update SelectedServerUrl and SelectedServerUrlImage | |||
/// </summary> | |||
private void UpdateUrlAndImage() | |||
{ | |||
// update SelectedServerUrl | |||
SelectedServerUrl = SelectedServer.GetURL(_config.generateLegacyUrl); | |||
// generate QR code | |||
var qrCode = ZXing.QrCode.Internal.Encoder.encode(SelectedServerUrl, ZXing.QrCode.Internal.ErrorCorrectionLevel.L); | |||
var byteMatrix = qrCode.Matrix; | |||
// paint bitmap | |||
int blockSize = Math.Max(1024 / byteMatrix.Height, 1); | |||
Bitmap drawArea = new Bitmap((byteMatrix.Width * blockSize), (byteMatrix.Height * blockSize)); | |||
using (var graphics = Graphics.FromImage(drawArea)) | |||
{ | |||
graphics.Clear(Color.White); | |||
using (var solidBrush = new SolidBrush(Color.Black)) | |||
{ | |||
for (int row = 0; row < byteMatrix.Width; row++) | |||
{ | |||
for (int column = 0; column < byteMatrix.Height; column++) | |||
{ | |||
if (byteMatrix[row, column] != 0) | |||
{ | |||
graphics.FillRectangle(solidBrush, blockSize * row, blockSize * column, blockSize, blockSize); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
// transform to BitmapImage for binding | |||
BitmapImage bitmapImage = new BitmapImage(); | |||
using (MemoryStream memoryStream = new MemoryStream()) | |||
{ | |||
drawArea.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp); | |||
memoryStream.Position = 0; | |||
bitmapImage.BeginInit(); | |||
bitmapImage.StreamSource = memoryStream; | |||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad; | |||
bitmapImage.EndInit(); | |||
} | |||
SelectedServerUrlImage = bitmapImage; | |||
} | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
using Newtonsoft.Json.Linq; | |||
using ReactiveUI; | |||
using Shadowsocks.Controller; | |||
using System.Reactive; | |||
namespace Shadowsocks.ViewModels | |||
{ | |||
public class VersionUpdatePromptViewModel : ReactiveObject | |||
{ | |||
public VersionUpdatePromptViewModel(JToken releaseObject) | |||
{ | |||
_updateChecker = Program.MenuController.updateChecker; | |||
_releaseObject = releaseObject; | |||
ReleaseNotes = string.Concat( | |||
$"# {((bool)_releaseObject["prerelease"] ? "⚠ Pre-release" : "ℹ Release")} {(string)_releaseObject["tag_name"] ?? "Failed to get tag name"}\r\n", | |||
(string)_releaseObject["body"] ?? "Failed to get release notes"); | |||
Update = ReactiveCommand.CreateFromTask(_updateChecker.DoUpdate); | |||
SkipVersion = ReactiveCommand.Create(_updateChecker.SkipUpdate); | |||
NotNow = ReactiveCommand.Create(_updateChecker.CloseVersionUpdatePromptWindow); | |||
} | |||
private readonly UpdateChecker _updateChecker; | |||
private readonly JToken _releaseObject; | |||
public string ReleaseNotes { get; } | |||
public ReactiveCommand<Unit, Unit> Update { get; } | |||
public ReactiveCommand<Unit, Unit> SkipVersion { get; } | |||
public ReactiveCommand<Unit, Unit> NotNow { get; } | |||
} | |||
} |
@@ -0,0 +1,100 @@ | |||
<reactiveui:ReactiveUserControl | |||
x:Class="Shadowsocks.Views.ForwardProxyView" | |||
x:TypeArguments="vms:ForwardProxyViewModel" | |||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||
xmlns:local="clr-namespace:Shadowsocks.Views" | |||
xmlns:vms="clr-namespace:Shadowsocks.ViewModels" | |||
xmlns:reactiveui="http://reactiveui.net" | |||
xmlns:lex="http://wpflocalizeextension.codeplex.com" | |||
lex:LocalizeDictionary.DesignCulture="en" | |||
lex:ResxLocalizationProvider.DefaultAssembly="Shadowsocks" | |||
lex:ResxLocalizationProvider.DefaultDictionary="Strings" | |||
mc:Ignorable="d" | |||
d:DesignHeight="380" d:DesignWidth="280"> | |||
<Grid Margin="8"> | |||
<Grid.RowDefinitions> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="*" /> | |||
</Grid.RowDefinitions> | |||
<Grid.ColumnDefinitions> | |||
<ColumnDefinition Width="Auto" /> | |||
<ColumnDefinition Width="*" /> | |||
</Grid.ColumnDefinitions> | |||
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" | |||
Margin="4 8 4 8" | |||
FontSize="16" | |||
Text="{lex:Loc Type}"/> | |||
<RadioButton x:Name="noProxyRadioButton" Grid.Row="1" Grid.Column="0" | |||
Margin="4" | |||
GroupName="ProxyType" | |||
Content="{lex:Loc NoProxy}"/> | |||
<RadioButton x:Name="socks5RadioButton" Grid.Row="2" Grid.Column="0" | |||
Margin="4" | |||
GroupName="ProxyType" | |||
Content="{lex:Loc SOCKS5}"/> | |||
<RadioButton x:Name="httpRadioButton" Grid.Row="3" Grid.Column="0" | |||
Margin="4" | |||
GroupName="ProxyType" | |||
Content="{lex:Loc HTTP}"/> | |||
<TextBlock Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" | |||
Margin="4 8 4 8" | |||
FontSize="16" | |||
Text="{lex:Loc Details}"/> | |||
<TextBlock Grid.Row="5" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc Address}"/> | |||
<TextBox x:Name="addressTextBox" Grid.Row="5" Grid.Column="1" | |||
Margin="4"/> | |||
<TextBlock Grid.Row="6" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc Port}"/> | |||
<TextBox x:Name="portTextBox" Grid.Row="6" Grid.Column="1" | |||
Margin="4" | |||
HorizontalContentAlignment="Right"/> | |||
<TextBlock Grid.Row="7" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc Timeout}"/> | |||
<TextBox x:Name="timeoutTextBox" Grid.Row="7" Grid.Column="1" | |||
Margin="4" | |||
HorizontalContentAlignment="Right"/> | |||
<TextBlock Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" | |||
Margin="4 8 4 8" | |||
FontSize="16" | |||
Text="{lex:Loc CredentialsOptional}"/> | |||
<TextBlock Grid.Row="9" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc Username}"/> | |||
<TextBox x:Name="usernameTextBox" Grid.Row="9" Grid.Column="1" | |||
Margin="4"/> | |||
<TextBlock Grid.Row="10" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc Password}"/> | |||
<TextBox x:Name="passwordTextBox" Grid.Row="10" Grid.Column="1" | |||
Margin="4"/> | |||
<StackPanel Grid.Row="11" Grid.Column="0" Grid.ColumnSpan="2" | |||
Orientation="Horizontal" | |||
HorizontalAlignment="Right" | |||
VerticalAlignment="Bottom"> | |||
<Button x:Name="saveButton" Margin="4" MinWidth="75" Content="{lex:Loc}"/> | |||
<Button x:Name="cancelButton" Margin="4" MinWidth="75" Content="{lex:Loc}"/> | |||
</StackPanel> | |||
</Grid> | |||
</reactiveui:ReactiveUserControl> |
@@ -0,0 +1,84 @@ | |||
using ReactiveUI; | |||
using Shadowsocks.ViewModels; | |||
using System.Reactive.Disposables; | |||
namespace Shadowsocks.Views | |||
{ | |||
/// <summary> | |||
/// Interaction logic for ForwardProxyView.xaml | |||
/// </summary> | |||
public partial class ForwardProxyView : ReactiveUserControl<ForwardProxyViewModel> | |||
{ | |||
public ForwardProxyView() | |||
{ | |||
InitializeComponent(); | |||
ViewModel = new ForwardProxyViewModel(); | |||
this.WhenActivated(disposables => | |||
{ | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.NoProxy, | |||
view => view.noProxyRadioButton.IsChecked) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.UseSocks5Proxy, | |||
view => view.socks5RadioButton.IsChecked) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.UseHttpProxy, | |||
view => view.httpRadioButton.IsChecked) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.Address, | |||
view => view.addressTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.Port, | |||
view => view.portTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.Timeout, | |||
view => view.timeoutTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.CanModifyDetails, | |||
view => view.addressTextBox.IsEnabled) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.CanModifyDetails, | |||
view => view.portTextBox.IsEnabled) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.CanModifyDetails, | |||
view => view.timeoutTextBox.IsEnabled) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.Username, | |||
view => view.usernameTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.Password, | |||
view => view.passwordTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.UseHttpProxy, | |||
view => view.usernameTextBox.IsEnabled) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.UseHttpProxy, | |||
view => view.passwordTextBox.IsEnabled) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.Save, | |||
view => view.saveButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.Cancel, | |||
view => view.cancelButton) | |||
.DisposeWith(disposables); | |||
}); | |||
} | |||
} | |||
} |
@@ -0,0 +1,115 @@ | |||
<reactiveui:ReactiveUserControl | |||
x:Class="Shadowsocks.Views.HotkeysView" | |||
x:TypeArguments="vms:HotkeysViewModel" | |||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||
xmlns:local="clr-namespace:Shadowsocks.Views" | |||
xmlns:vms="clr-namespace:Shadowsocks.ViewModels" | |||
xmlns:reactiveui="http://reactiveui.net" | |||
xmlns:lex="http://wpflocalizeextension.codeplex.com" | |||
lex:LocalizeDictionary.DesignCulture="en" | |||
lex:ResxLocalizationProvider.DefaultAssembly="Shadowsocks" | |||
lex:ResxLocalizationProvider.DefaultDictionary="Strings" | |||
mc:Ignorable="d" | |||
d:DesignHeight="240" d:DesignWidth="280"> | |||
<Grid Margin="8"> | |||
<Grid.RowDefinitions> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="*" /> | |||
</Grid.RowDefinitions> | |||
<Grid.ColumnDefinitions> | |||
<ColumnDefinition Width="Auto" /> | |||
<ColumnDefinition Width="*" /> | |||
<ColumnDefinition Width="Auto" /> | |||
</Grid.ColumnDefinitions> | |||
<TextBlock Grid.Row="0" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc ToggleSystemProxy}"/> | |||
<TextBox x:Name="systemProxyTextBox" | |||
Grid.Row="0" Grid.Column="1" | |||
Margin="4" IsReadOnly="True" /> | |||
<TextBlock x:Name="systemProxyStatusTextBlock" | |||
Grid.Row="0" Grid.Column="2" | |||
Margin="4" /> | |||
<TextBlock Grid.Row="1" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc ToggleProxyMode}"/> | |||
<TextBox x:Name="proxyModeTextBox" | |||
Grid.Row="1" Grid.Column="1" | |||
Margin="4" IsReadOnly="True" /> | |||
<TextBlock x:Name="proxyModeStatusTextBlock" | |||
Grid.Row="1" Grid.Column="2" | |||
Margin="4" /> | |||
<TextBlock Grid.Row="2" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc AllowClientsFromLAN}"/> | |||
<TextBox x:Name="allowLanTextBox" | |||
Grid.Row="2" Grid.Column="1" | |||
Margin="4" IsReadOnly="True" /> | |||
<TextBlock x:Name="allowLanStatusTextBlock" | |||
Grid.Row="2" Grid.Column="2" | |||
Margin="4" /> | |||
<TextBlock Grid.Row="3" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc OpenLogsWindow}"/> | |||
<TextBox x:Name="openLogsTextBox" | |||
Grid.Row="3" Grid.Column="1" | |||
Margin="4" IsReadOnly="True" /> | |||
<TextBlock x:Name="openLogsStatusTextBlock" | |||
Grid.Row="3" Grid.Column="2" | |||
Margin="4" /> | |||
<TextBlock Grid.Row="4" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc SwitchToPreviousServer}"/> | |||
<TextBox x:Name="switchPrevTextBox" | |||
Grid.Row="4" Grid.Column="1" | |||
Margin="4" IsReadOnly="True" /> | |||
<TextBlock x:Name="switchPrevStatusTextBlock" | |||
Grid.Row="4" Grid.Column="2" | |||
Margin="4" /> | |||
<TextBlock Grid.Row="5" Grid.Column="0" | |||
Margin="4" | |||
Text="{lex:Loc SwitchToNextServer}"/> | |||
<TextBox x:Name="switchNextTextBox" | |||
Grid.Row="5" Grid.Column="1" | |||
Margin="4" IsReadOnly="True" /> | |||
<TextBlock x:Name="switchNextStatusTextBlock" | |||
Grid.Row="5" Grid.Column="2" | |||
Margin="4" /> | |||
<StackPanel Grid.Row="6" Grid.ColumnSpan="2" Orientation="Horizontal"> | |||
<CheckBox x:Name="registerAtStartupCheckBox" Margin="4" VerticalAlignment="Center"/> | |||
<TextBlock Margin="4" Text="{lex:Loc RegisterHotkeysAtStartup}"/> | |||
</StackPanel> | |||
<StackPanel Grid.Row="7" | |||
Grid.ColumnSpan="3" | |||
Orientation="Horizontal" | |||
HorizontalAlignment="Left" | |||
VerticalAlignment="Bottom"> | |||
<Button x:Name="registerAllButton" Margin="4" MinWidth="75" Content="{lex:Loc}"/> | |||
</StackPanel> | |||
<StackPanel Grid.Row="7" | |||
Grid.ColumnSpan="3" | |||
Orientation="Horizontal" | |||
HorizontalAlignment="Right" | |||
VerticalAlignment="Bottom"> | |||
<Button x:Name="saveButton" Margin="4" MinWidth="75" Content="{lex:Loc}"/> | |||
<Button x:Name="cancelButton" Margin="4" MinWidth="75" Content="{lex:Loc}"/> | |||
</StackPanel> | |||
</Grid> | |||
</reactiveui:ReactiveUserControl> |
@@ -0,0 +1,169 @@ | |||
using ReactiveUI; | |||
using Shadowsocks.ViewModels; | |||
using System; | |||
using System.Reactive.Disposables; | |||
using System.Windows; | |||
using System.Windows.Controls; | |||
using System.Windows.Data; | |||
using System.Windows.Documents; | |||
using System.Windows.Input; | |||
using System.Windows.Media; | |||
using System.Windows.Media.Imaging; | |||
using System.Windows.Navigation; | |||
namespace Shadowsocks.Views | |||
{ | |||
/// <summary> | |||
/// Interaction logic for HotkeysView.xaml | |||
/// </summary> | |||
public partial class HotkeysView : ReactiveUserControl<HotkeysViewModel> | |||
{ | |||
public HotkeysView() | |||
{ | |||
InitializeComponent(); | |||
ViewModel = new HotkeysViewModel(); | |||
this.WhenActivated(disposables => | |||
{ | |||
systemProxyTextBox | |||
.Events().KeyDown | |||
.Subscribe(keyEventArgs => ViewModel.RecordKeyDown(0, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
systemProxyTextBox | |||
.Events().KeyUp | |||
.Subscribe(keyEventArgs => ViewModel.FinishOnKeyUp(0, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
proxyModeTextBox | |||
.Events().KeyDown | |||
.Subscribe(keyEventArgs => ViewModel.RecordKeyDown(1, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
proxyModeTextBox | |||
.Events().KeyUp | |||
.Subscribe(keyEventArgs => ViewModel.FinishOnKeyUp(1, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
allowLanTextBox | |||
.Events().KeyDown | |||
.Subscribe(keyEventArgs => ViewModel.RecordKeyDown(2, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
allowLanTextBox | |||
.Events().KeyUp | |||
.Subscribe(keyEventArgs => ViewModel.FinishOnKeyUp(2, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
openLogsTextBox | |||
.Events().KeyDown | |||
.Subscribe(keyEventArgs => ViewModel.RecordKeyDown(3, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
openLogsTextBox | |||
.Events().KeyUp | |||
.Subscribe(keyEventArgs => ViewModel.FinishOnKeyUp(3, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
switchPrevTextBox | |||
.Events().KeyDown | |||
.Subscribe(keyEventArgs => ViewModel.RecordKeyDown(4, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
switchPrevTextBox | |||
.Events().KeyUp | |||
.Subscribe(keyEventArgs => ViewModel.FinishOnKeyUp(4, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
switchNextTextBox | |||
.Events().KeyDown | |||
.Subscribe(keyEventArgs => ViewModel.RecordKeyDown(5, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
switchNextTextBox | |||
.Events().KeyUp | |||
.Subscribe(keyEventArgs => ViewModel.FinishOnKeyUp(5, keyEventArgs)) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeySystemProxy, | |||
view => view.systemProxyTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeyProxyMode, | |||
view => view.proxyModeTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeyAllowLan, | |||
view => view.allowLanTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeyOpenLogs, | |||
view => view.openLogsTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeySwitchPrev, | |||
view => view.switchPrevTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeySwitchNext, | |||
view => view.switchNextTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.RegisterAtStartup, | |||
view => view.registerAtStartupCheckBox.IsChecked) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeySystemProxyStatus, | |||
view => view.systemProxyStatusTextBlock.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeyProxyModeStatus, | |||
view => view.proxyModeStatusTextBlock.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeyAllowLanStatus, | |||
view => view.allowLanStatusTextBlock.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeyOpenLogsStatus, | |||
view => view.openLogsStatusTextBlock.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeySwitchPrevStatus, | |||
view => view.switchPrevStatusTextBlock.Text) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.HotkeySwitchNextStatus, | |||
view => view.switchNextStatusTextBlock.Text) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.RegisterAll, | |||
view => view.registerAllButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.Save, | |||
view => view.saveButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.Cancel, | |||
view => view.cancelButton) | |||
.DisposeWith(disposables); | |||
}); | |||
} | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
<reactiveui:ReactiveUserControl | |||
x:Class="Shadowsocks.Views.OnlineConfigView" | |||
x:TypeArguments="vms:OnlineConfigViewModel" | |||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||
xmlns:local="clr-namespace:Shadowsocks.Views" | |||
xmlns:vms="clr-namespace:Shadowsocks.ViewModels" | |||
xmlns:reactiveui="http://reactiveui.net" | |||
xmlns:lex="http://wpflocalizeextension.codeplex.com" | |||
lex:LocalizeDictionary.DesignCulture="en" | |||
lex:ResxLocalizationProvider.DefaultAssembly="Shadowsocks" | |||
lex:ResxLocalizationProvider.DefaultDictionary="Strings" | |||
mc:Ignorable="d" | |||
d:DesignHeight="500" d:DesignWidth="480"> | |||
<Grid Margin="8"> | |||
<Grid.RowDefinitions> | |||
<RowDefinition Height="*" /> | |||
<RowDefinition Height="Auto" /> | |||
</Grid.RowDefinitions> | |||
<Grid.ColumnDefinitions> | |||
<ColumnDefinition Width="Auto" /> | |||
<ColumnDefinition Width="*" /> | |||
<ColumnDefinition Width="Auto" /> | |||
</Grid.ColumnDefinitions> | |||
<ListBox x:Name="sourcesListBox" Margin="8 8 4 4" | |||
Grid.Row="0" | |||
Grid.Column="0" | |||
Grid.ColumnSpan="2"> | |||
<ListBox.ItemTemplate> | |||
<DataTemplate> | |||
<TextBlock Text="{Binding}"/> | |||
</DataTemplate> | |||
</ListBox.ItemTemplate> | |||
</ListBox> | |||
<StackPanel Grid.Row="0" Grid.Column="2"> | |||
<Button x:Name="updateButton" Margin="4 8 8 4" | |||
MinWidth="75" Content="{lex:Loc}"/> | |||
<Button x:Name="updateAllButton" Margin="4 4 8 4" | |||
MinWidth="75" Content="{lex:Loc}"/> | |||
<Button x:Name="copyLinkButton" Margin="4 4 8 8" | |||
MinWidth="75" Content="{lex:Loc}"/> | |||
<Button x:Name="removeButton" Margin="4 8 8 8" | |||
MinWidth="75" Content="{lex:Loc}"/> | |||
</StackPanel> | |||
<TextBlock Margin="8 4 4 8" | |||
Grid.Row="1" | |||
Grid.Column="0">URL</TextBlock> | |||
<TextBox x:Name="urlTextBox" Margin="4 4 4 8" | |||
Grid.Row="1" | |||
Grid.Column="1"/> | |||
<Button x:Name="addButton" Margin="4 4 8 8" | |||
Grid.Row="1" | |||
Grid.Column="2" | |||
MinWidth="75" | |||
Content="{lex:Loc}"/> | |||
</Grid> | |||
</reactiveui:ReactiveUserControl> |
@@ -0,0 +1,54 @@ | |||
using ReactiveUI; | |||
using Shadowsocks.ViewModels; | |||
using System.Reactive.Disposables; | |||
namespace Shadowsocks.Views | |||
{ | |||
/// <summary> | |||
/// Interaction logic for OnlineConfigView.xaml | |||
/// </summary> | |||
public partial class OnlineConfigView : ReactiveUserControl<OnlineConfigViewModel> | |||
{ | |||
public OnlineConfigView() | |||
{ | |||
InitializeComponent(); | |||
ViewModel = new OnlineConfigViewModel(); | |||
this.WhenActivated(disposables => | |||
{ | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.Sources, | |||
view => view.sourcesListBox.ItemsSource) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.SelectedSource, | |||
view => view.sourcesListBox.SelectedItem) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.Address, | |||
view => view.urlTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.Update, | |||
view => view.updateButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.UpdateAll, | |||
view => view.updateAllButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.CopyLink, | |||
view => view.copyLinkButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.Remove, | |||
view => view.removeButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.Add, | |||
view => view.addButton) | |||
.DisposeWith(disposables); | |||
}); | |||
} | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
<reactiveui:ReactiveUserControl | |||
x:Class="Shadowsocks.Views.ServerSharingView" | |||
x:TypeArguments="vms:ServerSharingViewModel" | |||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||
xmlns:local="clr-namespace:Shadowsocks.Views" | |||
xmlns:vms="clr-namespace:Shadowsocks.ViewModels" | |||
xmlns:reactiveui="http://reactiveui.net" | |||
xmlns:lex="http://wpflocalizeextension.codeplex.com" | |||
lex:LocalizeDictionary.DesignCulture="en" | |||
lex:ResxLocalizationProvider.DefaultAssembly="Shadowsocks" | |||
lex:ResxLocalizationProvider.DefaultDictionary="Strings" | |||
mc:Ignorable="d" | |||
d:DesignHeight="400" d:DesignWidth="660"> | |||
<Grid Margin="8"> | |||
<Grid.RowDefinitions> | |||
<RowDefinition Height="*" /> | |||
<RowDefinition Height="Auto" /> | |||
</Grid.RowDefinitions> | |||
<Grid.ColumnDefinitions> | |||
<ColumnDefinition Width="*" /> | |||
<ColumnDefinition Width="*" /> | |||
</Grid.ColumnDefinitions> | |||
<Image x:Name="qrCodeImage" Grid.Row="0" | |||
Grid.Column="0" | |||
Margin="8"/> | |||
<ListBox x:Name="serversListBox" Grid.Row="0" | |||
Grid.Column="1" | |||
Margin="8"> | |||
<ListBox.ItemTemplate> | |||
<DataTemplate> | |||
<TextBlock Text="{Binding}"/> | |||
</DataTemplate> | |||
</ListBox.ItemTemplate> | |||
</ListBox> | |||
<Grid Grid.Row="1" | |||
Grid.Column="0" | |||
Grid.ColumnSpan="2"> | |||
<Grid.ColumnDefinitions> | |||
<ColumnDefinition Width="*" /> | |||
<ColumnDefinition Width="Auto" /> | |||
</Grid.ColumnDefinitions> | |||
<TextBox x:Name="urlTextBox" | |||
Grid.Column="0" | |||
Margin="8 8 4 8" | |||
IsReadOnly="True" | |||
PreviewMouseDoubleClick="urlTextBox_PreviewMouseDoubleClick"/> | |||
<Button x:Name="copyLinkButton" Grid.Column="1" Margin="4 8 8 8" MinWidth="36" Content="{lex:Loc Copy}"/> | |||
</Grid> | |||
</Grid> | |||
</reactiveui:ReactiveUserControl> |
@@ -0,0 +1,48 @@ | |||
using ReactiveUI; | |||
using Shadowsocks.ViewModels; | |||
using System.Reactive.Disposables; | |||
using System.Windows.Input; | |||
namespace Shadowsocks.Views | |||
{ | |||
/// <summary> | |||
/// Interaction logic for ServerSharingView.xaml | |||
/// </summary> | |||
public partial class ServerSharingView : ReactiveUserControl<ServerSharingViewModel> | |||
{ | |||
public ServerSharingView() | |||
{ | |||
InitializeComponent(); | |||
ViewModel = new ServerSharingViewModel(); | |||
this.WhenActivated(disposables => | |||
{ | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.SelectedServerUrlImage, | |||
view => view.qrCodeImage.Source) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.Servers, | |||
view => view.serversListBox.ItemsSource) | |||
.DisposeWith(disposables); | |||
this.Bind(ViewModel, | |||
viewModel => viewModel.SelectedServer, | |||
view => view.serversListBox.SelectedItem) | |||
.DisposeWith(disposables); | |||
this.OneWayBind(ViewModel, | |||
viewModel => viewModel.SelectedServerUrl, | |||
view => view.urlTextBox.Text) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.CopyLink, | |||
view => view.copyLinkButton) | |||
.DisposeWith(disposables); | |||
}); | |||
} | |||
private void urlTextBox_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) | |||
{ | |||
urlTextBox.SelectAll(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
<reactiveui:ReactiveUserControl | |||
x:Class="Shadowsocks.Views.VersionUpdatePromptView" | |||
x:TypeArguments="vms:VersionUpdatePromptViewModel" | |||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |||
xmlns:local="clr-namespace:Shadowsocks.Views" | |||
xmlns:vms="clr-namespace:Shadowsocks.ViewModels" | |||
xmlns:reactiveui="http://reactiveui.net" | |||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml" | |||
xmlns:lex="http://wpflocalizeextension.codeplex.com" | |||
lex:LocalizeDictionary.DesignCulture="en" | |||
lex:ResxLocalizationProvider.DefaultAssembly="Shadowsocks" | |||
lex:ResxLocalizationProvider.DefaultDictionary="Strings" | |||
mc:Ignorable="d" | |||
d:DesignHeight="480" d:DesignWidth="640"> | |||
<Grid Margin="8"> | |||
<Grid.RowDefinitions> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="Auto" /> | |||
<RowDefinition Height="*" /> | |||
<RowDefinition Height="Auto" /> | |||
</Grid.RowDefinitions> | |||
<TextBlock Margin="8 8 8 4" Grid.Row="0" | |||
FontSize="16" | |||
Text="{lex:Loc updatePromptTitle}" /> | |||
<TextBlock Margin="8 4 8 8" Grid.Row="1" | |||
Text="{lex:Loc updatePromptBody}" /> | |||
<mdxam:MarkdownScrollViewer x:Name="releaseNotesMarkdownScrollViewer" | |||
Margin="8" | |||
Grid.Row="2" | |||
Markdown="{Binding ReleaseNotes}" | |||
MarkdownStyle="{x:Static mdxam:MarkdownStyle.GithubLike}"/> | |||
<StackPanel Orientation="Horizontal" Grid.Row="3"> | |||
<Button x:Name="updateButton" Margin="8 8 4 4" Width="75" Height="23" Content="{lex:Loc}"/> | |||
</StackPanel> | |||
<StackPanel Orientation="Horizontal" Grid.Row="3" HorizontalAlignment="Right"> | |||
<Button x:Name="skipVersionButton" Margin="4 8 4 4" Width="75" Height="23" Content="{lex:Loc}"/> | |||
<Button x:Name="notNowButton" Margin="4 8 8 4" Width="75" Height="23" Content="{lex:Loc}"/> | |||
</StackPanel> | |||
</Grid> | |||
</reactiveui:ReactiveUserControl> |
@@ -0,0 +1,40 @@ | |||
using Newtonsoft.Json.Linq; | |||
using ReactiveUI; | |||
using Shadowsocks.ViewModels; | |||
using System.Reactive.Disposables; | |||
namespace Shadowsocks.Views | |||
{ | |||
/// <summary> | |||
/// Interaction logic for VersionUpdatePromptView.xaml | |||
/// </summary> | |||
public partial class VersionUpdatePromptView : ReactiveUserControl<VersionUpdatePromptViewModel> | |||
{ | |||
public VersionUpdatePromptView(JToken releaseObject) | |||
{ | |||
InitializeComponent(); | |||
ViewModel = new VersionUpdatePromptViewModel(releaseObject); | |||
DataContext = ViewModel; // for compatibility with MdXaml | |||
this.WhenActivated(disposables => | |||
{ | |||
/*this.OneWayBind(ViewModel, | |||
viewModel => viewModel.ReleaseNotes, | |||
view => releaseNotesMarkdownScrollViewer.Markdown) | |||
.DisposeWith(disposables);*/ | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.Update, | |||
view => view.updateButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.SkipVersion, | |||
view => view.skipVersionButton) | |||
.DisposeWith(disposables); | |||
this.BindCommand(ViewModel, | |||
viewModel => viewModel.NotNow, | |||
view => view.notNowButton) | |||
.DisposeWith(disposables); | |||
}); | |||
} | |||
} | |||
} |
@@ -14,18 +14,6 @@ namespace Shadowsocks.Test | |||
[TestClass] | |||
public class UnitTest | |||
{ | |||
[TestMethod] | |||
public void TestCompareVersion() | |||
{ | |||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("2.3.1.0", "2.3.1") == 0); | |||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.2", "1.3") < 0); | |||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3", "1.2") > 0); | |||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3", "1.3") == 0); | |||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.2.1", "1.2") > 0); | |||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("2.3.1", "2.4") < 0); | |||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0); | |||
} | |||
[TestMethod] | |||
public void TestHotKey2Str() | |||
{ | |||