OSDN Git Service

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