using NLog;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using Shadowsocks.Util.SystemProxy;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
namespace Shadowsocks.View
{
public class MenuViewController
{
private static Logger logger = LogManager.GetCurrentClassLogger();
// yes this is just a menu view controller
// when config form is closed, it moves away from RAM
// and it should just do anything related to the config form
private ShadowsocksController controller;
private UpdateChecker updateChecker;
private NotifyIcon _notifyIcon;
private Icon icon, icon_in, icon_out, icon_both, previousIcon;
private bool _isFirstRun;
private bool _isStartupChecking;
private string _urlToOpen;
private ContextMenuStrip contextMenu1;
private ToolStripMenuItem disableItem;
private ToolStripMenuItem AutoStartupItem;
private ToolStripMenuItem ShareOverLANItem;
private ToolStripSeparator SeperatorItem;
private ToolStripMenuItem ConfigItem;
private ToolStripMenuItem ProtocolHandlerItem;
private ToolStripMenuItem ServersItem;
private ToolStripMenuItem globalModeItem;
private ToolStripMenuItem PACModeItem;
private ToolStripMenuItem localPACItem;
private ToolStripMenuItem onlinePACItem;
private ToolStripMenuItem editLocalPACItem;
private ToolStripMenuItem updateFromGFWListItem;
private ToolStripMenuItem editGFWUserRuleItem;
private ToolStripMenuItem editOnlinePACItem;
private ToolStripMenuItem secureLocalPacUrlToggleItem;
private ToolStripMenuItem autoCheckUpdatesToggleItem;
private ToolStripMenuItem checkPreReleaseToggleItem;
private ToolStripMenuItem proxyItem;
private ToolStripMenuItem hotKeyItem;
private ToolStripMenuItem VerboseLoggingToggleItem;
private ToolStripMenuItem ShowPluginOutputToggleItem;
private ToolStripMenuItem WriteI18NFileItem;
private ConfigForm configForm;
private ProxyForm proxyForm;
private LogForm logForm;
private HotkeySettingsForm hotkeySettingsForm;
// color definition for icon color transformation
private readonly Color colorMaskBlue = Color.FromArgb(255, 25, 125, 191);
private readonly Color colorMaskDarkSilver = Color.FromArgb(128, 192, 192, 192);
private readonly Color colorMaskLightSilver = Color.FromArgb(192, 192, 192);
private readonly Color colorMaskEclipse = Color.FromArgb(192, 64, 64, 64);
public MenuViewController(ShadowsocksController controller)
{
this.controller = controller;
LoadMenu();
controller.EnableStatusChanged += controller_EnableStatusChanged;
controller.ConfigChanged += controller_ConfigChanged;
controller.PACFileReadyToOpen += controller_FileReadyToOpen;
controller.UserRuleFileReadyToOpen += controller_FileReadyToOpen;
controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged;
controller.VerboseLoggingStatusChanged += controller_VerboseLoggingStatusChanged;
controller.ShowPluginOutputChanged += controller_ShowPluginOutputChanged;
controller.EnableGlobalChanged += controller_EnableGlobalChanged;
controller.Errored += controller_Errored;
controller.UpdatePACFromGFWListCompleted += controller_UpdatePACFromGFWListCompleted;
controller.UpdatePACFromGFWListError += controller_UpdatePACFromGFWListError;
_notifyIcon = new NotifyIcon();
UpdateTrayIconAndNotifyText();
_notifyIcon.Visible = true;
_notifyIcon.ContextMenuStrip = contextMenu1;
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
_notifyIcon.MouseClick += notifyIcon1_Click;
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
controller.TrafficChanged += controller_TrafficChanged;
this.updateChecker = new UpdateChecker();
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted;
LoadCurrentConfiguration();
Configuration config = controller.GetConfigurationCopy();
if (config.isDefault)
{
_isFirstRun = true;
ShowConfigForm();
}
else if (config.autoCheckUpdate)
{
_isStartupChecking = true;
updateChecker.CheckUpdate(config, 3000);
}
}
private void controller_TrafficChanged(object sender, EventArgs e)
{
if (icon == null)
return;
Icon newIcon;
bool hasInbound = controller.trafficPerSecondQueue.Last().inboundIncreasement > 0;
bool hasOutbound = controller.trafficPerSecondQueue.Last().outboundIncreasement > 0;
if (hasInbound && hasOutbound)
newIcon = icon_both;
else if (hasInbound)
newIcon = icon_in;
else if (hasOutbound)
newIcon = icon_out;
else
newIcon = icon;
if (newIcon != this.previousIcon)
{
this.previousIcon = newIcon;
_notifyIcon.Icon = newIcon;
}
}
void controller_Errored(object sender, System.IO.ErrorEventArgs e)
{
MessageBox.Show(e.GetException().ToString(), I18N.GetString("Shadowsocks Error: {0}", e.GetException().Message));
}
#region Tray Icon
private void UpdateTrayIconAndNotifyText()
{
Configuration config = controller.GetConfigurationCopy();
bool enabled = config.enabled;
bool global = config.global;
Color colorMask = SelectColorMask(enabled, global);
Size iconSize = SelectIconSize();
UpdateIconSet(colorMask, iconSize, out icon, out icon_in, out icon_out, out icon_both);
previousIcon = icon;
_notifyIcon.Icon = previousIcon;
string serverInfo = null;
if (controller.GetCurrentStrategy() != null)
{
serverInfo = controller.GetCurrentStrategy().Name;
}
else
{
serverInfo = config.GetCurrentServer().FriendlyName();
}
// show more info by hacking the P/Invoke declaration for NOTIFYICONDATA inside Windows Forms
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
(enabled ?
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
I18N.GetString("Running: Port {0}", config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ "\n" + serverInfo;
if (text.Length > 127)
{
text = text.Substring(0, 126 - 3) + "...";
}
ViewUtils.SetNotifyIconText(_notifyIcon, text);
}
///
/// Determine the icon size based on the screen DPI.
///
///
/// https://stackoverflow.com/a/40851713/2075611
private Size SelectIconSize()
{
Size size = new Size(32, 32);
int dpi = ViewUtils.GetScreenDpi();
if (dpi < 97)
{
// dpi = 96;
size = new Size(16, 16);
}
else if (dpi < 121)
{
// dpi = 120;
size = new Size(20, 20);
}
else if (dpi < 145)
{
// dpi = 144;
size = new Size(24, 24);
}
else
{
// dpi = 168;
size = new Size(28, 28);
}
return size;
}
private Color SelectColorMask(bool isProxyEnabled, bool isGlobalProxy)
{
Color colorMask = Color.White;
Utils.WindowsThemeMode currentWindowsThemeMode = Utils.GetWindows10SystemThemeSetting();
if (isProxyEnabled)
{
if (isGlobalProxy) // global
{
colorMask = colorMaskBlue;
}
else // PAC
{
if (currentWindowsThemeMode == Utils.WindowsThemeMode.Light)
{
colorMask = colorMaskEclipse;
}
}
}
else // disabled
{
if (currentWindowsThemeMode == Utils.WindowsThemeMode.Light)
{
colorMask = colorMaskDarkSilver;
}
else
{
colorMask = colorMaskLightSilver;
}
}
return colorMask;
}
private void UpdateIconSet(Color colorMask, Size size,
out Icon icon, out Icon icon_in, out Icon icon_out, out Icon icon_both)
{
Bitmap iconBitmap;
// generate the base icon
iconBitmap = ViewUtils.ChangeBitmapColor(Resources.ss32Fill, colorMask);
iconBitmap = ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32Outline);
icon = Icon.FromHandle(ViewUtils.ResizeBitmap(iconBitmap, size.Width, size.Height).GetHicon());
icon_in = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In), size.Width, size.Height).GetHicon());
icon_out = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In), size.Width, size.Height).GetHicon());
icon_both = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In, Resources.ss32Out), size.Width, size.Height).GetHicon());
}
#endregion
#region ToolStripMenuItems and MenuGroups
private ToolStripMenuItem CreateToolStripMenuItem(string text, EventHandler click)
{
return new ToolStripMenuItem(I18N.GetString(text), null, click);
}
private ToolStripMenuItem CreateMenuGroup(string text, ToolStripItem[] items)
{
return new ToolStripMenuItem(I18N.GetString(text), null, items);
}
private void LoadMenu()
{
this.contextMenu1 = new ContextMenuStrip();
contextMenu1.Items.AddRange(new ToolStripItem[]{
CreateMenuGroup("System Proxy", new ToolStripMenuItem[] {
this.disableItem = CreateToolStripMenuItem("Disable", new EventHandler(this.EnableItem_Click)),
this.PACModeItem = CreateToolStripMenuItem("PAC", new EventHandler(this.PACModeItem_Click)),
this.globalModeItem = CreateToolStripMenuItem("Global", new EventHandler(this.GlobalModeItem_Click))
}),
this.ServersItem = CreateMenuGroup("Servers", new ToolStripItem [] {
this.SeperatorItem = new ToolStripSeparator(),
this.ConfigItem = CreateToolStripMenuItem("Edit Servers...", new EventHandler(this.Config_Click)),
CreateToolStripMenuItem("Statistics Config...", StatisticsConfigItem_Click),
new ToolStripSeparator(),
CreateToolStripMenuItem("Share Server Config...", new EventHandler(this.QRCodeItem_Click)),
CreateToolStripMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)),
CreateToolStripMenuItem("Import URL from Clipboard...", new EventHandler(this.ImportURLItem_Click))
}),
CreateMenuGroup("PAC ", new ToolStripItem[] {
this.localPACItem = CreateToolStripMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)),
this.onlinePACItem = CreateToolStripMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)),
new ToolStripSeparator(),
this.editLocalPACItem = CreateToolStripMenuItem("Edit Local PAC File...", new EventHandler(this.EditPACFileItem_Click)),
this.updateFromGFWListItem = CreateToolStripMenuItem("Update Local PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)),
this.editGFWUserRuleItem = CreateToolStripMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)),
this.secureLocalPacUrlToggleItem = CreateToolStripMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)),
CreateToolStripMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)),
this.editOnlinePACItem = CreateToolStripMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)),
}),
this.proxyItem = CreateToolStripMenuItem("Forward Proxy...", new EventHandler(this.proxyItem_Click)),
new ToolStripSeparator( ),
this.AutoStartupItem = CreateToolStripMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.ProtocolHandlerItem = CreateToolStripMenuItem("Associate ss:// Links", new EventHandler(this.ProtocolHandlerItem_Click)),
this.ShareOverLANItem = CreateToolStripMenuItem("Allow other Devices to connect", new EventHandler(this.ShareOverLANItem_Click)),
new ToolStripSeparator( ),
this.hotKeyItem = CreateToolStripMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
CreateMenuGroup("Help", new ToolStripItem[] {
CreateToolStripMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
this.VerboseLoggingToggleItem = CreateToolStripMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
this.ShowPluginOutputToggleItem = CreateToolStripMenuItem("Show Plugin Output", new EventHandler(this.ShowPluginOutputToggleItem_Click)),
this.WriteI18NFileItem = CreateToolStripMenuItem("Write translation template",new EventHandler(WriteI18NFileItem_Click)),
CreateMenuGroup("Updates...", new ToolStripItem[] {
CreateToolStripMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new ToolStripSeparator(),
this.autoCheckUpdatesToggleItem = CreateToolStripMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
this.checkPreReleaseToggleItem = CreateToolStripMenuItem("Check Pre-release Version", new EventHandler(this.checkPreReleaseToggleItem_Click)),
}),
CreateToolStripMenuItem("About...", new EventHandler(this.AboutItem_Click)),
}),
new ToolStripSeparator(),
CreateToolStripMenuItem("Quit", new EventHandler(this.Quit_Click))
});
}
#endregion
private void controller_ConfigChanged(object sender, EventArgs e)
{
LoadCurrentConfiguration();
UpdateTrayIconAndNotifyText();
}
private void controller_EnableStatusChanged(object sender, EventArgs e)
{
disableItem.Checked = !controller.GetConfigurationCopy().enabled;
}
void controller_ShareOverLANStatusChanged(object sender, EventArgs e)
{
ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan;
}
void controller_VerboseLoggingStatusChanged(object sender, EventArgs e)
{
VerboseLoggingToggleItem.Checked = controller.GetConfigurationCopy().isVerboseLogging;
}
void controller_ShowPluginOutputChanged(object sender, EventArgs e)
{
ShowPluginOutputToggleItem.Checked = controller.GetConfigurationCopy().showPluginOutput;
}
void controller_EnableGlobalChanged(object sender, EventArgs e)
{
globalModeItem.Checked = controller.GetConfigurationCopy().global;
PACModeItem.Checked = !globalModeItem.Checked;
}
void controller_FileReadyToOpen(object sender, ShadowsocksController.PathEventArgs e)
{
string argument = @"/select, " + e.Path;
Process.Start("explorer.exe", argument);
}
void ShowBalloonTip(string title, string content, ToolTipIcon icon, int timeout)
{
_notifyIcon.BalloonTipTitle = title;
_notifyIcon.BalloonTipText = content;
_notifyIcon.BalloonTipIcon = icon;
_notifyIcon.ShowBalloonTip(timeout);
}
void controller_UpdatePACFromGFWListError(object sender, System.IO.ErrorEventArgs e)
{
ShowBalloonTip(I18N.GetString("Failed to update PAC file"), e.GetException().Message, ToolTipIcon.Error, 5000);
logger.LogUsefulException(e.GetException());
}
void controller_UpdatePACFromGFWListCompleted(object sender, GFWListUpdater.ResultEventArgs e)
{
string result = e.Success
? I18N.GetString("PAC updated")
: I18N.GetString("No updates found. Please report to GFWList if you have problems with it.");
ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000);
}
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)
{
ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000);
}
_isStartupChecking = false;
}
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
{
if (updateChecker.NewVersionFound)
{
updateChecker.NewVersionFound = false; /* Reset the flag */
if (File.Exists(updateChecker.LatestVersionLocalName))
{
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\"";
Process.Start("explorer.exe", argument);
}
}
}
private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e)
{
if (updateChecker.NewVersionFound)
{
updateChecker.NewVersionFound = false; /* Reset the flag */
}
}
private void LoadCurrentConfiguration()
{
Configuration config = controller.GetConfigurationCopy();
UpdateServersMenu();
UpdateSystemProxyItemsEnabledStatus(config);
ShareOverLANItem.Checked = config.shareOverLan;
VerboseLoggingToggleItem.Checked = config.isVerboseLogging;
ShowPluginOutputToggleItem.Checked = config.showPluginOutput;
AutoStartupItem.Checked = AutoStartup.Check();
ProtocolHandlerItem.Checked = ProtocolHandler.Check();
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked;
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac;
UpdatePACItemsEnabledStatus();
UpdateUpdateMenu();
}
private void UpdateServersMenu()
{
var items = ServersItem.DropDownItems;
while (items[0] != SeperatorItem)
{
items.RemoveAt(0);
}
int strategyCount = 0;
foreach (var strategy in controller.GetStrategies())
{
if (!items.OfType().Any(ts => ts.Text == strategy.Name))
{
ToolStripMenuItem item = new ToolStripMenuItem(strategy.Name);
item.Tag = strategy.ID;
item.Click += AStrategyItem_Click;
items.Add(item);
strategyCount++;
}
}
if (!items.OfType().Any(ts => ts.Tag?.ToString() == "-server-"))
{
// user wants a seperator item between strategy and servers menugroup
items.Add(new ToolStripSeparator() { Tag = "-server-" });
}
int serverCount = 0;
Configuration configuration = controller.GetConfigurationCopy();
foreach (var server in configuration.configs)
{
if (Configuration.ChecksServer(server))
{
var name = server.FriendlyName();
if (!items.OfType().Any(ts => ts.Text == name))
{
ToolStripMenuItem item = new ToolStripMenuItem(name);
item.Tag = configuration.configs.FindIndex(s => s == server);
item.Click += AServerItem_Click;
items.Add(item);
serverCount++;
}
}
}
foreach (var item in items)
{
var menuItem = item as ToolStripMenuItem;
if (menuItem == null || menuItem.Tag == null) continue;
if (
menuItem.Tag.ToString() == configuration.index.ToString()
|| menuItem.Tag.ToString() == configuration.strategy
)
{
menuItem.Checked = true;
}
else
{
menuItem.Checked = false;
}
}
}
private void ShowConfigForm()
{
if (configForm != null)
{
configForm.Activate();
}
else
{
configForm = new ConfigForm(controller);
configForm.Show();
configForm.Activate();
configForm.FormClosed += configForm_FormClosed;
}
}
private void ShowProxyForm()
{
if (proxyForm != null)
{
proxyForm.Activate();
}
else
{
proxyForm = new ProxyForm(controller);
proxyForm.Show();
proxyForm.Activate();
proxyForm.FormClosed += proxyForm_FormClosed;
}
}
private void ShowHotKeySettingsForm()
{
if (hotkeySettingsForm != null)
{
hotkeySettingsForm.Activate();
}
else
{
hotkeySettingsForm = new HotkeySettingsForm(controller);
hotkeySettingsForm.Show();
hotkeySettingsForm.Activate();
hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed;
}
}
private void ShowLogForm()
{
if (logForm != null)
{
logForm.Activate();
}
else
{
logForm = new LogForm(controller);
logForm.Show();
logForm.Activate();
logForm.FormClosed += logForm_FormClosed;
}
}
void logForm_FormClosed(object sender, FormClosedEventArgs e)
{
logForm.Dispose();
logForm = null;
}
void configForm_FormClosed(object sender, FormClosedEventArgs e)
{
configForm.Dispose();
configForm = null;
if (_isFirstRun)
{
CheckUpdateForFirstRun();
ShowBalloonTip(
I18N.GetString("Shadowsocks is here"),
I18N.GetString("You can turn on/off Shadowsocks in the context menu"),
ToolTipIcon.Info,
0
);
_isFirstRun = false;
}
}
void proxyForm_FormClosed(object sender, FormClosedEventArgs e)
{
proxyForm.Dispose();
proxyForm = null;
}
void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e)
{
hotkeySettingsForm.Dispose();
hotkeySettingsForm = null;
}
private void Config_Click(object sender, EventArgs e)
{
ShowConfigForm();
}
private void Quit_Click(object sender, EventArgs e)
{
controller.Stop();
_notifyIcon.Visible = false;
Application.Exit();
}
private void CheckUpdateForFirstRun()
{
Configuration config = controller.GetConfigurationCopy();
if (config.isDefault) return;
_isStartupChecking = true;
updateChecker.CheckUpdate(config, 3000);
}
private void AboutItem_Click(object sender, EventArgs e)
{
Utils.OpenInBrowser("https://github.com/shadowsocks/shadowsocks-windows");
}
private void notifyIcon1_Click(object sender, MouseEventArgs e)
{
UpdateTrayIconAndNotifyText();
if (e.Button == MouseButtons.Middle)
{
ShowLogForm();
}
}
private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ShowConfigForm();
}
}
private void EnableItem_Click(object sender, EventArgs e)
{
controller.ToggleEnable(false);
Configuration config = controller.GetConfigurationCopy();
UpdateSystemProxyItemsEnabledStatus(config);
}
private void UpdateSystemProxyItemsEnabledStatus(Configuration config)
{
disableItem.Checked = !config.enabled;
if (!config.enabled)
{
globalModeItem.Checked = false;
PACModeItem.Checked = false;
}
else
{
globalModeItem.Checked = config.global;
PACModeItem.Checked = !config.global;
}
globalModeItem.Enabled = WinINet.operational;
PACModeItem.Enabled = WinINet.operational;
}
private void GlobalModeItem_Click(object sender, EventArgs e)
{
controller.ToggleEnable(true);
controller.ToggleGlobal(true);
Configuration config = controller.GetConfigurationCopy();
UpdateSystemProxyItemsEnabledStatus(config);
}
private void PACModeItem_Click(object sender, EventArgs e)
{
controller.ToggleEnable(true);
controller.ToggleGlobal(false);
Configuration config = controller.GetConfigurationCopy();
UpdateSystemProxyItemsEnabledStatus(config);
}
private void ShareOverLANItem_Click(object sender, EventArgs e)
{
ShareOverLANItem.Checked = !ShareOverLANItem.Checked;
controller.ToggleShareOverLAN(ShareOverLANItem.Checked);
}
private void EditPACFileItem_Click(object sender, EventArgs e)
{
controller.TouchPACFile();
}
private void UpdatePACFromGFWListItem_Click(object sender, EventArgs e)
{
controller.UpdatePACFromGFWList();
}
private void EditUserRuleFileForGFWListItem_Click(object sender, EventArgs e)
{
controller.TouchUserRuleFile();
}
private void AServerItem_Click(object sender, EventArgs e)
{
ToolStripMenuItem item = (ToolStripMenuItem)sender;
controller.SelectServerIndex((int)item.Tag);
}
private void AStrategyItem_Click(object sender, EventArgs e)
{
ToolStripMenuItem item = (ToolStripMenuItem)sender;
controller.SelectStrategy((string)item.Tag);
}
private void VerboseLoggingToggleItem_Click(object sender, EventArgs e)
{
VerboseLoggingToggleItem.Checked = !VerboseLoggingToggleItem.Checked;
controller.ToggleVerboseLogging(VerboseLoggingToggleItem.Checked);
}
private void ShowPluginOutputToggleItem_Click(object sender, EventArgs e)
{
ShowPluginOutputToggleItem.Checked = !ShowPluginOutputToggleItem.Checked;
controller.ToggleShowPluginOutput(ShowPluginOutputToggleItem.Checked);
}
private void WriteI18NFileItem_Click(object sender, EventArgs e)
{
File.WriteAllText(I18N.I18N_FILE, Resources.i18n_csv, Encoding.UTF8);
}
private void StatisticsConfigItem_Click(object sender, EventArgs e)
{
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller);
form.Show();
}
private void QRCodeItem_Click(object sender, EventArgs e)
{
QRCodeForm qrCodeForm = new QRCodeForm(controller.GetServerURLForCurrentServer());
//qrCodeForm.Icon = this.Icon;
// TODO
qrCodeForm.Show();
}
private void ScanQRCodeItem_Click(object sender, EventArgs e)
{
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 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;
}
}
}
}
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)
{
if (controller.AskAddServerBySSURL(Clipboard.GetText(TextDataFormat.Text)))
{
ShowConfigForm();
}
}
void splash_FormClosed(object sender, FormClosedEventArgs e)
{
ShowConfigForm();
}
void openURLFromQRCode(object sender, FormClosedEventArgs e)
{
Utils.OpenInBrowser(_urlToOpen);
}
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 LocalPACItem_Click(object sender, EventArgs e)
{
if (!localPACItem.Checked)
{
localPACItem.Checked = true;
onlinePACItem.Checked = false;
controller.UseOnlinePAC(false);
UpdatePACItemsEnabledStatus();
}
}
private void OnlinePACItem_Click(object sender, EventArgs e)
{
if (!onlinePACItem.Checked)
{
if (controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty())
{
UpdateOnlinePACURLItem_Click(sender, e);
}
if (!controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty())
{
localPACItem.Checked = false;
onlinePACItem.Checked = true;
controller.UseOnlinePAC(true);
}
UpdatePACItemsEnabledStatus();
}
}
private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e)
{
string origPacUrl = controller.GetConfigurationCopy().pacUrl;
#if NET472
string pacUrl = Microsoft.VisualBasic.Interaction.InputBox(
I18N.GetString("Please input PAC Url"),
I18N.GetString("Edit Online PAC URL"),
origPacUrl, -1, -1);
#else
string pacUrl = ViewUtils.InputBox(
I18N.GetString("Please input PAC Url"),
I18N.GetString("Edit Online PAC URL"),
origPacUrl, -1, -1);
#endif
if (!pacUrl.IsNullOrEmpty() && pacUrl != origPacUrl)
{
controller.SavePACUrl(pacUrl);
}
}
private void SecureLocalPacUrlToggleItem_Click(object sender, EventArgs e)
{
Configuration configuration = controller.GetConfigurationCopy();
controller.ToggleSecureLocalPac(!configuration.secureLocalPac);
}
private void CopyLocalPacUrlItem_Click(object sender, EventArgs e)
{
controller.CopyPacUrl();
}
private void UpdatePACItemsEnabledStatus()
{
if (this.localPACItem.Checked)
{
this.editLocalPACItem.Enabled = true;
this.updateFromGFWListItem.Enabled = true;
this.editGFWUserRuleItem.Enabled = true;
this.editOnlinePACItem.Enabled = false;
}
else
{
this.editLocalPACItem.Enabled = false;
this.updateFromGFWListItem.Enabled = false;
this.editGFWUserRuleItem.Enabled = false;
this.editOnlinePACItem.Enabled = true;
}
}
private void UpdateUpdateMenu()
{
Configuration configuration = controller.GetConfigurationCopy();
autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate;
checkPreReleaseToggleItem.Checked = configuration.checkPreRelease;
}
private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e)
{
Configuration configuration = controller.GetConfigurationCopy();
controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate);
UpdateUpdateMenu();
}
private void checkPreReleaseToggleItem_Click(object sender, EventArgs e)
{
Configuration configuration = controller.GetConfigurationCopy();
controller.ToggleCheckingPreRelease(!configuration.checkPreRelease);
UpdateUpdateMenu();
}
private void checkUpdatesItem_Click(object sender, EventArgs e)
{
updateChecker.CheckUpdate(controller.GetConfigurationCopy());
}
private void proxyItem_Click(object sender, EventArgs e)
{
ShowProxyForm();
}
private void hotKeyItem_Click(object sender, EventArgs e)
{
ShowHotKeySettingsForm();
}
private void ShowLogItem_Click(object sender, EventArgs e)
{
ShowLogForm();
}
public void ShowLogForm_HotKey()
{
ShowLogForm();
}
}
}