@@ -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 | 4.2.0.1 2020-09-26 | ||||
- Fix domain rule handling in PAC script (#2956) | - Fix domain rule handling in PAC script (#2956) | ||||
@@ -5,7 +5,7 @@ | |||||
[中文说明] | [中文说明] | ||||
#### Features | |||||
## Features | |||||
1. System proxy configuration | 1. System proxy configuration | ||||
2. PAC mode and global mode | 2. PAC mode and global mode | ||||
@@ -15,15 +15,15 @@ | |||||
6. Supports UDP relay (see Usage) | 6. Supports UDP relay (see Usage) | ||||
7. Supports plugins | 7. Supports plugins | ||||
#### Download | |||||
## Downloads | |||||
Download the latest release from [release page]. | Download the latest release from [release page]. | ||||
#### Requirements | |||||
## Requirements | |||||
Microsoft [.NET Framework 4.7.2] or higher, Microsoft [Visual C++ 2015 Redistributable] (x86) . | Microsoft [.NET Framework 4.7.2] or higher, Microsoft [Visual C++ 2015 Redistributable] (x86) . | ||||
#### Basic | |||||
## Basics | |||||
1. Find Shadowsocks icon in the notification tray | 1. Find Shadowsocks icon in the notification tray | ||||
2. You can add multiple servers in servers menu | 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 | system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this | ||||
port in `Servers -> Edit Servers` | 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: | For Windows10 Store and related applications, please execute the following command under Admin privilege: | ||||
``` | ``` | ||||
netsh winhttp import proxy source=ie | netsh winhttp import proxy source=ie | ||||
``` | ``` | ||||
#### Server Auto Switching | |||||
## Server Auto Switching | |||||
1. Load balance: choosing server randomly | 1. Load balance: choosing server randomly | ||||
2. High availability: choosing the best server (low latency and packet loss) | 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 | `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! | 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 | For UDP, you need to use SocksCap or ProxyCap to force programs you want | ||||
to be proxied to tunnel over Shadowsocks | to be proxied to tunnel over Shadowsocks | ||||
#### Multiple Instances | |||||
## Multiple Instances | |||||
If you want to manage multiple servers using other tools like SwitchyOmega, | If you want to manage multiple servers using other tools like SwitchyOmega, | ||||
you can start multiple Shadowsocks instances. To avoid configuration conflicts, | you can start multiple Shadowsocks instances. To avoid configuration conflicts, | ||||
copy Shadowsocks to a new directory and choose a different local port. | 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 | If you would like to connect to server via a plugin, please set the plugin's | ||||
path (relative or absolute) on Edit Servers form. | 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: | Details: | ||||
[Working with non SIP003 standard Plugin]. | [Working with non SIP003 standard Plugin]. | ||||
#### Global hotkeys | |||||
## Global hotkeys | |||||
Hotkeys could be registered automatically on startup. | Hotkeys could be registered automatically on startup. | ||||
If you are using multiple instances of Shadowsocks, | If you are using multiple instances of Shadowsocks, | ||||
you must set different key combination for each instance. | you must set different key combination for each instance. | ||||
##### How to input? | |||||
### How to input? | |||||
1. Put focus in the corresponding textbox. | 1. Put focus in the corresponding textbox. | ||||
2. Press the key combination that you want to use. | 2. Press the key combination that you want to use. | ||||
3. Release all keys when you think it is ready. | 3. Release all keys when you think it is ready. | ||||
4. Your input appears in the textbox. | 4. Your input appears in the textbox. | ||||
##### How to change? | |||||
### How to change? | |||||
1. Put focus in the corresponding textbox. | 1. Put focus in the corresponding textbox. | ||||
2. Press BackSpace key to clear content. | 2. Press BackSpace key to clear content. | ||||
3. Re-input new key combination. | 3. Re-input new key combination. | ||||
##### How to deactivate? | |||||
### How to deactivate? | |||||
1. Clear content in the textbox that you want to deactivate, | 1. Clear content in the textbox that you want to deactivate, | ||||
if you want to deactivate all, please clear all textboxes. | if you want to deactivate all, please clear all textboxes. | ||||
2. Press OK button to confirm. | 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. | - 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. | - Yellow: This key combination is occupied by other programs and you have to change to another one. | ||||
- Transparent without color: The initial status. | - Transparent without color: The initial status. | ||||
#### Server Configuration | |||||
## Server Configuration | |||||
Please visit [Servers] for more information. | Please visit [Servers] for more information. | ||||
#### Experimental | |||||
## Experimental | |||||
[Experimental Features] | [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, | 2. It is recommended to share your idea on the Issue Board before you start to work, | ||||
especially for feature development. | especially for feature development. | ||||
#### License | |||||
## License | |||||
[GPLv3] | [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 | [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 | [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 | [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(); | private static Logger logger = LogManager.GetCurrentClassLogger(); | ||||
public static void RegAllHotkeys() | public static void RegAllHotkeys() | ||||
{ | { | ||||
var hotkeyConfig = Configuration.Load().hotkey; | |||||
var hotkeyConfig = Program.MainController.GetCurrentConfiguration().hotkey; | |||||
if (hotkeyConfig == null || !hotkeyConfig.RegHotkeysAtStartup) | if (hotkeyConfig == null || !hotkeyConfig.RegHotkeysAtStartup) | ||||
return; | return; | ||||
@@ -50,7 +50,7 @@ namespace Shadowsocks.Controller | |||||
var callback = _callback as HotKeys.HotKeyCallBackHandler; | var callback = _callback as HotKeys.HotKeyCallBackHandler; | ||||
if (hotkeyStr.IsNullOrEmpty()) | |||||
if (string.IsNullOrEmpty(hotkeyStr)) | |||||
{ | { | ||||
HotKeys.UnregExistingHotkey(callback); | HotKeys.UnregExistingHotkey(callback); | ||||
onComplete?.Invoke(RegResult.UnregSuccess); | 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 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_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 readonly string GEOSITE_SHA256SUM_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat.sha256sum"; | ||||
private static byte[] geositeDB; | private static byte[] geositeDB; | ||||
@@ -45,9 +43,6 @@ namespace Shadowsocks.Controller | |||||
static GeositeUpdater() | static GeositeUpdater() | ||||
{ | { | ||||
//socketsHttpHandler = new SocketsHttpHandler(); | |||||
//httpClient = new HttpClient(socketsHttpHandler); | |||||
if (File.Exists(DATABASE_PATH) && new FileInfo(DATABASE_PATH).Length > 0) | if (File.Exists(DATABASE_PATH) && new FileInfo(DATABASE_PATH).Length > 0) | ||||
{ | { | ||||
geositeDB = File.ReadAllBytes(DATABASE_PATH); | geositeDB = File.ReadAllBytes(DATABASE_PATH); | ||||
@@ -68,7 +63,7 @@ namespace Shadowsocks.Controller | |||||
var list = GeositeList.Parser.ParseFrom(geositeDB); | var list = GeositeList.Parser.ParseFrom(geositeDB); | ||||
foreach (var item in list.Entries) | 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; | string geositeSha256sumUrl = GEOSITE_SHA256SUM_URL; | ||||
SHA256 mySHA256 = SHA256.Create(); | SHA256 mySHA256 = SHA256.Create(); | ||||
var config = Program.MainController.GetCurrentConfiguration(); | 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)) | if (!string.IsNullOrWhiteSpace(config.geositeUrl)) | ||||
{ | { | ||||
logger.Info("Found custom Geosite URL in config file"); | logger.Info("Found custom Geosite URL in config file"); | ||||
@@ -94,21 +89,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
logger.Info($"Checking Geosite from {geositeUrl}"); | 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 | try | ||||
{ | { | ||||
// download checksum first | // download checksum first | ||||
@@ -150,32 +130,26 @@ namespace Shadowsocks.Controller | |||||
// update stuff | // update stuff | ||||
geositeDB = downloadedBytes; | geositeDB = downloadedBytes; | ||||
LoadGeositeList(); | LoadGeositeList(); | ||||
bool pacFileChanged = MergeAndWritePACFile(group, blacklist); | |||||
bool pacFileChanged = MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, blacklist); | |||||
UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged)); | UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged)); | ||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
{ | { | ||||
Error?.Invoke(null, new ErrorEventArgs(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)) | if (File.Exists(PACDaemon.PAC_FILE)) | ||||
{ | { | ||||
string original = FileManager.NonExclusiveReadAllText(PACDaemon.PAC_FILE, Encoding.UTF8); | string original = FileManager.NonExclusiveReadAllText(PACDaemon.PAC_FILE, Encoding.UTF8); | ||||
@@ -188,7 +162,44 @@ namespace Shadowsocks.Controller | |||||
return true; | 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; | string abpContent; | ||||
if (File.Exists(PACDaemon.USER_ABP_FILE)) | if (File.Exists(PACDaemon.USER_ABP_FILE)) | ||||
@@ -204,27 +215,25 @@ namespace Shadowsocks.Controller | |||||
if (File.Exists(PACDaemon.USER_RULE_FILE)) | if (File.Exists(PACDaemon.USER_RULE_FILE)) | ||||
{ | { | ||||
string userrulesString = FileManager.NonExclusiveReadAllText(PACDaemon.USER_RULE_FILE, Encoding.UTF8); | 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 = | abpContent = | ||||
$@"var __USERRULES__ = {JsonConvert.SerializeObject(userruleLines, Formatting.Indented)}; | $@"var __USERRULES__ = {JsonConvert.SerializeObject(userruleLines, Formatting.Indented)}; | ||||
var __RULES__ = {JsonConvert.SerializeObject(gfwLines, Formatting.Indented)}; | |||||
var __RULES__ = {JsonConvert.SerializeObject(ruleLines, Formatting.Indented)}; | |||||
{abpContent}"; | {abpContent}"; | ||||
return 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>(); | 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; | continue; | ||||
valid_lines.Add(line); | valid_lines.Add(line); | ||||
} | } | ||||
@@ -232,44 +241,105 @@ var __RULES__ = {JsonConvert.SerializeObject(gfwLines, Formatting.Indented)}; | |||||
return valid_lines; | 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(); | .ToList(); | ||||
} | |||||
} | } | ||||
} | } |
@@ -11,27 +11,15 @@ namespace Shadowsocks.Controller.Service | |||||
{ | { | ||||
public class OnlineConfigResolver | 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); | string server_json = await httpClient.GetStringAsync(url); | ||||
var servers = server_json.GetServers(); | var servers = server_json.GetServers(); | ||||
foreach (var server in servers) | foreach (var server in servers) | ||||
{ | { | ||||
server.group = url; | server.group = url; | ||||
} | } | ||||
return servers.ToList(); | return servers.ToList(); | ||||
} | } | ||||
} | } | ||||
@@ -45,7 +45,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (!File.Exists(PAC_FILE)) | if (!File.Exists(PAC_FILE)) | ||||
{ | { | ||||
GeositeUpdater.MergeAndWritePACFile(config.geositeGroup, config.geositeBlacklistMode); | |||||
GeositeUpdater.MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, config.geositePreferDirect); | |||||
} | } | ||||
return PAC_FILE; | return PAC_FILE; | ||||
} | } | ||||
@@ -63,7 +63,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (!File.Exists(PAC_FILE)) | 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); | return File.ReadAllText(PAC_FILE, Encoding.UTF8); | ||||
} | } | ||||
@@ -43,7 +43,7 @@ namespace Shadowsocks.Controller | |||||
_config = config; | _config = config; | ||||
string usedSecret = _config.secureLocalPac ? $"&secret={PacSecret}" : ""; | string usedSecret = _config.secureLocalPac ? $"&secret={PacSecret}" : ""; | ||||
string contentHash = GetHash(_pacDaemon.GetPACContent()); | 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); | logger.Debug("Set PAC URL:" + PacUrl); | ||||
} | } | ||||
@@ -122,12 +122,13 @@ namespace Shadowsocks.Controller.Service | |||||
public string ExpandEnvironmentVariables(string name, StringDictionary environmentVariables = null) | public string ExpandEnvironmentVariables(string name, StringDictionary environmentVariables = null) | ||||
{ | { | ||||
name = name.ToLower(); | |||||
// Expand the environment variables from the new process itself | // Expand the environment variables from the new process itself | ||||
if (environmentVariables != null) | if (environmentVariables != null) | ||||
{ | { | ||||
foreach(string key in environmentVariables.Keys) | 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) | // Also expand the environment variables from current main process (system) | ||||
@@ -239,7 +239,7 @@ namespace Shadowsocks.Controller | |||||
public DateTime lastActivity; | public DateTime lastActivity; | ||||
private readonly ShadowsocksController _controller; | private readonly ShadowsocksController _controller; | ||||
private readonly ProxyConfig _config; | |||||
private readonly ForwardProxyConfig _config; | |||||
private readonly Socket _connection; | private readonly Socket _connection; | ||||
private IEncryptor encryptor; | private IEncryptor encryptor; | ||||
@@ -708,10 +708,10 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
switch (_config.proxyType) | switch (_config.proxyType) | ||||
{ | { | ||||
case ProxyConfig.PROXY_SOCKS5: | |||||
case ForwardProxyConfig.PROXY_SOCKS5: | |||||
remote = new Socks5Proxy(); | remote = new Socks5Proxy(); | ||||
break; | break; | ||||
case ProxyConfig.PROXY_HTTP: | |||||
case ForwardProxyConfig.PROXY_HTTP: | |||||
remote = new HttpProxy(); | remote = new HttpProxy(); | ||||
break; | break; | ||||
default: | default: | ||||
@@ -1,55 +1,36 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Diagnostics; | |||||
using System.IO; | |||||
using System.Net; | using System.Net; | ||||
using System.Net.Http; | |||||
using System.Reflection; | using System.Reflection; | ||||
using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||
using System.Threading.Tasks; | |||||
using System.Windows; | |||||
using Newtonsoft.Json.Linq; | using Newtonsoft.Json.Linq; | ||||
using NLog; | using NLog; | ||||
using Shadowsocks.Localization; | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using Shadowsocks.Views; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
public class UpdateChecker | 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 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) | private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) | ||||
{ | { | ||||
@@ -60,204 +41,146 @@ namespace Shadowsocks.Controller | |||||
timer.Dispose(); | timer.Dispose(); | ||||
CheckUpdate(config); | 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 | 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 | 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.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | using System.Net; | ||||
using System.Net.Http; | |||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
@@ -15,12 +16,14 @@ using Shadowsocks.Controller.Service; | |||||
using Shadowsocks.Controller.Strategy; | using Shadowsocks.Controller.Strategy; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using WPFLocalizeExtension.Engine; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
public class ShadowsocksController | public class ShadowsocksController | ||||
{ | { | ||||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||||
private readonly Logger logger; | |||||
private readonly HttpClient httpClient; | |||||
// controller: | // controller: | ||||
// handle user actions | // handle user actions | ||||
@@ -38,9 +41,6 @@ namespace Shadowsocks.Controller | |||||
private PrivoxyRunner privoxyRunner; | private PrivoxyRunner privoxyRunner; | ||||
private readonly ConcurrentDictionary<Server, Sip003Plugin> _pluginsByServer; | private readonly ConcurrentDictionary<Server, Sip003Plugin> _pluginsByServer; | ||||
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; | |||||
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } | |||||
private long _inboundCounter = 0; | private long _inboundCounter = 0; | ||||
private long _outboundCounter = 0; | private long _outboundCounter = 0; | ||||
public long InboundCounter => Interlocked.Read(ref _inboundCounter); | public long InboundCounter => Interlocked.Read(ref _inboundCounter); | ||||
@@ -92,8 +92,10 @@ namespace Shadowsocks.Controller | |||||
public ShadowsocksController() | public ShadowsocksController() | ||||
{ | { | ||||
logger = LogManager.GetCurrentClassLogger(); | |||||
httpClient = new HttpClient(); | |||||
_config = Configuration.Load(); | _config = Configuration.Load(); | ||||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | |||||
Configuration.Process(ref _config); | |||||
_strategyManager = new StrategyManager(this); | _strategyManager = new StrategyManager(this); | ||||
_pluginsByServer = new ConcurrentDictionary<Server, Sip003Plugin>(); | _pluginsByServer = new ConcurrentDictionary<Server, Sip003Plugin>(); | ||||
StartTrafficStatistics(61); | StartTrafficStatistics(61); | ||||
@@ -106,23 +108,34 @@ namespace Shadowsocks.Controller | |||||
#region Basic | #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() | ProgramUpdated.Invoke(this, new UpdatedEventArgs() | ||||
{ | { | ||||
OldVersion = _config.version, | OldVersion = _config.version, | ||||
NewVersion = UpdateChecker.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); | Configuration.Save(_config); | ||||
} | } | ||||
Reload(); | Reload(); | ||||
if (regHotkeys) | |||||
{ | |||||
if (!systemWakeUp) | |||||
HotkeyReg.RegAllHotkeys(); | HotkeyReg.RegAllHotkeys(); | ||||
} | |||||
} | } | ||||
public void Stop() | public void Stop() | ||||
@@ -150,10 +163,24 @@ namespace Shadowsocks.Controller | |||||
Encryption.RNG.Reload(); | Encryption.RNG.Reload(); | ||||
// some logic in configuration updated the config when saving, we need to read it again | // some logic in configuration updated the config when saving, we need to read it again | ||||
_config = Configuration.Load(); | _config = Configuration.Load(); | ||||
Configuration.Process(ref _config); | |||||
NLogConfig.LoadConfiguration(); | 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(); | privoxyRunner = privoxyRunner ?? new PrivoxyRunner(); | ||||
@@ -167,7 +194,6 @@ namespace Shadowsocks.Controller | |||||
GeositeUpdater.UpdateCompleted += PacServer_PACUpdateCompleted; | GeositeUpdater.UpdateCompleted += PacServer_PACUpdateCompleted; | ||||
GeositeUpdater.Error += PacServer_PACUpdateError; | GeositeUpdater.Error += PacServer_PACUpdateError; | ||||
availabilityStatistics.UpdateConfiguration(this); | |||||
_tcpListener?.Stop(); | _tcpListener?.Stop(); | ||||
_udpListener?.Stop(); | _udpListener?.Stop(); | ||||
StopPlugins(); | StopPlugins(); | ||||
@@ -186,7 +212,6 @@ namespace Shadowsocks.Controller | |||||
privoxyRunner.Start(_config); | privoxyRunner.Start(_config); | ||||
TCPRelay tcpRelay = new TCPRelay(this, _config); | TCPRelay tcpRelay = new TCPRelay(this, _config); | ||||
tcpRelay.OnConnected += UpdateLatency; | |||||
tcpRelay.OnInbound += UpdateInboundCounter; | tcpRelay.OnInbound += UpdateInboundCounter; | ||||
tcpRelay.OnOutbound += UpdateOutboundCounter; | tcpRelay.OnOutbound += UpdateOutboundCounter; | ||||
tcpRelay.OnFailed += (o, e) => GetCurrentStrategy()?.SetFailure(e.server); | tcpRelay.OnFailed += (o, e) => GetCurrentStrategy()?.SetFailure(e.server); | ||||
@@ -239,22 +264,9 @@ namespace Shadowsocks.Controller | |||||
Errored?.Invoke(this, new ErrorEventArgs(e)); | 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) | public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint) | ||||
{ | { | ||||
@@ -270,13 +282,6 @@ namespace Shadowsocks.Controller | |||||
return GetCurrentServer(); | 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) | public void SaveServers(List<Server> servers, int localPort, bool portableMode) | ||||
{ | { | ||||
_config.configs = servers; | _config.configs = servers; | ||||
@@ -285,6 +290,13 @@ namespace Shadowsocks.Controller | |||||
Configuration.Save(_config); | Configuration.Save(_config); | ||||
} | } | ||||
public void SelectServerIndex(int index) | |||||
{ | |||||
_config.index = index; | |||||
_config.strategy = null; | |||||
SaveConfig(_config); | |||||
} | |||||
public void ToggleShareOverLAN(bool enabled) | public void ToggleShareOverLAN(bool enabled) | ||||
{ | { | ||||
_config.shareOverLan = enabled; | _config.shareOverLan = enabled; | ||||
@@ -313,7 +325,7 @@ namespace Shadowsocks.Controller | |||||
EnableGlobalChanged?.Invoke(this, new EventArgs()); | EnableGlobalChanged?.Invoke(this, new EventArgs()); | ||||
} | } | ||||
public void SaveProxy(ProxyConfig proxyConfig) | |||||
public void SaveProxy(ForwardProxyConfig proxyConfig) | |||||
{ | { | ||||
_config.proxy = proxyConfig; | _config.proxy = proxyConfig; | ||||
SaveConfig(_config); | SaveConfig(_config); | ||||
@@ -346,7 +358,7 @@ namespace Shadowsocks.Controller | |||||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | ||||
private void PacDaemon_UserRuleFileChanged(object sender, EventArgs e) | private void PacDaemon_UserRuleFileChanged(object sender, EventArgs e) | ||||
{ | { | ||||
GeositeUpdater.MergeAndWritePACFile(_config.geositeGroup, _config.geositeBlacklistMode); | |||||
GeositeUpdater.MergeAndWritePACFile(_config.geositeDirectGroups, _config.geositeProxiedGroups, _config.geositePreferDirect); | |||||
UpdateSystemProxy(); | UpdateSystemProxy(); | ||||
} | } | ||||
@@ -393,6 +405,13 @@ namespace Shadowsocks.Controller | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | ConfigChanged?.Invoke(this, new EventArgs()); | ||||
} | } | ||||
public void ToggleRegeneratePacOnUpdate(bool enabled) | |||||
{ | |||||
_config.regeneratePacOnUpdate = enabled; | |||||
SaveConfig(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
#endregion | #endregion | ||||
#region SIP002 | #region SIP002 | ||||
@@ -419,7 +438,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
if (ssURL.IsNullOrEmpty() || ssURL.IsWhiteSpace()) | |||||
if (string.IsNullOrWhiteSpace(ssURL)) | |||||
return false; | return false; | ||||
var servers = Server.GetServers(ssURL); | var servers = Server.GetServers(ssURL); | ||||
@@ -474,6 +493,12 @@ namespace Shadowsocks.Controller | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | ConfigChanged?.Invoke(this, new EventArgs()); | ||||
} | } | ||||
public void SaveSkippedUpdateVerion(string version) | |||||
{ | |||||
_config.skippedUpdateVersion = version; | |||||
Configuration.Save(_config); | |||||
} | |||||
public void SaveLogViewerConfig(LogViewerConfig newConfig) | public void SaveLogViewerConfig(LogViewerConfig newConfig) | ||||
{ | { | ||||
_config.logViewer = newConfig; | _config.logViewer = newConfig; | ||||
@@ -493,7 +518,7 @@ namespace Shadowsocks.Controller | |||||
#endregion | #endregion | ||||
#region Statistic | |||||
#region Strategy | |||||
public void SelectStrategy(string strategyID) | public void SelectStrategy(string strategyID) | ||||
{ | { | ||||
@@ -502,12 +527,6 @@ namespace Shadowsocks.Controller | |||||
SaveConfig(_config); | SaveConfig(_config); | ||||
} | } | ||||
public void SaveStrategyConfigurations(StatisticsStrategyConfiguration configuration) | |||||
{ | |||||
StatisticsConfiguration = configuration; | |||||
StatisticsStrategyConfiguration.Save(configuration); | |||||
} | |||||
public IList<IStrategy> GetStrategies() | public IList<IStrategy> GetStrategies() | ||||
{ | { | ||||
return _strategyManager.GetStrategies(); | return _strategyManager.GetStrategies(); | ||||
@@ -525,43 +544,16 @@ namespace Shadowsocks.Controller | |||||
return null; | 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) | public void UpdateInboundCounter(object sender, SSTransmitEventArgs args) | ||||
{ | { | ||||
GetCurrentStrategy()?.UpdateLastRead(args.server); | GetCurrentStrategy()?.UpdateLastRead(args.server); | ||||
Interlocked.Add(ref _inboundCounter, args.length); | Interlocked.Add(ref _inboundCounter, args.length); | ||||
if (_config.availabilityStatistics) | |||||
{ | |||||
availabilityStatistics.UpdateInboundCounter(args.server, args.length); | |||||
} | |||||
} | } | ||||
public void UpdateOutboundCounter(object sender, SSTransmitEventArgs args) | public void UpdateOutboundCounter(object sender, SSTransmitEventArgs args) | ||||
{ | { | ||||
GetCurrentStrategy()?.UpdateLastWrite(args.server); | GetCurrentStrategy()?.UpdateLastWrite(args.server); | ||||
Interlocked.Add(ref _outboundCounter, args.length); | Interlocked.Add(ref _outboundCounter, args.length); | ||||
if (_config.availabilityStatistics) | |||||
{ | |||||
availabilityStatistics.UpdateOutboundCounter(args.server, args.length); | |||||
} | |||||
} | } | ||||
#endregion | #endregion | ||||
@@ -620,7 +612,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
#endregion | #endregion | ||||
#region Traffic Statistics | #region Traffic Statistics | ||||
private void StartTrafficStatistics(int queueMaxSize) | private void StartTrafficStatistics(int queueMaxSize) | ||||
@@ -668,7 +660,7 @@ namespace Shadowsocks.Controller | |||||
public async Task<int> UpdateOnlineConfigInternal(string url) | 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 = Configuration.SortByOnlineConfig( | ||||
_config.configs | _config.configs | ||||
.Where(c => c.group != url) | .Where(c => c.group != url) | ||||
@@ -695,10 +687,10 @@ namespace Shadowsocks.Controller | |||||
return true; | return true; | ||||
} | } | ||||
public async Task<int> UpdateAllOnlineConfig() | |||||
public async Task<List<string>> UpdateAllOnlineConfig() | |||||
{ | { | ||||
var selected = GetCurrentServer(); | var selected = GetCurrentServer(); | ||||
int failCount = 0; | |||||
var failedUrls = new List<string>(); | |||||
foreach (var url in _config.onlineConfigSource) | foreach (var url in _config.onlineConfigSource) | ||||
{ | { | ||||
try | try | ||||
@@ -708,18 +700,18 @@ namespace Shadowsocks.Controller | |||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
logger.LogUsefulException(e); | logger.LogUsefulException(e); | ||||
failCount++; | |||||
failedUrls.Add(url); | |||||
} | } | ||||
} | } | ||||
_config.index = _config.configs.IndexOf(selected); | _config.index = _config.configs.IndexOf(selected); | ||||
SaveConfig(_config); | 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); | 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 = new List<IStrategy>(); | ||||
_strategies.Add(new BalancingStrategy(controller)); | _strategies.Add(new BalancingStrategy(controller)); | ||||
_strategies.Add(new HighAvailabilityStrategy(controller)); | _strategies.Add(new HighAvailabilityStrategy(controller)); | ||||
_strategies.Add(new StatisticsStrategy(controller)); | |||||
// TODO: load DLL plugins | // TODO: load DLL plugins | ||||
} | } | ||||
public IList<IStrategy> GetStrategies() | public IList<IStrategy> GetStrategies() | ||||
@@ -23,7 +23,7 @@ namespace Shadowsocks.Controller.Hotkeys | |||||
/// <returns></returns> | /// <returns></returns> | ||||
public static Delegate GetCallback(string methodname) | 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, | MethodInfo dynMethod = typeof(HotkeyCallbacks).GetMethod(methodname, | ||||
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase); | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase); | ||||
return dynMethod == null ? null : Delegate.CreateDelegate(typeof(HotKeys.HotKeyCallBackHandler), Instance, dynMethod); | return dynMethod == null ? null : Delegate.CreateDelegate(typeof(HotKeys.HotKeyCallBackHandler), Instance, dynMethod); | ||||
@@ -46,21 +46,20 @@ namespace Shadowsocks.Controller.Hotkeys | |||||
private void SwitchSystemProxyCallback() | private void SwitchSystemProxyCallback() | ||||
{ | { | ||||
bool enabled = _controller.GetConfigurationCopy().enabled; | |||||
bool enabled = _controller.GetCurrentConfiguration().enabled; | |||||
_controller.ToggleEnable(!enabled); | _controller.ToggleEnable(!enabled); | ||||
} | } | ||||
private void SwitchSystemProxyModeCallback() | 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() | private void SwitchAllowLanCallback() | ||||
{ | { | ||||
var status = _controller.GetConfigurationCopy().shareOverLan; | |||||
var status = _controller.GetCurrentConfiguration().shareOverLan; | |||||
_controller.ToggleShareOverLAN(!status); | _controller.ToggleShareOverLAN(!status); | ||||
} | } | ||||
@@ -110,7 +110,7 @@ namespace Shadowsocks.Controller.Hotkeys | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
if (s.IsNullOrEmpty()) return null; | |||||
if (string.IsNullOrEmpty(s)) return null; | |||||
int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase); | int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase); | ||||
if (offset <= 0) return null; | if (offset <= 0) return null; | ||||
string modifierStr = s.Substring(0, offset).Trim(); | string modifierStr = s.Substring(0, offset).Trim(); | ||||
@@ -31,7 +31,7 @@ namespace Shadowsocks.Controller | |||||
else | else | ||||
{ | { | ||||
string pacUrl; | string pacUrl; | ||||
if (config.useOnlinePac && !config.pacUrl.IsNullOrEmpty()) | |||||
if (config.useOnlinePac && !string.IsNullOrEmpty(config.pacUrl)) | |||||
{ | { | ||||
pacUrl = config.pacUrl; | pacUrl = config.pacUrl; | ||||
} | } | ||||
@@ -16,7 +16,6 @@ PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PACモード | |||||
Global,Для всей системы,全局模式,全局模式,グローバルプロキシ,전역,Global | Global,Для всей системы,全局模式,全局模式,グローバルプロキシ,전역,Global | ||||
Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs | Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs | ||||
Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… | Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… | ||||
Statistics Config...,Настройки статистики…,统计配置...,統計設定檔...,統計情報の設定...,통계 설정,Configuration des statistiques… | |||||
Online Config...,,在线配置...,線上配置...,,, | Online Config...,,在线配置...,線上配置...,,, | ||||
Start on Boot,Автозагрузка,开机启动,開機啟動,システム起動時に実行,시스템 시작 시에 시작하기,Démarrage automatique | Start on Boot,Автозагрузка,开机启动,開機啟動,システム起動時に実行,시스템 시작 시에 시작하기,Démarrage automatique | ||||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,ss:// リンクの関連付け,ss:// 링크 연결, | 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 | 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 ... | 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 | 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 | 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 ... | Share Server Config...,Поделиться конфигурацией сервера…,分享服务器配置...,分享伺服器設定檔...,サーバーの設定を共有...,서버 설정 공유,Partager la configuration du serveur ... | ||||
Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... | 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 | Edit Servers,Редактирование серверов,编辑服务器,編輯伺服器,サーバーの編集,서버 수정,Éditer serveurs | ||||
Load Balance,Балансировка нагрузки,负载均衡,負載平衡,サーバーロードバランス,로드밸런싱,Répartition de charge | Load Balance,Балансировка нагрузки,负载均衡,負載平衡,サーバーロードバランス,로드밸런싱,Répartition de charge | ||||
High Availability,Высокая доступность,高可用,高可用性,高可用性,고가용성,Haute disponibilité | High Availability,Высокая доступность,高可用,高可用性,高可用性,고가용성,Haute disponibilité | ||||
Choose by statistics,На основе статистики,根据统计,根據統計,統計で選ぶ,통계 기반,Choisissez par statistiques | |||||
Show Plugin Output,События плагинов в журнале,显示插件输出,,プラグインの出力情報を表示,플러그인 출력 보이기,Afficher la sortie du plugin | Show Plugin Output,События плагинов в журнале,显示插件输出,,プラグインの出力情報を表示,플러그인 출력 보이기,Afficher la sortie du plugin | ||||
Write translation template,Создать шаблон для перевода,写入翻译模板,,翻訳テンプレートファイルを書き込む,번역 템플릿 쓰기,Écrire un modèle de traduction | Write translation template,Создать шаблон для перевода,写入翻译模板,,翻訳テンプレートファイルを書き込む,번역 템플릿 쓰기,Écrire un modèle de traduction | ||||
,,,,,, | ,,,,,, | ||||
@@ -80,55 +79,6 @@ Move D&own,Ниже,下移(&O),下移 (&O),下に移動 (&O),아래로 (&O),Desc | |||||
deprecated,Устаревшее,不推荐,不推薦,非推奨,더 이상 사용되지 않음,Obsolète | 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}" | "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,,,,,, | # Log Form,,,,,, | ||||
,,,,,, | ,,,,,, | ||||
&File,Файл,文件(&F),檔案 (&F),ファイル (&F),파일 (&F),Fichier | &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 ... | 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 | 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,,,,,, | # Messages,,,,,, | ||||
,,,,,, | ,,,,,, | ||||
Shadowsocks Error: {0},Ошибка Shadowsocks: {0},Shadowsocks 错误: {0},Shadowsocks 錯誤: {0},Shadowsocks エラー: {0},Shadowsocks 오류: {0},Erreur shadowsocks: {0} | 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 | Password can not be blank,Пароль не может быть пустым,密码不能为空,密碼不能為空,パスワードが指定されていません。,비밀번호는 비어있으면 안됩니다.,Le mot de passe ne peut pas être vide | ||||
Port out of range,Порт выходит за допустимый диапазон,端口超出范围,連接埠號碼超出範圍,ポート番号は範囲外です。,올바른 포트 범위가 아닙니다.,Port hors de portée | 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 | 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 | 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 | 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 | 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é | 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. | 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. | 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." | "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 | Failed to update registry,Не удалось обновить запись в реестре,无法修改注册表,無法修改登錄檔,レジストリの更新に失敗しました。,레지스트리를 업데이트하는데에 실패했습니다.,Impossible de mettre à jour de la base de registre | ||||
Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,{0}:このURLからインポートしますか?,, | Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,{0}:このURLからインポートしますか?,, | ||||
Successfully imported from {0},Успешно импортировано из {0},导入成功:{0},導入成功:{0},{0}:インポートしました。,, | 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? | "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" | "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) | public static IEncryptor GetEncryptor(string method, string password) | ||||
{ | { | ||||
if (method.IsNullOrEmpty()) | |||||
if (string.IsNullOrEmpty(method)) | |||||
{ | { | ||||
method = Model.Server.DefaultMethod; | method = Model.Server.DefaultMethod; | ||||
} | } | ||||
@@ -2,4 +2,5 @@ | |||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
<Caseless StringComparison="Ordinal" /> | <Caseless StringComparison="Ordinal" /> | ||||
<Costura /> | <Costura /> | ||||
<ReactiveUI /> | |||||
</Weavers> | </Weavers> |
@@ -4,6 +4,7 @@ | |||||
<xs:element name="Weavers"> | <xs:element name="Weavers"> | ||||
<xs:complexType> | <xs:complexType> | ||||
<xs:all> | <xs:all> | ||||
<xs:element name="ReactiveUI" minOccurs="0" maxOccurs="1" type="xs:anyType" /> | |||||
<xs:element name="Costura" minOccurs="0" maxOccurs="1"> | <xs:element name="Costura" minOccurs="0" maxOccurs="1"> | ||||
<xs:complexType> | <xs:complexType> | ||||
<xs:all> | <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> | </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> | </root> |
@@ -117,13 +117,64 @@ | |||||
<resheader name="writer"> | <resheader name="writer"> | ||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||
</resheader> | </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> | </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> | </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.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | using System.Net; | ||||
using System.Windows; | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using NLog; | using NLog; | ||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
@@ -27,33 +28,36 @@ namespace Shadowsocks.Model | |||||
public bool global; | public bool global; | ||||
public bool enabled; | public bool enabled; | ||||
public bool shareOverLan; | public bool shareOverLan; | ||||
public bool isDefault; | |||||
public bool firstRun; | |||||
public int localPort; | public int localPort; | ||||
public bool portableMode; | public bool portableMode; | ||||
public bool showPluginOutput; | public bool showPluginOutput; | ||||
public string pacUrl; | public string pacUrl; | ||||
public bool useOnlinePac; | 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 autoCheckUpdate; | ||||
public bool checkPreRelease; | public bool checkPreRelease; | ||||
public string skippedUpdateVersion; // skip the update with this version number | |||||
public bool isVerboseLogging; | public bool isVerboseLogging; | ||||
// hidden options | // hidden options | ||||
public bool isIPv6Enabled; // for experimental ipv6 support | 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 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 NLogConfig.LogLevel logLevel; | ||||
public LogViewerConfig logViewer; | public LogViewerConfig logViewer; | ||||
public ProxyConfig proxy; | |||||
public ForwardProxyConfig proxy; | |||||
public HotkeyConfig hotkey; | public HotkeyConfig hotkey; | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public bool updated; | |||||
public bool firstRunOnNewVersion; | |||||
public Configuration() | public Configuration() | ||||
{ | { | ||||
@@ -63,35 +67,48 @@ namespace Shadowsocks.Model | |||||
global = false; | global = false; | ||||
enabled = false; | enabled = false; | ||||
shareOverLan = false; | shareOverLan = false; | ||||
isDefault = true; | |||||
firstRun = true; | |||||
localPort = 1080; | localPort = 1080; | ||||
portableMode = true; | portableMode = true; | ||||
showPluginOutput = false; | showPluginOutput = false; | ||||
pacUrl = ""; | pacUrl = ""; | ||||
useOnlinePac = false; | useOnlinePac = false; | ||||
secureLocalPac = true; | secureLocalPac = true; | ||||
availabilityStatistics = false; | |||||
regeneratePacOnUpdate = true; | |||||
autoCheckUpdate = false; | autoCheckUpdate = false; | ||||
checkPreRelease = false; | checkPreRelease = false; | ||||
skippedUpdateVersion = ""; | |||||
isVerboseLogging = false; | isVerboseLogging = false; | ||||
// hidden options | // hidden options | ||||
isIPv6Enabled = false; | isIPv6Enabled = false; | ||||
generateLegacyUrl = false; | generateLegacyUrl = false; | ||||
geositeUrl = ""; | 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(); | logViewer = new LogViewerConfig(); | ||||
proxy = new ProxyConfig(); | |||||
proxy = new ForwardProxyConfig(); | |||||
hotkey = new HotkeyConfig(); | hotkey = new HotkeyConfig(); | ||||
updated = false; | |||||
firstRunOnNewVersion = false; | |||||
configs = new List<Server>(); | configs = new List<Server>(); | ||||
onlineConfigSource = new List<string>(); | onlineConfigSource = new List<string>(); | ||||
} | } | ||||
[JsonIgnore] | |||||
public string userAgentString; // $version substituted with numeral version in it | |||||
[JsonIgnore] | [JsonIgnore] | ||||
NLogConfig nLogConfig; | NLogConfig nLogConfig; | ||||
@@ -103,11 +120,8 @@ namespace Shadowsocks.Model | |||||
#endif | #endif | ||||
[JsonIgnore] | [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() | public Server GetCurrentServer() | ||||
{ | { | ||||
if (index >= 0 && index < configs.Count) | if (index >= 0 && index < configs.Count) | ||||
@@ -124,6 +138,11 @@ namespace Shadowsocks.Model | |||||
localPort) | localPort) | ||||
: null; | : 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) | public static void CheckServer(Server server) | ||||
{ | { | ||||
CheckServer(server.server); | CheckServer(server.server); | ||||
@@ -132,52 +151,70 @@ namespace Shadowsocks.Model | |||||
CheckTimeout(server.timeout, Server.MaxServerTimeoutSec); | 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() | public static Configuration Load() | ||||
{ | { | ||||
Configuration config; | 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 | try | ||||
{ | { | ||||
config.nLogConfig = NLogConfig.LoadXML(); | config.nLogConfig = NLogConfig.LoadXML(); | ||||
@@ -197,46 +234,42 @@ namespace Shadowsocks.Model | |||||
} | } | ||||
catch (Exception e) | 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) | public static void Save(Configuration config) | ||||
{ | { | ||||
config.configs = SortByOnlineConfig(config.configs); | 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 | 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); | logger.LogUsefulException(e); | ||||
} | } | ||||
finally | |||||
{ | |||||
if (configStreamWriter != null) | |||||
configStreamWriter.Dispose(); | |||||
if (configFileStream != null) | |||||
configFileStream.Dispose(); | |||||
} | |||||
} | } | ||||
public static List<Server> SortByOnlineConfig(IEnumerable<Server> servers) | public static List<Server> SortByOnlineConfig(IEnumerable<Server> servers) | ||||
@@ -248,6 +281,49 @@ namespace Shadowsocks.Model | |||||
return ret; | 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) | public static Server AddDefaultServerOrServer(Configuration config, Server server = null, int? index = null) | ||||
{ | { | ||||
if (config?.configs != null) | if (config?.configs != null) | ||||
@@ -284,13 +360,13 @@ namespace Shadowsocks.Model | |||||
private static void CheckPassword(string password) | private static void CheckPassword(string password) | ||||
{ | { | ||||
if (password.IsNullOrEmpty()) | |||||
if (string.IsNullOrEmpty(password)) | |||||
throw new ArgumentException(I18N.GetString("Password can not be blank")); | throw new ArgumentException(I18N.GetString("Password can not be blank")); | ||||
} | } | ||||
public static void CheckServer(string server) | public static void CheckServer(string server) | ||||
{ | { | ||||
if (server.IsNullOrEmpty()) | |||||
if (string.IsNullOrEmpty(server)) | |||||
throw new ArgumentException(I18N.GetString("Server IP can not be blank")); | throw new ArgumentException(I18N.GetString("Server IP can not be blank")); | ||||
} | } | ||||
@@ -300,17 +376,5 @@ namespace Shadowsocks.Model | |||||
throw new ArgumentException( | throw new ArgumentException( | ||||
I18N.GetString("Timeout is invalid, it should not exceed {0}", maxTimeout)); | 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; | 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() | public override string ToString() | ||||
{ | { | ||||
if (server.IsNullOrEmpty()) | |||||
if (string.IsNullOrEmpty(server)) | |||||
{ | { | ||||
return I18N.GetString("New server"); | return I18N.GetString("New server"); | ||||
} | } | ||||
string serverStr = $"{FormalHostName}:{server_port}"; | string serverStr = $"{FormalHostName}:{server_port}"; | ||||
return remarks.IsNullOrEmpty() | |||||
return string.IsNullOrEmpty(remarks) | |||||
? serverStr | ? serverStr | ||||
: $"{remarks} ({serverStr})"; | : $"{remarks} ({serverStr})"; | ||||
} | } | ||||
@@ -93,7 +89,7 @@ namespace Shadowsocks.Model | |||||
u.Port = server_port; | u.Port = server_port; | ||||
u.Fragment = HttpUtility.UrlEncode(remarks, Encoding.UTF8); | u.Fragment = HttpUtility.UrlEncode(remarks, Encoding.UTF8); | ||||
if (!plugin.IsNullOrWhiteSpace()) | |||||
if (!string.IsNullOrWhiteSpace(plugin)) | |||||
{ | { | ||||
NameValueCollection param = HttpUtility.ParseQueryString(""); | NameValueCollection param = HttpUtility.ParseQueryString(""); | ||||
@@ -147,7 +143,7 @@ namespace Shadowsocks.Model | |||||
Server server = new Server(); | Server server = new Server(); | ||||
var base64 = match.Groups["base64"].Value.TrimEnd('/'); | var base64 = match.Groups["base64"].Value.TrimEnd('/'); | ||||
var tag = match.Groups["tag"].Value; | var tag = match.Groups["tag"].Value; | ||||
if (!tag.IsNullOrEmpty()) | |||||
if (!string.IsNullOrEmpty(tag)) | |||||
{ | { | ||||
server.remarks = HttpUtility.UrlDecode(tag, Encoding.UTF8); | server.remarks = HttpUtility.UrlDecode(tag, Encoding.UTF8); | ||||
} | } | ||||
@@ -173,7 +169,7 @@ namespace Shadowsocks.Model | |||||
public static Server ParseURL(string serverUrl) | public static Server ParseURL(string serverUrl) | ||||
{ | { | ||||
string _serverUrl = serverUrl.Trim(); | string _serverUrl = serverUrl.Trim(); | ||||
if (!_serverUrl.BeginWith("ss://", StringComparison.InvariantCultureIgnoreCase)) | |||||
if (!_serverUrl.StartsWith("ss://", StringComparison.InvariantCultureIgnoreCase)) | |||||
{ | { | ||||
return null; | 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; | ||||
using System.IO.Pipes; | using System.IO.Pipes; | ||||
using System.Net; | using System.Net; | ||||
using System.Reflection; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -10,10 +11,13 @@ using System.Windows.Forms; | |||||
using CommandLine; | using CommandLine; | ||||
using Microsoft.Win32; | using Microsoft.Win32; | ||||
using NLog; | using NLog; | ||||
using ReactiveUI; | |||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Controller.Hotkeys; | using Shadowsocks.Controller.Hotkeys; | ||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using Shadowsocks.View; | using Shadowsocks.View; | ||||
using Splat; | |||||
using WPFLocalizeExtension.Engine; | |||||
namespace Shadowsocks | namespace Shadowsocks | ||||
{ | { | ||||
@@ -89,6 +93,16 @@ namespace Shadowsocks | |||||
// we have to do this for self-contained executables | // we have to do this for self-contained executables | ||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)); | 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 | #if DEBUG | ||||
// truncate privoxy log file while debugging | // truncate privoxy log file while debugging | ||||
string privoxyLogFilename = Utils.GetTempPath("privoxy.log"); | string privoxyLogFilename = Utils.GetTempPath("privoxy.log"); | ||||
@@ -163,7 +177,7 @@ namespace Shadowsocks | |||||
Thread.Sleep(10 * 1000); | Thread.Sleep(10 * 1000); | ||||
try | try | ||||
{ | { | ||||
MainController.Start(false); | |||||
MainController.Start(true); | |||||
logger.Info("controller started"); | logger.Info("controller started"); | ||||
} | } | ||||
catch (Exception ex) | 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> | // <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> | // </auto-generated> | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
@@ -13,16 +13,16 @@ namespace Shadowsocks.Properties { | |||||
/// <summary> | /// <summary> | ||||
/// 一个强类型的资源类,用于查找本地化的字符串等。 | |||||
/// A strongly-typed resource class, for looking up localized strings, etc. | |||||
/// </summary> | /// </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.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] | ||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | ||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | ||||
internal class Resources { | |||||
public class Resources { | |||||
private static global::System.Resources.ResourceManager resourceMan; | private static global::System.Resources.ResourceManager resourceMan; | ||||
@@ -33,10 +33,10 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 返回此类使用的缓存的 ResourceManager 实例。 | |||||
/// Returns the cached ResourceManager instance used by this class. | |||||
/// </summary> | /// </summary> | ||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | ||||
internal static global::System.Resources.ResourceManager ResourceManager { | |||||
public static global::System.Resources.ResourceManager ResourceManager { | |||||
get { | get { | ||||
if (object.ReferenceEquals(resourceMan, null)) { | if (object.ReferenceEquals(resourceMan, null)) { | ||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.Properties.Resources", typeof(Resources).Assembly); | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.Properties.Resources", typeof(Resources).Assembly); | ||||
@@ -47,11 +47,11 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 重写当前线程的 CurrentUICulture 属性 | |||||
/// 重写当前线程的 CurrentUICulture 属性。 | |||||
/// Overrides the current thread's CurrentUICulture property for all | |||||
/// resource lookups using this strongly typed resource class. | |||||
/// </summary> | /// </summary> | ||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | ||||
internal static global::System.Globalization.CultureInfo Culture { | |||||
public static global::System.Globalization.CultureInfo Culture { | |||||
get { | get { | ||||
return resourceCulture; | return resourceCulture; | ||||
} | } | ||||
@@ -61,7 +61,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找类似 /* eslint-disable */ | |||||
/// Looks up a localized string similar to /* eslint-disable */ | |||||
///// Was generated by gfwlist2pac in precise mode | ///// Was generated by gfwlist2pac in precise mode | ||||
///// https://github.com/clowwindy/gfwlist2pac | ///// https://github.com/clowwindy/gfwlist2pac | ||||
/// | /// | ||||
@@ -69,25 +69,28 @@ namespace Shadowsocks.Properties { | |||||
///// 2019-02-08: Updated to support shadowsocks-windows user rules. | ///// 2019-02-08: Updated to support shadowsocks-windows user rules. | ||||
/// | /// | ||||
///var proxy = __PROXY__; | ///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> | /// </summary> | ||||
internal static string abp_js { | |||||
public static string abp_js { | |||||
get { | get { | ||||
return ResourceManager.GetString("abp_js", resourceCulture); | return ResourceManager.GetString("abp_js", resourceCulture); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] dlc_dat { | |||||
public static byte[] dlc_dat { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("dlc_dat", resourceCulture); | object obj = ResourceManager.GetObject("dlc_dat", resourceCulture); | ||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
@@ -95,7 +98,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <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,,,,,, | ///#Restart program to apply translation,,,,,, | ||||
///#This is comment line,,,,,, | ///#This is comment line,,,,,, | ||||
///#Always keep language name at head of file,,,,,, | ///#Always keep language name at head of file,,,,,, | ||||
@@ -107,31 +110,31 @@ namespace Shadowsocks.Properties { | |||||
///,,,,,, | ///,,,,,, | ||||
///#Menu,,,,,, | ///#Menu,,,,,, | ||||
///,,,,,, | ///,,,,,, | ||||
///System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,P [字符串的其余部分被截断]"; 的本地化字符串。 | |||||
///System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,P [rest of string was truncated]";. | |||||
/// </summary> | /// </summary> | ||||
internal static string i18n_csv { | |||||
public static string i18n_csv { | |||||
get { | get { | ||||
return ResourceManager.GetString("i18n_csv", resourceCulture); | return ResourceManager.GetString("i18n_csv", resourceCulture); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <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. --> | ///<!-- Warning: Configuration may reset after shadowsocks upgrade. --> | ||||
///<!-- If you messed it up, delete this file and Shadowsocks will create a new one. --> | ///<!-- 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"> | ///<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||||
/// <targets> | /// <targets> | ||||
/// <!-- This line is managed by Shadowsocks. Do not modify it unless you know what you are doing.--> | /// <!-- 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> | /// </summary> | ||||
internal static string NLog_config { | |||||
public static string NLog_config { | |||||
get { | get { | ||||
return ResourceManager.GetString("NLog_config", resourceCulture); | return ResourceManager.GetString("NLog_config", resourceCulture); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <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 | ///toggle 0 | ||||
///logfile ss_privoxy.log | ///logfile ss_privoxy.log | ||||
///show-on-task-bar 0 | ///show-on-task-bar 0 | ||||
@@ -139,18 +142,18 @@ namespace Shadowsocks.Properties { | |||||
///forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . | ///forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . | ||||
///max-client-connections 2048 | ///max-client-connections 2048 | ||||
///hide-console | ///hide-console | ||||
/// 的本地化字符串。 | |||||
///. | |||||
/// </summary> | /// </summary> | ||||
internal static string privoxy_conf { | |||||
public static string privoxy_conf { | |||||
get { | get { | ||||
return ResourceManager.GetString("privoxy_conf", resourceCulture); | return ResourceManager.GetString("privoxy_conf", resourceCulture); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] privoxy_exe { | |||||
public static byte[] privoxy_exe { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture); | object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture); | ||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
@@ -158,9 +161,9 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ss32Fill { | |||||
public static System.Drawing.Bitmap ss32Fill { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("ss32Fill", resourceCulture); | object obj = ResourceManager.GetObject("ss32Fill", resourceCulture); | ||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
@@ -168,9 +171,9 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ss32In { | |||||
public static System.Drawing.Bitmap ss32In { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("ss32In", resourceCulture); | object obj = ResourceManager.GetObject("ss32In", resourceCulture); | ||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
@@ -178,9 +181,9 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ss32Out { | |||||
public static System.Drawing.Bitmap ss32Out { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("ss32Out", resourceCulture); | object obj = ResourceManager.GetObject("ss32Out", resourceCulture); | ||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
@@ -188,9 +191,9 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ss32Outline { | |||||
public static System.Drawing.Bitmap ss32Outline { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("ss32Outline", resourceCulture); | object obj = ResourceManager.GetObject("ss32Outline", resourceCulture); | ||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
@@ -198,9 +201,9 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ssw128 { | |||||
public static System.Drawing.Bitmap ssw128 { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("ssw128", resourceCulture); | object obj = ResourceManager.GetObject("ssw128", resourceCulture); | ||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
@@ -208,9 +211,9 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] sysproxy_exe { | |||||
public static byte[] sysproxy_exe { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("sysproxy_exe", resourceCulture); | object obj = ResourceManager.GetObject("sysproxy_exe", resourceCulture); | ||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
@@ -218,9 +221,9 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] sysproxy64_exe { | |||||
public static byte[] sysproxy64_exe { | |||||
get { | get { | ||||
object obj = ResourceManager.GetObject("sysproxy64_exe", resourceCulture); | object obj = ResourceManager.GetObject("sysproxy64_exe", resourceCulture); | ||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
@@ -228,11 +231,11 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <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 | ///! See https://adblockplus.org/en/filter-cheatsheet | ||||
/// 的本地化字符串。 | |||||
///. | |||||
/// </summary> | /// </summary> | ||||
internal static string user_rule { | |||||
public static string user_rule { | |||||
get { | get { | ||||
return ResourceManager.GetString("user_rule", resourceCulture); | return ResourceManager.GetString("user_rule", resourceCulture); | ||||
} | } | ||||
@@ -39,8 +39,6 @@ namespace Shadowsocks.Proxy | |||||
public object AsyncState { get; set; } | public object AsyncState { get; set; } | ||||
public int BytesToRead; | |||||
public Exception ex { get; set; } | public Exception ex { get; set; } | ||||
} | } | ||||
@@ -199,7 +197,7 @@ namespace Shadowsocks.Proxy | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
if (line.IsNullOrEmpty()) | |||||
if (string.IsNullOrEmpty(line)) | |||||
{ | { | ||||
return true; | 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 Microsoft.Win32; | ||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using System.Drawing; | |||||
using ZXing; | |||||
using ZXing.QrCode; | |||||
using ZXing.Common; | |||||
namespace Shadowsocks.Util | namespace Shadowsocks.Util | ||||
{ | { | ||||
@@ -183,7 +187,7 @@ namespace Shadowsocks.Util | |||||
// we are building x86 binary for both x86 and x64, which will | // we are building x86 binary for both x86 and x64, which will | ||||
// cause problem when opening registry key | // cause problem when opening registry key | ||||
// detect operating system instead of CPU | // detect operating system instead of CPU | ||||
if (name.IsNullOrEmpty()) throw new ArgumentException(nameof(name)); | |||||
if (string.IsNullOrEmpty(name)) throw new ArgumentException(nameof(name)); | |||||
try | try | ||||
{ | { | ||||
RegistryKey userKey = RegistryKey.OpenBaseKey(hive, | RegistryKey userKey = RegistryKey.OpenBaseKey(hive, | ||||
@@ -208,10 +212,47 @@ namespace Shadowsocks.Util | |||||
return Environment.OSVersion.Version.Major > 5; | 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) | 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; | password = null; | ||||
string outPassword; | 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())) | 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() | private void LoadCurrentConfiguration() | ||||
{ | { | ||||
_modifiedConfiguration = controller.GetConfigurationCopy(); | |||||
_modifiedConfiguration = controller.GetCurrentConfiguration(); | |||||
LoadServerNameListToUI(_modifiedConfiguration); | LoadServerNameListToUI(_modifiedConfiguration); | ||||
_lastSelectedIndex = _modifiedConfiguration.index; | _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."); | 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; | topMostTrigger = config.topMost; | ||||
wrapTextTrigger = config.wrapText; | wrapTextTrigger = config.wrapText; | ||||
@@ -244,7 +244,7 @@ namespace Shadowsocks.View | |||||
timer.Tick += Timer_Tick; | timer.Tick += Timer_Tick; | ||||
timer.Start(); | timer.Start(); | ||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||||
LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; | |||||
Height = config.Height; | Height = config.Height; | ||||
Width = config.Width; | Width = config.Width; | ||||
@@ -270,7 +270,7 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
timer.Stop(); | timer.Stop(); | ||||
controller.TrafficChanged -= controller_TrafficChanged; | controller.TrafficChanged -= controller_TrafficChanged; | ||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||||
LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; | |||||
config.topMost = topMostTrigger; | config.topMost = topMostTrigger; | ||||
config.wrapText = wrapTextTrigger; | config.wrapText = wrapTextTrigger; | ||||
@@ -1,9 +1,11 @@ | |||||
using NLog; | using NLog; | ||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Localization; | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Properties; | using Shadowsocks.Properties; | ||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using Shadowsocks.Util.SystemProxy; | using Shadowsocks.Util.SystemProxy; | ||||
using Shadowsocks.Views; | |||||
using System; | using System; | ||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.Drawing; | using System.Drawing; | ||||
@@ -11,7 +13,8 @@ using System.IO; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Text; | using System.Text; | ||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using System.Windows.Media.Imaging; | |||||
using System.Windows.Forms.Integration; | |||||
using System.Windows.Threading; | |||||
using ZXing; | using ZXing; | ||||
using ZXing.Common; | using ZXing.Common; | ||||
using ZXing.QrCode; | using ZXing.QrCode; | ||||
@@ -20,19 +23,15 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
public class MenuViewController | 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 ShadowsocksController controller; | ||||
private UpdateChecker updateChecker; | |||||
public UpdateChecker updateChecker; | |||||
private NotifyIcon _notifyIcon; | private NotifyIcon _notifyIcon; | ||||
private Icon icon, icon_in, icon_out, icon_both, previousIcon; | private Icon icon, icon_in, icon_out, icon_both, previousIcon; | ||||
private bool _isFirstRun; | |||||
private bool _isStartupChecking; | |||||
private bool _isStartupCheck; | |||||
private string _urlToOpen; | private string _urlToOpen; | ||||
private ContextMenuStrip contextMenu1; | private ContextMenuStrip contextMenu1; | ||||
@@ -52,6 +51,7 @@ namespace Shadowsocks.View | |||||
private ToolStripMenuItem editGFWUserRuleItem; | private ToolStripMenuItem editGFWUserRuleItem; | ||||
private ToolStripMenuItem editOnlinePACItem; | private ToolStripMenuItem editOnlinePACItem; | ||||
private ToolStripMenuItem secureLocalPacUrlToggleItem; | private ToolStripMenuItem secureLocalPacUrlToggleItem; | ||||
private ToolStripMenuItem regenerateLocalPacOnUpdateItem; | |||||
private ToolStripMenuItem autoCheckUpdatesToggleItem; | private ToolStripMenuItem autoCheckUpdatesToggleItem; | ||||
private ToolStripMenuItem checkPreReleaseToggleItem; | private ToolStripMenuItem checkPreReleaseToggleItem; | ||||
private ToolStripMenuItem proxyItem; | private ToolStripMenuItem proxyItem; | ||||
@@ -62,12 +62,12 @@ namespace Shadowsocks.View | |||||
private ToolStripMenuItem onlineConfigItem; | private ToolStripMenuItem onlineConfigItem; | ||||
private ConfigForm configForm; | private ConfigForm configForm; | ||||
private ProxyForm proxyForm; | |||||
private LogForm logForm; | 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 | // color definition for icon color transformation | ||||
private readonly Color colorMaskBlue = Color.FromArgb(255, 25, 125, 191); | private readonly Color colorMaskBlue = Color.FromArgb(255, 25, 125, 191); | ||||
@@ -103,22 +103,21 @@ namespace Shadowsocks.View | |||||
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; | _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; | ||||
controller.TrafficChanged += controller_TrafficChanged; | controller.TrafficChanged += controller_TrafficChanged; | ||||
this.updateChecker = new UpdateChecker(); | |||||
updateChecker = new UpdateChecker(); | |||||
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; | updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; | ||||
LoadCurrentConfiguration(); | LoadCurrentConfiguration(); | ||||
Configuration config = controller.GetConfigurationCopy(); | |||||
Configuration config = controller.GetCurrentConfiguration(); | |||||
if (config.isDefault) | |||||
if (config.firstRun) | |||||
{ | { | ||||
_isFirstRun = true; | |||||
ShowConfigForm(); | ShowConfigForm(); | ||||
} | } | ||||
else if (config.autoCheckUpdate) | 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() | private void UpdateTrayIconAndNotifyText() | ||||
{ | { | ||||
Configuration config = controller.GetConfigurationCopy(); | |||||
Configuration config = controller.GetCurrentConfiguration(); | |||||
bool enabled = config.enabled; | bool enabled = config.enabled; | ||||
bool global = config.global; | 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()); | icon_both = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In, Resources.ss32Out), size.Width, size.Height).GetHicon()); | ||||
} | } | ||||
#endregion | #endregion | ||||
#region ToolStripMenuItems and MenuGroups | #region ToolStripMenuItems and MenuGroups | ||||
@@ -270,7 +267,6 @@ namespace Shadowsocks.View | |||||
this.ServersItem = CreateMenuGroup("Servers", new ToolStripItem [] { | this.ServersItem = CreateMenuGroup("Servers", new ToolStripItem [] { | ||||
this.SeperatorItem = new ToolStripSeparator(), | this.SeperatorItem = new ToolStripSeparator(), | ||||
this.ConfigItem = CreateToolStripMenuItem("Edit Servers...", new EventHandler(this.Config_Click)), | this.ConfigItem = CreateToolStripMenuItem("Edit Servers...", new EventHandler(this.Config_Click)), | ||||
CreateToolStripMenuItem("Statistics Config...", StatisticsConfigItem_Click), | |||||
new ToolStripSeparator(), | new ToolStripSeparator(), | ||||
CreateToolStripMenuItem("Share Server Config...", new EventHandler(this.QRCodeItem_Click)), | CreateToolStripMenuItem("Share Server Config...", new EventHandler(this.QRCodeItem_Click)), | ||||
CreateToolStripMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_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.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.editGFWUserRuleItem = CreateToolStripMenuItem("Edit User Rule for Geosite...", new EventHandler(this.EditUserRuleFileForGeositeItem_Click)), | ||||
this.secureLocalPacUrlToggleItem = CreateToolStripMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_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)), | CreateToolStripMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)), | ||||
this.editOnlinePACItem = CreateToolStripMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_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)); | MessageBox.Show(e.GetException().ToString(), I18N.GetString("Shadowsocks Error: {0}", e.GetException().Message)); | ||||
} | } | ||||
@@ -354,7 +351,7 @@ namespace Shadowsocks.View | |||||
private void LoadCurrentConfiguration() | private void LoadCurrentConfiguration() | ||||
{ | { | ||||
Configuration config = controller.GetConfigurationCopy(); | |||||
Configuration config = controller.GetCurrentConfiguration(); | |||||
UpdateServersMenu(); | UpdateServersMenu(); | ||||
UpdateSystemProxyItemsEnabledStatus(config); | UpdateSystemProxyItemsEnabledStatus(config); | ||||
ShareOverLANItem.Checked = config.shareOverLan; | ShareOverLANItem.Checked = config.shareOverLan; | ||||
@@ -365,6 +362,7 @@ namespace Shadowsocks.View | |||||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | ||||
localPACItem.Checked = !onlinePACItem.Checked; | localPACItem.Checked = !onlinePACItem.Checked; | ||||
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | ||||
regenerateLocalPacOnUpdateItem.Checked = config.regeneratePacOnUpdate; | |||||
UpdatePACItemsEnabledStatus(); | UpdatePACItemsEnabledStatus(); | ||||
UpdateUpdateMenu(); | 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() | private void ShowLogForm() | ||||
{ | { | ||||
if (logForm != null) | 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) | void logForm_FormClosed(object sender, FormClosedEventArgs e) | ||||
{ | { | ||||
logForm.Dispose(); | logForm.Dispose(); | ||||
@@ -457,7 +409,8 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
configForm.Dispose(); | configForm.Dispose(); | ||||
configForm = null; | configForm = null; | ||||
if (_isFirstRun) | |||||
var config = controller.GetCurrentConfiguration(); | |||||
if (config.firstRun) | |||||
{ | { | ||||
CheckUpdateForFirstRun(); | CheckUpdateForFirstRun(); | ||||
ShowBalloonTip( | ShowBalloonTip( | ||||
@@ -466,28 +419,10 @@ namespace Shadowsocks.View | |||||
ToolTipIcon.Info, | ToolTipIcon.Info, | ||||
0 | 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 | #endregion | ||||
#region Misc | #region Misc | ||||
@@ -502,23 +437,10 @@ namespace Shadowsocks.View | |||||
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) | 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) | 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) | 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() | public void ShowLogForm_HotKey() | ||||
@@ -555,6 +478,144 @@ namespace Shadowsocks.View | |||||
#region Main menu | #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) | void controller_ShareOverLANStatusChanged(object sender, EventArgs e) | ||||
{ | { | ||||
ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; | ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; | ||||
@@ -620,13 +681,13 @@ namespace Shadowsocks.View | |||||
private void EnableItem_Click(object sender, EventArgs e) | private void EnableItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
controller.ToggleEnable(false); | controller.ToggleEnable(false); | ||||
Configuration config = controller.GetConfigurationCopy(); | |||||
Configuration config = controller.GetCurrentConfiguration(); | |||||
UpdateSystemProxyItemsEnabledStatus(config); | UpdateSystemProxyItemsEnabledStatus(config); | ||||
} | } | ||||
void controller_EnableGlobalChanged(object sender, EventArgs e) | void controller_EnableGlobalChanged(object sender, EventArgs e) | ||||
{ | { | ||||
globalModeItem.Checked = controller.GetConfigurationCopy().global; | |||||
globalModeItem.Checked = controller.GetCurrentConfiguration().global; | |||||
PACModeItem.Checked = !globalModeItem.Checked; | PACModeItem.Checked = !globalModeItem.Checked; | ||||
} | } | ||||
@@ -652,7 +713,7 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
controller.ToggleEnable(true); | controller.ToggleEnable(true); | ||||
controller.ToggleGlobal(true); | controller.ToggleGlobal(true); | ||||
Configuration config = controller.GetConfigurationCopy(); | |||||
Configuration config = controller.GetCurrentConfiguration(); | |||||
UpdateSystemProxyItemsEnabledStatus(config); | UpdateSystemProxyItemsEnabledStatus(config); | ||||
} | } | ||||
@@ -660,7 +721,7 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
controller.ToggleEnable(true); | controller.ToggleEnable(true); | ||||
controller.ToggleGlobal(false); | controller.ToggleGlobal(false); | ||||
Configuration config = controller.GetConfigurationCopy(); | |||||
Configuration config = controller.GetCurrentConfiguration(); | |||||
UpdateSystemProxyItemsEnabledStatus(config); | UpdateSystemProxyItemsEnabledStatus(config); | ||||
} | } | ||||
@@ -693,7 +754,7 @@ namespace Shadowsocks.View | |||||
items.Add(new ToolStripSeparator() { Tag = "-server-" }); | items.Add(new ToolStripSeparator() { Tag = "-server-" }); | ||||
} | } | ||||
int serverCount = 0; | int serverCount = 0; | ||||
Configuration configuration = controller.GetConfigurationCopy(); | |||||
Configuration configuration = controller.GetCurrentConfiguration(); | |||||
foreach (var server in configuration.configs) | foreach (var server in configuration.configs) | ||||
{ | { | ||||
if (Configuration.ChecksServer(server)) | if (Configuration.ChecksServer(server)) | ||||
@@ -746,112 +807,58 @@ namespace Shadowsocks.View | |||||
ShowConfigForm(); | ShowConfigForm(); | ||||
} | } | ||||
void splash_FormClosed(object sender, FormClosedEventArgs e) | |||||
{ | |||||
ShowConfigForm(); | |||||
} | |||||
void openURLFromQRCode(object sender, FormClosedEventArgs e) | void openURLFromQRCode(object sender, FormClosedEventArgs e) | ||||
{ | { | ||||
Utils.OpenInBrowser(_urlToOpen); | 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) | 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) | private void ImportURLItem_Click(object sender, EventArgs e) | ||||
@@ -881,11 +888,11 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
if (!onlinePACItem.Checked) | if (!onlinePACItem.Checked) | ||||
{ | { | ||||
if (controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty()) | |||||
if (string.IsNullOrEmpty(controller.GetCurrentConfiguration().pacUrl)) | |||||
{ | { | ||||
UpdateOnlinePACURLItem_Click(sender, e); | UpdateOnlinePACURLItem_Click(sender, e); | ||||
} | } | ||||
if (!controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty()) | |||||
if (!string.IsNullOrEmpty(controller.GetCurrentConfiguration().pacUrl)) | |||||
{ | { | ||||
localPACItem.Checked = false; | localPACItem.Checked = false; | ||||
onlinePACItem.Checked = true; | onlinePACItem.Checked = true; | ||||
@@ -897,12 +904,12 @@ namespace Shadowsocks.View | |||||
private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) | private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
string origPacUrl = controller.GetConfigurationCopy().pacUrl; | |||||
string origPacUrl = controller.GetCurrentConfiguration().pacUrl; | |||||
string pacUrl = ViewUtils.InputBox( | string pacUrl = ViewUtils.InputBox( | ||||
I18N.GetString("Please input PAC Url"), | I18N.GetString("Please input PAC Url"), | ||||
I18N.GetString("Edit Online PAC URL"), | I18N.GetString("Edit Online PAC URL"), | ||||
origPacUrl, -1, -1); | origPacUrl, -1, -1); | ||||
if (!pacUrl.IsNullOrEmpty() && pacUrl != origPacUrl) | |||||
if (!string.IsNullOrEmpty(pacUrl) && pacUrl != origPacUrl) | |||||
{ | { | ||||
controller.SavePACUrl(pacUrl); | controller.SavePACUrl(pacUrl); | ||||
} | } | ||||
@@ -910,10 +917,16 @@ namespace Shadowsocks.View | |||||
private void SecureLocalPacUrlToggleItem_Click(object sender, EventArgs e) | private void SecureLocalPacUrlToggleItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
Configuration configuration = controller.GetConfigurationCopy(); | |||||
Configuration configuration = controller.GetCurrentConfiguration(); | |||||
controller.ToggleSecureLocalPac(!configuration.secureLocalPac); | 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) | private void CopyLocalPacUrlItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
controller.CopyPacUrl(); | controller.CopyPacUrl(); | ||||
@@ -937,7 +950,6 @@ namespace Shadowsocks.View | |||||
} | } | ||||
} | } | ||||
private void EditPACFileItem_Click(object sender, EventArgs e) | private void EditPACFileItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
controller.TouchPACFile(); | controller.TouchPACFile(); | ||||
@@ -980,12 +992,12 @@ namespace Shadowsocks.View | |||||
void controller_VerboseLoggingStatusChanged(object sender, EventArgs e) | void controller_VerboseLoggingStatusChanged(object sender, EventArgs e) | ||||
{ | { | ||||
VerboseLoggingToggleItem.Checked = controller.GetConfigurationCopy().isVerboseLogging; | |||||
VerboseLoggingToggleItem.Checked = controller.GetCurrentConfiguration().isVerboseLogging; | |||||
} | } | ||||
void controller_ShowPluginOutputChanged(object sender, EventArgs e) | 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) | private void VerboseLoggingToggleItem_Click(object sender, EventArgs e) | ||||
@@ -1016,41 +1028,37 @@ namespace Shadowsocks.View | |||||
void updateChecker_CheckUpdateCompleted(object sender, EventArgs e) | 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); | ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000); | ||||
} | } | ||||
_isStartupChecking = false; | |||||
_isStartupCheck = false; | |||||
} | } | ||||
private void UpdateUpdateMenu() | private void UpdateUpdateMenu() | ||||
{ | { | ||||
Configuration configuration = controller.GetConfigurationCopy(); | |||||
Configuration configuration = controller.GetCurrentConfiguration(); | |||||
autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate; | autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate; | ||||
checkPreReleaseToggleItem.Checked = configuration.checkPreRelease; | checkPreReleaseToggleItem.Checked = configuration.checkPreRelease; | ||||
} | } | ||||
private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e) | private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
Configuration configuration = controller.GetConfigurationCopy(); | |||||
Configuration configuration = controller.GetCurrentConfiguration(); | |||||
controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate); | controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate); | ||||
UpdateUpdateMenu(); | UpdateUpdateMenu(); | ||||
} | } | ||||
private void checkPreReleaseToggleItem_Click(object sender, EventArgs e) | private void checkPreReleaseToggleItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
Configuration configuration = controller.GetConfigurationCopy(); | |||||
Configuration configuration = controller.GetCurrentConfiguration(); | |||||
controller.ToggleCheckingPreRelease(!configuration.checkPreRelease); | controller.ToggleCheckingPreRelease(!configuration.checkPreRelease); | ||||
UpdateUpdateMenu(); | 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) | 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] | [TestClass] | ||||
public class UnitTest | 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] | [TestMethod] | ||||
public void TestHotKey2Str() | public void TestHotKey2Str() | ||||
{ | { | ||||