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

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