OSDN Git Service

タイマーの時刻を保持するTimeStepクラスを導入する
[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.ComponentModel;\r
18 using System.Diagnostics;\r
19 using System.Drawing;\r
20 using System.Globalization;\r
21 using System.IO;\r
22 using System.Linq;\r
23 using System.Net;\r
24 using System.Runtime.InteropServices;\r
25 using System.Text;\r
26 using System.Text.RegularExpressions;\r
27 using System.Threading;\r
28 using System.Threading.Tasks;\r
29 using System.Windows.Forms;\r
30 using DynaJson;\r
31 using KancolleSniffer.Log;\r
32 using KancolleSniffer.Model;\r
33 using KancolleSniffer.Net;\r
34 using KancolleSniffer.Notification;\r
35 using KancolleSniffer.Util;\r
36 using KancolleSniffer.View;\r
37 using Microsoft.CSharp.RuntimeBinder;\r
38 using Clipboard = KancolleSniffer.Util.Clipboard;\r
39 using Timer = System.Windows.Forms.Timer;\r
40 \r
41 namespace KancolleSniffer\r
42 {\r
43     public partial class MainForm : Form\r
44     {\r
45         private readonly ConfigDialog _configDialog;\r
46         private readonly ProxyManager _proxyManager;\r
47         private readonly ResizableToolTip _toolTip = new ResizableToolTip();\r
48         private readonly ResizableToolTip _tooltipCopy = new ResizableToolTip {ShowAlways = false, AutomaticDelay = 0};\r
49         private readonly ListFormGroup _listFormGroup;\r
50 \r
51         private readonly Notifier _notifier;\r
52         private bool _started;\r
53         private bool _timerEnabled;\r
54         private string _debugLogFile;\r
55         private IEnumerator<string> _playLog;\r
56         private readonly TimeStep _step = new TimeStep();\r
57         private IEnumerable<IUpdateContext> _updateable;\r
58         private IEnumerable<IUpdateTimers> _timers;\r
59 \r
60         private readonly ErrorDialog _errorDialog = new ErrorDialog();\r
61         private readonly ErrorLog _errorLog;\r
62 \r
63         public Sniffer Sniffer { get; } = new Sniffer();\r
64         public Config Config { get; } = new Config();\r
65 \r
66         public MainForm()\r
67         {\r
68             InitializeComponent();\r
69             HttpProxy.AfterSessionComplete += HttpProxy_AfterSessionComplete;\r
70             Config.Load();\r
71             _configDialog = new ConfigDialog(this);\r
72             _listFormGroup = new ListFormGroup(this);\r
73             _notifier = new Notifier(FlashWindow, ShowTaster, PlaySound);\r
74             SetupView();\r
75             _proxyManager = new ProxyManager(this);\r
76             _proxyManager.UpdatePacFile();\r
77             _errorLog = new ErrorLog(Sniffer);\r
78             LoadData();\r
79             Sniffer.RepeatingTimerController = _notifier;\r
80         }\r
81 \r
82         private void SetupView()\r
83         {\r
84             SetScaleFactorOfDpiScaling();\r
85             SetupFleetClick();\r
86             SetupQuestPanel();\r
87             shipInfoPanel.AkashiRepairTimer = labelAkashiRepairTimer;\r
88             shipInfoPanel.ShowShipOnList = ShowShipOnShipList;\r
89             panelRepairList.CreateLabels(panelRepairList_Click);\r
90             ndockPanel.SetClickHandler(labelNDockCaption);\r
91             missionPanel.SetClickHandler(labelMissionCaption);\r
92             materialHistoryPanel.SetClickHandler(labelMaterialCaption, dropDownButtonMaterialHistory);\r
93             SetupUpdateable();\r
94             PerformZoom();\r
95         }\r
96 \r
97         private void SetupUpdateable()\r
98         {\r
99             _updateable = new IUpdateContext[]\r
100             {\r
101                 hqPanel, missionPanel, kdockPanel, ndockPanel, materialHistoryPanel, shipInfoPanel, chargeStatus1,\r
102                 chargeStatus2, chargeStatus3, chargeStatus4, _notifier\r
103             };\r
104             var context = new UpdateContext(Sniffer, Config, () => _step);\r
105             foreach (var updateable in _updateable)\r
106                 updateable.Context = context;\r
107             _timers = new IUpdateTimers[] {missionPanel, kdockPanel, ndockPanel, shipInfoPanel};\r
108         }\r
109 \r
110         private void SetScaleFactorOfDpiScaling()\r
111         {\r
112             var autoScaleDimensions = new SizeF(6f, 12f); // AutoScaleDimensionの初期値\r
113             Scaler.Factor = new SizeF(CurrentAutoScaleDimensions.Width / autoScaleDimensions.Width,\r
114                 CurrentAutoScaleDimensions.Height / autoScaleDimensions.Height);\r
115         }\r
116 \r
117         private void SetupQuestPanel()\r
118         {\r
119             int prevHeight = questPanel.Height;\r
120             questPanel.CreateLabels(Config.QuestLines, labelQuest_DoubleClick);\r
121             Height += questPanel.Height - prevHeight;\r
122         }\r
123 \r
124         private readonly FileSystemWatcher _watcher = new FileSystemWatcher\r
125         {\r
126             Path = AppDomain.CurrentDomain.BaseDirectory,\r
127             NotifyFilter = NotifyFilters.LastWrite\r
128         };\r
129 \r
130         private readonly Timer _watcherTimer = new Timer {Interval = 1000};\r
131 \r
132         private void LoadData()\r
133         {\r
134             var target = "";\r
135             Sniffer.LoadState();\r
136             _watcher.SynchronizingObject = this;\r
137             _watcherTimer.Tick += (sender, ev) =>\r
138             {\r
139                 _watcherTimer.Stop();\r
140                 switch (target)\r
141                 {\r
142                     case "status.xml":\r
143                         Sniffer.LoadState();\r
144                         break;\r
145                     case "TP.csv":\r
146                         Sniffer.AdditionalData.LoadTpSpec();\r
147                         break;\r
148                 }\r
149             };\r
150             _watcher.Changed += (sender, ev) =>\r
151             {\r
152                 target = ev.Name;\r
153                 _watcherTimer.Stop();\r
154                 _watcherTimer.Start();\r
155             };\r
156             _watcher.EnableRaisingEvents = true;\r
157         }\r
158 \r
159         private void HttpProxy_AfterSessionComplete(HttpProxy.Session session)\r
160         {\r
161             BeginInvoke(new Action<HttpProxy.Session>(ProcessRequest), session);\r
162         }\r
163 \r
164         private void ProcessRequest(HttpProxy.Session session)\r
165         {\r
166             var url = session.Request.PathAndQuery;\r
167             if (!url.Contains("kcsapi/"))\r
168                 return;\r
169             var request = session.Request.BodyAsString;\r
170             var response = session.Response.BodyAsString;\r
171             Privacy.Remove(ref url, ref request, ref response);\r
172             if (response == null || !response.StartsWith("svdata="))\r
173             {\r
174                 WriteDebugLog(url, request, response);\r
175                 return;\r
176             }\r
177             response = UnEscapeString(response.Remove(0, "svdata=".Length));\r
178             WriteDebugLog(url, request, response);\r
179             ProcessRequestMain(url, request, response);\r
180         }\r
181 \r
182         private void ProcessRequestMain(string url, string request, string response)\r
183         {\r
184             try\r
185             {\r
186                 UpdateInfo(Sniffer.Sniff(url, request, JsonObject.Parse(response)));\r
187                 _errorLog.CheckBattleApi(url, request, response);\r
188             }\r
189 \r
190             catch (RuntimeBinderException e)\r
191             {\r
192                 if (_errorDialog.ShowDialog(this,\r
193                         "艦これに仕様変更があったか、受信内容が壊れています。",\r
194                         _errorLog.GenerateErrorLog(url, request, response, e.ToString())) == DialogResult.Abort)\r
195                     Exit();\r
196             }\r
197             catch (LogIOException e)\r
198             {\r
199                 // ReSharper disable once PossibleNullReferenceException\r
200                 if (_errorDialog.ShowDialog(this, e.Message, e.InnerException.ToString()) == DialogResult.Abort)\r
201                     Exit();\r
202             }\r
203             catch (BattleResultError)\r
204             {\r
205                 if (_errorDialog.ShowDialog(this, "戦闘結果の計算に誤りがあります。",\r
206                         _errorLog.GenerateBattleErrorLog()) == DialogResult.Abort)\r
207                     Exit();\r
208             }\r
209             catch (Exception e)\r
210             {\r
211                 if (_errorDialog.ShowDialog(this, "エラーが発生しました。",\r
212                         _errorLog.GenerateErrorLog(url, request, response, e.ToString())) == DialogResult.Abort)\r
213                     Exit();\r
214             }\r
215         }\r
216 \r
217         private void Exit()\r
218         {\r
219             _proxyManager.Shutdown();\r
220             Environment.Exit(1);\r
221         }\r
222 \r
223         private void WriteDebugLog(string url, string request, string response)\r
224         {\r
225             if (_debugLogFile != null)\r
226             {\r
227                 File.AppendAllText(_debugLogFile,\r
228                     $"date: {DateTime.Now:g}\nurl: {url}\nrequest: {request}\nresponse: {response ?? "(null)"}\n");\r
229             }\r
230         }\r
231 \r
232         private string UnEscapeString(string s)\r
233         {\r
234             try\r
235             {\r
236                 var rx = new Regex(@"\\[uU]([0-9A-Fa-f]{4})");\r
237                 return rx.Replace(s,\r
238                     match => ((char)int.Parse(match.Value.Substring(2), NumberStyles.HexNumber)).ToString());\r
239             }\r
240             catch (ArgumentException)\r
241             {\r
242                 return s;\r
243             }\r
244         }\r
245 \r
246         private void UpdateInfo(Sniffer.Update update)\r
247         {\r
248             if (update == Sniffer.Update.Start)\r
249             {\r
250                 hqPanel.Login.Visible = false;\r
251                 shipInfoPanel.Guide.Visible = false;\r
252                 _started = true;\r
253                 _notifier.StopAllRepeat();\r
254                 return;\r
255             }\r
256             if (!_started)\r
257                 return;\r
258             if (_step.Now == DateTime.MinValue)\r
259                 _step.SetNow();\r
260             if ((update & Sniffer.Update.Item) != 0)\r
261                 UpdateItemInfo();\r
262             if ((update & Sniffer.Update.Timer) != 0)\r
263                 UpdateTimers();\r
264             if ((update & Sniffer.Update.NDock) != 0)\r
265                 UpdateNDocLabels();\r
266             if ((update & Sniffer.Update.Mission) != 0)\r
267                 UpdateMissionLabels();\r
268             if ((update & Sniffer.Update.QuestList) != 0)\r
269                 UpdateQuestList();\r
270             if ((update & Sniffer.Update.Ship) != 0)\r
271                 UpdateShipInfo();\r
272             if ((update & Sniffer.Update.Battle) != 0)\r
273                 UpdateBattleInfo();\r
274             if ((update & Sniffer.Update.Cell) != 0)\r
275                 UpdateCellInfo();\r
276         }\r
277 \r
278         private void MainForm_Load(object sender, EventArgs e)\r
279         {\r
280             SuppressActivate.Start();\r
281             RestoreLocation();\r
282             if (Config.HideOnMinimized && WindowState == FormWindowState.Minimized)\r
283                 ShowInTaskbar = false;\r
284             if (Config.ShowHpInPercent)\r
285                 shipInfoPanel.ToggleHpPercent();\r
286             if (Config.ShipList.Visible)\r
287                 _listFormGroup.Show();\r
288             ApplyConfig();\r
289             ApplyDebugLogSetting();\r
290             ApplyLogSetting();\r
291             ApplyProxySetting();\r
292             CheckVersionUp((current, latest) =>\r
293             {\r
294                 if (latest == current)\r
295                     return;\r
296                 var guide = shipInfoPanel.Guide;\r
297                 guide.Text = $"バージョン{latest}があります。";\r
298                 guide.LinkArea = new LinkArea(0, guide.Text.Length);\r
299                 guide.Click += (obj, ev) =>\r
300                 {\r
301                     Process.Start("https://ja.osdn.net/rel/kancollesniffer/" + latest);\r
302                 };\r
303             });\r
304         }\r
305 \r
306         public async void CheckVersionUp(Action<string, string> action)\r
307         {\r
308             var current = string.Join(".", Application.ProductVersion.Split('.').Take(2));\r
309             try\r
310             {\r
311                 var latest = (await new WebClient().DownloadStringTaskAsync("http://kancollesniffer.osdn.jp/version"))\r
312                     .TrimEnd();\r
313                 try\r
314                 {\r
315                     action(current, latest);\r
316                 }\r
317                 catch (InvalidOperationException)\r
318                 {\r
319                 }\r
320             }\r
321             catch (WebException)\r
322             {\r
323             }\r
324         }\r
325 \r
326         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)\r
327         {\r
328             if (!Config.ExitSilently)\r
329             {\r
330                 using var dialog = new ConfirmDialog();\r
331                 if (dialog.ShowDialog(this) != DialogResult.Yes)\r
332                 {\r
333                     e.Cancel = true;\r
334                     return;\r
335                 }\r
336             }\r
337             _listFormGroup.Close();\r
338             Sniffer.FlashLog();\r
339             Config.Location = (WindowState == FormWindowState.Normal ? Bounds : RestoreBounds).Location;\r
340             Config.ShowHpInPercent = shipInfoPanel.ShowHpInPercent;\r
341             Config.Save();\r
342             Sniffer.SaveState();\r
343             _proxyManager.Shutdown();\r
344         }\r
345 \r
346         private void MainForm_Resize(object sender, EventArgs e)\r
347         {\r
348             if (_listFormGroup == null) // DPIが100%でないときにInitializeComponentから呼ばれるので\r
349                 return;\r
350             SuppressActivate.Start();\r
351             if (WindowState == FormWindowState.Minimized)\r
352             {\r
353                 if (Config.HideOnMinimized)\r
354                     ShowInTaskbar = false;\r
355             }\r
356             _listFormGroup.Main.ChangeWindowState(WindowState);\r
357         }\r
358 \r
359         public readonly TimeOutChecker SuppressActivate = new TimeOutChecker();\r
360 \r
361         private void MainForm_Activated(object sender, EventArgs e)\r
362         {\r
363             if (SuppressActivate.Check())\r
364                 return;\r
365             if (NeedRaise)\r
366                 RaiseBothWindows();\r
367         }\r
368 \r
369         private bool NeedRaise => _listFormGroup.Visible && WindowState != FormWindowState.Minimized;\r
370 \r
371         private void RaiseBothWindows()\r
372         {\r
373             _listFormGroup.Main.Owner = null;\r
374             Owner = _listFormGroup.Main;\r
375             BringToFront();\r
376             Owner = null;\r
377         }\r
378 \r
379         public class TimeOutChecker\r
380         {\r
381             private DateTime _lastCheck;\r
382             private readonly TimeSpan _timeout = TimeSpan.FromMilliseconds(500);\r
383 \r
384             public void Start()\r
385             {\r
386                 _lastCheck = DateTime.Now;\r
387             }\r
388 \r
389             public bool Check()\r
390             {\r
391                 var now = DateTime.Now;\r
392                 var last = _lastCheck;\r
393                 _lastCheck = now;\r
394                 return now - last < _timeout;\r
395             }\r
396         }\r
397 \r
398         private void notifyIconMain_MouseDoubleClick(object sender, MouseEventArgs e)\r
399         {\r
400             NotifyIconOpenToolStripMenuItem_Click(sender, e);\r
401         }\r
402 \r
403         private void NotifyIconOpenToolStripMenuItem_Click(object sender, EventArgs e)\r
404         {\r
405             ShowInTaskbar = true;\r
406             WindowState = FormWindowState.Normal;\r
407             TopMost = _listFormGroup.TopMost = Config.TopMost; // 最前面に表示されなくなることがあるのを回避する\r
408         }\r
409 \r
410         private void ExitToolStripMenuItem_Click(object sender, EventArgs e)\r
411         {\r
412             Close();\r
413         }\r
414 \r
415         private void ConfigToolStripMenuItem_Click(object sender, EventArgs e)\r
416         {\r
417             if (_configDialog.ShowDialog(this) == DialogResult.OK)\r
418             {\r
419                 Config.Save();\r
420                 ApplyConfig();\r
421                 _notifier.StopRepeatingTimer(_configDialog.RepeatSettingsChanged);\r
422             }\r
423         }\r
424 \r
425         private void PerformZoom()\r
426         {\r
427             if (Config.Zoom == 100)\r
428             {\r
429                 ShipLabel.Name.BaseFont = Font;\r
430                 ShipLabel.Name.LatinFont = LatinFont();\r
431                 return;\r
432             }\r
433             var prev = CurrentAutoScaleDimensions;\r
434             foreach (var control in new Control[]\r
435             {\r
436                 this, shipInfoPanel.Guide, hqPanel.Login,\r
437                 _configDialog, _configDialog.NotificationConfigDialog,\r
438                 contextMenuStripMain, _errorDialog\r
439             })\r
440             {\r
441                 control.Font = ZoomFont(control.Font);\r
442             }\r
443             _listFormGroup.Font = ZoomFont(_listFormGroup.Font);\r
444             foreach (var toolTip in new[] {_toolTip, _tooltipCopy})\r
445             {\r
446                 toolTip.Font = ZoomFont(toolTip.Font);\r
447             }\r
448             ShipLabel.Name.BaseFont = Font;\r
449             ShipLabel.Name.LatinFont = LatinFont();\r
450             var cur = CurrentAutoScaleDimensions;\r
451             Scaler.Factor = Scaler.Scale(cur.Width / prev.Width, cur.Height / prev.Height);\r
452         }\r
453 \r
454         private Font ZoomFont(Font font)\r
455         {\r
456             return new Font(font.FontFamily, font.Size * Config.Zoom / 100);\r
457         }\r
458 \r
459         private Font LatinFont()\r
460         {\r
461             return new Font("Tahoma", 8f * Config.Zoom / 100);\r
462         }\r
463 \r
464         private void RestoreLocation()\r
465         {\r
466             if (Config.Location.X == int.MinValue)\r
467                 return;\r
468             if (IsTitleBarOnAnyScreen(Config.Location))\r
469                 Location = Config.Location;\r
470         }\r
471 \r
472         private void ApplyConfig()\r
473         {\r
474             if (TopMost != Config.TopMost)\r
475                 TopMost = _listFormGroup.TopMost = Config.TopMost;\r
476             Sniffer.ShipCounter.Margin = Config.MarginShips;\r
477             Sniffer.ItemCounter.Margin = Config.MarginEquips;\r
478             hqPanel.Update();\r
479             _notifier.NotifyShipItemCount();\r
480             Sniffer.Achievement.ResetHours = Config.ResetHours;\r
481             labelAkashiRepair.Visible = labelAkashiRepairTimer.Visible = Config.UsePresetAkashi;\r
482             Sniffer.WarnBadDamageWithDameCon = Config.WarnBadDamageWithDameCon;\r
483         }\r
484 \r
485         public void ApplyDebugLogSetting()\r
486         {\r
487             _debugLogFile = Config.DebugLogging ? Config.DebugLogFile : null;\r
488         }\r
489 \r
490         public bool ApplyProxySetting()\r
491         {\r
492             return _proxyManager.ApplyConfig();\r
493         }\r
494 \r
495         public void ApplyLogSetting()\r
496         {\r
497             LogServer.OutputDir = Config.Log.OutputDir;\r
498             LogServer.LogProcessor = new LogProcessor(Sniffer.Material.MaterialHistory, Sniffer.MapDictionary);\r
499             Sniffer.EnableLog(Config.Log.On ? LogType.All : LogType.None);\r
500             Sniffer.MaterialLogInterval = Config.Log.MaterialLogInterval;\r
501             Sniffer.LogOutputDir = Config.Log.OutputDir;\r
502         }\r
503 \r
504         public static bool IsTitleBarOnAnyScreen(Point location)\r
505         {\r
506             var rect = new Rectangle(\r
507                 new Point(location.X + SystemInformation.IconSize.Width + SystemInformation.HorizontalFocusThickness,\r
508                     location.Y + SystemInformation.CaptionHeight), new Size(60, 1));\r
509             return Screen.AllScreens.Any(screen => screen.WorkingArea.Contains(rect));\r
510         }\r
511 \r
512         private void timerMain_Tick(object sender, EventArgs e)\r
513         {\r
514             if (_timerEnabled)\r
515             {\r
516                 try\r
517                 {\r
518                     _step.SetNow();\r
519                     UpdateTimers();\r
520                     _notifier.NotifyTimers();\r
521                     _step.SetPrev();\r
522                 }\r
523                 catch (Exception ex)\r
524                 {\r
525                     if (_errorDialog.ShowDialog(this, "エラーが発生しました。", ex.ToString()) == DialogResult.Abort)\r
526                         Exit();\r
527                 }\r
528             }\r
529             if (_playLog == null || _configDialog.Visible)\r
530             {\r
531                 hqPanel.PlayLog.Visible = false;\r
532                 return;\r
533             }\r
534             PlayLog();\r
535         }\r
536 \r
537         public void SetPlayLog(string file)\r
538         {\r
539             _playLog = File.ReadLines(file).GetEnumerator();\r
540         }\r
541 \r
542         private void PlayLog()\r
543         {\r
544             var lines = new List<string>();\r
545             foreach (var s in new[] {"url: ", "request: ", "response: "})\r
546             {\r
547                 do\r
548                 {\r
549                     if (!_playLog.MoveNext() || _playLog.Current == null)\r
550                     {\r
551                         hqPanel.PlayLog.Visible = false;\r
552                         return;\r
553                     }\r
554                 } while (!_playLog.Current.StartsWith(s));\r
555                 lines.Add(_playLog.Current.Substring(s.Length));\r
556             }\r
557             hqPanel.PlayLog.Visible = !hqPanel.PlayLog.Visible;\r
558             ProcessRequestMain(lines[0], lines[1], lines[2]);\r
559         }\r
560 \r
561         private void ShowShipOnShipList(int id)\r
562         {\r
563             if (!_listFormGroup.Visible)\r
564                 return;\r
565             _listFormGroup.ShowShip(id);\r
566         }\r
567 \r
568         private void UpdateItemInfo()\r
569         {\r
570             hqPanel.Update();\r
571             _notifier.NotifyShipItemCount();\r
572             materialHistoryPanel.Update();\r
573             if (_listFormGroup.Visible)\r
574                 _listFormGroup.UpdateList();\r
575         }\r
576 \r
577         private void UpdateShipInfo()\r
578         {\r
579             shipInfoPanel.SetCurrentFleet();\r
580             shipInfoPanel.Update();\r
581             _notifier.NotifyDamagedShip();\r
582             UpdateChargeInfo();\r
583             UpdateRepairList();\r
584             UpdateMissionLabels();\r
585             if (_listFormGroup.Visible)\r
586                 _listFormGroup.UpdateList();\r
587         }\r
588 \r
589         private void UpdatePanelShipInfo()\r
590         {\r
591             shipInfoPanel.Update();\r
592             ShowCurrentFleetNumber();\r
593             labelFleet1.Text = shipInfoPanel.CombinedFleet ? CombinedName : "第一";\r
594         }\r
595 \r
596         private void ShowCurrentFleetNumber()\r
597         {\r
598             var labels = new[] {triangleMark1, triangleMark2, triangleMark3, triangleMark4};\r
599             for (var i = 0; i < labels.Length; i++)\r
600                 labels[i].Visible = shipInfoPanel.CurrentFleet == i;\r
601         }\r
602 \r
603         private string CombinedName\r
604         {\r
605             get\r
606             {\r
607                 switch (Sniffer.Fleets[0].CombinedType)\r
608                 {\r
609                     case CombinedType.Carrier:\r
610                         return "機動";\r
611                     case CombinedType.Surface:\r
612                         return "水上";\r
613                     case CombinedType.Transport:\r
614                         return "輸送";\r
615                     default:\r
616                         return "連合";\r
617                 }\r
618             }\r
619         }\r
620 \r
621         private void UpdateBattleInfo()\r
622         {\r
623             _listFormGroup.UpdateBattleResult();\r
624             _listFormGroup.UpdateAirBattleResult();\r
625             shipInfoPanel.UpdateBattleInfo();\r
626         }\r
627 \r
628         private void UpdateCellInfo()\r
629         {\r
630             _listFormGroup.UpdateCellInfo();\r
631         }\r
632 \r
633         private void UpdateChargeInfo()\r
634         {\r
635             foreach (var status in new[] {chargeStatus1, chargeStatus2, chargeStatus3, chargeStatus4})\r
636             {\r
637                 status.Update();\r
638                 _toolTip.SetToolTip(status, status.Text);\r
639             }\r
640         }\r
641 \r
642         private void UpdateNDocLabels()\r
643         {\r
644             ndockPanel.Update();\r
645         }\r
646 \r
647         private void UpdateMissionLabels()\r
648         {\r
649             missionPanel.Update();\r
650         }\r
651 \r
652         private void UpdateTimers()\r
653         {\r
654             foreach (var timer in _timers)\r
655                 timer.UpdateTimers();\r
656             _timerEnabled = true;\r
657         }\r
658 \r
659         private void UpdateRepairList()\r
660         {\r
661             panelRepairList.SetRepairList(Sniffer.RepairList);\r
662             _toolTip.SetToolTip(label31, new RepairShipCount(Sniffer.RepairList).ToString());\r
663         }\r
664 \r
665         private void UpdateQuestList()\r
666         {\r
667             questPanel.Update(Sniffer.Quests);\r
668             labelQuestCount.Text = Sniffer.Quests.Length.ToString();\r
669             _notifier.NotifyQuestComplete();\r
670         }\r
671 \r
672         private void FlashWindow()\r
673         {\r
674             Win32API.FlashWindow(Handle);\r
675         }\r
676 \r
677         private void ShowTaster(string title, string message)\r
678         {\r
679             notifyIconMain.ShowBalloonTip(20000, title, message, ToolTipIcon.Info);\r
680         }\r
681 \r
682         [DllImport("winmm.dll")]\r
683         private static extern int mciSendString(String command,\r
684             StringBuilder buffer, int bufferSize, IntPtr hWndCallback);\r
685 \r
686 // ReSharper disable InconsistentNaming\r
687         // ReSharper disable once IdentifierTypo\r
688         private const int MM_MCINOTIFY = 0x3B9;\r
689 \r
690         private const int MCI_NOTIFY_SUCCESSFUL = 1;\r
691 // ReSharper restore InconsistentNaming\r
692 \r
693         public void PlaySound(string file, int volume)\r
694         {\r
695             if (!File.Exists(file))\r
696                 return;\r
697             mciSendString("close sound", null, 0, IntPtr.Zero);\r
698             if (mciSendString("open \"" + file + "\" type mpegvideo alias sound", null, 0, IntPtr.Zero) != 0)\r
699                 return;\r
700             mciSendString("setaudio sound volume to " + volume * 10, null, 0, IntPtr.Zero);\r
701             mciSendString("play sound notify", null, 0, Handle);\r
702         }\r
703 \r
704         protected override void WndProc(ref Message m)\r
705         {\r
706             if (m.Msg == MM_MCINOTIFY && (int)m.WParam == MCI_NOTIFY_SUCCESSFUL)\r
707                 mciSendString("close sound", null, 0, IntPtr.Zero);\r
708             base.WndProc(ref m);\r
709         }\r
710 \r
711         private void SetupFleetClick()\r
712         {\r
713             var labels = new[]\r
714             {\r
715                 new Control[] {labelFleet1, labelFleet2, labelFleet3, labelFleet4},\r
716                 new Control[] {chargeStatus1, chargeStatus2, chargeStatus3, chargeStatus4}\r
717             };\r
718             foreach (var a in labels)\r
719             {\r
720                 a[0].Tag = 0;\r
721                 a[0].Click += labelFleet1_Click;\r
722                 a[0].DoubleClick += labelFleet1_DoubleClick;\r
723                 for (var fleet = 1; fleet < labels[0].Length; fleet++)\r
724                 {\r
725                     a[fleet].Tag = fleet;\r
726                     a[fleet].Click += labelFleet_Click;\r
727                     a[fleet].DoubleClick += labelFleet_DoubleClick;\r
728                 }\r
729             }\r
730         }\r
731 \r
732         private void labelFleet_Click(object sender, EventArgs e)\r
733         {\r
734             if (!_started)\r
735                 return;\r
736             var fleet = (int)((Control)sender).Tag;\r
737             if (shipInfoPanel.CurrentFleet == fleet)\r
738                 return;\r
739             shipInfoPanel.CombinedFleet = false;\r
740             shipInfoPanel.CurrentFleet = fleet;\r
741             UpdatePanelShipInfo();\r
742         }\r
743 \r
744         private readonly SemaphoreSlim _clickSemaphore = new SemaphoreSlim(1);\r
745         private readonly SemaphoreSlim _doubleClickSemaphore = new SemaphoreSlim(0);\r
746 \r
747         private async void labelFleet1_Click(object sender, EventArgs e)\r
748         {\r
749             if (!_started)\r
750                 return;\r
751             if (shipInfoPanel.CurrentFleet != 0)\r
752             {\r
753                 labelFleet_Click(sender, e);\r
754                 return;\r
755             }\r
756             if (!_clickSemaphore.Wait(0))\r
757                 return;\r
758             try\r
759             {\r
760                 if (await _doubleClickSemaphore.WaitAsync(SystemInformation.DoubleClickTime))\r
761                     return;\r
762             }\r
763             finally\r
764             {\r
765                 _clickSemaphore.Release();\r
766             }\r
767             shipInfoPanel.CombinedFleet = Sniffer.IsCombinedFleet && !shipInfoPanel.CombinedFleet;\r
768             UpdatePanelShipInfo();\r
769         }\r
770 \r
771         private void labelFleet1_MouseHover(object sender, EventArgs e)\r
772         {\r
773             labelFleet1.Text = shipInfoPanel.CurrentFleet == 0 && Sniffer.IsCombinedFleet && !shipInfoPanel.CombinedFleet ? "連合" : "第一";\r
774         }\r
775 \r
776         private void labelFleet1_MouseLeave(object sender, EventArgs e)\r
777         {\r
778             labelFleet1.Text = shipInfoPanel.CombinedFleet ? CombinedName : "第一";\r
779         }\r
780 \r
781         private void labelFleet_DoubleClick(object sender, EventArgs e)\r
782         {\r
783             if (!_started)\r
784                 return;\r
785             var fleet = (int)((Control)sender).Tag;\r
786             var text = TextGenerator.GenerateFleetData(Sniffer, fleet);\r
787             CopyFleetText(text, (Control)sender);\r
788         }\r
789 \r
790         private void labelFleet1_DoubleClick(object sender, EventArgs e)\r
791         {\r
792             if (!_started)\r
793                 return;\r
794             _doubleClickSemaphore.Release();\r
795             var text = TextGenerator.GenerateFleetData(Sniffer, 0);\r
796             if (shipInfoPanel.CombinedFleet)\r
797                 text += TextGenerator.GenerateFleetData(Sniffer, 1);\r
798             CopyFleetText(text, (Control)sender);\r
799         }\r
800 \r
801         private void CopyFleetText(string text, Control fleetButton)\r
802         {\r
803             if (string.IsNullOrEmpty(text))\r
804                 return;\r
805             Clipboard.SetText(text);\r
806             _tooltipCopy.Active = true;\r
807             _tooltipCopy.Show("コピーしました。", fleetButton);\r
808             Task.Run(async () =>\r
809             {\r
810                 await Task.Delay(1000);\r
811                 _tooltipCopy.Active = false;\r
812             });\r
813         }\r
814 \r
815         public void ResetAchievement()\r
816         {\r
817             Sniffer.Achievement.Reset();\r
818             UpdateItemInfo();\r
819         }\r
820 \r
821         private void labelRepairListButton_Click(object sender, EventArgs e)\r
822         {\r
823             if (panelRepairList.Visible)\r
824             {\r
825                 panelRepairList.Visible = false;\r
826                 dropDownButtonRepairList.BackColor = DefaultBackColor;\r
827             }\r
828             else\r
829             {\r
830                 panelRepairList.Visible = true;\r
831                 panelRepairList.BringToFront();\r
832                 dropDownButtonRepairList.BackColor = CustomColors.ActiveButtonColor;\r
833             }\r
834         }\r
835 \r
836         private void panelRepairList_Click(object sender, EventArgs e)\r
837         {\r
838             panelRepairList.Visible = false;\r
839             dropDownButtonRepairList.BackColor = DefaultBackColor;\r
840         }\r
841 \r
842         private void ShipListToolStripMenuItem_Click(object sender, EventArgs e)\r
843         {\r
844             _listFormGroup.ShowOrCreate();\r
845         }\r
846 \r
847         private void LogToolStripMenuItem_Click(object sender, EventArgs e)\r
848         {\r
849             Process.Start("http://localhost:" + Config.Proxy.Listen + "/");\r
850         }\r
851 \r
852         private void labelClearQuest_Click(object sender, EventArgs e)\r
853         {\r
854             Sniffer.ClearQuests();\r
855             UpdateQuestList();\r
856         }\r
857 \r
858         private void labelClearQuest_MouseDown(object sender, MouseEventArgs e)\r
859         {\r
860             labelClearQuest.BackColor = CustomColors.ActiveButtonColor;\r
861         }\r
862 \r
863         private void labelClearQuest_MouseUp(object sender, MouseEventArgs e)\r
864         {\r
865             labelClearQuest.BackColor = DefaultBackColor;\r
866         }\r
867 \r
868         private void labelQuest_DoubleClick(object sender, EventArgs e)\r
869         {\r
870             var label = (Label)sender;\r
871             if (string.IsNullOrEmpty(label.Text))\r
872                 return;\r
873             Clipboard.SetText(label.Text);\r
874             _tooltipCopy.Active = true;\r
875             _tooltipCopy.Show("コピーしました。", label);\r
876             Task.Run(async () =>\r
877             {\r
878                 await Task.Delay(1000);\r
879                 _tooltipCopy.Active = false;\r
880             });\r
881         }\r
882 \r
883         private void CaptureToolStripMenuItem_Click(object sender, EventArgs e)\r
884         {\r
885             try\r
886             {\r
887                 var proc = new ProcessStartInfo("BurageSnap.exe") {WorkingDirectory = "Capture"};\r
888                 Process.Start(proc);\r
889             }\r
890             catch (FileNotFoundException)\r
891             {\r
892             }\r
893             catch (Win32Exception)\r
894             {\r
895             }\r
896         }\r
897     }\r
898 }