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 42 kB

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