mirror of
https://github.com/netchx/netch.git
synced 2026-03-14 17:43:18 +08:00
1595 lines
50 KiB
C#
1595 lines
50 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Drawing;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Forms;
|
||
using Microsoft.Win32;
|
||
using Netch.Controllers;
|
||
using Netch.Forms.Mode;
|
||
using Netch.Models;
|
||
using Netch.Utils;
|
||
|
||
namespace Netch.Forms
|
||
{
|
||
public partial class MainForm : Form
|
||
{
|
||
#region Start
|
||
|
||
private readonly Dictionary<string, object> _mainFormText = new();
|
||
|
||
private bool _comboBoxInitialized;
|
||
private bool _textRecorded;
|
||
public MainForm()
|
||
{
|
||
InitializeComponent();
|
||
|
||
AddAddServerToolStripMenuItems();
|
||
|
||
#region i18N Translations
|
||
|
||
_mainFormText.Add(UninstallServiceToolStripMenuItem.Name, new[] {"Uninstall {0}", "NF Service"});
|
||
_mainFormText.Add(UninstallTapDriverToolStripMenuItem.Name, new[] {"Uninstall {0}", "TUN/TAP driver"});
|
||
|
||
#endregion
|
||
|
||
// 监听电源事件
|
||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||
|
||
ModeComboBox.KeyUp += (sender, args) =>
|
||
{
|
||
switch (args.KeyData)
|
||
{
|
||
case Keys.Escape:
|
||
{
|
||
SelectLastMode();
|
||
return;
|
||
}
|
||
}
|
||
};
|
||
|
||
CheckForIllegalCrossThreadCalls = false;
|
||
}
|
||
|
||
private void AddAddServerToolStripMenuItems()
|
||
{
|
||
foreach (var serversUtil in ServerHelper.ServerUtils.Where(i => !string.IsNullOrEmpty(i.FullName)))
|
||
{
|
||
var fullName = serversUtil.FullName;
|
||
var control = new ToolStripMenuItem
|
||
{
|
||
Name = $"Add{fullName}ServerToolStripMenuItem",
|
||
Size = new Size(259, 22),
|
||
Text = i18N.TranslateFormat("Add [{0}] Server", fullName)
|
||
};
|
||
_mainFormText.Add(control.Name, new[] {"Add [{0}] Server", fullName});
|
||
control.Click += AddServerToolStripMenuItem_Click;
|
||
ServerToolStripMenuItem.DropDownItems.Add(control);
|
||
}
|
||
}
|
||
|
||
private void MainForm_Load(object sender, EventArgs e)
|
||
{
|
||
OnlyInstance.Called += OnCalled;
|
||
|
||
// 计算 ComboBox绘制 目标宽度
|
||
RecordSize();
|
||
|
||
InitServer();
|
||
ServerHelper.DelayTestHelper.UpdateInterval();
|
||
|
||
ModeHelper.Load();
|
||
InitMode();
|
||
_comboBoxInitialized = true;
|
||
|
||
// 加载翻译
|
||
InitText();
|
||
|
||
// 隐藏 NatTypeStatusLabel
|
||
NatTypeStatusText();
|
||
|
||
// 加载快速配置
|
||
InitProfile();
|
||
|
||
// 打开软件时启动加速,产生开始按钮点击事件
|
||
if (Global.Settings.StartWhenOpened)
|
||
ControlButton_Click(null, null);
|
||
|
||
Task.Run(() =>
|
||
{
|
||
// 检查更新
|
||
if (Global.Settings.CheckUpdateWhenOpened)
|
||
CheckUpdate();
|
||
});
|
||
|
||
Task.Run(async () =>
|
||
{
|
||
// 检查订阅更新
|
||
if (Global.Settings.UpdateServersWhenOpened)
|
||
await UpdateServersFromSubscribe(Global.Settings.UseProxyToUpdateSubscription);
|
||
});
|
||
}
|
||
|
||
private void RecordSize()
|
||
{
|
||
_numberBoxWidth = ServerComboBox.Width / 10;
|
||
_numberBoxX = _numberBoxWidth * 9;
|
||
_numberBoxWrap = _numberBoxWidth / 30;
|
||
|
||
_configurationGroupBoxHeight = ConfigurationGroupBox.Height;
|
||
_profileConfigurationHeight = ConfigurationGroupBox.Controls[0].Height / 3; // 因为 AutoSize, 所以得到的是Controls的总高度
|
||
}
|
||
|
||
private void InitText()
|
||
{
|
||
#region Record English
|
||
|
||
if (!_textRecorded)
|
||
{
|
||
void RecordText(Component component)
|
||
{
|
||
try
|
||
{
|
||
switch (component)
|
||
{
|
||
case TextBoxBase _:
|
||
case ListControl _:
|
||
break;
|
||
case Control c:
|
||
_mainFormText.Add(c.Name, c.Text);
|
||
break;
|
||
case ToolStripItem c:
|
||
_mainFormText.Add(c.Name, c.Text);
|
||
break;
|
||
}
|
||
}
|
||
catch (ArgumentException)
|
||
{
|
||
// ignored
|
||
}
|
||
}
|
||
|
||
Utils.Utils.ComponentIterator(this, RecordText);
|
||
Utils.Utils.ComponentIterator(NotifyMenu, RecordText);
|
||
_textRecorded = true;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Translate
|
||
|
||
void TranslateText(Component component)
|
||
{
|
||
switch (component)
|
||
{
|
||
case TextBoxBase _:
|
||
case ListControl _:
|
||
break;
|
||
case Control c:
|
||
if (_mainFormText.ContainsKey(c.Name))
|
||
c.Text = ControlText(c.Name);
|
||
break;
|
||
case ToolStripItem c:
|
||
if (_mainFormText.ContainsKey(c.Name))
|
||
c.Text = ControlText(c.Name);
|
||
break;
|
||
}
|
||
|
||
string ControlText(string name)
|
||
{
|
||
var value = _mainFormText[name];
|
||
if (value.Equals(string.Empty)) return string.Empty;
|
||
|
||
if (value is object[] values)
|
||
return i18N.TranslateFormat(values.First() as string, values.Skip(1).ToArray());
|
||
return i18N.Translate(value);
|
||
}
|
||
}
|
||
|
||
Utils.Utils.ComponentIterator(this, TranslateText);
|
||
Utils.Utils.ComponentIterator(NotifyMenu, TranslateText);
|
||
|
||
#endregion
|
||
|
||
UsedBandwidthLabel.Text = $@"{i18N.Translate("Used", ": ")}0 KB";
|
||
State = State;
|
||
VersionLabel.Text = UpdateChecker.Version;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Controls
|
||
|
||
#region MenuStrip
|
||
|
||
#region Server
|
||
|
||
private void ImportServersFromClipboardToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
var texts = Clipboard.GetText();
|
||
if (!string.IsNullOrWhiteSpace(texts))
|
||
{
|
||
var servers = ShareLink.ParseText(texts);
|
||
Global.Settings.Server.AddRange(servers);
|
||
NotifyTip(i18N.TranslateFormat("Import {0} server(s) form Clipboard", servers.Count));
|
||
|
||
InitServer();
|
||
Configuration.Save();
|
||
}
|
||
}
|
||
|
||
private void AddServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
var s = ((ToolStripMenuItem) sender).Text;
|
||
|
||
var start = s.IndexOf("[", StringComparison.Ordinal) + 1;
|
||
var end = s.IndexOf("]", start, StringComparison.Ordinal);
|
||
var result = s.Substring(start, end - start);
|
||
|
||
Hide();
|
||
ServerHelper.GetUtilByFullName(result).Create();
|
||
|
||
InitServer();
|
||
Configuration.Save();
|
||
Show();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Mode
|
||
|
||
private void CreateProcessModeToolStripButton_Click(object sender, EventArgs e)
|
||
{
|
||
Hide();
|
||
new Process().ShowDialog();
|
||
Show();
|
||
}
|
||
|
||
private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Enabled = false;
|
||
try
|
||
{
|
||
ModeHelper.Load();
|
||
InitMode();
|
||
NotifyTip(i18N.Translate("Modes have been reload"));
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// ignored
|
||
}
|
||
finally
|
||
{
|
||
Enabled = true;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Subscription
|
||
|
||
private void ManageSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Hide();
|
||
new SubscribeForm().ShowDialog();
|
||
InitServer();
|
||
Show();
|
||
}
|
||
|
||
private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Global.Settings.UseProxyToUpdateSubscription = false;
|
||
await UpdateServersFromSubscribe();
|
||
}
|
||
|
||
private async void UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Global.Settings.UseProxyToUpdateSubscription = true;
|
||
await UpdateServersFromSubscribe(true);
|
||
}
|
||
|
||
private async Task UpdateServersFromSubscribe(bool useProxy = false)
|
||
{
|
||
void DisableItems(bool v)
|
||
{
|
||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ProfileGroupBox.Enabled = ControlButton.Enabled = v;
|
||
}
|
||
|
||
if (useProxy && ServerComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||
return;
|
||
}
|
||
|
||
if (Global.Settings.SubscribeLink.Count <= 0)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("No subscription link"));
|
||
return;
|
||
}
|
||
|
||
StatusText(i18N.Translate("Starting update subscription"));
|
||
DisableItems(false);
|
||
try
|
||
{
|
||
string proxyServer = null;
|
||
if (useProxy)
|
||
{
|
||
var mode = new Models.Mode
|
||
{
|
||
Remark = "ProxyUpdate",
|
||
Type = 5
|
||
};
|
||
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
|
||
proxyServer = $"http://127.0.0.1:{Global.Settings.HTTPLocalPort}";
|
||
}
|
||
|
||
await Subscription.UpdateServersAsync(proxyServer);
|
||
|
||
InitServer();
|
||
Configuration.Save();
|
||
StatusText(i18N.Translate("Subscription updated"));
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// ignored
|
||
}
|
||
finally
|
||
{
|
||
if (useProxy)
|
||
try
|
||
{
|
||
await MainController.Stop();
|
||
}
|
||
catch
|
||
{
|
||
// ignored
|
||
}
|
||
|
||
DisableItems(true);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Options
|
||
|
||
private void CheckForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Task.Run(() =>
|
||
{
|
||
void OnNewVersionNotFound(object o, EventArgs args)
|
||
{
|
||
UpdateChecker.NewVersionNotFound -= OnNewVersionNotFound;
|
||
NotifyTip(i18N.Translate("Already latest version"));
|
||
}
|
||
|
||
void OnNewVersionFoundFailed(object o, EventArgs args)
|
||
{
|
||
UpdateChecker.NewVersionFoundFailed -= OnNewVersionFoundFailed;
|
||
NotifyTip(i18N.Translate("New version found failed"), info: false);
|
||
}
|
||
|
||
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
|
||
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
|
||
CheckUpdate();
|
||
});
|
||
}
|
||
|
||
private void OpenDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Utils.Utils.Open(".\\");
|
||
}
|
||
|
||
private async void CleanDNSCacheToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
NativeMethods.FlushDNSResolverCache();
|
||
DNS.Cache.Clear();
|
||
});
|
||
|
||
NotifyTip(i18N.Translate("DNS cache cleanup succeeded"));
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// ignored
|
||
}
|
||
finally
|
||
{
|
||
StatusText();
|
||
}
|
||
}
|
||
|
||
private void updateACLWithProxyToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
UpdateACL(true);
|
||
}
|
||
|
||
private void updateACLToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
UpdateACL(false);
|
||
}
|
||
|
||
private async void UpdateACL(bool useProxy)
|
||
{
|
||
if (useProxy && ServerComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||
return;
|
||
}
|
||
|
||
Enabled = false;
|
||
StatusText(i18N.TranslateFormat("Updating {0}", "ACL"));
|
||
try
|
||
{
|
||
if (useProxy)
|
||
{
|
||
var mode = new Models.Mode
|
||
{
|
||
Remark = "ProxyUpdate",
|
||
Type = 5
|
||
};
|
||
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
|
||
}
|
||
|
||
var req = WebUtil.CreateRequest(Global.Settings.ACL);
|
||
if (useProxy)
|
||
req.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
|
||
|
||
await WebUtil.DownloadFileAsync(req, Path.Combine(Global.NetchDir, "bin\\default.acl"));
|
||
NotifyTip(i18N.Translate("ACL updated successfully"));
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
NotifyTip(i18N.Translate("ACL update failed") + "\n" + e.Message, info: false);
|
||
Logging.Error("更新 ACL 失败!" + e);
|
||
}
|
||
finally
|
||
{
|
||
if (useProxy)
|
||
{
|
||
await MainController.Stop();
|
||
}
|
||
|
||
StatusText();
|
||
Enabled = true;
|
||
}
|
||
}
|
||
|
||
private async void updatePACToolStripMenuItem_Click(object sender, EventArgs eventArgs)
|
||
{
|
||
Enabled = false;
|
||
|
||
StatusText(i18N.TranslateFormat("Updating {0}", "PAC"));
|
||
try
|
||
{
|
||
var req = WebUtil.CreateRequest(Global.Settings.PAC);
|
||
|
||
var pac = Path.Combine(Global.NetchDir, "bin\\pac.txt");
|
||
|
||
await WebUtil.DownloadFileAsync(req, pac);
|
||
|
||
NotifyTip(i18N.Translate("PAC updated successfully"));
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
NotifyTip(i18N.Translate("PAC update failed") + "\n" + e.Message, info: false);
|
||
Logging.Error("更新 PAC 失败!" + e);
|
||
}
|
||
finally
|
||
{
|
||
StatusText();
|
||
Enabled = true;
|
||
}
|
||
}
|
||
|
||
private async void UninstallServiceToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Enabled = false;
|
||
StatusText(i18N.TranslateFormat("Uninstalling {0}", "NF Service"));
|
||
try
|
||
{
|
||
await Task.Run(() =>
|
||
{
|
||
if (NFController.UninstallDriver())
|
||
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "NF Service"));
|
||
});
|
||
}
|
||
finally
|
||
{
|
||
StatusText();
|
||
Enabled = true;
|
||
}
|
||
}
|
||
|
||
private async void UninstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Enabled = false;
|
||
StatusText(i18N.TranslateFormat("Uninstalling {0}", "TUN/TAP driver"));
|
||
try
|
||
{
|
||
await Task.Run(TUNTAP.deltapall);
|
||
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "TUN/TAP driver"));
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
Logging.Error($"卸载 TUN/TAP 适配器失败: {exception}");
|
||
}
|
||
finally
|
||
{
|
||
StatusText();
|
||
Enabled = true;
|
||
}
|
||
}
|
||
|
||
private void RemoveNetchFirewallRulesToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Firewall.RemoveNetchFwRules();
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 菜单栏强制退出
|
||
/// </summary>
|
||
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Exit(true);
|
||
}
|
||
|
||
private void VersionLabel_Click(object sender, EventArgs e)
|
||
{
|
||
Utils.Utils.Open($"https://github.com/{UpdateChecker.Owner}/{UpdateChecker.Repo}/releases");
|
||
}
|
||
|
||
private void AboutToolStripButton_Click(object sender, EventArgs e)
|
||
{
|
||
Hide();
|
||
new AboutForm().ShowDialog();
|
||
Show();
|
||
}
|
||
|
||
private void fAQToolStripMenuItem_Click(object sender, EventArgs e)
|
||
{
|
||
Utils.Utils.Open("https://netch.org/#/docs/zh-CN/faq");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ControlButton
|
||
|
||
private async void ControlButton_Click(object sender, EventArgs e)
|
||
{
|
||
if (!IsWaiting())
|
||
{
|
||
// 停止
|
||
State = State.Stopping;
|
||
await MainController.Stop();
|
||
State = State.Stopped;
|
||
return;
|
||
}
|
||
|
||
Configuration.Save();
|
||
|
||
// 服务器、模式 需选择
|
||
if (!(ServerComboBox.SelectedItem is Server server))
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||
return;
|
||
}
|
||
|
||
if (!(ModeComboBox.SelectedItem is Models.Mode mode))
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||
return;
|
||
}
|
||
|
||
// 清除模式搜索框文本选择
|
||
ModeComboBox.Select(0, 0);
|
||
|
||
State = State.Starting;
|
||
|
||
if (!await MainController.Start(server, mode))
|
||
{
|
||
State = State.Stopped;
|
||
StatusText(i18N.Translate("Start failed"));
|
||
return;
|
||
}
|
||
|
||
State = State.Started;
|
||
|
||
_ = Task.Run(Bandwidth.NetTraffic);
|
||
_ = Task.Run(NatTest);
|
||
|
||
if (Global.Settings.MinimizeWhenStarted)
|
||
Minimize();
|
||
|
||
// 自动检测延迟
|
||
_ = Task.Run(() =>
|
||
{
|
||
while (State == State.Started)
|
||
{
|
||
bool StartedPingEnabled()
|
||
{
|
||
return Global.Settings.StartedPingInterval >= 0;
|
||
}
|
||
|
||
if (StartedPingEnabled())
|
||
{
|
||
server.Test();
|
||
ServerComboBox.Refresh();
|
||
}
|
||
|
||
if (StartedPingEnabled())
|
||
Thread.Sleep(Global.Settings.StartedPingInterval * 1000);
|
||
else
|
||
Thread.Sleep(5000);
|
||
}
|
||
});
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region SettingsButton
|
||
|
||
private void SettingsButton_Click(object sender, EventArgs e)
|
||
{
|
||
var oldSettings = Global.Settings.Clone();
|
||
|
||
Hide();
|
||
new SettingForm().ShowDialog();
|
||
|
||
if (oldSettings.Language != Global.Settings.Language)
|
||
{
|
||
i18N.Load(Global.Settings.Language);
|
||
InitText();
|
||
InitMode();
|
||
InitProfile();
|
||
}
|
||
|
||
if (oldSettings.DetectionTick != Global.Settings.DetectionTick)
|
||
ServerHelper.DelayTestHelper.UpdateInterval();
|
||
|
||
if (oldSettings.ProfileCount != Global.Settings.ProfileCount)
|
||
InitProfile();
|
||
|
||
Show();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Server
|
||
|
||
private void InitServer()
|
||
{
|
||
var comboBoxInitialized = _comboBoxInitialized;
|
||
_comboBoxInitialized = false;
|
||
|
||
ServerComboBox.Items.Clear();
|
||
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
|
||
SelectLastServer();
|
||
_comboBoxInitialized = comboBoxInitialized;
|
||
}
|
||
|
||
public void SelectLastServer()
|
||
{
|
||
// 如果值合法,选中该位置
|
||
if (Global.Settings.ServerComboBoxSelectedIndex > 0 &&
|
||
Global.Settings.ServerComboBoxSelectedIndex < ServerComboBox.Items.Count)
|
||
ServerComboBox.SelectedIndex = Global.Settings.ServerComboBoxSelectedIndex;
|
||
// 如果值非法,且当前 ServerComboBox 中有元素,选择第一个位置
|
||
else if (ServerComboBox.Items.Count > 0)
|
||
ServerComboBox.SelectedIndex = 0;
|
||
|
||
// 如果当前 ServerComboBox 中没元素,不做处理
|
||
}
|
||
|
||
private void ServerComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
||
{
|
||
if (!_comboBoxInitialized) return;
|
||
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
|
||
}
|
||
|
||
private void EditServerPictureBox_Click(object sender, EventArgs e)
|
||
{
|
||
// 当前ServerComboBox中至少有一项
|
||
if (ServerComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||
return;
|
||
}
|
||
|
||
Hide();
|
||
var server = Global.Settings.Server[ServerComboBox.SelectedIndex];
|
||
ServerHelper.GetUtilByTypeName(server.Type).Edit(server);
|
||
InitServer();
|
||
Configuration.Save();
|
||
Show();
|
||
}
|
||
|
||
private void SpeedPictureBox_Click(object sender, EventArgs e)
|
||
{
|
||
Enabled = false;
|
||
StatusText(i18N.Translate("Testing"));
|
||
|
||
if (IsWaiting())
|
||
{
|
||
ServerHelper.DelayTestHelper.TestDelayFinished += OnTestDelayFinished;
|
||
_ = Task.Run(ServerHelper.DelayTestHelper.TestAllDelay);
|
||
|
||
void OnTestDelayFinished(object o1, EventArgs e1)
|
||
{
|
||
Refresh();
|
||
|
||
ServerHelper.DelayTestHelper.TestDelayFinished -= OnTestDelayFinished;
|
||
Enabled = true;
|
||
StatusText();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
(ServerComboBox.SelectedItem as Server)?.Test();
|
||
ServerComboBox.Refresh();
|
||
Enabled = true;
|
||
StatusText();
|
||
}
|
||
}
|
||
|
||
private void CopyLinkPictureBox_Click(object sender, EventArgs e)
|
||
{
|
||
// 当前ServerComboBox中至少有一项
|
||
if (ServerComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
//听说巨硬BUG经常会炸,所以Catch一下 :D
|
||
var server = (Server) ServerComboBox.SelectedItem;
|
||
string text;
|
||
if (ModifierKeys == Keys.Control)
|
||
text = ShareLink.GetNetchLink(server);
|
||
else
|
||
text = ShareLink.GetShareLink(server);
|
||
|
||
Clipboard.SetText(text);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// ignored
|
||
}
|
||
}
|
||
|
||
private void DeleteServerPictureBox_Click(object sender, EventArgs e)
|
||
{
|
||
// 当前 ServerComboBox 中至少有一项
|
||
if (ServerComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||
return;
|
||
}
|
||
|
||
Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Server);
|
||
InitServer();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Mode
|
||
|
||
public void InitMode()
|
||
{
|
||
var comboBoxInitialized = _comboBoxInitialized;
|
||
_comboBoxInitialized = false;
|
||
|
||
ModeComboBox.Items.Clear();
|
||
ModeComboBox.Items.AddRange(Global.Modes.ToArray());
|
||
ModeComboBox.Tag = null;
|
||
SelectLastMode();
|
||
_comboBoxInitialized = comboBoxInitialized;
|
||
}
|
||
|
||
public void SelectLastMode()
|
||
{
|
||
// 如果值合法,选中该位置
|
||
if (Global.Settings.ModeComboBoxSelectedIndex > 0 &&
|
||
Global.Settings.ModeComboBoxSelectedIndex < ModeComboBox.Items.Count)
|
||
ModeComboBox.SelectedIndex = Global.Settings.ModeComboBoxSelectedIndex;
|
||
// 如果值非法,且当前 ModeComboBox 中有元素,选择第一个位置
|
||
else if (ModeComboBox.Items.Count > 0)
|
||
ModeComboBox.SelectedIndex = 0;
|
||
|
||
// 如果当前 ModeComboBox 中没元素,不做处理
|
||
}
|
||
|
||
private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
||
{
|
||
if (!_comboBoxInitialized) return;
|
||
try
|
||
{
|
||
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode) ModeComboBox.SelectedItem);
|
||
}
|
||
catch
|
||
{
|
||
Global.Settings.ModeComboBoxSelectedIndex = 0;
|
||
}
|
||
}
|
||
|
||
private void EditModePictureBox_Click(object sender, EventArgs e)
|
||
{
|
||
// 当前ModeComboBox中至少有一项
|
||
if (ModeComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||
return;
|
||
}
|
||
|
||
var mode = (Models.Mode) ModeComboBox.SelectedItem;
|
||
if (ModifierKeys == Keys.Control)
|
||
{
|
||
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath));
|
||
return;
|
||
}
|
||
|
||
switch (mode.Type)
|
||
{
|
||
case 0:
|
||
Hide();
|
||
new Process(mode).ShowDialog();
|
||
Show();
|
||
break;
|
||
default:
|
||
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath));
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void DeleteModePictureBox_Click(object sender, EventArgs e)
|
||
{
|
||
// 当前ModeComboBox中至少有一项
|
||
if (ModeComboBox.Items.Count <= 0 || ModeComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||
return;
|
||
}
|
||
|
||
ModeHelper.Delete((Models.Mode) ModeComboBox.SelectedItem);
|
||
SelectLastMode();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Profile
|
||
|
||
private int _configurationGroupBoxHeight;
|
||
private int _profileConfigurationHeight;
|
||
|
||
private void InitProfile()
|
||
{
|
||
// Clear
|
||
foreach (var button in ProfileButtons)
|
||
button.Dispose();
|
||
|
||
ProfileButtons.Clear();
|
||
ProfileTable.ColumnStyles.Clear();
|
||
ProfileTable.RowStyles.Clear();
|
||
|
||
var numProfile = Global.Settings.ProfileCount;
|
||
if (numProfile == 0)
|
||
{
|
||
// Hide Profile GroupBox, Change window size
|
||
configLayoutPanel.RowStyles[2].SizeType = SizeType.Percent;
|
||
configLayoutPanel.RowStyles[2].Height = 0;
|
||
ProfileGroupBox.Visible = false;
|
||
|
||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight - _profileConfigurationHeight);
|
||
}
|
||
else
|
||
{
|
||
// Load Profiles
|
||
ProfileTable.ColumnCount = numProfile;
|
||
|
||
while (Global.Settings.Profiles.Count < numProfile)
|
||
Global.Settings.Profiles.Add(new Profile());
|
||
|
||
for (var i = 0; i < numProfile; ++i)
|
||
{
|
||
var b = new Button();
|
||
b.Click += ProfileButton_Click;
|
||
b.Dock = DockStyle.Fill;
|
||
b.Text = !Global.Settings.Profiles[i].IsDummy ? Global.Settings.Profiles[i].ProfileName : i18N.Translate("None");
|
||
|
||
ProfileTable.Controls.Add(b, i, 0);
|
||
ProfileButtons.Add(b);
|
||
}
|
||
|
||
// equal column
|
||
for (var i = 1; i <= ProfileTable.RowCount; i++)
|
||
ProfileTable.RowStyles.Add(new RowStyle(SizeType.Percent, 1));
|
||
|
||
for (var i = 1; i <= ProfileTable.ColumnCount; i++)
|
||
ProfileTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 1));
|
||
|
||
configLayoutPanel.RowStyles[2].SizeType = SizeType.AutoSize;
|
||
ProfileGroupBox.Visible = true;
|
||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight);
|
||
}
|
||
}
|
||
|
||
private void LoadProfile(int index)
|
||
{
|
||
var p = Global.Settings.Profiles[index];
|
||
ProfileNameText.Text = p.ProfileName;
|
||
ModeComboBox.ResetCompletionList();
|
||
|
||
if (p.IsDummy)
|
||
throw new Exception("Profile not found.");
|
||
|
||
var server = ServerComboBox.Items.Cast<Server>().FirstOrDefault(s => s.Remark.Equals(p.ServerRemark));
|
||
var mode = ModeComboBox.Items.Cast<Models.Mode>().FirstOrDefault(m => m.Remark.Equals(p.ModeRemark));
|
||
|
||
if (server == null)
|
||
throw new Exception("Server not found.");
|
||
|
||
if (mode == null)
|
||
throw new Exception("Mode not found.");
|
||
|
||
ServerComboBox.SelectedItem = server;
|
||
ModeComboBox.SelectedItem = mode;
|
||
}
|
||
|
||
private void SaveProfile(int index)
|
||
{
|
||
var selectedServer = (Server) ServerComboBox.SelectedItem;
|
||
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
|
||
var name = ProfileNameText.Text;
|
||
|
||
Global.Settings.Profiles[index] = new Profile(selectedServer, selectedMode, name);
|
||
}
|
||
|
||
private void RemoveProfile(int index)
|
||
{
|
||
Global.Settings.Profiles[index] = new Profile();
|
||
}
|
||
|
||
private readonly List<Button> ProfileButtons = new();
|
||
|
||
private async void ProfileButton_Click(object sender, EventArgs e)
|
||
{
|
||
var index = ProfileButtons.IndexOf((Button) sender);
|
||
|
||
if (ModifierKeys == Keys.Control)
|
||
{
|
||
if (ServerComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||
}
|
||
else if (ModeComboBox.SelectedIndex == -1)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||
}
|
||
else if (ProfileNameText.Text == "")
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please enter a profile name first"));
|
||
}
|
||
else
|
||
{
|
||
SaveProfile(index);
|
||
ProfileButtons[index].Text = ProfileNameText.Text;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
if (Global.Settings.Profiles[index].IsDummy)
|
||
{
|
||
MessageBoxX.Show(
|
||
i18N.Translate("No saved profile here. Save a profile first by Ctrl+Click on the button"));
|
||
return;
|
||
}
|
||
|
||
if (ModifierKeys == Keys.Shift)
|
||
{
|
||
if (MessageBoxX.Show(i18N.Translate("Remove this Profile?"), confirm: true) != DialogResult.OK) return;
|
||
RemoveProfile(index);
|
||
ProfileButtons[index].Text = i18N.Translate("None");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
LoadProfile(index);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
MessageBoxX.Show(exception.Message, LogLevel.ERROR);
|
||
return;
|
||
}
|
||
|
||
// start the profile
|
||
ControlButton_Click(null, null);
|
||
if (State == State.Stopping || State == State.Stopped)
|
||
{
|
||
while (State != State.Stopped)
|
||
await Task.Delay(250);
|
||
|
||
ControlButton_Click(null, null);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region State
|
||
|
||
private State _state = State.Waiting;
|
||
|
||
/// <summary>
|
||
/// 当前状态
|
||
/// </summary>
|
||
public State State
|
||
{
|
||
get => _state;
|
||
private set
|
||
{
|
||
void StartDisableItems(bool enabled)
|
||
{
|
||
ServerComboBox.Enabled =
|
||
ModeComboBox.Enabled =
|
||
EditModePictureBox.Enabled =
|
||
EditServerPictureBox.Enabled =
|
||
DeleteModePictureBox.Enabled =
|
||
DeleteServerPictureBox.Enabled = enabled;
|
||
|
||
// 启动需要禁用的控件
|
||
UninstallServiceToolStripMenuItem.Enabled =
|
||
UpdateACLToolStripMenuItem.Enabled =
|
||
updateACLWithProxyToolStripMenuItem.Enabled =
|
||
updatePACToolStripMenuItem.Enabled =
|
||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled =
|
||
UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Enabled =
|
||
UninstallTapDriverToolStripMenuItem.Enabled =
|
||
ReloadModesToolStripMenuItem.Enabled = enabled;
|
||
}
|
||
|
||
_state = value;
|
||
|
||
ServerHelper.DelayTestHelper.Enabled = IsWaiting(_state);
|
||
|
||
StatusText();
|
||
switch (value)
|
||
{
|
||
case State.Waiting:
|
||
ControlButton.Enabled = true;
|
||
ControlButton.Text = i18N.Translate("Start");
|
||
|
||
break;
|
||
case State.Starting:
|
||
ControlButton.Enabled = false;
|
||
ControlButton.Text = "...";
|
||
|
||
ProfileGroupBox.Enabled = false;
|
||
StartDisableItems(false);
|
||
break;
|
||
case State.Started:
|
||
ControlButton.Enabled = true;
|
||
ControlButton.Text = i18N.Translate("Stop");
|
||
|
||
ProfileGroupBox.Enabled = true;
|
||
|
||
break;
|
||
case State.Stopping:
|
||
ControlButton.Enabled = false;
|
||
ControlButton.Text = "...";
|
||
|
||
ProfileGroupBox.Enabled = false;
|
||
BandwidthState(false);
|
||
NatTypeStatusText();
|
||
break;
|
||
case State.Stopped:
|
||
ControlButton.Enabled = true;
|
||
ControlButton.Text = i18N.Translate("Start");
|
||
|
||
LastUploadBandwidth = 0;
|
||
LastDownloadBandwidth = 0;
|
||
Bandwidth.Stop();
|
||
|
||
ProfileGroupBox.Enabled = true;
|
||
StartDisableItems(true);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private bool IsWaiting()
|
||
{
|
||
return State == State.Waiting || State == State.Stopped;
|
||
}
|
||
private static bool IsWaiting(State state)
|
||
{
|
||
return state == State.Waiting || state == State.Stopped;
|
||
}
|
||
|
||
public static class StatusPortInfoText
|
||
{
|
||
private static ushort? _socks5Port;
|
||
private static ushort? _httpPort;
|
||
private static bool _shareLan;
|
||
|
||
public static ushort HttpPort
|
||
{
|
||
set => _httpPort = value;
|
||
}
|
||
|
||
public static ushort Socks5Port
|
||
{
|
||
set => _socks5Port = value;
|
||
}
|
||
|
||
public static string Value
|
||
{
|
||
get
|
||
{
|
||
var strings = new List<string>();
|
||
|
||
if (_socks5Port != null)
|
||
strings.Add($"Socks5 {i18N.Translate("Local Port", ": ")}{_socks5Port}");
|
||
|
||
if (_httpPort != null)
|
||
strings.Add($"HTTP {i18N.Translate("Local Port", ": ")}{_httpPort}");
|
||
|
||
if (!strings.Any())
|
||
return string.Empty;
|
||
|
||
return $" ({(_shareLan ? i18N.Translate("Allow other Devices to connect") + " " : "")}{string.Join(" | ", strings)})";
|
||
}
|
||
}
|
||
|
||
public static void UpdateShareLan()
|
||
{
|
||
_shareLan = Global.Settings.LocalAddress != "127.0.0.1";
|
||
}
|
||
|
||
public static void Reset()
|
||
{
|
||
_httpPort = _socks5Port = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新状态栏文本
|
||
/// </summary>
|
||
/// <param name="text"></param>
|
||
public void StatusText(string text = null)
|
||
{
|
||
if (InvokeRequired)
|
||
{
|
||
BeginInvoke(new Action<string>(StatusText), text);
|
||
return;
|
||
}
|
||
|
||
text ??= i18N.Translate(StateExtension.GetStatusString(State));
|
||
StatusLabel.Text = i18N.Translate("Status", ": ") + text;
|
||
if (_state == State.Started)
|
||
StatusLabel.Text += StatusPortInfoText.Value;
|
||
}
|
||
|
||
public void BandwidthState(bool state)
|
||
{
|
||
if (InvokeRequired)
|
||
{
|
||
BeginInvoke(new Action<bool>(BandwidthState), state);
|
||
return;
|
||
}
|
||
|
||
if (IsWaiting())
|
||
return;
|
||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = state;
|
||
}
|
||
|
||
public void NatTypeStatusText(string text = "", string country = "")
|
||
{
|
||
if (InvokeRequired)
|
||
{
|
||
BeginInvoke(new Action<string, string>(NatTypeStatusText), text, country);
|
||
return;
|
||
}
|
||
|
||
if (State != State.Started)
|
||
{
|
||
NatTypeStatusLabel.Text = "";
|
||
NatTypeStatusLabel.Visible = NatTypeStatusLightLabel.Visible = false;
|
||
return;
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(text))
|
||
{
|
||
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} {(country != string.Empty ? $"[{country}]" : "")}";
|
||
|
||
UpdateNatTypeLight(int.TryParse(text, out var natType) ? natType : -1);
|
||
}
|
||
else
|
||
{
|
||
NatTypeStatusLabel.Text = $@"NAT{i18N.Translate(": ", "Test failed")}";
|
||
}
|
||
|
||
NatTypeStatusLabel.Visible = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新 NAT指示灯颜色
|
||
/// </summary>
|
||
/// <param name="natType"></param>
|
||
private void UpdateNatTypeLight(int natType = -1)
|
||
{
|
||
if (natType > 0 && natType < 5)
|
||
{
|
||
NatTypeStatusLightLabel.Visible = Global.Flags.IsWindows10Upper;
|
||
Color c;
|
||
switch (natType)
|
||
{
|
||
case 1:
|
||
c = Color.LimeGreen;
|
||
break;
|
||
case 2:
|
||
c = Color.Yellow;
|
||
break;
|
||
case 3:
|
||
c = Color.Red;
|
||
break;
|
||
case 4:
|
||
c = Color.Black;
|
||
break;
|
||
default:
|
||
c = Color.Black;
|
||
break;
|
||
}
|
||
|
||
NatTypeStatusLightLabel.ForeColor = c;
|
||
}
|
||
else
|
||
{
|
||
NatTypeStatusLightLabel.Visible = false;
|
||
}
|
||
}
|
||
|
||
private void NatTypeStatusLabel_Click(object sender, EventArgs e)
|
||
{
|
||
if (_state == State.Started && NttTested)
|
||
NatTest();
|
||
}
|
||
|
||
private static bool NttTested;
|
||
|
||
/// <summary>
|
||
/// 测试 NAT
|
||
/// </summary>
|
||
public void NatTest()
|
||
{
|
||
if (!MainController.Mode.TestNatRequired())
|
||
return;
|
||
NttTested = false;
|
||
Task.Run(() =>
|
||
{
|
||
NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||
// Thread.Sleep(1000);
|
||
var (result, localEnd, publicEnd) = MainController.NTTController.Start();
|
||
|
||
if (!string.IsNullOrEmpty(publicEnd))
|
||
{
|
||
var country = Utils.Utils.GetCityCode(publicEnd);
|
||
NatTypeStatusText(result, country);
|
||
}
|
||
else
|
||
{
|
||
NatTypeStatusText(result ?? "Error");
|
||
}
|
||
|
||
NttTested = true;
|
||
});
|
||
}
|
||
|
||
#endregion
|
||
|
||
#endregion
|
||
|
||
#region Misc
|
||
|
||
#region PowerEvent
|
||
|
||
private bool _resumeFlag;
|
||
|
||
private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||
{
|
||
switch (e.Mode)
|
||
{
|
||
case PowerModes.Suspend: //操作系统即将挂起
|
||
if (!IsWaiting())
|
||
{
|
||
_resumeFlag = true;
|
||
Logging.Info("操作系统即将挂起,自动停止");
|
||
ControlButton_Click(null, null);
|
||
}
|
||
break;
|
||
case PowerModes.Resume: //操作系统即将从挂起状态继续
|
||
if (_resumeFlag)
|
||
{
|
||
_resumeFlag = false;
|
||
Logging.Info("操作系统即将从挂起状态继续,自动重启");
|
||
ControlButton_Click(null, null);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
private void Minimize()
|
||
{
|
||
// 使关闭时窗口向右下角缩小的效果
|
||
WindowState = FormWindowState.Minimized;
|
||
|
||
if (_isFirstCloseWindow)
|
||
{
|
||
// 显示提示语
|
||
NotifyTip(i18N.Translate("Netch is now minimized to the notification bar, double click this icon to restore."));
|
||
_isFirstCloseWindow = false;
|
||
}
|
||
|
||
Hide();
|
||
}
|
||
|
||
private async void Exit(bool forceExit = false)
|
||
{
|
||
if (!IsWaiting() && !Global.Settings.StopWhenExited && !forceExit)
|
||
{
|
||
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
|
||
|
||
NotifyIcon_MouseDoubleClick(null, null);
|
||
return;
|
||
}
|
||
|
||
State = State.Terminating;
|
||
NotifyIcon.Visible = false;
|
||
Hide();
|
||
Configuration.Save();
|
||
|
||
foreach (var file in new[] {"data\\last.json", "data\\privoxy.conf"})
|
||
if (File.Exists(file))
|
||
File.Delete(file);
|
||
|
||
if (!IsWaiting())
|
||
await MainController.Stop();
|
||
|
||
Dispose();
|
||
Environment.Exit(Environment.ExitCode);
|
||
}
|
||
|
||
private void OnCalled(object sender, OnlyInstance.Commands e)
|
||
{
|
||
switch (e)
|
||
{
|
||
case OnlyInstance.Commands.Show:
|
||
NotifyIcon_MouseDoubleClick(null, null);
|
||
break;
|
||
case OnlyInstance.Commands.Exit:
|
||
Exit(true);
|
||
break;
|
||
default:
|
||
throw new ArgumentOutOfRangeException(nameof(e), e, null);
|
||
}
|
||
}
|
||
|
||
#region FormClosingButton
|
||
|
||
private bool _isFirstCloseWindow = true;
|
||
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
||
{
|
||
if (e.CloseReason == CloseReason.UserClosing && State != State.Terminating)
|
||
{
|
||
// 取消"关闭窗口"事件
|
||
e.Cancel = true; // 取消关闭窗体
|
||
|
||
// 如果未勾选关闭窗口时退出,隐藏至右下角托盘图标
|
||
if (!Global.Settings.ExitWhenClosed)
|
||
Minimize();
|
||
// 如果勾选了关闭时退出,自动点击退出按钮
|
||
else
|
||
Exit();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Updater
|
||
|
||
private void CheckUpdate()
|
||
{
|
||
UpdateChecker.NewVersionFound += (_, _) =>
|
||
{
|
||
NotifyTip($"{i18N.Translate(@"New version available", ": ")}{UpdateChecker.LatestVersionNumber}");
|
||
NewVersionLabel.Visible = true;
|
||
};
|
||
UpdateChecker.Check(Global.Settings.CheckBetaUpdate);
|
||
}
|
||
|
||
private async void NewVersionLabel_Click(object sender, EventArgs e)
|
||
{
|
||
if (!UpdateChecker.LatestRelease.assets.Any())
|
||
{
|
||
Utils.Utils.Open(UpdateChecker.LatestVersionUrl);
|
||
return;
|
||
}
|
||
|
||
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
|
||
return;
|
||
NotifyTip(i18N.Translate("Start downloading new version"));
|
||
|
||
NewVersionLabel.Enabled = false;
|
||
NewVersionLabel.Text = "...";
|
||
try
|
||
{
|
||
void OnDownloadProgressChanged(object o1, DownloadProgressChangedEventArgs args)
|
||
{
|
||
BeginInvoke(new Action(() => { NewVersionLabel.Text = $"{args.ProgressPercentage}%"; }));
|
||
}
|
||
|
||
await UpdateChecker.UpdateNetch(OnDownloadProgressChanged);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
Logging.Error(exception.Message);
|
||
NotifyTip(exception.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region NetTraffic
|
||
|
||
/// <summary>
|
||
/// 上一次下载的流量
|
||
/// </summary>
|
||
public ulong LastDownloadBandwidth;
|
||
|
||
/// <summary>
|
||
/// 上一次上传的流量
|
||
/// </summary>
|
||
public ulong LastUploadBandwidth;
|
||
|
||
public void OnBandwidthUpdated(ulong download)
|
||
{
|
||
if (InvokeRequired)
|
||
{
|
||
BeginInvoke(new Action<ulong>(OnBandwidthUpdated), download);
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
UsedBandwidthLabel.Text = $"{i18N.Translate("Used", ": ")}{Bandwidth.Compute(download)}";
|
||
//UploadSpeedLabel.Text = $"↑: {Utils.Bandwidth.Compute(upload - LastUploadBandwidth)}/s";
|
||
DownloadSpeedLabel.Text = $"↑↓: {Bandwidth.Compute(download - LastDownloadBandwidth)}/s";
|
||
|
||
//LastUploadBandwidth = upload;
|
||
LastDownloadBandwidth = download;
|
||
Refresh();
|
||
}
|
||
catch
|
||
{
|
||
// ignored
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region NotifyIcon
|
||
|
||
private void ShowMainFormToolStripButton_Click(object sender, EventArgs e)
|
||
{
|
||
if (WindowState == FormWindowState.Minimized)
|
||
{
|
||
Visible = true;
|
||
ShowInTaskbar = true; // 显示在系统任务栏
|
||
WindowState = FormWindowState.Normal; // 还原窗体
|
||
}
|
||
|
||
Activate();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通知图标右键菜单退出
|
||
/// </summary>
|
||
private void ExitToolStripButton_Click(object sender, EventArgs e)
|
||
{
|
||
Exit();
|
||
}
|
||
|
||
private void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
|
||
{
|
||
if (WindowState == FormWindowState.Minimized)
|
||
{
|
||
Visible = true;
|
||
ShowInTaskbar = true; //显示在系统任务栏
|
||
WindowState = FormWindowState.Normal; //还原窗体
|
||
}
|
||
|
||
Activate();
|
||
}
|
||
|
||
public void NotifyTip(string text, int timeout = 0, bool info = true)
|
||
{
|
||
// 会阻塞线程 timeout 秒
|
||
NotifyIcon.ShowBalloonTip(timeout,
|
||
UpdateChecker.Name,
|
||
text,
|
||
info ? ToolTipIcon.Info : ToolTipIcon.Error);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ComboBox_DrawItem
|
||
|
||
private readonly SolidBrush _greenBrush = new(Color.FromArgb(50, 255, 56));
|
||
private int _numberBoxWidth;
|
||
private int _numberBoxX;
|
||
private int _numberBoxWrap;
|
||
|
||
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
|
||
{
|
||
if (!(sender is ComboBox cbx))
|
||
return;
|
||
|
||
// 绘制背景颜色
|
||
e.Graphics.FillRectangle(Brushes.White, e.Bounds);
|
||
|
||
if (e.Index < 0) return;
|
||
|
||
// 绘制 备注/名称 字符串
|
||
TextRenderer.DrawText(e.Graphics, cbx.Items[e.Index].ToString(), cbx.Font, e.Bounds, Color.Black, TextFormatFlags.Left);
|
||
|
||
switch (cbx.Items[e.Index])
|
||
{
|
||
case Server item:
|
||
{
|
||
// 计算延迟底色
|
||
var numBoxBackBrush = item.Delay switch
|
||
{
|
||
> 200 => Brushes.Red,
|
||
> 80 => Brushes.Yellow,
|
||
>= 0 => _greenBrush,
|
||
_ => Brushes.Gray
|
||
};
|
||
|
||
// 绘制延迟底色
|
||
e.Graphics.FillRectangle(numBoxBackBrush, _numberBoxX, e.Bounds.Y, _numberBoxWidth, e.Bounds.Height);
|
||
|
||
// 绘制延迟字符串
|
||
TextRenderer.DrawText(e.Graphics, item.Delay.ToString(), cbx.Font, new Point(_numberBoxX + _numberBoxWrap, e.Bounds.Y), Color.Black, TextFormatFlags.Left);
|
||
break;
|
||
}
|
||
case Models.Mode item:
|
||
{
|
||
// 绘制 模式Box 底色
|
||
e.Graphics.FillRectangle(Brushes.Gray, _numberBoxX, e.Bounds.Y, _numberBoxWidth,
|
||
e.Bounds.Height);
|
||
|
||
// 绘制 模式行数 字符串
|
||
TextRenderer.DrawText(e.Graphics, item.Rule.Count.ToString(), cbx.Font, new Point(_numberBoxX + _numberBoxWrap, e.Bounds.Y), Color.Black, TextFormatFlags.Left);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#endregion
|
||
}
|
||
} |