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

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5. using System.Drawing.Imaging;
  6. using System.Windows.Forms;
  7. using ZXing;
  8. using ZXing.Common;
  9. using ZXing.QrCode;
  10. using Shadowsocks.Controller;
  11. using Shadowsocks.Model;
  12. using Shadowsocks.Properties;
  13. using Shadowsocks.Util;
  14. namespace Shadowsocks.View
  15. {
  16. public class MenuViewController
  17. {
  18. // yes this is just a menu view controller
  19. // when config form is closed, it moves away from RAM
  20. // and it should just do anything related to the config form
  21. private ShadowsocksController controller;
  22. private UpdateChecker updateChecker;
  23. private NotifyIcon _notifyIcon;
  24. private Bitmap icon_baseBitmap;
  25. private Icon icon_base, icon_in, icon_out, icon_both, targetIcon;
  26. private ContextMenu contextMenu1;
  27. private bool _isFirstRun;
  28. private bool _isStartupChecking;
  29. private MenuItem enableItem;
  30. private MenuItem modeItem;
  31. private MenuItem AutoStartupItem;
  32. private MenuItem ShareOverLANItem;
  33. private MenuItem SeperatorItem;
  34. private MenuItem ConfigItem;
  35. private MenuItem ServersItem;
  36. private MenuItem globalModeItem;
  37. private MenuItem PACModeItem;
  38. private MenuItem localPACItem;
  39. private MenuItem onlinePACItem;
  40. private MenuItem editLocalPACItem;
  41. private MenuItem updateFromGFWListItem;
  42. private MenuItem editGFWUserRuleItem;
  43. private MenuItem editOnlinePACItem;
  44. private MenuItem autoCheckUpdatesToggleItem;
  45. private MenuItem proxyItem;
  46. private MenuItem hotKeyItem;
  47. private MenuItem VerboseLoggingToggleItem;
  48. private ConfigForm configForm;
  49. private ProxyForm proxyForm;
  50. private LogForm logForm;
  51. private HotkeySettingsForm hotkeySettingsForm;
  52. private string _urlToOpen;
  53. public MenuViewController(ShadowsocksController controller)
  54. {
  55. this.controller = controller;
  56. LoadMenu();
  57. controller.EnableStatusChanged += controller_EnableStatusChanged;
  58. controller.ConfigChanged += controller_ConfigChanged;
  59. controller.PACFileReadyToOpen += controller_FileReadyToOpen;
  60. controller.UserRuleFileReadyToOpen += controller_FileReadyToOpen;
  61. controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged;
  62. controller.VerboseLoggingStatusChanged += controller_VerboseLoggingStatusChanged;
  63. controller.EnableGlobalChanged += controller_EnableGlobalChanged;
  64. controller.Errored += controller_Errored;
  65. controller.UpdatePACFromGFWListCompleted += controller_UpdatePACFromGFWListCompleted;
  66. controller.UpdatePACFromGFWListError += controller_UpdatePACFromGFWListError;
  67. _notifyIcon = new NotifyIcon();
  68. UpdateTrayIcon();
  69. _notifyIcon.Visible = true;
  70. _notifyIcon.ContextMenu = contextMenu1;
  71. _notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
  72. _notifyIcon.MouseClick += notifyIcon1_Click;
  73. _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
  74. _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
  75. controller.TrafficChanged += controller_TrafficChanged;
  76. this.updateChecker = new UpdateChecker();
  77. updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted;
  78. LoadCurrentConfiguration();
  79. Configuration config = controller.GetConfigurationCopy();
  80. if (config.isDefault)
  81. {
  82. _isFirstRun = true;
  83. ShowConfigForm();
  84. }
  85. else if(config.autoCheckUpdate)
  86. {
  87. _isStartupChecking = true;
  88. updateChecker.CheckUpdate(config, 3000);
  89. }
  90. }
  91. private void controller_TrafficChanged(object sender, EventArgs e)
  92. {
  93. if (icon_baseBitmap == null)
  94. return;
  95. Icon newIcon;
  96. bool hasInbound = controller.traffic.Last.inboundIncreasement > 0;
  97. bool hasOutbound = controller.traffic.Last.outboundIncreasement > 0;
  98. if (hasInbound && hasOutbound)
  99. newIcon = icon_both;
  100. else if (hasInbound)
  101. newIcon = icon_in;
  102. else if (hasOutbound)
  103. newIcon = icon_out;
  104. else
  105. newIcon = icon_base;
  106. if (newIcon != this.targetIcon)
  107. {
  108. this.targetIcon = newIcon;
  109. _notifyIcon.Icon = newIcon;
  110. }
  111. }
  112. void controller_Errored(object sender, System.IO.ErrorEventArgs e)
  113. {
  114. MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message));
  115. }
  116. #region Tray Icon
  117. private void UpdateTrayIcon()
  118. {
  119. int dpi;
  120. Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);
  121. dpi = (int)graphics.DpiX;
  122. graphics.Dispose();
  123. icon_baseBitmap = null;
  124. if (dpi < 97)
  125. {
  126. // dpi = 96;
  127. icon_baseBitmap = Resources.ss16;
  128. }
  129. else if (dpi < 121)
  130. {
  131. // dpi = 120;
  132. icon_baseBitmap = Resources.ss20;
  133. }
  134. else
  135. {
  136. icon_baseBitmap = Resources.ss24;
  137. }
  138. Configuration config = controller.GetConfigurationCopy();
  139. bool enabled = config.enabled;
  140. bool global = config.global;
  141. icon_baseBitmap = getTrayIconByState(icon_baseBitmap, enabled, global);
  142. icon_base = Icon.FromHandle(icon_baseBitmap.GetHicon());
  143. targetIcon = icon_base;
  144. icon_in = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssIn24).GetHicon());
  145. icon_out = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssOut24).GetHicon());
  146. icon_both = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssIn24, Resources.ssOut24).GetHicon());
  147. _notifyIcon.Icon = targetIcon;
  148. string serverInfo = null;
  149. if (controller.GetCurrentStrategy() != null)
  150. {
  151. serverInfo = controller.GetCurrentStrategy().Name;
  152. }
  153. else
  154. {
  155. serverInfo = config.GetCurrentServer().FriendlyName();
  156. }
  157. // we want to show more details but notify icon title is limited to 63 characters
  158. string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
  159. (enabled ?
  160. I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
  161. String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
  162. + "\n" + serverInfo;
  163. _notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length));
  164. }
  165. private Bitmap getTrayIconByState(Bitmap originIcon, bool enabled, bool global)
  166. {
  167. Bitmap iconCopy = new Bitmap(originIcon);
  168. for (int x = 0; x < iconCopy.Width; x++)
  169. {
  170. for (int y = 0; y < iconCopy.Height; y++)
  171. {
  172. Color color = originIcon.GetPixel(x, y);
  173. if (color.A != 0 && color.R > 30)
  174. {
  175. if (!enabled)
  176. {
  177. iconCopy.SetPixel(x, y, Color.FromArgb((byte)(color.A / 1.25), color.R, color.G, color.B));
  178. }
  179. else if (global)
  180. {
  181. Color flyBlue = Color.FromArgb(25, 125, 191);
  182. // Multiply with flyBlue
  183. int red = color.R * flyBlue.R / 255;
  184. int green = color.G * flyBlue.G / 255;
  185. int blue = color.B * flyBlue.B / 255;
  186. iconCopy.SetPixel(x, y, Color.FromArgb(color.A, red, green, blue));
  187. }
  188. }
  189. else
  190. {
  191. iconCopy.SetPixel(x, y, Color.FromArgb(color.A, color.R, color.G, color.B));
  192. }
  193. }
  194. }
  195. return iconCopy;
  196. }
  197. private Bitmap AddBitmapOverlay(Bitmap original, params Bitmap[] overlays)
  198. {
  199. Bitmap bitmap = new Bitmap(original.Width, original.Height, PixelFormat.Format64bppArgb);
  200. Graphics canvas = Graphics.FromImage(bitmap);
  201. canvas.DrawImage(original, new Point(0, 0));
  202. foreach (Bitmap overlay in overlays)
  203. {
  204. canvas.DrawImage(new Bitmap(overlay, original.Size), new Point(0, 0));
  205. }
  206. canvas.Save();
  207. return bitmap;
  208. }
  209. #endregion
  210. #region MenuItems and MenuGroups
  211. private MenuItem CreateMenuItem(string text, EventHandler click)
  212. {
  213. return new MenuItem(I18N.GetString(text), click);
  214. }
  215. private MenuItem CreateMenuGroup(string text, MenuItem[] items)
  216. {
  217. return new MenuItem(I18N.GetString(text), items);
  218. }
  219. private void LoadMenu()
  220. {
  221. this.contextMenu1 = new ContextMenu(new MenuItem[] {
  222. this.enableItem = CreateMenuItem("Enable System Proxy", new EventHandler(this.EnableItem_Click)),
  223. this.modeItem = CreateMenuGroup("Mode", new MenuItem[] {
  224. this.PACModeItem = CreateMenuItem("PAC", new EventHandler(this.PACModeItem_Click)),
  225. this.globalModeItem = CreateMenuItem("Global", new EventHandler(this.GlobalModeItem_Click))
  226. }),
  227. this.ServersItem = CreateMenuGroup("Servers", new MenuItem[] {
  228. this.SeperatorItem = new MenuItem("-"),
  229. this.ConfigItem = CreateMenuItem("Edit Servers...", new EventHandler(this.Config_Click)),
  230. CreateMenuItem("Statistics Config...", StatisticsConfigItem_Click),
  231. CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)),
  232. CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click))
  233. }),
  234. CreateMenuGroup("PAC ", new MenuItem[] {
  235. this.localPACItem = CreateMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)),
  236. this.onlinePACItem = CreateMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)),
  237. new MenuItem("-"),
  238. this.editLocalPACItem = CreateMenuItem("Edit Local PAC File...", new EventHandler(this.EditPACFileItem_Click)),
  239. this.updateFromGFWListItem = CreateMenuItem("Update Local PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)),
  240. this.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)),
  241. this.editOnlinePACItem = CreateMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)),
  242. }),
  243. this.proxyItem = CreateMenuItem("SOCKS5 Proxy...", new EventHandler(this.proxyItem_Click)),
  244. new MenuItem("-"),
  245. this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
  246. this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
  247. new MenuItem("-"),
  248. CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
  249. this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
  250. this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
  251. CreateMenuGroup("Updates...", new MenuItem[] {
  252. CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
  253. new MenuItem("-"),
  254. this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
  255. }),
  256. CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
  257. new MenuItem("-"),
  258. CreateMenuItem("Quit", new EventHandler(this.Quit_Click))
  259. });
  260. }
  261. #endregion
  262. private void controller_ConfigChanged(object sender, EventArgs e)
  263. {
  264. LoadCurrentConfiguration();
  265. UpdateTrayIcon();
  266. }
  267. private void controller_EnableStatusChanged(object sender, EventArgs e)
  268. {
  269. enableItem.Checked = controller.GetConfigurationCopy().enabled;
  270. modeItem.Enabled = enableItem.Checked;
  271. }
  272. void controller_ShareOverLANStatusChanged(object sender, EventArgs e)
  273. {
  274. ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan;
  275. }
  276. void controller_VerboseLoggingStatusChanged(object sender, EventArgs e) {
  277. VerboseLoggingToggleItem.Checked = controller.GetConfigurationCopy().isVerboseLogging;
  278. }
  279. void controller_EnableGlobalChanged(object sender, EventArgs e)
  280. {
  281. globalModeItem.Checked = controller.GetConfigurationCopy().global;
  282. PACModeItem.Checked = !globalModeItem.Checked;
  283. }
  284. void controller_FileReadyToOpen(object sender, ShadowsocksController.PathEventArgs e)
  285. {
  286. string argument = @"/select, " + e.Path;
  287. System.Diagnostics.Process.Start("explorer.exe", argument);
  288. }
  289. void ShowBalloonTip(string title, string content, ToolTipIcon icon, int timeout)
  290. {
  291. _notifyIcon.BalloonTipTitle = title;
  292. _notifyIcon.BalloonTipText = content;
  293. _notifyIcon.BalloonTipIcon = icon;
  294. _notifyIcon.ShowBalloonTip(timeout);
  295. }
  296. void controller_UpdatePACFromGFWListError(object sender, System.IO.ErrorEventArgs e)
  297. {
  298. ShowBalloonTip(I18N.GetString("Failed to update PAC file"), e.GetException().Message, ToolTipIcon.Error, 5000);
  299. Logging.LogUsefulException(e.GetException());
  300. }
  301. void controller_UpdatePACFromGFWListCompleted(object sender, GFWListUpdater.ResultEventArgs e)
  302. {
  303. string result = e.Success
  304. ? I18N.GetString("PAC updated")
  305. : I18N.GetString("No updates found. Please report to GFWList if you have problems with it.");
  306. ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000);
  307. }
  308. void updateChecker_CheckUpdateCompleted(object sender, EventArgs e)
  309. {
  310. if (updateChecker.NewVersionFound)
  311. {
  312. ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000);
  313. }
  314. else if (!_isStartupChecking)
  315. {
  316. ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000);
  317. }
  318. _isStartupChecking = false;
  319. }
  320. void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
  321. {
  322. if (updateChecker.NewVersionFound)
  323. {
  324. updateChecker.NewVersionFound = false; /* Reset the flag */
  325. if (System.IO.File.Exists(updateChecker.LatestVersionLocalName))
  326. {
  327. string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\"";
  328. System.Diagnostics.Process.Start("explorer.exe", argument);
  329. }
  330. }
  331. }
  332. private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e)
  333. {
  334. if (updateChecker.NewVersionFound)
  335. {
  336. updateChecker.NewVersionFound = false; /* Reset the flag */
  337. }
  338. }
  339. private void LoadCurrentConfiguration()
  340. {
  341. Configuration config = controller.GetConfigurationCopy();
  342. UpdateServersMenu();
  343. enableItem.Checked = config.enabled;
  344. modeItem.Enabled = config.enabled;
  345. globalModeItem.Checked = config.global;
  346. PACModeItem.Checked = !config.global;
  347. ShareOverLANItem.Checked = config.shareOverLan;
  348. VerboseLoggingToggleItem.Checked = config.isVerboseLogging;
  349. AutoStartupItem.Checked = AutoStartup.Check();
  350. onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
  351. localPACItem.Checked = !onlinePACItem.Checked;
  352. UpdatePACItemsEnabledStatus();
  353. UpdateUpdateMenu();
  354. }
  355. private void UpdateServersMenu()
  356. {
  357. var items = ServersItem.MenuItems;
  358. while (items[0] != SeperatorItem)
  359. {
  360. items.RemoveAt(0);
  361. }
  362. int i = 0;
  363. foreach (var strategy in controller.GetStrategies())
  364. {
  365. MenuItem item = new MenuItem(strategy.Name);
  366. item.Tag = strategy.ID;
  367. item.Click += AStrategyItem_Click;
  368. items.Add(i, item);
  369. i++;
  370. }
  371. // user wants a seperator item between strategy and servers menugroup
  372. items.Add( i++, new MenuItem("-") );
  373. int strategyCount = i;
  374. Configuration configuration = controller.GetConfigurationCopy();
  375. foreach (var server in configuration.configs)
  376. {
  377. MenuItem item = new MenuItem(server.FriendlyName());
  378. item.Tag = i - strategyCount;
  379. item.Click += AServerItem_Click;
  380. items.Add(i, item);
  381. i++;
  382. }
  383. foreach (MenuItem item in items)
  384. {
  385. if (item.Tag != null && (item.Tag.ToString() == configuration.index.ToString() || item.Tag.ToString() == configuration.strategy))
  386. {
  387. item.Checked = true;
  388. }
  389. }
  390. }
  391. private void ShowConfigForm()
  392. {
  393. if (configForm != null)
  394. {
  395. configForm.Activate();
  396. }
  397. else
  398. {
  399. configForm = new ConfigForm(controller);
  400. configForm.Show();
  401. configForm.Activate();
  402. configForm.FormClosed += configForm_FormClosed;
  403. }
  404. }
  405. private void ShowProxyForm()
  406. {
  407. if (proxyForm != null)
  408. {
  409. proxyForm.Activate();
  410. }
  411. else
  412. {
  413. proxyForm = new ProxyForm(controller);
  414. proxyForm.Show();
  415. proxyForm.Activate();
  416. proxyForm.FormClosed += proxyForm_FormClosed;
  417. }
  418. }
  419. private void ShowHotKeySettingsForm()
  420. {
  421. if (hotkeySettingsForm != null)
  422. {
  423. hotkeySettingsForm.Activate();
  424. }
  425. else
  426. {
  427. hotkeySettingsForm = new HotkeySettingsForm(controller);
  428. hotkeySettingsForm.Show();
  429. hotkeySettingsForm.Activate();
  430. hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed;
  431. }
  432. }
  433. private void ShowLogForm()
  434. {
  435. if (logForm != null)
  436. {
  437. logForm.Activate();
  438. }
  439. else
  440. {
  441. logForm = new LogForm(controller, Logging.LogFilePath);
  442. logForm.Show();
  443. logForm.Activate();
  444. logForm.FormClosed += logForm_FormClosed;
  445. }
  446. }
  447. void logForm_FormClosed(object sender, FormClosedEventArgs e)
  448. {
  449. logForm = null;
  450. Utils.ReleaseMemory(true);
  451. }
  452. void configForm_FormClosed(object sender, FormClosedEventArgs e)
  453. {
  454. configForm = null;
  455. Utils.ReleaseMemory(true);
  456. if (_isFirstRun)
  457. {
  458. CheckUpdateForFirstRun();
  459. ShowFirstTimeBalloon();
  460. _isFirstRun = false;
  461. }
  462. }
  463. void proxyForm_FormClosed(object sender, FormClosedEventArgs e)
  464. {
  465. proxyForm = null;
  466. Utils.ReleaseMemory(true);
  467. }
  468. void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e)
  469. {
  470. hotkeySettingsForm = null;
  471. Utils.ReleaseMemory(true);
  472. }
  473. private void Config_Click(object sender, EventArgs e)
  474. {
  475. ShowConfigForm();
  476. }
  477. private void Quit_Click(object sender, EventArgs e)
  478. {
  479. controller.Stop();
  480. _notifyIcon.Visible = false;
  481. Application.Exit();
  482. }
  483. private void CheckUpdateForFirstRun()
  484. {
  485. Configuration config = controller.GetConfigurationCopy();
  486. if (config.isDefault) return;
  487. _isStartupChecking = true;
  488. updateChecker.CheckUpdate(config, 3000);
  489. }
  490. private void ShowFirstTimeBalloon()
  491. {
  492. _notifyIcon.BalloonTipTitle = I18N.GetString("Shadowsocks is here");
  493. _notifyIcon.BalloonTipText = I18N.GetString("You can turn on/off Shadowsocks in the context menu");
  494. _notifyIcon.BalloonTipIcon = ToolTipIcon.Info;
  495. _notifyIcon.ShowBalloonTip(0);
  496. }
  497. private void AboutItem_Click(object sender, EventArgs e)
  498. {
  499. Process.Start("https://github.com/shadowsocks/shadowsocks-windows");
  500. }
  501. private void notifyIcon1_Click(object sender, MouseEventArgs e)
  502. {
  503. if (e.Button == MouseButtons.Left)
  504. {
  505. // TODO: show something interesting
  506. }
  507. else if (e.Button == MouseButtons.Middle)
  508. {
  509. ShowLogForm();
  510. }
  511. }
  512. private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e)
  513. {
  514. if (e.Button == MouseButtons.Left)
  515. {
  516. ShowConfigForm();
  517. }
  518. }
  519. private void EnableItem_Click(object sender, EventArgs e)
  520. {
  521. controller.ToggleEnable(!enableItem.Checked);
  522. }
  523. private void GlobalModeItem_Click(object sender, EventArgs e)
  524. {
  525. controller.ToggleGlobal(true);
  526. }
  527. private void PACModeItem_Click(object sender, EventArgs e)
  528. {
  529. controller.ToggleGlobal(false);
  530. }
  531. private void ShareOverLANItem_Click(object sender, EventArgs e)
  532. {
  533. ShareOverLANItem.Checked = !ShareOverLANItem.Checked;
  534. controller.ToggleShareOverLAN(ShareOverLANItem.Checked);
  535. }
  536. private void EditPACFileItem_Click(object sender, EventArgs e)
  537. {
  538. controller.TouchPACFile();
  539. }
  540. private void UpdatePACFromGFWListItem_Click(object sender, EventArgs e)
  541. {
  542. controller.UpdatePACFromGFWList();
  543. }
  544. private void EditUserRuleFileForGFWListItem_Click(object sender, EventArgs e)
  545. {
  546. controller.TouchUserRuleFile();
  547. }
  548. private void AServerItem_Click(object sender, EventArgs e)
  549. {
  550. MenuItem item = (MenuItem)sender;
  551. controller.SelectServerIndex((int)item.Tag);
  552. }
  553. private void AStrategyItem_Click(object sender, EventArgs e)
  554. {
  555. MenuItem item = (MenuItem)sender;
  556. controller.SelectStrategy((string)item.Tag);
  557. }
  558. private void VerboseLoggingToggleItem_Click( object sender, EventArgs e ) {
  559. VerboseLoggingToggleItem.Checked = ! VerboseLoggingToggleItem.Checked;
  560. controller.ToggleVerboseLogging( VerboseLoggingToggleItem.Checked );
  561. }
  562. private void StatisticsConfigItem_Click(object sender, EventArgs e)
  563. {
  564. StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller);
  565. form.Show();
  566. }
  567. private void QRCodeItem_Click(object sender, EventArgs e)
  568. {
  569. QRCodeForm qrCodeForm = new QRCodeForm(controller.GetQRCodeForCurrentServer());
  570. //qrCodeForm.Icon = this.Icon;
  571. // TODO
  572. qrCodeForm.Show();
  573. }
  574. private void ScanQRCodeItem_Click(object sender, EventArgs e)
  575. {
  576. foreach (Screen screen in Screen.AllScreens)
  577. {
  578. using (Bitmap fullImage = new Bitmap(screen.Bounds.Width,
  579. screen.Bounds.Height))
  580. {
  581. using (Graphics g = Graphics.FromImage(fullImage))
  582. {
  583. g.CopyFromScreen(screen.Bounds.X,
  584. screen.Bounds.Y,
  585. 0, 0,
  586. fullImage.Size,
  587. CopyPixelOperation.SourceCopy);
  588. }
  589. int maxTry = 10;
  590. for (int i = 0; i < maxTry; i++)
  591. {
  592. int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
  593. int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
  594. Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
  595. Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height);
  596. double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width;
  597. using (Graphics g = Graphics.FromImage(target))
  598. {
  599. g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
  600. cropRect,
  601. GraphicsUnit.Pixel);
  602. }
  603. var source = new BitmapLuminanceSource(target);
  604. var bitmap = new BinaryBitmap(new HybridBinarizer(source));
  605. QRCodeReader reader = new QRCodeReader();
  606. var result = reader.decode(bitmap);
  607. if (result != null)
  608. {
  609. var success = controller.AddServerBySSURL(result.Text);
  610. QRCodeSplashForm splash = new QRCodeSplashForm();
  611. if (success)
  612. {
  613. splash.FormClosed += splash_FormClosed;
  614. }
  615. else if (result.Text.StartsWith("http://") || result.Text.StartsWith("https://"))
  616. {
  617. _urlToOpen = result.Text;
  618. splash.FormClosed += openURLFromQRCode;
  619. }
  620. else
  621. {
  622. MessageBox.Show(I18N.GetString("Failed to decode QRCode"));
  623. return;
  624. }
  625. double minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0;
  626. foreach (ResultPoint point in result.ResultPoints)
  627. {
  628. minX = Math.Min(minX, point.X);
  629. minY = Math.Min(minY, point.Y);
  630. maxX = Math.Max(maxX, point.X);
  631. maxY = Math.Max(maxY, point.Y);
  632. }
  633. minX /= imageScale;
  634. minY /= imageScale;
  635. maxX /= imageScale;
  636. maxY /= imageScale;
  637. // make it 20% larger
  638. double margin = (maxX - minX) * 0.20f;
  639. minX += -margin + marginLeft;
  640. maxX += margin + marginLeft;
  641. minY += -margin + marginTop;
  642. maxY += margin + marginTop;
  643. splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y);
  644. // we need a panel because a window has a minimal size
  645. // TODO: test on high DPI
  646. splash.TargetRect = new Rectangle((int)minX + screen.Bounds.X, (int)minY + screen.Bounds.Y, (int)maxX - (int)minX, (int)maxY - (int)minY);
  647. splash.Size = new Size(fullImage.Width, fullImage.Height);
  648. splash.Show();
  649. return;
  650. }
  651. }
  652. }
  653. }
  654. MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen."));
  655. }
  656. void splash_FormClosed(object sender, FormClosedEventArgs e)
  657. {
  658. ShowConfigForm();
  659. }
  660. void openURLFromQRCode(object sender, FormClosedEventArgs e)
  661. {
  662. Process.Start(_urlToOpen);
  663. }
  664. private void AutoStartupItem_Click(object sender, EventArgs e)
  665. {
  666. AutoStartupItem.Checked = !AutoStartupItem.Checked;
  667. if (!AutoStartup.Set(AutoStartupItem.Checked))
  668. {
  669. MessageBox.Show(I18N.GetString("Failed to update registry"));
  670. }
  671. }
  672. private void LocalPACItem_Click(object sender, EventArgs e)
  673. {
  674. if (!localPACItem.Checked)
  675. {
  676. localPACItem.Checked = true;
  677. onlinePACItem.Checked = false;
  678. controller.UseOnlinePAC(false);
  679. UpdatePACItemsEnabledStatus();
  680. }
  681. }
  682. private void OnlinePACItem_Click(object sender, EventArgs e)
  683. {
  684. if (!onlinePACItem.Checked)
  685. {
  686. if (controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty())
  687. {
  688. UpdateOnlinePACURLItem_Click(sender, e);
  689. }
  690. if (!controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty())
  691. {
  692. localPACItem.Checked = false;
  693. onlinePACItem.Checked = true;
  694. controller.UseOnlinePAC(true);
  695. }
  696. UpdatePACItemsEnabledStatus();
  697. }
  698. }
  699. private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e)
  700. {
  701. string origPacUrl = controller.GetConfigurationCopy().pacUrl;
  702. string pacUrl = Microsoft.VisualBasic.Interaction.InputBox(
  703. I18N.GetString("Please input PAC Url"),
  704. I18N.GetString("Edit Online PAC URL"),
  705. origPacUrl, -1, -1);
  706. if (!pacUrl.IsNullOrEmpty() && pacUrl != origPacUrl)
  707. {
  708. controller.SavePACUrl(pacUrl);
  709. }
  710. }
  711. private void UpdatePACItemsEnabledStatus()
  712. {
  713. if (this.localPACItem.Checked)
  714. {
  715. this.editLocalPACItem.Enabled = true;
  716. this.updateFromGFWListItem.Enabled = true;
  717. this.editGFWUserRuleItem.Enabled = true;
  718. this.editOnlinePACItem.Enabled = false;
  719. }
  720. else
  721. {
  722. this.editLocalPACItem.Enabled = false;
  723. this.updateFromGFWListItem.Enabled = false;
  724. this.editGFWUserRuleItem.Enabled = false;
  725. this.editOnlinePACItem.Enabled = true;
  726. }
  727. }
  728. private void UpdateUpdateMenu()
  729. {
  730. Configuration configuration = controller.GetConfigurationCopy();
  731. autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate;
  732. }
  733. private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e)
  734. {
  735. Configuration configuration = controller.GetConfigurationCopy();
  736. controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate);
  737. UpdateUpdateMenu();
  738. }
  739. private void checkUpdatesItem_Click(object sender, EventArgs e)
  740. {
  741. updateChecker.CheckUpdate(controller.GetConfigurationCopy());
  742. }
  743. private void proxyItem_Click(object sender, EventArgs e)
  744. {
  745. ShowProxyForm();
  746. }
  747. private void hotKeyItem_Click(object sender, EventArgs e)
  748. {
  749. ShowHotKeySettingsForm();
  750. }
  751. private void ShowLogItem_Click(object sender, EventArgs e)
  752. {
  753. ShowLogForm();
  754. }
  755. public void ShowLogForm_HotKey()
  756. {
  757. ShowLogForm();
  758. }
  759. }
  760. }