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.

LogForm.cs 14 kB

10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 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
10 years ago
10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. using System;
  2. using System.Drawing;
  3. using System.IO;
  4. using System.Windows.Forms;
  5. using System.Windows.Forms.DataVisualization.Charting;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using Shadowsocks.Controller;
  9. using Shadowsocks.Properties;
  10. using Shadowsocks.Model;
  11. using Shadowsocks.Util;
  12. namespace Shadowsocks.View
  13. {
  14. public partial class LogForm : Form
  15. {
  16. long lastOffset;
  17. string filename;
  18. Timer timer;
  19. const int BACK_OFFSET = 65536;
  20. ShadowsocksController controller;
  21. #region chart
  22. List<float> inboundPoints = new List<float>();
  23. List<float> outboundPoints = new List<float>();
  24. long maxSpeed = 0;
  25. Tuple<float, string, long> bandwidthScale = new Tuple<float, string, long>(0, "B", 1);
  26. TextAnnotation inboundAnnotation = new TextAnnotation();
  27. TextAnnotation outboundAnnotation = new TextAnnotation();
  28. #endregion
  29. public LogForm(ShadowsocksController controller, string filename)
  30. {
  31. this.controller = controller;
  32. this.filename = filename;
  33. InitializeComponent();
  34. Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
  35. LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
  36. if (config == null)
  37. {
  38. config = new LogViewerConfig();
  39. }
  40. else
  41. {
  42. topMostTrigger = config.topMost;
  43. wrapTextTrigger = config.wrapText;
  44. toolbarTrigger = config.toolbarShown;
  45. LogMessageTextBox.BackColor = config.GetBackgroundColor();
  46. LogMessageTextBox.ForeColor = config.GetTextColor();
  47. LogMessageTextBox.Font = config.GetFont();
  48. }
  49. controller.TrafficChanged += controller_TrafficChanged;
  50. UpdateTexts();
  51. }
  52. private void controller_TrafficChanged(object sender, EventArgs e)
  53. {
  54. inboundPoints.Clear();
  55. outboundPoints.Clear();
  56. foreach (var trafficPerSecond in controller.traffic)
  57. {
  58. inboundPoints.Add(trafficPerSecond.inboundIncreasement);
  59. outboundPoints.Add(trafficPerSecond.outboundIncreasement);
  60. maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
  61. }
  62. bandwidthScale = Utils.GetBandwidthScale(maxSpeed);
  63. //rescale the original data points, since it is List<float>, .ForEach does not work
  64. inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
  65. outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
  66. try
  67. {
  68. if (trafficChart.InvokeRequired && trafficChart.IsHandleCreated)
  69. {
  70. trafficChart.Invoke(new Action(() =>
  71. {
  72. trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
  73. trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
  74. trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2;
  75. inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
  76. inboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.inboundIncreasement);
  77. outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
  78. outboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.outboundIncreasement);
  79. trafficChart.Annotations.Clear();
  80. trafficChart.Annotations.Add(inboundAnnotation);
  81. trafficChart.Annotations.Add(outboundAnnotation);
  82. }));
  83. }
  84. }
  85. catch (ObjectDisposedException)
  86. {
  87. // suppress the thread race error:
  88. // when closing the form but the Invoked Action is still working and cause 'Chart is Disposed' exception
  89. }
  90. }
  91. private void UpdateTexts()
  92. {
  93. FileMenuItem.Text = I18N.GetString("&File");
  94. OpenLocationMenuItem.Text = I18N.GetString("&Open Location");
  95. ExitMenuItem.Text = I18N.GetString("E&xit");
  96. CleanLogsButton.Text = I18N.GetString("&Clean Logs");
  97. ChangeFontButton.Text = I18N.GetString("Change &Font");
  98. WrapTextCheckBox.Text = I18N.GetString("&Wrap Text");
  99. TopMostCheckBox.Text = I18N.GetString("&Top Most");
  100. ViewMenuItem.Text = I18N.GetString("&View");
  101. CleanLogsMenuItem.Text = I18N.GetString("&Clean Logs");
  102. ChangeFontMenuItem.Text = I18N.GetString("Change &Font");
  103. WrapTextMenuItem.Text = I18N.GetString("&Wrap Text");
  104. TopMostMenuItem.Text = I18N.GetString("&Top Most");
  105. ShowToolbarMenuItem.Text = I18N.GetString("&Show Toolbar");
  106. Text = I18N.GetString("Log Viewer");
  107. // traffic chart
  108. trafficChart.Series["Inbound"].LegendText = I18N.GetString("Inbound");
  109. trafficChart.Series["Outbound"].LegendText = I18N.GetString("Outbound");
  110. }
  111. private void Timer_Tick(object sender, EventArgs e)
  112. {
  113. UpdateContent();
  114. }
  115. private void InitContent()
  116. {
  117. using (StreamReader reader = new StreamReader(new FileStream(filename,
  118. FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
  119. {
  120. if (reader.BaseStream.Length > BACK_OFFSET)
  121. {
  122. reader.BaseStream.Seek(-BACK_OFFSET, SeekOrigin.End);
  123. reader.ReadLine();
  124. }
  125. string line = "";
  126. while ((line = reader.ReadLine()) != null)
  127. LogMessageTextBox.AppendText(line + Environment.NewLine);
  128. LogMessageTextBox.ScrollToCaret();
  129. lastOffset = reader.BaseStream.Position;
  130. }
  131. }
  132. private void UpdateContent()
  133. {
  134. try {
  135. using(StreamReader reader = new StreamReader(new FileStream(filename,
  136. FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) {
  137. reader.BaseStream.Seek(lastOffset, SeekOrigin.Begin);
  138. string line = "";
  139. bool changed = false;
  140. while((line = reader.ReadLine()) != null) {
  141. changed = true;
  142. LogMessageTextBox.AppendText(line + Environment.NewLine);
  143. }
  144. if(changed) {
  145. LogMessageTextBox.ScrollToCaret();
  146. }
  147. lastOffset = reader.BaseStream.Position;
  148. }
  149. } catch(FileNotFoundException) {
  150. }
  151. this.Text = I18N.GetString("Log Viewer") +
  152. $" [in: {Utils.FormatBandwidth(controller.InboundCounter)}, out: {Utils.FormatBandwidth(controller.OutboundCounter)}]";
  153. }
  154. private void LogForm_Load(object sender, EventArgs e)
  155. {
  156. InitContent();
  157. timer = new Timer();
  158. timer.Interval = 300;
  159. timer.Tick += Timer_Tick;
  160. timer.Start();
  161. LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
  162. if (config == null)
  163. config = new LogViewerConfig();
  164. Height = config.height;
  165. Width = config.width;
  166. Top = config.GetBestTop();
  167. Left = config.GetBestLeft();
  168. if(config.maximized) {
  169. WindowState = FormWindowState.Maximized;
  170. }
  171. topMostTriggerLock = true;
  172. TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
  173. topMostTriggerLock = false;
  174. wrapTextTriggerLock = true;
  175. LogMessageTextBox.WordWrap = WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger;
  176. wrapTextTriggerLock = false;
  177. ToolbarFlowLayoutPanel.Visible = ShowToolbarMenuItem.Checked = toolbarTrigger;
  178. }
  179. private void LogForm_FormClosing(object sender, FormClosingEventArgs e)
  180. {
  181. timer.Stop();
  182. controller.TrafficChanged -= controller_TrafficChanged;
  183. LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
  184. if (config == null)
  185. config = new LogViewerConfig();
  186. config.topMost = topMostTrigger;
  187. config.wrapText = wrapTextTrigger;
  188. config.toolbarShown = toolbarTrigger;
  189. config.SetFont(LogMessageTextBox.Font);
  190. config.SetBackgroundColor(LogMessageTextBox.BackColor);
  191. config.SetTextColor(LogMessageTextBox.ForeColor);
  192. if(!(config.maximized = WindowState == FormWindowState.Maximized)) {
  193. config.top = Top;
  194. config.left = Left;
  195. config.height = Height;
  196. config.width = Width;
  197. }
  198. controller.SaveLogViewerConfig(config);
  199. }
  200. private void OpenLocationMenuItem_Click(object sender, EventArgs e)
  201. {
  202. string argument = "/select, \"" + filename + "\"";
  203. Logging.Debug(argument);
  204. System.Diagnostics.Process.Start("explorer.exe", argument);
  205. }
  206. private void ExitMenuItem_Click(object sender, EventArgs e)
  207. {
  208. Close();
  209. }
  210. private void LogForm_Shown(object sender, EventArgs e)
  211. {
  212. LogMessageTextBox.ScrollToCaret();
  213. }
  214. #region Clean up the content in LogMessageTextBox.
  215. private void DoCleanLogs()
  216. {
  217. Logging.Clear();
  218. lastOffset = 0;
  219. LogMessageTextBox.Clear();
  220. }
  221. private void CleanLogsMenuItem_Click(object sender, EventArgs e)
  222. {
  223. DoCleanLogs();
  224. }
  225. private void CleanLogsButton_Click(object sender, EventArgs e)
  226. {
  227. DoCleanLogs();
  228. }
  229. #endregion
  230. #region Change the font settings applied in LogMessageTextBox.
  231. private void DoChangeFont()
  232. {
  233. try
  234. {
  235. FontDialog fd = new FontDialog();
  236. fd.Font = LogMessageTextBox.Font;
  237. if (fd.ShowDialog() == DialogResult.OK)
  238. {
  239. LogMessageTextBox.Font = new Font(fd.Font.FontFamily, fd.Font.Size, fd.Font.Style);
  240. }
  241. }
  242. catch (Exception ex)
  243. {
  244. Logging.LogUsefulException(ex);
  245. MessageBox.Show(ex.Message);
  246. }
  247. }
  248. private void ChangeFontMenuItem_Click(object sender, EventArgs e)
  249. {
  250. DoChangeFont();
  251. }
  252. private void ChangeFontButton_Click(object sender, EventArgs e)
  253. {
  254. DoChangeFont();
  255. }
  256. #endregion
  257. #region Trigger the log messages to wrapable, or not.
  258. bool wrapTextTrigger = false;
  259. bool wrapTextTriggerLock = false;
  260. private void TriggerWrapText()
  261. {
  262. wrapTextTriggerLock = true;
  263. wrapTextTrigger = !wrapTextTrigger;
  264. LogMessageTextBox.WordWrap = wrapTextTrigger;
  265. LogMessageTextBox.ScrollToCaret();
  266. WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger;
  267. wrapTextTriggerLock = false;
  268. }
  269. private void WrapTextMenuItem_Click(object sender, EventArgs e)
  270. {
  271. if (!wrapTextTriggerLock)
  272. {
  273. TriggerWrapText();
  274. }
  275. }
  276. private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e)
  277. {
  278. if (!wrapTextTriggerLock)
  279. {
  280. TriggerWrapText();
  281. }
  282. }
  283. #endregion
  284. #region Trigger the window to top most, or not.
  285. bool topMostTrigger = false;
  286. bool topMostTriggerLock = false;
  287. private void TriggerTopMost()
  288. {
  289. topMostTriggerLock = true;
  290. topMostTrigger = !topMostTrigger;
  291. TopMost = topMostTrigger;
  292. TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
  293. topMostTriggerLock = false;
  294. }
  295. private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e)
  296. {
  297. if (!topMostTriggerLock)
  298. {
  299. TriggerTopMost();
  300. }
  301. }
  302. private void TopMostMenuItem_Click(object sender, EventArgs e)
  303. {
  304. if (!topMostTriggerLock)
  305. {
  306. TriggerTopMost();
  307. }
  308. }
  309. #endregion
  310. private bool toolbarTrigger = false;
  311. private void ShowToolbarMenuItem_Click(object sender, EventArgs e)
  312. {
  313. toolbarTrigger = !toolbarTrigger;
  314. ToolbarFlowLayoutPanel.Visible = toolbarTrigger;
  315. ShowToolbarMenuItem.Checked = toolbarTrigger;
  316. }
  317. }
  318. }