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