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