You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

MenuViewController.cs 43 kB

6 years ago
11 years ago
11 years ago
11 years ago
11 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
11 years ago
11 years ago
11 years ago
11 years ago
6 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
7 years ago
8 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
6 years ago
6 years ago
11 years ago
11 years ago
6 years ago
11 years ago
11 years ago
6 years ago
11 years ago
6 years ago
6 years ago
6 years ago
6 years ago
11 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
6 years ago
6 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
6 years ago
8 years ago
11 years ago
11 years ago
10 years ago
10 years ago
6 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. using NLog;
  2. using Shadowsocks.Controller;
  3. using Shadowsocks.Model;
  4. using Shadowsocks.Properties;
  5. using Shadowsocks.Util;
  6. using Shadowsocks.Util.SystemProxy;
  7. using System;
  8. using System.Diagnostics;
  9. using System.Drawing;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Text;
  13. using System.Windows.Forms;
  14. using System.Windows.Media.Imaging;
  15. using ZXing;
  16. using ZXing.Common;
  17. using ZXing.QrCode;
  18. namespace Shadowsocks.View
  19. {
  20. public class MenuViewController
  21. {
  22. private static Logger logger = LogManager.GetCurrentClassLogger();
  23. // yes this is just a menu view controller
  24. // when config form is closed, it moves away from RAM
  25. // and it should just do anything related to the config form
  26. private ShadowsocksController controller;
  27. private UpdateChecker updateChecker;
  28. private NotifyIcon _notifyIcon;
  29. private Icon icon, icon_in, icon_out, icon_both, previousIcon;
  30. private bool _isFirstRun;
  31. private bool _isStartupChecking;
  32. private string _urlToOpen;
  33. private ContextMenuStrip contextMenu1;
  34. private ToolStripMenuItem disableItem;
  35. private ToolStripMenuItem AutoStartupItem;
  36. private ToolStripMenuItem ShareOverLANItem;
  37. private ToolStripSeparator SeperatorItem;
  38. private ToolStripMenuItem ConfigItem;
  39. private ToolStripMenuItem ProtocolHandlerItem;
  40. private ToolStripMenuItem ServersItem;
  41. private ToolStripMenuItem globalModeItem;
  42. private ToolStripMenuItem PACModeItem;
  43. private ToolStripMenuItem localPACItem;
  44. private ToolStripMenuItem onlinePACItem;
  45. private ToolStripMenuItem editLocalPACItem;
  46. private ToolStripMenuItem updateFromGeositeItem;
  47. private ToolStripMenuItem editGFWUserRuleItem;
  48. private ToolStripMenuItem editOnlinePACItem;
  49. private ToolStripMenuItem secureLocalPacUrlToggleItem;
  50. private ToolStripMenuItem autoCheckUpdatesToggleItem;
  51. private ToolStripMenuItem checkPreReleaseToggleItem;
  52. private ToolStripMenuItem proxyItem;
  53. private ToolStripMenuItem hotKeyItem;
  54. private ToolStripMenuItem VerboseLoggingToggleItem;
  55. private ToolStripMenuItem ShowPluginOutputToggleItem;
  56. private ToolStripMenuItem WriteI18NFileItem;
  57. private ToolStripMenuItem onlineConfigItem;
  58. private ConfigForm configForm;
  59. private ProxyForm proxyForm;
  60. private LogForm logForm;
  61. private HotkeySettingsForm hotkeySettingsForm;
  62. private OnlineConfigForm onlineConfigForm;
  63. // color definition for icon color transformation
  64. private readonly Color colorMaskBlue = Color.FromArgb(255, 25, 125, 191);
  65. private readonly Color colorMaskDarkSilver = Color.FromArgb(128, 192, 192, 192);
  66. private readonly Color colorMaskLightSilver = Color.FromArgb(192, 192, 192);
  67. private readonly Color colorMaskEclipse = Color.FromArgb(192, 64, 64, 64);
  68. public MenuViewController(ShadowsocksController controller)
  69. {
  70. this.controller = controller;
  71. LoadMenu();
  72. controller.EnableStatusChanged += controller_EnableStatusChanged;
  73. controller.ConfigChanged += controller_ConfigChanged;
  74. controller.PACFileReadyToOpen += controller_FileReadyToOpen;
  75. controller.UserRuleFileReadyToOpen += controller_FileReadyToOpen;
  76. controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged;
  77. controller.VerboseLoggingStatusChanged += controller_VerboseLoggingStatusChanged;
  78. controller.ShowPluginOutputChanged += controller_ShowPluginOutputChanged;
  79. controller.EnableGlobalChanged += controller_EnableGlobalChanged;
  80. controller.Errored += controller_Errored;
  81. controller.UpdatePACFromGeositeCompleted += controller_UpdatePACFromGeositeCompleted;
  82. controller.UpdatePACFromGeositeError += controller_UpdatePACFromGeositeError;
  83. _notifyIcon = new NotifyIcon();
  84. UpdateTrayIconAndNotifyText();
  85. _notifyIcon.Visible = true;
  86. _notifyIcon.ContextMenuStrip = contextMenu1;
  87. _notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
  88. _notifyIcon.MouseClick += notifyIcon1_Click;
  89. _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
  90. _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
  91. controller.TrafficChanged += controller_TrafficChanged;
  92. this.updateChecker = new UpdateChecker();
  93. updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted;
  94. LoadCurrentConfiguration();
  95. Configuration config = controller.GetConfigurationCopy();
  96. if (config.isDefault)
  97. {
  98. _isFirstRun = true;
  99. ShowConfigForm();
  100. }
  101. else if (config.autoCheckUpdate)
  102. {
  103. _isStartupChecking = true;
  104. updateChecker.CheckUpdate(config, 3000);
  105. }
  106. }
  107. #region Tray Icon
  108. private void UpdateTrayIconAndNotifyText()
  109. {
  110. Configuration config = controller.GetConfigurationCopy();
  111. bool enabled = config.enabled;
  112. bool global = config.global;
  113. Color colorMask = SelectColorMask(enabled, global);
  114. Size iconSize = SelectIconSize();
  115. UpdateIconSet(colorMask, iconSize, out icon, out icon_in, out icon_out, out icon_both);
  116. previousIcon = icon;
  117. _notifyIcon.Icon = previousIcon;
  118. string serverInfo = null;
  119. if (controller.GetCurrentStrategy() != null)
  120. {
  121. serverInfo = controller.GetCurrentStrategy().Name;
  122. }
  123. else
  124. {
  125. serverInfo = config.GetCurrentServer().ToString();
  126. }
  127. // show more info by hacking the P/Invoke declaration for NOTIFYICONDATA inside Windows Forms
  128. string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
  129. (enabled ?
  130. I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
  131. I18N.GetString("Running: Port {0}", config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
  132. + "\n" + serverInfo;
  133. if (text.Length > 127)
  134. {
  135. text = text.Substring(0, 126 - 3) + "...";
  136. }
  137. ViewUtils.SetNotifyIconText(_notifyIcon, text);
  138. }
  139. /// <summary>
  140. /// Determine the icon size based on the screen DPI.
  141. /// </summary>
  142. /// <returns></returns>
  143. /// https://stackoverflow.com/a/40851713/2075611
  144. private Size SelectIconSize()
  145. {
  146. Size size = new Size(32, 32);
  147. int dpi = ViewUtils.GetScreenDpi();
  148. if (dpi < 97)
  149. {
  150. // dpi = 96;
  151. size = new Size(16, 16);
  152. }
  153. else if (dpi < 121)
  154. {
  155. // dpi = 120;
  156. size = new Size(20, 20);
  157. }
  158. else if (dpi < 145)
  159. {
  160. // dpi = 144;
  161. size = new Size(24, 24);
  162. }
  163. else
  164. {
  165. // dpi = 168;
  166. size = new Size(28, 28);
  167. }
  168. return size;
  169. }
  170. private Color SelectColorMask(bool isProxyEnabled, bool isGlobalProxy)
  171. {
  172. Color colorMask = Color.White;
  173. Utils.WindowsThemeMode currentWindowsThemeMode = Utils.GetWindows10SystemThemeSetting();
  174. if (isProxyEnabled)
  175. {
  176. if (isGlobalProxy) // global
  177. {
  178. colorMask = colorMaskBlue;
  179. }
  180. else // PAC
  181. {
  182. if (currentWindowsThemeMode == Utils.WindowsThemeMode.Light)
  183. {
  184. colorMask = colorMaskEclipse;
  185. }
  186. }
  187. }
  188. else // disabled
  189. {
  190. if (currentWindowsThemeMode == Utils.WindowsThemeMode.Light)
  191. {
  192. colorMask = colorMaskDarkSilver;
  193. }
  194. else
  195. {
  196. colorMask = colorMaskLightSilver;
  197. }
  198. }
  199. return colorMask;
  200. }
  201. private void UpdateIconSet(Color colorMask, Size size,
  202. out Icon icon, out Icon icon_in, out Icon icon_out, out Icon icon_both)
  203. {
  204. Bitmap iconBitmap;
  205. // generate the base icon
  206. iconBitmap = ViewUtils.ChangeBitmapColor(Resources.ss32Fill, colorMask);
  207. iconBitmap = ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32Outline);
  208. icon = Icon.FromHandle(ViewUtils.ResizeBitmap(iconBitmap, size.Width, size.Height).GetHicon());
  209. icon_in = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In), size.Width, size.Height).GetHicon());
  210. icon_out = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In), size.Width, size.Height).GetHicon());
  211. icon_both = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In, Resources.ss32Out), size.Width, size.Height).GetHicon());
  212. }
  213. #endregion
  214. #region ToolStripMenuItems and MenuGroups
  215. private ToolStripMenuItem CreateToolStripMenuItem(string text, EventHandler click)
  216. {
  217. return new ToolStripMenuItem(I18N.GetString(text), null, click);
  218. }
  219. private ToolStripMenuItem CreateMenuGroup(string text, ToolStripItem[] items)
  220. {
  221. return new ToolStripMenuItem(I18N.GetString(text), null, items);
  222. }
  223. private void LoadMenu()
  224. {
  225. this.contextMenu1 = new ContextMenuStrip();
  226. contextMenu1.Items.AddRange(new ToolStripItem[]{
  227. CreateMenuGroup("System Proxy", new ToolStripMenuItem[] {
  228. this.disableItem = CreateToolStripMenuItem("Disable", new EventHandler(this.EnableItem_Click)),
  229. this.PACModeItem = CreateToolStripMenuItem("PAC", new EventHandler(this.PACModeItem_Click)),
  230. this.globalModeItem = CreateToolStripMenuItem("Global", new EventHandler(this.GlobalModeItem_Click))
  231. }),
  232. this.ServersItem = CreateMenuGroup("Servers", new ToolStripItem [] {
  233. this.SeperatorItem = new ToolStripSeparator(),
  234. this.ConfigItem = CreateToolStripMenuItem("Edit Servers...", new EventHandler(this.Config_Click)),
  235. CreateToolStripMenuItem("Statistics Config...", StatisticsConfigItem_Click),
  236. new ToolStripSeparator(),
  237. CreateToolStripMenuItem("Share Server Config...", new EventHandler(this.QRCodeItem_Click)),
  238. CreateToolStripMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)),
  239. CreateToolStripMenuItem("Import URL from Clipboard...", new EventHandler(this.ImportURLItem_Click))
  240. }),
  241. CreateMenuGroup("PAC ", new ToolStripItem[] {
  242. this.localPACItem = CreateToolStripMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)),
  243. this.onlinePACItem = CreateToolStripMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)),
  244. new ToolStripSeparator(),
  245. this.editLocalPACItem = CreateToolStripMenuItem("Edit Local PAC File...", new EventHandler(this.EditPACFileItem_Click)),
  246. this.updateFromGeositeItem = CreateToolStripMenuItem("Update Local PAC from Geosite", new EventHandler(this.UpdatePACFromGeositeItem_Click)),
  247. this.editGFWUserRuleItem = CreateToolStripMenuItem("Edit User Rule for Geosite...", new EventHandler(this.EditUserRuleFileForGeositeItem_Click)),
  248. this.secureLocalPacUrlToggleItem = CreateToolStripMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)),
  249. CreateToolStripMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)),
  250. this.editOnlinePACItem = CreateToolStripMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)),
  251. }),
  252. this.proxyItem = CreateToolStripMenuItem("Forward Proxy...", new EventHandler(this.proxyItem_Click)),
  253. this.onlineConfigItem = CreateToolStripMenuItem("Online Config...", new EventHandler(this.OnlineConfig_Click)),
  254. new ToolStripSeparator( ),
  255. this.AutoStartupItem = CreateToolStripMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
  256. this.ProtocolHandlerItem = CreateToolStripMenuItem("Associate ss:// Links", new EventHandler(this.ProtocolHandlerItem_Click)),
  257. this.ShareOverLANItem = CreateToolStripMenuItem("Allow other Devices to connect", new EventHandler(this.ShareOverLANItem_Click)),
  258. new ToolStripSeparator( ),
  259. this.hotKeyItem = CreateToolStripMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
  260. CreateMenuGroup("Help", new ToolStripItem[] {
  261. CreateToolStripMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
  262. this.VerboseLoggingToggleItem = CreateToolStripMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
  263. this.ShowPluginOutputToggleItem = CreateToolStripMenuItem("Show Plugin Output", new EventHandler(this.ShowPluginOutputToggleItem_Click)),
  264. this.WriteI18NFileItem = CreateToolStripMenuItem("Write translation template",new EventHandler(WriteI18NFileItem_Click)),
  265. CreateMenuGroup("Updates...", new ToolStripItem[] {
  266. CreateToolStripMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
  267. new ToolStripSeparator(),
  268. this.autoCheckUpdatesToggleItem = CreateToolStripMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
  269. this.checkPreReleaseToggleItem = CreateToolStripMenuItem("Check Pre-release Version", new EventHandler(this.checkPreReleaseToggleItem_Click)),
  270. }),
  271. CreateToolStripMenuItem("About...", new EventHandler(this.AboutItem_Click)),
  272. }),
  273. new ToolStripSeparator(),
  274. CreateToolStripMenuItem("Quit", new EventHandler(this.Quit_Click))
  275. });
  276. }
  277. #endregion
  278. private void controller_TrafficChanged(object sender, EventArgs e)
  279. {
  280. if (icon == null)
  281. return;
  282. Icon newIcon;
  283. bool hasInbound = controller.trafficPerSecondQueue.Last().inboundIncreasement > 0;
  284. bool hasOutbound = controller.trafficPerSecondQueue.Last().outboundIncreasement > 0;
  285. if (hasInbound && hasOutbound)
  286. newIcon = icon_both;
  287. else if (hasInbound)
  288. newIcon = icon_in;
  289. else if (hasOutbound)
  290. newIcon = icon_out;
  291. else
  292. newIcon = icon;
  293. if (newIcon != this.previousIcon)
  294. {
  295. this.previousIcon = newIcon;
  296. _notifyIcon.Icon = newIcon;
  297. }
  298. }
  299. void controller_Errored(object sender, System.IO.ErrorEventArgs e)
  300. {
  301. MessageBox.Show(e.GetException().ToString(), I18N.GetString("Shadowsocks Error: {0}", e.GetException().Message));
  302. }
  303. private void controller_ConfigChanged(object sender, EventArgs e)
  304. {
  305. LoadCurrentConfiguration();
  306. UpdateTrayIconAndNotifyText();
  307. }
  308. private void LoadCurrentConfiguration()
  309. {
  310. Configuration config = controller.GetConfigurationCopy();
  311. UpdateServersMenu();
  312. UpdateSystemProxyItemsEnabledStatus(config);
  313. ShareOverLANItem.Checked = config.shareOverLan;
  314. VerboseLoggingToggleItem.Checked = config.isVerboseLogging;
  315. ShowPluginOutputToggleItem.Checked = config.showPluginOutput;
  316. AutoStartupItem.Checked = AutoStartup.Check();
  317. ProtocolHandlerItem.Checked = ProtocolHandler.Check();
  318. onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
  319. localPACItem.Checked = !onlinePACItem.Checked;
  320. secureLocalPacUrlToggleItem.Checked = config.secureLocalPac;
  321. UpdatePACItemsEnabledStatus();
  322. UpdateUpdateMenu();
  323. }
  324. #region Forms
  325. private void ShowConfigForm()
  326. {
  327. if (configForm != null)
  328. {
  329. configForm.Activate();
  330. }
  331. else
  332. {
  333. configForm = new ConfigForm(controller);
  334. configForm.Show();
  335. configForm.Activate();
  336. configForm.FormClosed += configForm_FormClosed;
  337. }
  338. }
  339. private void ShowProxyForm()
  340. {
  341. if (proxyForm != null)
  342. {
  343. proxyForm.Activate();
  344. }
  345. else
  346. {
  347. proxyForm = new ProxyForm(controller);
  348. proxyForm.Show();
  349. proxyForm.Activate();
  350. proxyForm.FormClosed += proxyForm_FormClosed;
  351. }
  352. }
  353. private void ShowHotKeySettingsForm()
  354. {
  355. if (hotkeySettingsForm != null)
  356. {
  357. hotkeySettingsForm.Activate();
  358. }
  359. else
  360. {
  361. hotkeySettingsForm = new HotkeySettingsForm(controller);
  362. hotkeySettingsForm.Show();
  363. hotkeySettingsForm.Activate();
  364. hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed;
  365. }
  366. }
  367. private void ShowLogForm()
  368. {
  369. if (logForm != null)
  370. {
  371. logForm.Activate();
  372. }
  373. else
  374. {
  375. logForm = new LogForm(controller);
  376. logForm.Show();
  377. logForm.Activate();
  378. logForm.FormClosed += logForm_FormClosed;
  379. }
  380. }
  381. private void ShowOnlineConfigForm()
  382. {
  383. if (onlineConfigForm != null)
  384. {
  385. onlineConfigForm.Activate();
  386. }
  387. else
  388. {
  389. onlineConfigForm = new OnlineConfigForm(controller);
  390. onlineConfigForm.Show();
  391. onlineConfigForm.Activate();
  392. onlineConfigForm.FormClosed += onlineConfigForm_FormClosed;
  393. }
  394. }
  395. void logForm_FormClosed(object sender, FormClosedEventArgs e)
  396. {
  397. logForm.Dispose();
  398. logForm = null;
  399. }
  400. void configForm_FormClosed(object sender, FormClosedEventArgs e)
  401. {
  402. configForm.Dispose();
  403. configForm = null;
  404. if (_isFirstRun)
  405. {
  406. CheckUpdateForFirstRun();
  407. ShowBalloonTip(
  408. I18N.GetString("Shadowsocks is here"),
  409. I18N.GetString("You can turn on/off Shadowsocks in the context menu"),
  410. ToolTipIcon.Info,
  411. 0
  412. );
  413. _isFirstRun = false;
  414. }
  415. }
  416. void proxyForm_FormClosed(object sender, FormClosedEventArgs e)
  417. {
  418. proxyForm.Dispose();
  419. proxyForm = null;
  420. }
  421. void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e)
  422. {
  423. hotkeySettingsForm.Dispose();
  424. hotkeySettingsForm = null;
  425. }
  426. void onlineConfigForm_FormClosed(object sender, FormClosedEventArgs e)
  427. {
  428. onlineConfigForm.Dispose();
  429. onlineConfigForm = null;
  430. }
  431. #endregion
  432. #region Misc
  433. void ShowBalloonTip(string title, string content, ToolTipIcon icon, int timeout)
  434. {
  435. _notifyIcon.BalloonTipTitle = title;
  436. _notifyIcon.BalloonTipText = content;
  437. _notifyIcon.BalloonTipIcon = icon;
  438. _notifyIcon.ShowBalloonTip(timeout);
  439. }
  440. void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
  441. {
  442. if (updateChecker.NewVersionFound)
  443. {
  444. updateChecker.NewVersionFound = false; /* Reset the flag */
  445. if (File.Exists(updateChecker.LatestVersionLocalName))
  446. {
  447. string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\"";
  448. Process.Start("explorer.exe", argument);
  449. }
  450. }
  451. }
  452. private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e)
  453. {
  454. if (updateChecker.NewVersionFound)
  455. {
  456. updateChecker.NewVersionFound = false; /* Reset the flag */
  457. }
  458. }
  459. private void notifyIcon1_Click(object sender, MouseEventArgs e)
  460. {
  461. UpdateTrayIconAndNotifyText();
  462. if (e.Button == MouseButtons.Middle)
  463. {
  464. ShowLogForm();
  465. }
  466. }
  467. private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e)
  468. {
  469. if (e.Button == MouseButtons.Left)
  470. {
  471. ShowConfigForm();
  472. }
  473. }
  474. private void CheckUpdateForFirstRun()
  475. {
  476. Configuration config = controller.GetConfigurationCopy();
  477. if (config.isDefault) return;
  478. _isStartupChecking = true;
  479. updateChecker.CheckUpdate(config, 3000);
  480. }
  481. public void ShowLogForm_HotKey()
  482. {
  483. ShowLogForm();
  484. }
  485. #endregion
  486. #region Main menu
  487. void controller_ShareOverLANStatusChanged(object sender, EventArgs e)
  488. {
  489. ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan;
  490. }
  491. private void proxyItem_Click(object sender, EventArgs e)
  492. {
  493. ShowProxyForm();
  494. }
  495. private void OnlineConfig_Click(object sender, EventArgs e)
  496. {
  497. ShowOnlineConfigForm();
  498. }
  499. private void hotKeyItem_Click(object sender, EventArgs e)
  500. {
  501. ShowHotKeySettingsForm();
  502. }
  503. private void ShareOverLANItem_Click(object sender, EventArgs e)
  504. {
  505. ShareOverLANItem.Checked = !ShareOverLANItem.Checked;
  506. controller.ToggleShareOverLAN(ShareOverLANItem.Checked);
  507. }
  508. private void AutoStartupItem_Click(object sender, EventArgs e)
  509. {
  510. AutoStartupItem.Checked = !AutoStartupItem.Checked;
  511. if (!AutoStartup.Set(AutoStartupItem.Checked))
  512. {
  513. MessageBox.Show(I18N.GetString("Failed to update registry"));
  514. }
  515. LoadCurrentConfiguration();
  516. }
  517. private void ProtocolHandlerItem_Click(object sender, EventArgs e)
  518. {
  519. ProtocolHandlerItem.Checked = !ProtocolHandlerItem.Checked;
  520. if (!ProtocolHandler.Set(ProtocolHandlerItem.Checked))
  521. {
  522. MessageBox.Show(I18N.GetString("Failed to update registry"));
  523. }
  524. LoadCurrentConfiguration();
  525. }
  526. private void Quit_Click(object sender, EventArgs e)
  527. {
  528. controller.Stop();
  529. _notifyIcon.Visible = false;
  530. Application.Exit();
  531. }
  532. #endregion
  533. #region System proxy
  534. private void controller_EnableStatusChanged(object sender, EventArgs e)
  535. {
  536. disableItem.Checked = !controller.GetConfigurationCopy().enabled;
  537. }
  538. private void EnableItem_Click(object sender, EventArgs e)
  539. {
  540. controller.ToggleEnable(false);
  541. Configuration config = controller.GetConfigurationCopy();
  542. UpdateSystemProxyItemsEnabledStatus(config);
  543. }
  544. void controller_EnableGlobalChanged(object sender, EventArgs e)
  545. {
  546. globalModeItem.Checked = controller.GetConfigurationCopy().global;
  547. PACModeItem.Checked = !globalModeItem.Checked;
  548. }
  549. private void UpdateSystemProxyItemsEnabledStatus(Configuration config)
  550. {
  551. disableItem.Checked = !config.enabled;
  552. if (!config.enabled)
  553. {
  554. globalModeItem.Checked = false;
  555. PACModeItem.Checked = false;
  556. }
  557. else
  558. {
  559. globalModeItem.Checked = config.global;
  560. PACModeItem.Checked = !config.global;
  561. }
  562. globalModeItem.Enabled = WinINet.operational;
  563. PACModeItem.Enabled = WinINet.operational;
  564. }
  565. private void GlobalModeItem_Click(object sender, EventArgs e)
  566. {
  567. controller.ToggleEnable(true);
  568. controller.ToggleGlobal(true);
  569. Configuration config = controller.GetConfigurationCopy();
  570. UpdateSystemProxyItemsEnabledStatus(config);
  571. }
  572. private void PACModeItem_Click(object sender, EventArgs e)
  573. {
  574. controller.ToggleEnable(true);
  575. controller.ToggleGlobal(false);
  576. Configuration config = controller.GetConfigurationCopy();
  577. UpdateSystemProxyItemsEnabledStatus(config);
  578. }
  579. #endregion
  580. #region Server
  581. private void UpdateServersMenu()
  582. {
  583. var items = ServersItem.DropDownItems;
  584. while (items[0] != SeperatorItem)
  585. {
  586. items.RemoveAt(0);
  587. }
  588. int strategyCount = 0;
  589. foreach (var strategy in controller.GetStrategies())
  590. {
  591. if (!items.OfType<ToolStripItem>().Any(ts => ts.Text == strategy.Name))
  592. {
  593. ToolStripMenuItem item = new ToolStripMenuItem(strategy.Name);
  594. item.Tag = strategy.ID;
  595. item.Click += AStrategyItem_Click;
  596. items.Add(item);
  597. strategyCount++;
  598. }
  599. }
  600. if (!items.OfType<ToolStripSeparator>().Any(ts => ts.Tag?.ToString() == "-server-"))
  601. {
  602. // user wants a seperator item between strategy and servers menugroup
  603. items.Add(new ToolStripSeparator() { Tag = "-server-" });
  604. }
  605. int serverCount = 0;
  606. Configuration configuration = controller.GetConfigurationCopy();
  607. foreach (var server in configuration.configs)
  608. {
  609. if (Configuration.ChecksServer(server))
  610. {
  611. var name = server.ToString();
  612. if (!items.OfType<ToolStripMenuItem>().Any(ts => ts.Text == name))
  613. {
  614. ToolStripMenuItem item = new ToolStripMenuItem(name);
  615. item.Tag = configuration.configs.FindIndex(s => s == server);
  616. item.Click += AServerItem_Click;
  617. items.Add(item);
  618. serverCount++;
  619. }
  620. }
  621. }
  622. foreach (var item in items)
  623. {
  624. var menuItem = item as ToolStripMenuItem;
  625. if (menuItem == null || menuItem.Tag == null) continue;
  626. if (
  627. menuItem.Tag.ToString() == configuration.index.ToString()
  628. || menuItem.Tag.ToString() == configuration.strategy
  629. )
  630. {
  631. menuItem.Checked = true;
  632. }
  633. else
  634. {
  635. menuItem.Checked = false;
  636. }
  637. }
  638. }
  639. private void AServerItem_Click(object sender, EventArgs e)
  640. {
  641. ToolStripMenuItem item = (ToolStripMenuItem)sender;
  642. controller.SelectServerIndex((int)item.Tag);
  643. }
  644. private void AStrategyItem_Click(object sender, EventArgs e)
  645. {
  646. ToolStripMenuItem item = (ToolStripMenuItem)sender;
  647. controller.SelectStrategy((string)item.Tag);
  648. }
  649. private void Config_Click(object sender, EventArgs e)
  650. {
  651. ShowConfigForm();
  652. }
  653. void splash_FormClosed(object sender, FormClosedEventArgs e)
  654. {
  655. ShowConfigForm();
  656. }
  657. void openURLFromQRCode(object sender, FormClosedEventArgs e)
  658. {
  659. Utils.OpenInBrowser(_urlToOpen);
  660. }
  661. private void StatisticsConfigItem_Click(object sender, EventArgs e)
  662. {
  663. StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller);
  664. form.Show();
  665. }
  666. private void QRCodeItem_Click(object sender, EventArgs e)
  667. {
  668. QRCodeForm qrCodeForm = new QRCodeForm(controller.GetServerURLForCurrentServer());
  669. //qrCodeForm.Icon = this.Icon;
  670. // TODO
  671. qrCodeForm.Show();
  672. }
  673. private void ScanQRCodeItem_Click(object sender, EventArgs e)
  674. {
  675. foreach (Screen screen in Screen.AllScreens)
  676. {
  677. using (Bitmap fullImage = new Bitmap(screen.Bounds.Width,
  678. screen.Bounds.Height))
  679. {
  680. using (Graphics g = Graphics.FromImage(fullImage))
  681. {
  682. g.CopyFromScreen(screen.Bounds.X,
  683. screen.Bounds.Y,
  684. 0, 0,
  685. fullImage.Size,
  686. CopyPixelOperation.SourceCopy);
  687. }
  688. int maxTry = 10;
  689. for (int i = 0; i < maxTry; i++)
  690. {
  691. int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
  692. int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
  693. Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
  694. Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height);
  695. double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width;
  696. using (Graphics g = Graphics.FromImage(target))
  697. {
  698. g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
  699. cropRect,
  700. GraphicsUnit.Pixel);
  701. }
  702. var source = new BitmapSourceLuminanceSource(null);
  703. var bitmap = new BinaryBitmap(new HybridBinarizer(source));
  704. QRCodeReader reader = new QRCodeReader();
  705. var result = reader.decode(bitmap);
  706. if (result != null)
  707. {
  708. var success = controller.AddServerBySSURL(result.Text);
  709. QRCodeSplashForm splash = new QRCodeSplashForm();
  710. if (success)
  711. {
  712. splash.FormClosed += splash_FormClosed;
  713. }
  714. else if (result.Text.ToLower().StartsWith("http://") || result.Text.ToLower().StartsWith("https://"))
  715. {
  716. _urlToOpen = result.Text;
  717. splash.FormClosed += openURLFromQRCode;
  718. }
  719. else
  720. {
  721. MessageBox.Show(I18N.GetString("Failed to decode QRCode"));
  722. return;
  723. }
  724. double minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0;
  725. foreach (ResultPoint point in result.ResultPoints)
  726. {
  727. minX = Math.Min(minX, point.X);
  728. minY = Math.Min(minY, point.Y);
  729. maxX = Math.Max(maxX, point.X);
  730. maxY = Math.Max(maxY, point.Y);
  731. }
  732. minX /= imageScale;
  733. minY /= imageScale;
  734. maxX /= imageScale;
  735. maxY /= imageScale;
  736. // make it 20% larger
  737. double margin = (maxX - minX) * 0.20f;
  738. minX += -margin + marginLeft;
  739. maxX += margin + marginLeft;
  740. minY += -margin + marginTop;
  741. maxY += margin + marginTop;
  742. splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y);
  743. // we need a panel because a window has a minimal size
  744. // TODO: test on high DPI
  745. splash.TargetRect = new Rectangle((int)minX, (int)minY, (int)maxX - (int)minX, (int)maxY - (int)minY);
  746. splash.Size = new Size(fullImage.Width, fullImage.Height);
  747. splash.Show();
  748. return;
  749. }
  750. }
  751. }
  752. }
  753. MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen."));
  754. }
  755. private void ImportURLItem_Click(object sender, EventArgs e)
  756. {
  757. if (controller.AskAddServerBySSURL(Clipboard.GetText(TextDataFormat.Text)))
  758. {
  759. ShowConfigForm();
  760. }
  761. }
  762. #endregion
  763. #region PAC
  764. private void LocalPACItem_Click(object sender, EventArgs e)
  765. {
  766. if (!localPACItem.Checked)
  767. {
  768. localPACItem.Checked = true;
  769. onlinePACItem.Checked = false;
  770. controller.UseOnlinePAC(false);
  771. UpdatePACItemsEnabledStatus();
  772. }
  773. }
  774. private void OnlinePACItem_Click(object sender, EventArgs e)
  775. {
  776. if (!onlinePACItem.Checked)
  777. {
  778. if (controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty())
  779. {
  780. UpdateOnlinePACURLItem_Click(sender, e);
  781. }
  782. if (!controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty())
  783. {
  784. localPACItem.Checked = false;
  785. onlinePACItem.Checked = true;
  786. controller.UseOnlinePAC(true);
  787. }
  788. UpdatePACItemsEnabledStatus();
  789. }
  790. }
  791. private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e)
  792. {
  793. string origPacUrl = controller.GetConfigurationCopy().pacUrl;
  794. string pacUrl = ViewUtils.InputBox(
  795. I18N.GetString("Please input PAC Url"),
  796. I18N.GetString("Edit Online PAC URL"),
  797. origPacUrl, -1, -1);
  798. if (!pacUrl.IsNullOrEmpty() && pacUrl != origPacUrl)
  799. {
  800. controller.SavePACUrl(pacUrl);
  801. }
  802. }
  803. private void SecureLocalPacUrlToggleItem_Click(object sender, EventArgs e)
  804. {
  805. Configuration configuration = controller.GetConfigurationCopy();
  806. controller.ToggleSecureLocalPac(!configuration.secureLocalPac);
  807. }
  808. private void CopyLocalPacUrlItem_Click(object sender, EventArgs e)
  809. {
  810. controller.CopyPacUrl();
  811. }
  812. private void UpdatePACItemsEnabledStatus()
  813. {
  814. if (this.localPACItem.Checked)
  815. {
  816. this.editLocalPACItem.Enabled = true;
  817. this.updateFromGeositeItem.Enabled = true;
  818. this.editGFWUserRuleItem.Enabled = true;
  819. this.editOnlinePACItem.Enabled = false;
  820. }
  821. else
  822. {
  823. this.editLocalPACItem.Enabled = false;
  824. this.updateFromGeositeItem.Enabled = false;
  825. this.editGFWUserRuleItem.Enabled = false;
  826. this.editOnlinePACItem.Enabled = true;
  827. }
  828. }
  829. private void EditPACFileItem_Click(object sender, EventArgs e)
  830. {
  831. controller.TouchPACFile();
  832. }
  833. private async void UpdatePACFromGeositeItem_Click(object sender, EventArgs e)
  834. {
  835. await GeositeUpdater.UpdatePACFromGeosite();
  836. }
  837. private void EditUserRuleFileForGeositeItem_Click(object sender, EventArgs e)
  838. {
  839. controller.TouchUserRuleFile();
  840. }
  841. void controller_FileReadyToOpen(object sender, ShadowsocksController.PathEventArgs e)
  842. {
  843. string argument = @"/select, " + e.Path;
  844. Process.Start("explorer.exe", argument);
  845. }
  846. void controller_UpdatePACFromGeositeError(object sender, System.IO.ErrorEventArgs e)
  847. {
  848. ShowBalloonTip(I18N.GetString("Failed to update PAC file"), e.GetException().Message, ToolTipIcon.Error, 5000);
  849. logger.LogUsefulException(e.GetException());
  850. }
  851. void controller_UpdatePACFromGeositeCompleted(object sender, GeositeResultEventArgs e)
  852. {
  853. string result = e.Success
  854. ? I18N.GetString("PAC updated")
  855. : I18N.GetString("No updates found. Please report to Geosite if you have problems with it.");
  856. ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000);
  857. }
  858. #endregion
  859. #region Help
  860. void controller_VerboseLoggingStatusChanged(object sender, EventArgs e)
  861. {
  862. VerboseLoggingToggleItem.Checked = controller.GetConfigurationCopy().isVerboseLogging;
  863. }
  864. void controller_ShowPluginOutputChanged(object sender, EventArgs e)
  865. {
  866. ShowPluginOutputToggleItem.Checked = controller.GetConfigurationCopy().showPluginOutput;
  867. }
  868. private void VerboseLoggingToggleItem_Click(object sender, EventArgs e)
  869. {
  870. VerboseLoggingToggleItem.Checked = !VerboseLoggingToggleItem.Checked;
  871. controller.ToggleVerboseLogging(VerboseLoggingToggleItem.Checked);
  872. }
  873. private void ShowLogItem_Click(object sender, EventArgs e)
  874. {
  875. ShowLogForm();
  876. }
  877. private void ShowPluginOutputToggleItem_Click(object sender, EventArgs e)
  878. {
  879. ShowPluginOutputToggleItem.Checked = !ShowPluginOutputToggleItem.Checked;
  880. controller.ToggleShowPluginOutput(ShowPluginOutputToggleItem.Checked);
  881. }
  882. private void WriteI18NFileItem_Click(object sender, EventArgs e)
  883. {
  884. File.WriteAllText(I18N.I18N_FILE, Resources.i18n_csv, Encoding.UTF8);
  885. }
  886. #endregion
  887. #region Update
  888. void updateChecker_CheckUpdateCompleted(object sender, EventArgs e)
  889. {
  890. if (updateChecker.NewVersionFound)
  891. {
  892. ShowBalloonTip(I18N.GetString("Shadowsocks {0} Update Found", updateChecker.LatestVersionNumber + updateChecker.LatestVersionSuffix), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000);
  893. }
  894. else if (!_isStartupChecking)
  895. {
  896. ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000);
  897. }
  898. _isStartupChecking = false;
  899. }
  900. private void UpdateUpdateMenu()
  901. {
  902. Configuration configuration = controller.GetConfigurationCopy();
  903. autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate;
  904. checkPreReleaseToggleItem.Checked = configuration.checkPreRelease;
  905. }
  906. private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e)
  907. {
  908. Configuration configuration = controller.GetConfigurationCopy();
  909. controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate);
  910. UpdateUpdateMenu();
  911. }
  912. private void checkPreReleaseToggleItem_Click(object sender, EventArgs e)
  913. {
  914. Configuration configuration = controller.GetConfigurationCopy();
  915. controller.ToggleCheckingPreRelease(!configuration.checkPreRelease);
  916. UpdateUpdateMenu();
  917. }
  918. private void checkUpdatesItem_Click(object sender, EventArgs e)
  919. {
  920. updateChecker.CheckUpdate(controller.GetConfigurationCopy());
  921. }
  922. private void AboutItem_Click(object sender, EventArgs e)
  923. {
  924. Utils.OpenInBrowser("https://github.com/shadowsocks/shadowsocks-windows");
  925. }
  926. #endregion
  927. }
  928. }