OSDN Git Service

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