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

9 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 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
9 years ago
9 years ago
10 years ago
10 years ago
9 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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)
  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 ex)
  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. using (StreamReader reader = new StreamReader(new FileStream(filename,
  135. FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
  136. {
  137. reader.BaseStream.Seek(lastOffset, SeekOrigin.Begin);
  138. string line = "";
  139. bool changed = false;
  140. while ((line = reader.ReadLine()) != null)
  141. {
  142. changed = true;
  143. LogMessageTextBox.AppendText(line + Environment.NewLine);
  144. }
  145. if (changed)
  146. {
  147. LogMessageTextBox.ScrollToCaret();
  148. }
  149. lastOffset = reader.BaseStream.Position;
  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. topMostTriggerLock = true;
  169. TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
  170. topMostTriggerLock = false;
  171. wrapTextTriggerLock = true;
  172. LogMessageTextBox.WordWrap = WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger;
  173. wrapTextTriggerLock = false;
  174. ToolbarFlowLayoutPanel.Visible = ShowToolbarMenuItem.Checked = toolbarTrigger;
  175. }
  176. private void LogForm_FormClosing(object sender, FormClosingEventArgs e)
  177. {
  178. timer.Stop();
  179. controller.TrafficChanged -= controller_TrafficChanged;
  180. LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
  181. if (config == null)
  182. config = new LogViewerConfig();
  183. config.topMost = topMostTrigger;
  184. config.wrapText = wrapTextTrigger;
  185. config.toolbarShown = toolbarTrigger;
  186. config.SetFont(LogMessageTextBox.Font);
  187. config.SetBackgroundColor(LogMessageTextBox.BackColor);
  188. config.SetTextColor(LogMessageTextBox.ForeColor);
  189. config.top = Top;
  190. config.left = Left;
  191. config.height = Height;
  192. config.width = Width;
  193. controller.SaveLogViewerConfig(config);
  194. }
  195. private void OpenLocationMenuItem_Click(object sender, EventArgs e)
  196. {
  197. string argument = "/select, \"" + filename + "\"";
  198. Logging.Debug(argument);
  199. System.Diagnostics.Process.Start("explorer.exe", argument);
  200. }
  201. private void ExitMenuItem_Click(object sender, EventArgs e)
  202. {
  203. Close();
  204. }
  205. private void LogForm_Shown(object sender, EventArgs e)
  206. {
  207. LogMessageTextBox.ScrollToCaret();
  208. }
  209. #region Clean up the content in LogMessageTextBox.
  210. private void DoCleanLogs()
  211. {
  212. LogMessageTextBox.Clear();
  213. }
  214. private void CleanLogsMenuItem_Click(object sender, EventArgs e)
  215. {
  216. DoCleanLogs();
  217. }
  218. private void CleanLogsButton_Click(object sender, EventArgs e)
  219. {
  220. DoCleanLogs();
  221. }
  222. #endregion
  223. #region Change the font settings applied in LogMessageTextBox.
  224. private void DoChangeFont()
  225. {
  226. try
  227. {
  228. FontDialog fd = new FontDialog();
  229. fd.Font = LogMessageTextBox.Font;
  230. if (fd.ShowDialog() == DialogResult.OK)
  231. {
  232. LogMessageTextBox.Font = new Font(fd.Font.FontFamily, fd.Font.Size, fd.Font.Style);
  233. }
  234. }
  235. catch (Exception ex)
  236. {
  237. Logging.LogUsefulException(ex);
  238. MessageBox.Show(ex.Message);
  239. }
  240. }
  241. private void ChangeFontMenuItem_Click(object sender, EventArgs e)
  242. {
  243. DoChangeFont();
  244. }
  245. private void ChangeFontButton_Click(object sender, EventArgs e)
  246. {
  247. DoChangeFont();
  248. }
  249. #endregion
  250. #region Trigger the log messages to wrapable, or not.
  251. bool wrapTextTrigger = false;
  252. bool wrapTextTriggerLock = false;
  253. private void TriggerWrapText()
  254. {
  255. wrapTextTriggerLock = true;
  256. wrapTextTrigger = !wrapTextTrigger;
  257. LogMessageTextBox.WordWrap = wrapTextTrigger;
  258. LogMessageTextBox.ScrollToCaret();
  259. WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger;
  260. wrapTextTriggerLock = false;
  261. }
  262. private void WrapTextMenuItem_Click(object sender, EventArgs e)
  263. {
  264. if (!wrapTextTriggerLock)
  265. {
  266. TriggerWrapText();
  267. }
  268. }
  269. private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e)
  270. {
  271. if (!wrapTextTriggerLock)
  272. {
  273. TriggerWrapText();
  274. }
  275. }
  276. #endregion
  277. #region Trigger the window to top most, or not.
  278. bool topMostTrigger = false;
  279. bool topMostTriggerLock = false;
  280. private void TriggerTopMost()
  281. {
  282. topMostTriggerLock = true;
  283. topMostTrigger = !topMostTrigger;
  284. TopMost = topMostTrigger;
  285. TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
  286. topMostTriggerLock = false;
  287. }
  288. private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e)
  289. {
  290. if (!topMostTriggerLock)
  291. {
  292. TriggerTopMost();
  293. }
  294. }
  295. private void TopMostMenuItem_Click(object sender, EventArgs e)
  296. {
  297. if (!topMostTriggerLock)
  298. {
  299. TriggerTopMost();
  300. }
  301. }
  302. #endregion
  303. private bool toolbarTrigger = false;
  304. private void ShowToolbarMenuItem_Click(object sender, EventArgs e)
  305. {
  306. toolbarTrigger = !toolbarTrigger;
  307. ToolbarFlowLayoutPanel.Visible = toolbarTrigger;
  308. ShowToolbarMenuItem.Checked = toolbarTrigger;
  309. }
  310. }
  311. }