OSDN Git Service

5d56b842e8099e0e1955fee3cfe92995736268a3
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / MainForm.cs
1 // Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Diagnostics;\r
18 using System.Drawing;\r
19 using System.Globalization;\r
20 using System.IO;\r
21 using System.Linq;\r
22 using System.Net;\r
23 using System.Runtime.InteropServices;\r
24 using System.Text;\r
25 using System.Text.RegularExpressions;\r
26 using System.Threading.Tasks;\r
27 using System.Windows.Forms;\r
28 using Microsoft.CSharp.RuntimeBinder;\r
29 using static System.Math;\r
30 \r
31 namespace KancolleSniffer\r
32 {\r
33     public partial class MainForm : Form\r
34     {\r
35         private readonly Sniffer _sniffer = new Sniffer();\r
36         private readonly Config _config = new Config();\r
37         private readonly ConfigDialog _configDialog;\r
38         private readonly ProxyManager _proxyManager;\r
39         private int _currentFleet;\r
40         private bool _combinedFleet;\r
41         private readonly Label[] _labelCheckFleets;\r
42         private readonly ShipLabels _shipLabels;\r
43         private readonly ListForm _listForm;\r
44         private readonly NotificationManager _notificationManager;\r
45         private bool _started;\r
46         private bool _timerEnabled;\r
47         private string _debugLogFile;\r
48         private IEnumerator<string> _playLog;\r
49 \r
50         private readonly ErrorDialog _errorDialog = new ErrorDialog();\r
51         private bool _missionFinishTimeMode;\r
52         private bool _ndockFinishTimeMode;\r
53         private readonly KancolleDb _kancolleDb = new KancolleDb();\r
54         private readonly ErrorLog _errorLog;\r
55 \r
56         public MainForm()\r
57         {\r
58             InitializeComponent();\r
59             HttpProxy.AfterSessionComplete += HttpProxy_AfterSessionComplete;\r
60             _configDialog = new ConfigDialog(_config, this);\r
61             _labelCheckFleets = new[] {labelCheckFleet1, labelCheckFleet2, labelCheckFleet3, labelCheckFleet4};\r
62 \r
63             // この時点でAutoScaleDimensions == CurrentAutoScaleDimensionsなので、\r
64             // MainForm.Designer.csのAutoScaleDimensionsの6f,12fを使う。\r
65             ShipLabel.ScaleFactor = new SizeF(CurrentAutoScaleDimensions.Width / 6f,\r
66                 CurrentAutoScaleDimensions.Height / 12f);\r
67 \r
68             SetupFleetClick();\r
69             _shipLabels = new ShipLabels();\r
70             _shipLabels.CreateAkashiTimers(panelShipInfo);\r
71             _shipLabels.CreateShipLabels(panelShipInfo, ShowShipOnShipList);\r
72             _shipLabels.CreateAkashiTimers7(panel7Ships);\r
73             _shipLabels.CreateShipLabels7(panel7Ships, ShowShipOnShipList);\r
74             _shipLabels.CreateCombinedShipLabels(panelCombinedFleet, ShowShipOnShipList);\r
75             _shipLabels.CreateNDockLabels(panelDock, labelNDock_Click);\r
76             panelRepairList.CreateLabels(panelRepairList_Click);\r
77             labelPresetAkashiTimer.BackColor = ShipLabels.ColumnColors[1];\r
78             _listForm = new ListForm(_sniffer, _config) {Owner = this};\r
79             _notificationManager = new NotificationManager(Alarm);\r
80             try\r
81             {\r
82                 _config.Load();\r
83             }\r
84             catch (Exception ex)\r
85             {\r
86                 throw new ConfigFileException("設定ファイルが壊れています。", ex);\r
87             }\r
88             _proxyManager = new ProxyManager(_config, this);\r
89             _errorLog = new ErrorLog(_sniffer);\r
90             _proxyManager.UpdatePacFile();\r
91             PerformZoom();\r
92             _shipLabels.AdjustAkashiTimers();\r
93             _sniffer.LoadState();\r
94             _sniffer.RepeatingTimerController = new RepeatingTimerController(_notificationManager, _config);\r
95         }\r
96 \r
97         private class RepeatingTimerController : Sniffer.IRepeatingTimerController\r
98         {\r
99             private readonly NotificationManager _manager;\r
100             private readonly Config _config;\r
101 \r
102             public RepeatingTimerController(NotificationManager manager, Config config)\r
103             {\r
104                 _manager = manager;\r
105                 _config = config;\r
106             }\r
107 \r
108             public void Stop(string key)\r
109             {\r
110                 _manager.StopRepeat(key,\r
111                     (key == "入渠終了" || key == "遠征終了") &&\r
112                     (_config.Notifications[key].Flags & NotificationType.Cont) != 0);\r
113             }\r
114 \r
115             public void Stop(string key, int fleet) => _manager.StopRepeat(key, fleet);\r
116 \r
117             public void Suspend() => _manager.SuspendRepeat();\r
118 \r
119             public void Resume() => _manager.ResumeRepeat();\r
120         }\r
121 \r
122         public class ConfigFileException : Exception\r
123         {\r
124             public ConfigFileException(string message, Exception innerException) : base(message, innerException)\r
125             {\r
126             }\r
127         }\r
128 \r
129         private void HttpProxy_AfterSessionComplete(HttpProxy.Session session)\r
130         {\r
131             Invoke(new Action<HttpProxy.Session>(ProcessRequest), session);\r
132         }\r
133 \r
134         private void ProcessRequest(HttpProxy.Session session)\r
135         {\r
136             var url = session.Request.PathAndQuery;\r
137             if (!url.Contains("kcsapi/"))\r
138                 return;\r
139             var request = session.Request.BodyAsString;\r
140             var response = session.Response.BodyAsString;\r
141             if (response == null || !response.StartsWith("svdata="))\r
142             {\r
143                 WriteDebugLog(url, request, response);\r
144                 return;\r
145             }\r
146             if (_config.KancolleDb.On)\r
147                 _kancolleDb.Send(url, request, response);\r
148             response = UnescapeString(response.Remove(0, "svdata=".Length));\r
149             WriteDebugLog(url, request, response);\r
150             ProcessRequestMain(url, request, response);\r
151         }\r
152 \r
153         private void ProcessRequestMain(string url, string request, string response)\r
154         {\r
155             try\r
156             {\r
157                 UpdateInfo(_sniffer.Sniff(url, request, JsonParser.Parse(response)));\r
158                 _errorLog.CheckBattleApi(url, request, response);\r
159             }\r
160 \r
161             catch (RuntimeBinderException e)\r
162             {\r
163                 if (_errorDialog.ShowDialog(this,\r
164                         "艦これに仕様変更があったか、受信内容が壊れています。",\r
165                         _errorLog.GenerateErrorLog(url, request, response, e.ToString())) == DialogResult.Abort)\r
166                     Application.Exit();\r
167             }\r
168             catch (LogIOException e)\r
169             {\r
170                 // ReSharper disable once PossibleNullReferenceException\r
171                 if (_errorDialog.ShowDialog(this, e.Message, e.InnerException.ToString()) == DialogResult.Abort)\r
172                     Application.Exit();\r
173             }\r
174             catch (BattleResultError)\r
175             {\r
176                 if (_errorDialog.ShowDialog(this, "戦闘結果の計算に誤りがあります。",\r
177                         _errorLog.GenerateBattleErrorLog()) == DialogResult.Abort)\r
178                     Application.Exit();\r
179             }\r
180             catch (Exception e)\r
181             {\r
182                 if (_errorDialog.ShowDialog(this, "エラーが発生しました。",\r
183                         _errorLog.GenerateErrorLog(url, request, response, e.ToString())) == DialogResult.Abort)\r
184                     Application.Exit();\r
185             }\r
186         }\r
187 \r
188         private void WriteDebugLog(string url, string request, string response)\r
189         {\r
190             if (_debugLogFile != null)\r
191             {\r
192                 File.AppendAllText(_debugLogFile,\r
193                     $"date: {DateTime.Now:g}\nurl: {url}\nrequest: {request}\nresponse: {response ?? "(null)"}\n");\r
194             }\r
195         }\r
196 \r
197         private string UnescapeString(string s)\r
198         {\r
199             try\r
200             {\r
201                 var rx = new Regex(@"\\[uU]([0-9A-Fa-f]{4})");\r
202                 return rx.Replace(s,\r
203                     match => ((char)int.Parse(match.Value.Substring(2), NumberStyles.HexNumber)).ToString());\r
204             }\r
205             catch (ArgumentException)\r
206             {\r
207                 return s;\r
208             }\r
209         }\r
210 \r
211         private void UpdateInfo(Sniffer.Update update)\r
212         {\r
213             if (update == Sniffer.Update.Start)\r
214             {\r
215                 labelLogin.Visible = false;\r
216                 linkLabelGuide.Visible = false;\r
217                 _started = true;\r
218                 return;\r
219             }\r
220             if (!_started)\r
221                 return;\r
222             if ((update & Sniffer.Update.Item) != 0)\r
223                 UpdateItemInfo();\r
224             if ((update & Sniffer.Update.Timer) != 0)\r
225                 UpdateTimers();\r
226             if ((update & Sniffer.Update.NDock) != 0)\r
227                 UpdateNDocLabels();\r
228             if ((update & Sniffer.Update.Mission) != 0)\r
229                 UpdateMissionLabels();\r
230             if ((update & Sniffer.Update.QuestList) != 0)\r
231                 UpdateQuestList();\r
232             if ((update & Sniffer.Update.Ship) != 0)\r
233                 UpdateShipInfo();\r
234             if ((update & Sniffer.Update.Battle) != 0)\r
235                 UpdateBattleInfo();\r
236         }\r
237 \r
238         private void MainForm_Load(object sender, EventArgs e)\r
239         {\r
240             RestoreLocation();\r
241             if (_config.HideOnMinimized && WindowState == FormWindowState.Minimized)\r
242                 ShowInTaskbar = false;\r
243             if (_config.ShowHpInPercent)\r
244                 _shipLabels.ToggleHpPercent();\r
245             if (_config.ShipList.Visible)\r
246                 _listForm.Show();\r
247             ApplyConfig();\r
248             ApplyDebugLogSetting();\r
249             ApplyLogSetting();\r
250             ApplyProxySetting();\r
251             if (_config.KancolleDb.On)\r
252                 _kancolleDb.Start(_config.KancolleDb.Token);\r
253             CheckVersionUp((current, latest) =>\r
254             {\r
255                 if (double.Parse(latest) <= double.Parse(current))\r
256                     return;\r
257                 linkLabelGuide.Text = $"バージョン{latest}があります。";\r
258                 linkLabelGuide.LinkArea = new LinkArea(0, linkLabelGuide.Text.Length);\r
259                 linkLabelGuide.Click += (obj, ev) =>\r
260                 {\r
261                     Process.Start("https://ja.osdn.net/rel/kancollesniffer/" + latest);\r
262                 };\r
263             });\r
264         }\r
265 \r
266         public async void CheckVersionUp(Action<string, string> action)\r
267         {\r
268             var current = string.Join(".", Application.ProductVersion.Split('.').Take(2));\r
269             try\r
270             {\r
271                 var latest = (await new WebClient().DownloadStringTaskAsync("http://kancollesniffer.osdn.jp/version"))\r
272                     .TrimEnd();\r
273                 try\r
274                 {\r
275                     action(current, latest);\r
276                 }\r
277                 catch (InvalidOperationException)\r
278                 {\r
279                 }\r
280             }\r
281             catch (WebException)\r
282             {\r
283             }\r
284         }\r
285 \r
286         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)\r
287         {\r
288             if (!_config.ExitSilently)\r
289             {\r
290                 using (var dialog = new ConfirmDialog())\r
291                 {\r
292                     if (dialog.ShowDialog(this) != DialogResult.Yes)\r
293                     {\r
294                         e.Cancel = true;\r
295                         return;\r
296                     }\r
297                 }\r
298             }\r
299             e.Cancel = false;\r
300             _sniffer.FlashLog();\r
301             _config.Location = (WindowState == FormWindowState.Normal ? Bounds : RestoreBounds).Location;\r
302             _config.ShowHpInPercent = _shipLabels.ShowHpInPercent;\r
303             _config.ShipList.Visible = _listForm.Visible && _listForm.WindowState == FormWindowState.Normal;\r
304             _config.Save();\r
305             _proxyManager.Shutdown();\r
306             _kancolleDb.Stop();\r
307         }\r
308 \r
309         private void MainForm_Resize(object sender, EventArgs e)\r
310         {\r
311             ShowInTaskbar = !(_config.HideOnMinimized && WindowState == FormWindowState.Minimized);\r
312         }\r
313 \r
314         private void notifyIconMain_MouseDoubleClick(object sender, MouseEventArgs e)\r
315         {\r
316             NotifyIconOpenToolStripMenuItem_Click(sender, e);\r
317         }\r
318 \r
319         private void NotifyIconOpenToolStripMenuItem_Click(object sender, EventArgs e)\r
320         {\r
321             ShowInTaskbar = true;\r
322             WindowState = FormWindowState.Normal;\r
323             TopMost = _config.TopMost; // 最前面に表示されなくなることがあるのを回避する\r
324             Activate();\r
325         }\r
326 \r
327         private void ExitToolStripMenuItem_Click(object sender, EventArgs e)\r
328         {\r
329             Close();\r
330         }\r
331 \r
332         private void ConfigToolStripMenuItem_Click(object sender, EventArgs e)\r
333         {\r
334             if (_configDialog.ShowDialog(this) == DialogResult.OK)\r
335             {\r
336                 ApplyConfig();\r
337                 StopRepeatingTimer(_configDialog.RepeatSettingsChanged);\r
338             }\r
339         }\r
340 \r
341         private void StopRepeatingTimer(IEnumerable<string> names)\r
342         {\r
343             foreach (var name in names)\r
344                 _notificationManager.StopRepeat(name);\r
345         }\r
346 \r
347         private void PerformZoom()\r
348         {\r
349             if (_config.Zoom == 100)\r
350                 return;\r
351             var prev = CurrentAutoScaleDimensions;\r
352             foreach (var control in new Control[]\r
353             {\r
354                 this, _listForm, labelLogin, linkLabelGuide,\r
355                 _configDialog, contextMenuStripMain, _errorDialog\r
356             })\r
357             {\r
358                 control.Font = new Font(control.Font.FontFamily, control.Font.Size * _config.Zoom / 100);\r
359             }\r
360             ShipLabel.LatinFont = new Font("Tahoma", 8f * _config.Zoom / 100);\r
361             var cur = CurrentAutoScaleDimensions;\r
362             ShipLabel.ScaleFactor = new SizeF(ShipLabel.ScaleFactor.Width * cur.Width / prev.Width,\r
363                 ShipLabel.ScaleFactor.Height * cur.Height / prev.Height);\r
364         }\r
365 \r
366         private void RestoreLocation()\r
367         {\r
368             if (_config.Location.X == int.MinValue)\r
369                 return;\r
370             if (IsTitleBarOnAnyScreen(_config.Location))\r
371                 Location = _config.Location;\r
372         }\r
373 \r
374         private void ApplyConfig()\r
375         {\r
376             _listForm.TopMost = TopMost = _config.TopMost;\r
377             _sniffer.Item.MarginShips = _config.MarginShips;\r
378             UpdateNumOfShips();\r
379             _sniffer.Item.MarginEquips = _config.MarginEquips;\r
380             UpdateNumOfEquips();\r
381             _sniffer.Achievement.ResetHours = _config.ResetHours;\r
382             labelAkashiRepair.Visible = labelAkashiRepairTimer.Visible =\r
383                 labelPresetAkashiTimer.Visible = _config.UsePresetAkashi;\r
384             if (_config.KancolleDb.On)\r
385                 _kancolleDb.Start(_config.KancolleDb.Token);\r
386         }\r
387 \r
388         public void ApplyDebugLogSetting()\r
389         {\r
390             _debugLogFile = _config.DebugLogging ? _config.DebugLogFile : null;\r
391         }\r
392 \r
393         public bool ApplyProxySetting()\r
394         {\r
395             return _proxyManager.ApplyConfig();\r
396         }\r
397 \r
398         public void ApplyLogSetting()\r
399         {\r
400             LogServer.OutputDir = _config.Log.OutputDir;\r
401             LogServer.MaterialHistory = _sniffer.Material.MaterialHistory;\r
402             _sniffer.EnableLog(_config.Log.On ? LogType.All : LogType.None);\r
403             _sniffer.MaterialLogInterval = _config.Log.MaterialLogInterval;\r
404             _sniffer.LogOutputDir = _config.Log.OutputDir;\r
405         }\r
406 \r
407         public static bool IsTitleBarOnAnyScreen(Point location)\r
408         {\r
409             var rect = new Rectangle(\r
410                 new Point(location.X + SystemInformation.IconSize.Width + SystemInformation.HorizontalFocusThickness,\r
411                     location.Y + SystemInformation.CaptionHeight), new Size(60, 1));\r
412             return Screen.AllScreens.Any(screen => screen.WorkingArea.Contains(rect));\r
413         }\r
414 \r
415         private void timerMain_Tick(object sender, EventArgs e)\r
416         {\r
417             if (_timerEnabled)\r
418             {\r
419                 try\r
420                 {\r
421                     UpdateTimers();\r
422                 }\r
423                 catch (Exception ex)\r
424                 {\r
425                     if (_errorDialog.ShowDialog(this, "エラーが発生しました。", ex.ToString()) == DialogResult.Abort)\r
426                         Application.Exit();\r
427                 }\r
428             }\r
429             if (_playLog == null || _configDialog.Visible)\r
430             {\r
431                 labelPlayLog.Visible = false;\r
432                 return;\r
433             }\r
434             PlayLog();\r
435         }\r
436 \r
437         public void SetPlayLog(string file)\r
438         {\r
439             _playLog = File.ReadLines(file).GetEnumerator();\r
440         }\r
441 \r
442         private void PlayLog()\r
443         {\r
444             var lines = new List<string>();\r
445             foreach (var s in new[] {"url: ", "request: ", "response: "})\r
446             {\r
447                 do\r
448                 {\r
449                     if (!_playLog.MoveNext() || _playLog.Current == null)\r
450                     {\r
451                         labelPlayLog.Visible = false;\r
452                         return;\r
453                     }\r
454                 } while (!_playLog.Current.StartsWith(s));\r
455                 lines.Add(_playLog.Current.Substring(s.Length));\r
456             }\r
457             labelPlayLog.Visible = !labelPlayLog.Visible;\r
458             ProcessRequestMain(lines[0], lines[1], lines[2]);\r
459         }\r
460 \r
461         private void ShowShipOnShipList(object sender, EventArgs ev)\r
462         {\r
463             if (!_listForm.Visible)\r
464                 return;\r
465             var idx = (int)((Control)sender).Tag;\r
466             var statuses = _sniffer.GetShipStatuses(_currentFleet);\r
467             if (statuses.Length <= idx)\r
468                 return;\r
469             _listForm.ShowShip(statuses[idx].Id);\r
470         }\r
471 \r
472         private void UpdateItemInfo()\r
473         {\r
474             UpdateNumOfShips();\r
475             UpdateNumOfEquips();\r
476             _notificationManager.Flash();\r
477             labelNumOfBuckets.Text = _sniffer.Material.MaterialHistory[(int)Material.Bucket].Now.ToString("D");\r
478             UpdateBucketHistory();\r
479             var ac = _sniffer.Achievement.Value;\r
480             if (ac >= 10000)\r
481                 ac = 9999;\r
482             labelAchievement.Text = ac >= 1000 ? ((int)ac).ToString("D") : ac.ToString("F1");\r
483             toolTipAchievement.SetToolTip(labelAchievement,\r
484                 "今月 " + _sniffer.Achievement.ValueOfMonth.ToString("F1") + "\n" +\r
485                 "EO " + _sniffer.ExMap.Achievement);\r
486             UpdateMaterialHistry();\r
487             if (_listForm.Visible)\r
488                 _listForm.UpdateList();\r
489         }\r
490 \r
491         private void UpdateNumOfShips()\r
492         {\r
493             var item = _sniffer.Item;\r
494             labelNumOfShips.Text = $"{item.NowShips:D}/{item.MaxShips:D}";\r
495             labelNumOfShips.ForeColor = item.TooManyShips ? CUDColor.Red : Color.Black;\r
496             if (item.AlarmShips)\r
497             {\r
498                 var message = $"残り{_sniffer.Item.MaxShips - _sniffer.Item.NowShips:D}隻";\r
499                 _notificationManager.Enqueue("艦娘数超過", message);\r
500                 item.AlarmShips = false;\r
501             }\r
502         }\r
503 \r
504         private void UpdateNumOfEquips()\r
505         {\r
506             var item = _sniffer.Item;\r
507             labelNumOfEquips.Text = $"{item.NowEquips:D}/{item.MaxEquips:D}";\r
508             labelNumOfEquips.ForeColor = item.TooManyEquips ? CUDColor.Red : Color.Black;\r
509             if (item.AlarmEquips)\r
510             {\r
511                 var message = $"残り{_sniffer.Item.MaxEquips - _sniffer.Item.NowEquips:D}個";\r
512                 _notificationManager.Enqueue("装備数超過", message);\r
513                 item.AlarmEquips = false;\r
514             }\r
515         }\r
516 \r
517         private void UpdateBucketHistory()\r
518         {\r
519             var count = _sniffer.Material.MaterialHistory[(int)Material.Bucket];\r
520             var day = CutOverflow(count.Now - count.BegOfDay, 999);\r
521             var week = CutOverflow(count.Now - count.BegOfWeek, 999);\r
522             labelBucketHistory.Text = $"{day:+#;-#;±0} 今日\n{week:+#;-#;±0} 今週";\r
523         }\r
524 \r
525         private void UpdateMaterialHistry()\r
526         {\r
527             var labels = new[] {labelFuelHistory, labelBulletHistory, labelSteelHistory, labelBouxiteHistory};\r
528             var text = new[] {"燃料", "弾薬", "鋼材", "ボーキ"};\r
529             for (var i = 0; i < labels.Length; i++)\r
530             {\r
531                 var count = _sniffer.Material.MaterialHistory[i];\r
532                 var port = CutOverflow(count.Now - _sniffer.Material.PrevPort[i], 99999);\r
533                 var day = CutOverflow(count.Now - count.BegOfDay, 99999);\r
534                 var week = CutOverflow(count.Now - count.BegOfWeek, 99999);\r
535                 labels[i].Text = $"{text[i]}\n{port:+#;-#;±0}\n{day:+#;-#;±0}\n{week:+#;-#;±0}";\r
536             }\r
537         }\r
538 \r
539         private int CutOverflow(int value, int limit)\r
540         {\r
541             if (value > limit)\r
542                 return limit;\r
543             if (value < -limit)\r
544                 return -limit;\r
545             return value;\r
546         }\r
547 \r
548         private void UpdateShipInfo()\r
549         {\r
550             UpdatePanelShipInfo();\r
551             NotifyDamagedShip();\r
552             UpdateChargeInfo();\r
553             UpdateRepairList();\r
554             if (_listForm.Visible)\r
555                 _listForm.UpdateList();\r
556         }\r
557 \r
558         private void UpdatePanelShipInfo()\r
559         {\r
560             var statuses = _sniffer.GetShipStatuses(_currentFleet);\r
561             panel7Ships.Visible = statuses.Length == 7;\r
562             _shipLabels.SetShipLabels(statuses);\r
563             if (_sniffer.CombinedFleetType == 0)\r
564                 _combinedFleet = false;\r
565             labelFleet1.Text = _combinedFleet ? "連合" : "第一";\r
566             panelCombinedFleet.Visible = _combinedFleet;\r
567             if (_combinedFleet)\r
568                 _shipLabels.SetCombinedShipLabels(_sniffer.GetShipStatuses(0), _sniffer.GetShipStatuses(1));\r
569             UpdateAkashiTimer();\r
570             UpdateFighterPower(_combinedFleet);\r
571             UpdateLoS();\r
572             UpdateCondTimers();\r
573         }\r
574 \r
575         private void NotifyDamagedShip()\r
576         {\r
577             if (!_sniffer.BadlyDamagedShips.Any())\r
578                 return;\r
579             _notificationManager.Enqueue("大破警告", string.Join(" ", _sniffer.BadlyDamagedShips));\r
580             _notificationManager.Flash();\r
581         }\r
582 \r
583         public void UpdateFighterPower(bool combined)\r
584         {\r
585             var fp = combined\r
586                 ? _sniffer.GetFighterPower(0).Zip(_sniffer.GetFighterPower(1), (a, b) => a + b).ToArray()\r
587                 : _sniffer.GetFighterPower(_currentFleet);\r
588             labelFighterPower.Text = fp[0].ToString("D");\r
589             var cr = combined\r
590                 ? _sniffer.GetContactTriggerRate(0) + _sniffer.GetContactTriggerRate(1)\r
591                 : _sniffer.GetContactTriggerRate(_currentFleet);\r
592             var text = "制空: " + (fp[0] == fp[1] ? $"{fp[0]}" : $"{fp[0]}~{fp[1]}") +\r
593                        $" 触接: {cr * 100:f1}";\r
594             toolTipFighterPower.SetToolTip(labelFighterPower, text);\r
595             toolTipFighterPower.SetToolTip(labelFighterPowerCaption, text);\r
596         }\r
597 \r
598         private void UpdateLoS()\r
599         {\r
600             labelLoS.Text = RoundDown(_sniffer.GetFleetLineOfSights(_currentFleet, 1)).ToString("F1");\r
601             var text = $"係数3: {RoundDown(_sniffer.GetFleetLineOfSights(_currentFleet, 3)):F1}\r\n" +\r
602                        $"係数4: {RoundDown(_sniffer.GetFleetLineOfSights(_currentFleet, 4)):F1}";\r
603             toolTipLoS.SetToolTip(labelLoS, text);\r
604             toolTipLoS.SetToolTip(labelLoSCaption, text);\r
605         }\r
606 \r
607         private double RoundDown(double number)\r
608         {\r
609             return Floor(number * 10) / 10.0;\r
610         }\r
611 \r
612         private void UpdateBattleInfo()\r
613         {\r
614             ResetBattleInfo();\r
615             _listForm.UpdateBattleResult();\r
616             if (_sniffer.Battle.BattleState == BattleState.None)\r
617                 return;\r
618             panelBattleInfo.BringToFront();\r
619             var battle = _sniffer.Battle;\r
620             labelFormation.Text = battle.Formation;\r
621             UpdateBattleFighterPower();\r
622             if ((_config.Spoilers & Spoiler.ResultRank) != 0)\r
623                 ShowResultRank();\r
624             if (_sniffer.Battle.BattleState == BattleState.Day)\r
625                 _listForm.UpdateAirBattleResult();\r
626         }\r
627 \r
628         private void ResetBattleInfo()\r
629         {\r
630             labelFormation.Text = "";\r
631             labelEnemyFighterPower.Text = "";\r
632             labelFighterPower.ForeColor = DefaultForeColor;\r
633             labelResultRank.Text = "判定";\r
634             panelBattleInfo.Visible = _sniffer.Battle.BattleState != BattleState.None;\r
635         }\r
636 \r
637         private void UpdateBattleFighterPower()\r
638         {\r
639             var battle = _sniffer.Battle;\r
640             var power = battle.EnemyFighterPower;\r
641             labelEnemyFighterPower.Text = power.AirCombat + power.UnknownMark;\r
642             if (power.AirCombat != power.Interception)\r
643             {\r
644                 var text = "防空: " + power.Interception + power.UnknownMark;\r
645                 toolTipFighterPower.SetToolTip(labelEnemyFighterPower, text);\r
646                 toolTipFighterPower.SetToolTip(labelEnemyFighterPowerCaption, text);\r
647             }\r
648             UpdateFighterPower(_sniffer.CombinedFleetType > 0 && battle.EnemyIsCombined);\r
649             labelFighterPower.ForeColor = new[]\r
650                 {DefaultForeColor, DefaultForeColor, CUDColor.Blue, CUDColor.Green, CUDColor.Orange, CUDColor.Red}[\r
651                 battle.AirControlLevel + 1];\r
652         }\r
653 \r
654         private void ShowResultRank()\r
655         {\r
656             var result = new[] {"完全S", "勝利S", "勝利A", "勝利B", "敗北C", "敗北D", "敗北E"};\r
657             labelResultRank.Text = result[(int)_sniffer.Battle.ResultRank];\r
658         }\r
659 \r
660         private void labelResultRank_Click(object sender, EventArgs e)\r
661         {\r
662             ShowResultRank();\r
663         }\r
664 \r
665         private void UpdateChargeInfo()\r
666         {\r
667             var fuelSq = new[] {labelFuelSq1, labelFuelSq2, labelFuelSq3, labelFuelSq4};\r
668             var bullSq = new[] {labelBullSq1, labelBullSq2, labelBullSq3, labelBullSq4};\r
669 \r
670             for (var i = 0; i < fuelSq.Length; i++)\r
671             {\r
672                 var stat = _sniffer.ChargeStatuses[i];\r
673                 fuelSq[i].ImageIndex = stat.Fuel;\r
674                 bullSq[i].ImageIndex = stat.Bull;\r
675             }\r
676         }\r
677 \r
678         private void UpdateNDocLabels()\r
679         {\r
680             _shipLabels.SetNDockLabels(_sniffer.NDock);\r
681         }\r
682 \r
683 \r
684         private void labelNDock_Click(object sender, EventArgs e)\r
685         {\r
686             _ndockFinishTimeMode = !_ndockFinishTimeMode;\r
687             UpdateTimers();\r
688         }\r
689 \r
690         private void UpdateMissionLabels()\r
691         {\r
692             foreach (var entry in\r
693                 new[] {labelMissionName1, labelMissionName2, labelMissionName3}.Zip(_sniffer.Missions,\r
694                     (label, mission) => new {label, mission.Name}))\r
695                 entry.label.Text = entry.Name;\r
696         }\r
697 \r
698         private void labelMission_Click(object sender, EventArgs e)\r
699         {\r
700             _missionFinishTimeMode = !_missionFinishTimeMode;\r
701             UpdateTimers();\r
702         }\r
703 \r
704         private DateTime _prev, _now;\r
705 \r
706         private void UpdateTimers()\r
707         {\r
708             _prev = _now;\r
709             _now = DateTime.Now;\r
710             var mission = new[] {labelMission1, labelMission2, labelMission3};\r
711             for (var i = 0; i < mission.Length; i++)\r
712             {\r
713                 var entry = _sniffer.Missions[i];\r
714                 SetTimerColor(mission[i], entry.Timer, _now);\r
715                 mission[i].Text = entry.Timer.ToString(_now, _missionFinishTimeMode);\r
716                 CheckAlarm("遠征終了", entry.Timer, i + 1, entry.Name);\r
717             }\r
718             for (var i = 0; i < _sniffer.NDock.Length; i++)\r
719             {\r
720                 var entry = _sniffer.NDock[i];\r
721                 _shipLabels.SetNDockTimer(i, entry.Timer, _now, _ndockFinishTimeMode);\r
722                 CheckAlarm("入渠終了", entry.Timer, i, entry.Name);\r
723             }\r
724             var kdock = new[] {labelConstruct1, labelConstruct2, labelConstruct3, labelConstruct4};\r
725             for (var i = 0; i < kdock.Length; i++)\r
726             {\r
727                 var timer = _sniffer.KDock[i];\r
728                 SetTimerColor(kdock[i], timer, _now);\r
729                 kdock[i].Text = timer.ToString(_now);\r
730                 CheckAlarm("建造完了", timer, 0, $"第{i + 1:D}ドック");\r
731             }\r
732             UpdateCondTimers();\r
733             UpdateAkashiTimer();\r
734             _notificationManager.Flash();\r
735             _timerEnabled = true;\r
736         }\r
737 \r
738         private void CheckAlarm(string key, AlarmTimer timer, int fleet, string subject)\r
739         {\r
740             if (timer.CheckAlarm(_prev, _now))\r
741             {\r
742                 SetNotification(key, fleet, subject);\r
743                 return;\r
744             }\r
745             var pre = TimeSpan.FromSeconds(_config.Notifications[key].PreliminaryPeriod);\r
746             if (pre == TimeSpan.Zero)\r
747                 return;\r
748             if (timer.CheckAlarm(_prev + pre, _now + pre))\r
749                 SetPreNotification(key, fleet, subject);\r
750         }\r
751 \r
752         private void SetTimerColor(Label label, AlarmTimer timer, DateTime now)\r
753         {\r
754             label.ForeColor = timer.IsFinished(now) ? CUDColor.Red : Color.Black;\r
755         }\r
756 \r
757         private void UpdateCondTimers()\r
758         {\r
759             DateTime timer;\r
760             if (_combinedFleet)\r
761             {\r
762                 var timer1 = _sniffer.GetConditionTimer(0);\r
763                 var timer2 = _sniffer.GetConditionTimer(1);\r
764                 timer = timer2 > timer1 ? timer2 : timer1;\r
765             }\r
766             else\r
767             {\r
768                 timer = _sniffer.GetConditionTimer(_currentFleet);\r
769             }\r
770             if (timer == DateTime.MinValue)\r
771             {\r
772                 labelCondTimerTitle.Text = "";\r
773                 labelCondTimer.Text = "";\r
774                 return;\r
775             }\r
776             var span = TimeSpan.FromSeconds(Ceiling((timer - _now).TotalSeconds));\r
777             if (span >= TimeSpan.FromMinutes(9))\r
778             {\r
779                 labelCondTimerTitle.Text = "cond40まで";\r
780                 labelCondTimer.Text = (span - TimeSpan.FromMinutes(9)).ToString(@"mm\:ss");\r
781                 labelCondTimer.ForeColor = DefaultForeColor;\r
782             }\r
783             else\r
784             {\r
785                 labelCondTimerTitle.Text = "cond49まで";\r
786                 labelCondTimer.Text = (span >= TimeSpan.Zero ? span : TimeSpan.Zero).ToString(@"mm\:ss");\r
787                 labelCondTimer.ForeColor = span <= TimeSpan.Zero ? CUDColor.Red : DefaultForeColor;\r
788             }\r
789             var notice = _sniffer.GetConditionNotice(_prev, _now);\r
790             var pre = TimeSpan.FromSeconds(_config.Notifications["疲労回復"].PreliminaryPeriod);\r
791             var preNotice = pre == TimeSpan.Zero\r
792                 ? new int[ShipInfo.FleetCount]\r
793                 : _sniffer.GetConditionNotice(_prev + pre, _now + pre);\r
794             for (var i = 0; i < ShipInfo.FleetCount; i++)\r
795             {\r
796                 if (_config.NotifyConditions.Contains(notice[i]))\r
797                 {\r
798                     SetNotification("疲労回復" + notice[i], i, "cond" + notice[i]);\r
799                 }\r
800                 else if (_config.NotifyConditions.Contains(preNotice[i]))\r
801                 {\r
802                     SetPreNotification("疲労回復" + preNotice[i], i, "cond" + notice[i]);\r
803                 }\r
804             }\r
805         }\r
806 \r
807         private void UpdateAkashiTimer()\r
808         {\r
809             if (_config.UsePresetAkashi)\r
810                 UpdatePresetAkashiTimer();\r
811             var statuses = _sniffer.GetShipStatuses(_currentFleet);\r
812             _shipLabels.SetAkashiTimer(statuses,\r
813                 _sniffer.AkashiTimer.GetTimers(_currentFleet));\r
814             NotifyAkashiTimer();\r
815         }\r
816 \r
817         private void UpdatePresetAkashiTimer()\r
818         {\r
819             var akashi = _sniffer.AkashiTimer;\r
820             var span = akashi.PresetDeckTimer;\r
821             var color = span == TimeSpan.Zero && akashi.CheckPresetRepairing() ? CUDColor.Red : DefaultForeColor;\r
822             var text = span == TimeSpan.MinValue ? "" : span.ToString(@"mm\:ss");\r
823             labelAkashiRepairTimer.ForeColor = color;\r
824             labelAkashiRepairTimer.Text = text;\r
825             if (akashi.CheckPresetRepairing() && !akashi.CheckRepairing(_currentFleet))\r
826             {\r
827                 labelPresetAkashiTimer.ForeColor = color;\r
828                 labelPresetAkashiTimer.Text = text;\r
829             }\r
830             else\r
831             {\r
832                 labelPresetAkashiTimer.ForeColor = DefaultForeColor;\r
833                 labelPresetAkashiTimer.Text = "";\r
834             }\r
835         }\r
836 \r
837         private void NotifyAkashiTimer()\r
838         {\r
839             var akashi = _sniffer.AkashiTimer;\r
840             var msgs = akashi.GetNotice(_prev, _now);\r
841             if (msgs.Length == 0)\r
842             {\r
843                 _notificationManager.StopRepeat("泊地修理");\r
844                 return;\r
845             }\r
846             if (!akashi.CheckRepairing() && !(akashi.CheckPresetRepairing() && _config.UsePresetAkashi))\r
847             {\r
848                 _notificationManager.StopRepeat("泊地修理");\r
849                 return;\r
850             }\r
851             var skipPreliminary = false;\r
852             if (msgs[0].Proceeded == "20分経過しました。")\r
853             {\r
854                 SetNotification("泊地修理20分経過", msgs[0].Proceeded);\r
855                 msgs[0].Proceeded = "";\r
856                 skipPreliminary = true;\r
857                 // 修理完了がいるかもしれないので続ける\r
858             }\r
859             for (var i = 0; i < ShipInfo.FleetCount; i++)\r
860             {\r
861                 if (msgs[i].Proceeded != "")\r
862                     SetNotification("泊地修理進行", i, msgs[i].Proceeded);\r
863                 if (msgs[i].Completed != "")\r
864                     SetNotification("泊地修理完了", i, msgs[i].Completed);\r
865             }\r
866             var pre = TimeSpan.FromSeconds(_config.Notifications["泊地修理20分経過"].PreliminaryPeriod);\r
867             if (skipPreliminary || pre == TimeSpan.Zero)\r
868                 return;\r
869             if ((msgs = akashi.GetNotice(_prev + pre, _now + pre))[0].Proceeded == "20分経過しました。")\r
870                 SetPreNotification("泊地修理20分経過", 0, msgs[0].Proceeded);\r
871         }\r
872 \r
873         private void SetNotification(string key, string subject)\r
874         {\r
875             SetNotification(key, 0, subject);\r
876         }\r
877 \r
878         private void SetNotification(string key, int fleet, string subject)\r
879         {\r
880             var spec = _config.Notifications[_notificationManager.KeyToName(key)];\r
881             _notificationManager.Enqueue(key, fleet, subject,\r
882                 (spec.Flags & _config.NotificationFlags & NotificationType.Repeat) == 0 ? 0 : spec.RepeatInterval);\r
883         }\r
884 \r
885         private void SetPreNotification(string key, int fleet, string subject)\r
886         {\r
887             _notificationManager.Enqueue(key, fleet, subject, 0, true);\r
888         }\r
889 \r
890         private void UpdateRepairList()\r
891         {\r
892             panelRepairList.SetRepairList(_sniffer.RepairList);\r
893         }\r
894 \r
895         private void UpdateQuestList()\r
896         {\r
897             var category = new[]\r
898             {\r
899                 labelQuestColor1, labelQuestColor2, labelQuestColor3, labelQuestColor4, labelQuestColor5,\r
900                 labelQuestColor6\r
901             };\r
902             var name = new[] {labelQuest1, labelQuest2, labelQuest3, labelQuest4, labelQuest5, labelQuest6};\r
903             var count = new[]\r
904             {\r
905                 labelQuestCount1, labelQuestCount2, labelQuestCount3, labelQuestCount4, labelQuestCount5,\r
906                 labelQuestCount6\r
907             };\r
908             var progress = new[]\r
909                 {labelProgress1, labelProgress2, labelProgress3, labelProgress4, labelProgress5, labelProgress6};\r
910             var quests = _sniffer.Quests;\r
911             for (var i = 0; i < name.Length; i++)\r
912             {\r
913                 if (i < quests.Length)\r
914                 {\r
915                     category[i].BackColor = quests[i].Color;\r
916                     name[i].Text = quests[i].Name;\r
917                     progress[i].Text = $"{quests[i].Progress:D}%";\r
918                     var c = quests[i].Count;\r
919                     if (c.Id == 0)\r
920                     {\r
921                         count[i].Text = "";\r
922                         count[i].ForeColor = Color.Black;\r
923                         continue;\r
924                     }\r
925                     count[i].Text = c.NowArray != null ? $"{string.Join("|", c.NowArray)}" : $"{c.Now}/{c.Max}";\r
926                     count[i].ForeColor =\r
927                         (c.NowArray?.Zip(c.Spec.MaxArray, (n, m) => n >= m).All(x => x) ?? c.Now >= c.Spec.Max)\r
928                             ? CUDColor.Green\r
929                             : Color.Black;\r
930                 }\r
931                 else\r
932                 {\r
933                     category[i].BackColor = DefaultBackColor;\r
934                     name[i].Text = count[i].Text = progress[i].Text = "";\r
935                 }\r
936             }\r
937         }\r
938 \r
939         private void Alarm(string balloonTitle, string balloonMessage, string name)\r
940         {\r
941             var flags = _config.Notifications[name].Flags;\r
942             var effective = _config.NotificationFlags & _config.Notifications[name].Flags;\r
943             if ((effective & NotificationType.FlashWindow) != 0)\r
944                 Win32API.FlashWindow(Handle);\r
945             if ((effective & NotificationType.ShowBaloonTip) != 0)\r
946                 notifyIconMain.ShowBalloonTip(20000, balloonTitle, balloonMessage, ToolTipIcon.Info);\r
947             if ((effective & NotificationType.PlaySound) != 0)\r
948                 PlaySound(_config.Sounds[name], _config.Sounds.Volume);\r
949             if (_config.Pushbullet.On && (flags & NotificationType.Push) != 0)\r
950             {\r
951                 Task.Run(() =>\r
952                 {\r
953                     PushNotification.PushToPushbullet(_config.Pushbullet.Token, balloonTitle, balloonMessage);\r
954                 });\r
955             }\r
956             if (_config.Pushover.On && (flags & NotificationType.Push) != 0)\r
957             {\r
958                 Task.Run(() =>\r
959                 {\r
960                     PushNotification.PushToPushover(_config.Pushover.ApiKey, _config.Pushover.UserKey,\r
961                         balloonTitle, balloonMessage);\r
962                 });\r
963             }\r
964         }\r
965 \r
966         [DllImport("winmm.dll")]\r
967         private static extern int mciSendString(String command,\r
968             StringBuilder buffer, int bufferSize, IntPtr hwndCallback);\r
969 \r
970 // ReSharper disable InconsistentNaming\r
971         private const int MM_MCINOTIFY = 0x3B9;\r
972 \r
973         private const int MCI_NOTIFY_SUCCESSFUL = 1;\r
974 // ReSharper restore InconsistentNaming\r
975 \r
976         public void PlaySound(string file, int volume)\r
977         {\r
978             if (!File.Exists(file))\r
979                 return;\r
980             mciSendString("close sound", null, 0, IntPtr.Zero);\r
981             if (mciSendString("open \"" + file + "\" type mpegvideo alias sound", null, 0, IntPtr.Zero) != 0)\r
982                 return;\r
983             mciSendString("setaudio sound volume to " + volume * 10, null, 0, IntPtr.Zero);\r
984             mciSendString("play sound notify", null, 0, Handle);\r
985         }\r
986 \r
987         protected override void WndProc(ref Message m)\r
988         {\r
989             if (m.Msg == MM_MCINOTIFY && (int)m.WParam == MCI_NOTIFY_SUCCESSFUL)\r
990                 mciSendString("close sound", null, 0, IntPtr.Zero);\r
991             base.WndProc(ref m);\r
992         }\r
993 \r
994         private void SetupFleetClick()\r
995         {\r
996             var labels = new[]\r
997             {\r
998                 new[] {labelFleet1, labelFleet2, labelFleet3, labelFleet4},\r
999                 new[] {labelFuelSq1, labelFuelSq2, labelFuelSq3, labelFuelSq4},\r
1000                 new[] {labelBullSq1, labelBullSq2, labelBullSq3, labelBullSq4}\r
1001             };\r
1002             foreach (var a in labels)\r
1003             {\r
1004                 for (var fleet = 0; fleet < labels[0].Length; fleet++)\r
1005                 {\r
1006                     a[fleet].Tag = fleet;\r
1007                     a[fleet].Click += labelFleet_Click;\r
1008                 }\r
1009             }\r
1010         }\r
1011 \r
1012         private void labelFleet_Click(object sender, EventArgs e)\r
1013         {\r
1014             if (!_started)\r
1015                 return;\r
1016             var fleet = (int)((Label)sender).Tag;\r
1017             if (_currentFleet == fleet)\r
1018             {\r
1019                 if (fleet > 0)\r
1020                     return;\r
1021                 _combinedFleet = _sniffer.CombinedFleetType > 0 && !_combinedFleet;\r
1022                 UpdatePanelShipInfo();\r
1023                 return;\r
1024             }\r
1025             _combinedFleet = false;\r
1026             _currentFleet = fleet;\r
1027             foreach (var label in _labelCheckFleets)\r
1028                 label.Visible = false;\r
1029             _labelCheckFleets[fleet].Visible = true;\r
1030             UpdatePanelShipInfo();\r
1031         }\r
1032 \r
1033         private void labelFleet1_MouseHover(object sender, EventArgs e)\r
1034         {\r
1035             labelFleet1.Text = _currentFleet == 0 && _sniffer.CombinedFleetType > 0 && !_combinedFleet ? "連合" : "第一";\r
1036         }\r
1037 \r
1038         private void labelFleet1_MouseLeave(object sender, EventArgs e)\r
1039         {\r
1040             labelFleet1.Text = _combinedFleet ? "連合" : "第一";\r
1041         }\r
1042 \r
1043         private readonly Color _activeButtonColor = Color.FromArgb(152, 179, 208);\r
1044 \r
1045         private void labelBucketHistoryButton_Click(object sender, EventArgs e)\r
1046         {\r
1047             if (labelBucketHistory.Visible)\r
1048             {\r
1049                 labelBucketHistory.Visible = false;\r
1050                 labelBucketHistoryButton.BackColor = DefaultBackColor;\r
1051             }\r
1052             else\r
1053             {\r
1054                 labelBucketHistory.Visible = true;\r
1055                 labelBucketHistory.BringToFront();\r
1056                 labelBucketHistoryButton.BackColor = _activeButtonColor;\r
1057             }\r
1058         }\r
1059 \r
1060         private void labelBucketHistory_Click(object sender, EventArgs e)\r
1061         {\r
1062             labelBucketHistory.Visible = false;\r
1063             labelBucketHistoryButton.BackColor = DefaultBackColor;\r
1064         }\r
1065 \r
1066         private void labelMaterialHistoryButton_Click(object sender, EventArgs e)\r
1067         {\r
1068             if (panelMaterialHistory.Visible)\r
1069             {\r
1070                 panelMaterialHistory.Visible = false;\r
1071                 labelMaterialHistoryButton.BackColor = DefaultBackColor;\r
1072             }\r
1073             else\r
1074             {\r
1075                 panelMaterialHistory.Visible = true;\r
1076                 panelMaterialHistory.BringToFront();\r
1077                 labelMaterialHistoryButton.BackColor = _activeButtonColor;\r
1078             }\r
1079         }\r
1080 \r
1081         private void panelMaterialHistory_Click(object sender, EventArgs e)\r
1082         {\r
1083             panelMaterialHistory.Visible = false;\r
1084             labelMaterialHistoryButton.BackColor = DefaultBackColor;\r
1085         }\r
1086 \r
1087         public void ResetAchievemnt()\r
1088         {\r
1089             _sniffer.Achievement.Reset();\r
1090             UpdateItemInfo();\r
1091         }\r
1092 \r
1093         private void labelRepairListButton_Click(object sender, EventArgs e)\r
1094         {\r
1095             if (panelRepairList.Visible)\r
1096             {\r
1097                 panelRepairList.Visible = false;\r
1098                 labelRepairListButton.BackColor = DefaultBackColor;\r
1099             }\r
1100             else\r
1101             {\r
1102                 panelRepairList.Visible = true;\r
1103                 panelRepairList.BringToFront();\r
1104                 labelRepairListButton.BackColor = _activeButtonColor;\r
1105             }\r
1106         }\r
1107 \r
1108         private void panelRepairList_Click(object sender, EventArgs e)\r
1109         {\r
1110             panelRepairList.Visible = false;\r
1111             labelRepairListButton.BackColor = DefaultBackColor;\r
1112         }\r
1113 \r
1114         private void ShipListToolStripMenuItem_Click(object sender, EventArgs e)\r
1115         {\r
1116             _listForm.UpdateList();\r
1117             _listForm.Show();\r
1118             if (_listForm.WindowState == FormWindowState.Minimized)\r
1119                 _listForm.WindowState = FormWindowState.Normal;\r
1120             _listForm.Activate();\r
1121         }\r
1122 \r
1123         private void LogToolStripMenuItem_Click(object sender, EventArgs e)\r
1124         {\r
1125             Process.Start("http://localhost:" + _config.Proxy.Listen + "/");\r
1126         }\r
1127 \r
1128         private void CaptureToolStripMenuItem_Click(object sender, EventArgs e)\r
1129         {\r
1130             try\r
1131             {\r
1132                 var proc = new ProcessStartInfo("BurageSnap.exe") {WorkingDirectory = "Capture"};\r
1133                 Process.Start(proc);\r
1134             }\r
1135             catch (FileNotFoundException)\r
1136             {\r
1137             }\r
1138         }\r
1139     }\r
1140 }