OSDN Git Service

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