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 40 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
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
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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  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. ToolStripMenuItem item = new ToolStripMenuItem(strategy.Name);
  404. item.Tag = strategy.ID;
  405. item.Click += AStrategyItem_Click;
  406. items.Add( item);
  407. strategyCount++;
  408. }
  409. // user wants a seperator item between strategy and servers menugroup
  410. items .Add(new ToolStripSeparator());
  411. int serverCount = 0;
  412. Configuration configuration = controller.GetConfigurationCopy();
  413. foreach (var server in configuration.configs)
  414. {
  415. if (Configuration.ChecksServer(server))
  416. {
  417. ToolStripMenuItem item = new ToolStripMenuItem(server.FriendlyName());
  418. item.Tag = configuration.configs.FindIndex(s => s == server);
  419. item.Click += AServerItem_Click;
  420. items.Add( item);
  421. serverCount++;
  422. }
  423. }
  424. foreach (var item in items)
  425. {
  426. var menuItem = item as ToolStripMenuItem;
  427. if (menuItem != null && menuItem.Tag != null && (menuItem.Tag.ToString() == configuration.index.ToString() || menuItem.Tag.ToString() == configuration.strategy))
  428. {
  429. menuItem.Checked = true;
  430. }
  431. }
  432. }
  433. private void ShowConfigForm()
  434. {
  435. if (configForm != null)
  436. {
  437. configForm.Activate();
  438. }
  439. else
  440. {
  441. configForm = new ConfigForm(controller);
  442. configForm.Show();
  443. configForm.Activate();
  444. configForm.FormClosed += configForm_FormClosed;
  445. }
  446. }
  447. private void ShowProxyForm()
  448. {
  449. if (proxyForm != null)
  450. {
  451. proxyForm.Activate();
  452. }
  453. else
  454. {
  455. proxyForm = new ProxyForm(controller);
  456. proxyForm.Show();
  457. proxyForm.Activate();
  458. proxyForm.FormClosed += proxyForm_FormClosed;
  459. }
  460. }
  461. private void ShowHotKeySettingsForm()
  462. {
  463. if (hotkeySettingsForm != null)
  464. {
  465. hotkeySettingsForm.Activate();
  466. }
  467. else
  468. {
  469. hotkeySettingsForm = new HotkeySettingsForm(controller);
  470. hotkeySettingsForm.Show();
  471. hotkeySettingsForm.Activate();
  472. hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed;
  473. }
  474. }
  475. private void ShowLogForm()
  476. {
  477. if (logForm != null)
  478. {
  479. logForm.Activate();
  480. }
  481. else
  482. {
  483. logForm = new LogForm(controller);
  484. logForm.Show();
  485. logForm.Activate();
  486. logForm.FormClosed += logForm_FormClosed;
  487. }
  488. }
  489. void logForm_FormClosed(object sender, FormClosedEventArgs e)
  490. {
  491. logForm.Dispose();
  492. logForm = null;
  493. Utils.ReleaseMemory(true);
  494. }
  495. void configForm_FormClosed(object sender, FormClosedEventArgs e)
  496. {
  497. configForm.Dispose();
  498. configForm = null;
  499. Utils.ReleaseMemory(true);
  500. if (_isFirstRun)
  501. {
  502. CheckUpdateForFirstRun();
  503. ShowBalloonTip(
  504. I18N.GetString("Shadowsocks is here"),
  505. I18N.GetString("You can turn on/off Shadowsocks in the context menu"),
  506. ToolTipIcon.Info,
  507. 0
  508. );
  509. _isFirstRun = false;
  510. }
  511. }
  512. void proxyForm_FormClosed(object sender, FormClosedEventArgs e)
  513. {
  514. proxyForm.Dispose();
  515. proxyForm = null;
  516. Utils.ReleaseMemory(true);
  517. }
  518. void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e)
  519. {
  520. hotkeySettingsForm.Dispose();
  521. hotkeySettingsForm = null;
  522. Utils.ReleaseMemory(true);
  523. }
  524. private void Config_Click(object sender, EventArgs e)
  525. {
  526. ShowConfigForm();
  527. }
  528. private void Quit_Click(object sender, EventArgs e)
  529. {
  530. controller.Stop();
  531. _notifyIcon.Visible = false;
  532. Application.Exit();
  533. }
  534. private void CheckUpdateForFirstRun()
  535. {
  536. Configuration config = controller.GetConfigurationCopy();
  537. if (config.isDefault) return;
  538. _isStartupChecking = true;
  539. updateChecker.CheckUpdate(config, 3000);
  540. }
  541. private void AboutItem_Click(object sender, EventArgs e)
  542. {
  543. Process.Start("https://github.com/shadowsocks/shadowsocks-windows");
  544. }
  545. private void notifyIcon1_Click(object sender, MouseEventArgs e)
  546. {
  547. UpdateTrayIconAndNotifyText();
  548. if (e.Button == MouseButtons.Middle)
  549. {
  550. ShowLogForm();
  551. }
  552. }
  553. private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e)
  554. {
  555. if (e.Button == MouseButtons.Left)
  556. {
  557. ShowConfigForm();
  558. }
  559. }
  560. private void EnableItem_Click(object sender, EventArgs e)
  561. {
  562. controller.ToggleEnable(false);
  563. Configuration config = controller.GetConfigurationCopy();
  564. UpdateSystemProxyItemsEnabledStatus(config);
  565. }
  566. private void UpdateSystemProxyItemsEnabledStatus(Configuration config)
  567. {
  568. disableItem.Checked = !config.enabled;
  569. if (!config.enabled)
  570. {
  571. globalModeItem.Checked = false;
  572. PACModeItem.Checked = false;
  573. }
  574. else
  575. {
  576. globalModeItem.Checked = config.global;
  577. PACModeItem.Checked = !config.global;
  578. }
  579. }
  580. private void GlobalModeItem_Click(object sender, EventArgs e)
  581. {
  582. controller.ToggleEnable(true);
  583. controller.ToggleGlobal(true);
  584. Configuration config = controller.GetConfigurationCopy();
  585. UpdateSystemProxyItemsEnabledStatus(config);
  586. }
  587. private void PACModeItem_Click(object sender, EventArgs e)
  588. {
  589. controller.ToggleEnable(true);
  590. controller.ToggleGlobal(false);
  591. Configuration config = controller.GetConfigurationCopy();
  592. UpdateSystemProxyItemsEnabledStatus(config);
  593. }
  594. private void ShareOverLANItem_Click(object sender, EventArgs e)
  595. {
  596. ShareOverLANItem.Checked = !ShareOverLANItem.Checked;
  597. controller.ToggleShareOverLAN(ShareOverLANItem.Checked);
  598. }
  599. private void EditPACFileItem_Click(object sender, EventArgs e)
  600. {
  601. controller.TouchPACFile();
  602. }
  603. private void UpdatePACFromGFWListItem_Click(object sender, EventArgs e)
  604. {
  605. controller.UpdatePACFromGFWList();
  606. }
  607. private void EditUserRuleFileForGFWListItem_Click(object sender, EventArgs e)
  608. {
  609. controller.TouchUserRuleFile();
  610. }
  611. private void AServerItem_Click(object sender, EventArgs e)
  612. {
  613. ToolStripMenuItem item = (ToolStripMenuItem)sender;
  614. controller.SelectServerIndex((int)item.Tag);
  615. }
  616. private void AStrategyItem_Click(object sender, EventArgs e)
  617. {
  618. ToolStripMenuItem item = (ToolStripMenuItem)sender;
  619. controller.SelectStrategy((string)item.Tag);
  620. }
  621. private void VerboseLoggingToggleItem_Click(object sender, EventArgs e)
  622. {
  623. VerboseLoggingToggleItem.Checked = !VerboseLoggingToggleItem.Checked;
  624. controller.ToggleVerboseLogging(VerboseLoggingToggleItem.Checked);
  625. }
  626. private void ShowPluginOutputToggleItem_Click(object sender, EventArgs e)
  627. {
  628. ShowPluginOutputToggleItem.Checked = !ShowPluginOutputToggleItem.Checked;
  629. controller.ToggleShowPluginOutput(ShowPluginOutputToggleItem.Checked);
  630. }
  631. private void WriteI18NFileItem_Click(object sender, EventArgs e)
  632. {
  633. File.WriteAllText(I18N.I18N_FILE, Resources.i18n_csv, Encoding.UTF8);
  634. }
  635. private void StatisticsConfigItem_Click(object sender, EventArgs e)
  636. {
  637. StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller);
  638. form.Show();
  639. }
  640. private void QRCodeItem_Click(object sender, EventArgs e)
  641. {
  642. QRCodeForm qrCodeForm = new QRCodeForm(controller.GetServerURLForCurrentServer());
  643. //qrCodeForm.Icon = this.Icon;
  644. // TODO
  645. qrCodeForm.Show();
  646. }
  647. private void ScanQRCodeItem_Click(object sender, EventArgs e)
  648. {
  649. foreach (Screen screen in Screen.AllScreens)
  650. {
  651. using (Bitmap fullImage = new Bitmap(screen.Bounds.Width,
  652. screen.Bounds.Height))
  653. {
  654. using (Graphics g = Graphics.FromImage(fullImage))
  655. {
  656. g.CopyFromScreen(screen.Bounds.X,
  657. screen.Bounds.Y,
  658. 0, 0,
  659. fullImage.Size,
  660. CopyPixelOperation.SourceCopy);
  661. }
  662. int maxTry = 10;
  663. for (int i = 0; i < maxTry; i++)
  664. {
  665. int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
  666. int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
  667. Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
  668. Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height);
  669. double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width;
  670. using (Graphics g = Graphics.FromImage(target))
  671. {
  672. g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
  673. cropRect,
  674. GraphicsUnit.Pixel);
  675. }
  676. var source = new BitmapSourceLuminanceSource(null);
  677. var bitmap = new BinaryBitmap(new HybridBinarizer(source));
  678. QRCodeReader reader = new QRCodeReader();
  679. var result = reader.decode(bitmap);
  680. if (result != null)
  681. {
  682. var success = controller.AddServerBySSURL(result.Text);
  683. QRCodeSplashForm splash = new QRCodeSplashForm();
  684. if (success)
  685. {
  686. splash.FormClosed += splash_FormClosed;
  687. }
  688. else if (result.Text.ToLower().StartsWith("http://") || result.Text.ToLower().StartsWith("https://"))
  689. {
  690. _urlToOpen = result.Text;
  691. splash.FormClosed += openURLFromQRCode;
  692. }
  693. else
  694. {
  695. MessageBox.Show(I18N.GetString("Failed to decode QRCode"));
  696. return;
  697. }
  698. double minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0;
  699. foreach (ResultPoint point in result.ResultPoints)
  700. {
  701. minX = Math.Min(minX, point.X);
  702. minY = Math.Min(minY, point.Y);
  703. maxX = Math.Max(maxX, point.X);
  704. maxY = Math.Max(maxY, point.Y);
  705. }
  706. minX /= imageScale;
  707. minY /= imageScale;
  708. maxX /= imageScale;
  709. maxY /= imageScale;
  710. // make it 20% larger
  711. double margin = (maxX - minX) * 0.20f;
  712. minX += -margin + marginLeft;
  713. maxX += margin + marginLeft;
  714. minY += -margin + marginTop;
  715. maxY += margin + marginTop;
  716. splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y);
  717. // we need a panel because a window has a minimal size
  718. // TODO: test on high DPI
  719. splash.TargetRect = new Rectangle((int)minX, (int)minY, (int)maxX - (int)minX, (int)maxY - (int)minY);
  720. splash.Size = new Size(fullImage.Width, fullImage.Height);
  721. splash.Show();
  722. return;
  723. }
  724. }
  725. }
  726. }
  727. MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen."));
  728. }
  729. private void ImportURLItem_Click(object sender, EventArgs e)
  730. {
  731. var success = controller.AddServerBySSURL(Clipboard.GetText(TextDataFormat.Text));
  732. if (success)
  733. {
  734. ShowConfigForm();
  735. }
  736. }
  737. void splash_FormClosed(object sender, FormClosedEventArgs e)
  738. {
  739. ShowConfigForm();
  740. }
  741. void openURLFromQRCode(object sender, FormClosedEventArgs e)
  742. {
  743. Process.Start(_urlToOpen);
  744. }
  745. private void AutoStartupItem_Click(object sender, EventArgs e)
  746. {
  747. AutoStartupItem.Checked = !AutoStartupItem.Checked;
  748. if (!AutoStartup.Set(AutoStartupItem.Checked))
  749. {
  750. MessageBox.Show(I18N.GetString("Failed to update registry"));
  751. }
  752. }
  753. private void LocalPACItem_Click(object sender, EventArgs e)
  754. {
  755. if (!localPACItem.Checked)
  756. {
  757. localPACItem.Checked = true;
  758. onlinePACItem.Checked = false;
  759. controller.UseOnlinePAC(false);
  760. UpdatePACItemsEnabledStatus();
  761. }
  762. }
  763. private void OnlinePACItem_Click(object sender, EventArgs e)
  764. {
  765. if (!onlinePACItem.Checked)
  766. {
  767. if (controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty())
  768. {
  769. UpdateOnlinePACURLItem_Click(sender, e);
  770. }
  771. if (!controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty())
  772. {
  773. localPACItem.Checked = false;
  774. onlinePACItem.Checked = true;
  775. controller.UseOnlinePAC(true);
  776. }
  777. UpdatePACItemsEnabledStatus();
  778. }
  779. }
  780. private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e)
  781. {
  782. string origPacUrl = controller.GetConfigurationCopy().pacUrl;
  783. #if NET472
  784. string pacUrl = Microsoft.VisualBasic.Interaction.InputBox(
  785. I18N.GetString("Please input PAC Url"),
  786. I18N.GetString("Edit Online PAC URL"),
  787. origPacUrl, -1, -1);
  788. #else
  789. string pacUrl = ViewUtils.InputBox(
  790. I18N.GetString("Please input PAC Url"),
  791. I18N.GetString("Edit Online PAC URL"),
  792. origPacUrl, -1, -1);
  793. #endif
  794. if (!pacUrl.IsNullOrEmpty() && pacUrl != origPacUrl)
  795. {
  796. controller.SavePACUrl(pacUrl);
  797. }
  798. }
  799. private void SecureLocalPacUrlToggleItem_Click(object sender, EventArgs e)
  800. {
  801. Configuration configuration = controller.GetConfigurationCopy();
  802. controller.ToggleSecureLocalPac(!configuration.secureLocalPac);
  803. }
  804. private void CopyLocalPacUrlItem_Click(object sender, EventArgs e)
  805. {
  806. controller.CopyPacUrl();
  807. }
  808. private void UpdatePACItemsEnabledStatus()
  809. {
  810. if (this.localPACItem.Checked)
  811. {
  812. this.editLocalPACItem.Enabled = true;
  813. this.updateFromGFWListItem.Enabled = true;
  814. this.editGFWUserRuleItem.Enabled = true;
  815. this.editOnlinePACItem.Enabled = false;
  816. }
  817. else
  818. {
  819. this.editLocalPACItem.Enabled = false;
  820. this.updateFromGFWListItem.Enabled = false;
  821. this.editGFWUserRuleItem.Enabled = false;
  822. this.editOnlinePACItem.Enabled = true;
  823. }
  824. }
  825. private void UpdateUpdateMenu()
  826. {
  827. Configuration configuration = controller.GetConfigurationCopy();
  828. autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate;
  829. checkPreReleaseToggleItem.Checked = configuration.checkPreRelease;
  830. }
  831. private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e)
  832. {
  833. Configuration configuration = controller.GetConfigurationCopy();
  834. controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate);
  835. UpdateUpdateMenu();
  836. }
  837. private void checkPreReleaseToggleItem_Click(object sender, EventArgs e)
  838. {
  839. Configuration configuration = controller.GetConfigurationCopy();
  840. controller.ToggleCheckingPreRelease(!configuration.checkPreRelease);
  841. UpdateUpdateMenu();
  842. }
  843. private void checkUpdatesItem_Click(object sender, EventArgs e)
  844. {
  845. updateChecker.CheckUpdate(controller.GetConfigurationCopy());
  846. }
  847. private void proxyItem_Click(object sender, EventArgs e)
  848. {
  849. ShowProxyForm();
  850. }
  851. private void hotKeyItem_Click(object sender, EventArgs e)
  852. {
  853. ShowHotKeySettingsForm();
  854. }
  855. private void ShowLogItem_Click(object sender, EventArgs e)
  856. {
  857. ShowLogForm();
  858. }
  859. public void ShowLogForm_HotKey()
  860. {
  861. ShowLogForm();
  862. }
  863. }
  864. }