OSDN Git Service

バージョン12.11の準備
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / MainWindow.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Drawing;\r
4 using System.Linq;\r
5 using System.Reflection;\r
6 using System.Windows.Forms;\r
7 using KancolleSniffer.Forms;\r
8 using KancolleSniffer.Notification;\r
9 using KancolleSniffer.Util;\r
10 using KancolleSniffer.View;\r
11 using KancolleSniffer.View.MainWindow;\r
12 \r
13 namespace KancolleSniffer\r
14 {\r
15     public class MainWindow\r
16     {\r
17         private readonly ResizableToolTip _toolTip = new ResizableToolTip();\r
18         private readonly ResizableToolTip _tooltipCopy = new ResizableToolTip {ShowAlways = false, AutomaticDelay = 0};\r
19         private readonly ListFormGroup _listFormGroup;\r
20         private readonly ContextMenuMain _contextMenuMain = new ContextMenuMain();\r
21         private readonly ContextMenuNotifyIcon _contextMenuNotifyIcon = new ContextMenuNotifyIcon();\r
22         private readonly Components _c;\r
23 \r
24         private IEnumerable<IUpdateContext> _updateable;\r
25         private IEnumerable<IUpdateTimers> _timers;\r
26         private Main _main;\r
27 \r
28         public static bool SystemShutdown { get; set; }\r
29         public Sniffer Sniffer { get; private set; }\r
30         public Config Config { get; private set; }\r
31         public Label PlayLogSign => _c.hqPanel.PlayLog;\r
32         public Notifier Notifier { get; }\r
33         public Form Form { get; }\r
34 \r
35         private class Components\r
36         {\r
37             // ReSharper disable InconsistentNaming\r
38             // ReSharper disable UnusedAutoPropertyAccessor.Local\r
39             public NotifyIcon notifyIconMain { get; set; }\r
40             public HqPanel hqPanel { get; set; }\r
41             public FleetPanel fleetPanel { get; set; }\r
42             public Label labelNDockCaption { get; set; }\r
43             public NDockPanel ndockPanel { get; set; }\r
44             public Label labelKDockCaption { get; set; }\r
45             public KDockPanel kdockPanel { get; set; }\r
46             public Label labelMissionCaption { get; set; }\r
47             public MissionPanel missionPanel { get; set; }\r
48             public Label labelMaterialCaption { get; set; }\r
49             public DropDownButton dropDownButtonMaterialHistory { get; set; }\r
50             public MaterialHistoryPanel materialHistoryPanel { get; set; }\r
51             public Label labelQuestCaption { get; set; }\r
52             public Label labelQuestCount { get; set; }\r
53             public QuestPanel questPanel { get; set; }\r
54             public Label labelAkashiRepair { get; set; }\r
55             public Label labelAkashiRepairTimer { get; set; }\r
56             public Label labelRepairListCaption { get; set; }\r
57             public Label dropDownButtonRepairList { get; set; }\r
58             public RepairListPanel panelRepairList { get; set; }\r
59             // ReSharper restore InconsistentNaming\r
60             // ReSharper restore UnusedAutoPropertyAccessor.Local\r
61         }\r
62 \r
63         public MainWindow(Main main, Form form)\r
64         {\r
65             _c = GetComponents(form);\r
66             Form = form;\r
67             _c.notifyIconMain.ContextMenuStrip = _contextMenuNotifyIcon;\r
68             Form.ContextMenuStrip = _contextMenuMain;\r
69             Form.ContextMenuStrip = _contextMenuMain;\r
70             SetupMain(main);\r
71             _listFormGroup = new ListFormGroup(this);\r
72             Notifier = new Notifier(FlashWindow, ShowTaster, PlaySound);\r
73             SetupView();\r
74         }\r
75 \r
76         private Components GetComponents(Form form)\r
77         {\r
78             var r = new Components();\r
79             foreach (var prop in typeof(Components).GetProperties())\r
80             {\r
81                 // ReSharper disable once PossibleNullReferenceException\r
82                 prop.SetValue(r,\r
83                     form.GetType().GetField(prop.Name, BindingFlags.NonPublic | BindingFlags.Instance).GetValue(form));\r
84             }\r
85             return r;\r
86         }\r
87 \r
88         private void SetupMain(Main main)\r
89         {\r
90             _main = main;\r
91             Config = main.Config;\r
92             Sniffer = main.Sniffer;\r
93         }\r
94 \r
95         private void SetupView()\r
96         {\r
97             SetScaleFactorOfDpiScaling();\r
98             if (Config.Shape == "横長2")\r
99                 ChangeHorizontalLayout();\r
100             SetupQuestPanel();\r
101             SetEventHandlers();\r
102             _c.fleetPanel.AkashiRepairTimer = _c.labelAkashiRepairTimer;\r
103             _c.fleetPanel.ShowShipOnList = ShowShipOnShipList;\r
104             _c.panelRepairList.CreateLabels(panelRepairList_Click);\r
105             _c.ndockPanel.SetClickHandler(_c.labelNDockCaption);\r
106             _c.missionPanel.SetClickHandler(_c.labelMissionCaption);\r
107             _c.materialHistoryPanel.SetClickHandler(_c.labelMaterialCaption, _c.dropDownButtonMaterialHistory);\r
108             SetupUpdateable();\r
109             PerformZoom();\r
110         }\r
111 \r
112         private void ChangeHorizontalLayout()\r
113         {\r
114             var diff = _c.missionPanel.Left - _c.hqPanel.Left;\r
115             foreach (var control in new Control[]\r
116             {\r
117                 _c.hqPanel, _c.questPanel, _c.labelQuestCaption, _c.labelQuestCount, _c.materialHistoryPanel,\r
118                 _c.dropDownButtonMaterialHistory, _c.labelMaterialCaption\r
119             })\r
120             {\r
121                 control.Left += diff;\r
122             }\r
123             foreach (var control in new Control[]\r
124             {\r
125                 _c.missionPanel, _c.labelMissionCaption, _c.labelRepairListCaption, _c.panelRepairList,\r
126                 _c.dropDownButtonRepairList, _c.labelAkashiRepair, _c.labelAkashiRepairTimer,\r
127                 _c.ndockPanel, _c.labelNDockCaption, _c.kdockPanel, _c.labelKDockCaption\r
128             })\r
129             {\r
130                 control.Left -= diff;\r
131             }\r
132         }\r
133 \r
134         private void SetEventHandlers()\r
135         {\r
136             SetMainFormEventHandler();\r
137             SetContextMenuMainEventHandler();\r
138             SetContextMenuNotifyIconEventHandler();\r
139             SetNotifyIconEventHandler();\r
140             SetRepairListEventHandler();\r
141         }\r
142 \r
143         private void SetMainFormEventHandler()\r
144         {\r
145             Form.Load += MainForm_Load;\r
146             Form.FormClosing += MainForm_FormClosing;\r
147             Form.Resize += MainForm_Resize;\r
148             Form.Activated += MainForm_Activated;\r
149         }\r
150 \r
151         private void SetContextMenuMainEventHandler()\r
152         {\r
153             _contextMenuMain.SetClickHandlers(\r
154                 _listFormGroup.ShowOrCreate,\r
155                 _main.ShowReport,\r
156                 _main.StartCapture,\r
157                 _main.ShowConfigDialog,\r
158                 Form.Close);\r
159         }\r
160 \r
161         private void SetContextMenuNotifyIconEventHandler()\r
162         {\r
163             _contextMenuNotifyIcon.SetEventHandlers(RevertFromIcon, Form.Close);\r
164         }\r
165 \r
166         private void SetNotifyIconEventHandler()\r
167         {\r
168             _c.notifyIconMain.MouseDoubleClick += notifyIconMain_MouseDoubleClick;\r
169         }\r
170 \r
171         private void SetRepairListEventHandler()\r
172         {\r
173             _c.labelRepairListCaption.Click += labelRepairListButton_Click;\r
174             _c.dropDownButtonRepairList.Click += labelRepairListButton_Click;\r
175         }\r
176 \r
177         private void SetupUpdateable()\r
178         {\r
179             _updateable = new IUpdateContext[]\r
180             {\r
181                 _c.hqPanel, _c.missionPanel, _c.kdockPanel, _c.ndockPanel, _c.materialHistoryPanel, _c.fleetPanel,\r
182                 Notifier\r
183             };\r
184             var context = new UpdateContext(Sniffer, Config, () => _main.Step);\r
185             foreach (var updateable in _updateable)\r
186                 updateable.Context = context;\r
187             _timers = new IUpdateTimers[] {_c.missionPanel, _c.kdockPanel, _c.ndockPanel, _c.fleetPanel};\r
188         }\r
189 \r
190         private void SetScaleFactorOfDpiScaling()\r
191         {\r
192             var autoScaleDimensions = new SizeF(6f, 12f); // AutoScaleDimensionの初期値\r
193             Scaler.Factor = new SizeF(Form.CurrentAutoScaleDimensions.Width / autoScaleDimensions.Width,\r
194                 Form.CurrentAutoScaleDimensions.Height / autoScaleDimensions.Height);\r
195         }\r
196 \r
197         private void SetupQuestPanel()\r
198         {\r
199             var prevHeight = _c.questPanel.Height;\r
200             _c.questPanel.CreateLabels(Config.QuestLines);\r
201             Form.Height += _c.questPanel.Height - prevHeight;\r
202         }\r
203 \r
204         public void UpdateInfo(Sniffer.Update update)\r
205         {\r
206             if (update == Sniffer.Update.Start)\r
207             {\r
208                 _c.hqPanel.Login.Visible = false;\r
209                 _c.fleetPanel.Start();\r
210                 Notifier.StopAllRepeat();\r
211                 return;\r
212             }\r
213             if (!Sniffer.Started)\r
214                 return;\r
215             if ((update & Sniffer.Update.Item) != 0)\r
216                 UpdateItemInfo();\r
217             if ((update & Sniffer.Update.Timer) != 0)\r
218                 UpdateTimers();\r
219             if ((update & Sniffer.Update.NDock) != 0)\r
220                 UpdateNDocLabels();\r
221             if ((update & Sniffer.Update.Mission) != 0)\r
222                 UpdateMissionLabels();\r
223             if ((update & Sniffer.Update.QuestList) != 0)\r
224                 UpdateQuestList();\r
225             if ((update & Sniffer.Update.Ship) != 0)\r
226                 UpdateShipInfo();\r
227             if ((update & Sniffer.Update.Battle) != 0)\r
228                 UpdateBattleInfo();\r
229             if ((update & Sniffer.Update.Cell) != 0)\r
230                 UpdateCellInfo();\r
231         }\r
232 \r
233         private void MainForm_Load(object sender, EventArgs e)\r
234         {\r
235             SuppressActivate.Start();\r
236             RestoreLocation();\r
237             if (Config.HideOnMinimized && Form.WindowState == FormWindowState.Minimized)\r
238                 Form.ShowInTaskbar = false;\r
239             if (Config.ShowHpInPercent)\r
240                 _c.fleetPanel.ToggleHpPercent();\r
241             if (Config.ShipList.Visible)\r
242                 _listFormGroup.Show();\r
243             _main.CheckVersionUpMain(_c.fleetPanel.Guide);\r
244         }\r
245 \r
246         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)\r
247         {\r
248             if (!Config.ExitSilently && !SystemShutdown)\r
249             {\r
250                 using var dialog = new ConfirmDialog();\r
251                 if (dialog.ShowDialog(Form) != DialogResult.Yes)\r
252                 {\r
253                     e.Cancel = true;\r
254                     return;\r
255                 }\r
256             }\r
257             if (SystemShutdown)\r
258                 _listFormGroup.WaitForCloseAll();\r
259             else\r
260                 _listFormGroup.Close(); // 各自で終了処理するのでシャットダウン時は不要\r
261             Config.Location = (Form.WindowState == FormWindowState.Normal ? Form.Bounds : Form.RestoreBounds).Location;\r
262             Config.ShowHpInPercent = _c.fleetPanel.ShowHpInPercent;\r
263         }\r
264 \r
265         private void MainForm_Resize(object sender, EventArgs e)\r
266         {\r
267             if (_listFormGroup == null) // DPIが100%でないときにInitializeComponentから呼ばれるので\r
268                 return;\r
269             SuppressActivate.Start();\r
270             if (Form.WindowState == FormWindowState.Minimized)\r
271             {\r
272                 if (Config.HideOnMinimized)\r
273                     Form.ShowInTaskbar = false;\r
274             }\r
275             _listFormGroup.Main.ChangeWindowState(Form.WindowState);\r
276         }\r
277 \r
278         public readonly TimeOutChecker SuppressActivate = new TimeOutChecker();\r
279 \r
280         private void MainForm_Activated(object sender, EventArgs e)\r
281         {\r
282             if (SuppressActivate.Check())\r
283                 return;\r
284             if (NeedRaise)\r
285                 RaiseBothWindows();\r
286         }\r
287 \r
288         private bool NeedRaise => _listFormGroup.Main.Visible && Form.WindowState != FormWindowState.Minimized;\r
289 \r
290         private void RaiseBothWindows()\r
291         {\r
292             _listFormGroup.Main.Owner = null;\r
293             Form.Owner = _listFormGroup.Main;\r
294             Form.BringToFront();\r
295             Form.Owner = null;\r
296         }\r
297 \r
298         public class TimeOutChecker\r
299         {\r
300             private DateTime _lastCheck;\r
301             private readonly TimeSpan _timeout = TimeSpan.FromMilliseconds(500);\r
302 \r
303             public void Start()\r
304             {\r
305                 _lastCheck = DateTime.Now;\r
306             }\r
307 \r
308             public bool Check()\r
309             {\r
310                 var now = DateTime.Now;\r
311                 var last = _lastCheck;\r
312                 _lastCheck = now;\r
313                 return now - last < _timeout;\r
314             }\r
315         }\r
316 \r
317         private void RevertFromIcon()\r
318         {\r
319             Form.ShowInTaskbar = true;\r
320             Form.WindowState = FormWindowState.Normal;\r
321             Form.TopMost = _listFormGroup.TopMost = Config.TopMost; // 最前面に表示されなくなることがあるのを回避する\r
322         }\r
323 \r
324         private void notifyIconMain_MouseDoubleClick(object sender, MouseEventArgs e)\r
325         {\r
326             RevertFromIcon();\r
327         }\r
328 \r
329         private void PerformZoom()\r
330         {\r
331             if (Config.Zoom == 100)\r
332             {\r
333                 ShipLabel.Name.BaseFont = Form.Font;\r
334                 ShipLabel.Name.LatinFont = LatinFont();\r
335                 return;\r
336             }\r
337             var prev = Form.CurrentAutoScaleDimensions;\r
338             foreach (var control in new Control[]\r
339             {\r
340                 Form, _c.fleetPanel.Guide, _c.hqPanel.Login,\r
341                 _contextMenuMain\r
342             }.Concat(_main.Controls))\r
343             {\r
344                 control.Font = ZoomFont(control.Font);\r
345             }\r
346             _listFormGroup.Font = ZoomFont(_listFormGroup.Font);\r
347             foreach (var toolTip in new[] {_toolTip, _tooltipCopy})\r
348             {\r
349                 toolTip.Font = ZoomFont(toolTip.Font);\r
350             }\r
351             ShipLabel.Name.BaseFont = Form.Font;\r
352             ShipLabel.Name.LatinFont = LatinFont();\r
353             var cur = Form.CurrentAutoScaleDimensions;\r
354             Scaler.Factor = Scaler.Scale(cur.Width / prev.Width, cur.Height / prev.Height);\r
355         }\r
356 \r
357         private Font ZoomFont(Font font)\r
358         {\r
359             return new Font(font.FontFamily, font.Size * Config.Zoom / 100);\r
360         }\r
361 \r
362         private Font LatinFont()\r
363         {\r
364             return new Font("Tahoma", 8f * Config.Zoom / 100);\r
365         }\r
366 \r
367         private void RestoreLocation()\r
368         {\r
369             if (Config.Location.X == int.MinValue)\r
370                 return;\r
371             if (IsTitleBarOnAnyScreen(Config.Location))\r
372                 Form.Location = Config.Location;\r
373         }\r
374 \r
375         public void ApplyConfig()\r
376         {\r
377             if (Form.TopMost != Config.TopMost)\r
378                 Form.TopMost = _listFormGroup.TopMost = Config.TopMost;\r
379             _c.hqPanel.Update();\r
380             _c.labelAkashiRepair.Visible = _c.labelAkashiRepairTimer.Visible = Config.UsePresetAkashi;\r
381         }\r
382 \r
383         public static bool IsTitleBarOnAnyScreen(Point location)\r
384         {\r
385             var rect = new Rectangle(\r
386                 new Point(location.X + SystemInformation.IconSize.Width + SystemInformation.HorizontalFocusThickness,\r
387                     location.Y + SystemInformation.CaptionHeight), new Size(60, 1));\r
388             return Screen.AllScreens.Any(screen => screen.WorkingArea.Contains(rect));\r
389         }\r
390 \r
391         private void ShowShipOnShipList(int id)\r
392         {\r
393             _listFormGroup.ShowShip(id);\r
394         }\r
395 \r
396         public void UpdateItemInfo()\r
397         {\r
398             _c.hqPanel.Update();\r
399             Notifier.NotifyShipItemCount();\r
400             _c.materialHistoryPanel.Update();\r
401             _listFormGroup.UpdateList();\r
402         }\r
403 \r
404         private void UpdateShipInfo()\r
405         {\r
406             _c.fleetPanel.Update();\r
407             Notifier.NotifyDamagedShip();\r
408             UpdateChargeInfo();\r
409             UpdateRepairList();\r
410             UpdateMissionLabels();\r
411             _listFormGroup.UpdateList();\r
412         }\r
413 \r
414         private void UpdateBattleInfo()\r
415         {\r
416             _listFormGroup.UpdateBattleResult();\r
417             _listFormGroup.UpdateAirBattleResult();\r
418             _c.fleetPanel.UpdateBattleInfo();\r
419         }\r
420 \r
421         private void UpdateCellInfo()\r
422         {\r
423             _listFormGroup.UpdateCellInfo();\r
424         }\r
425 \r
426         private void UpdateChargeInfo()\r
427         {\r
428             _c.fleetPanel.UpdateChargeInfo();\r
429         }\r
430 \r
431         private void UpdateNDocLabels()\r
432         {\r
433             _c.ndockPanel.Update();\r
434         }\r
435 \r
436         private void UpdateMissionLabels()\r
437         {\r
438             _c.missionPanel.Update();\r
439         }\r
440 \r
441         public void UpdateTimers()\r
442         {\r
443             foreach (var timer in _timers)\r
444                 timer.UpdateTimers();\r
445         }\r
446 \r
447         private void UpdateRepairList()\r
448         {\r
449             _c.panelRepairList.SetRepairList(Sniffer.RepairList);\r
450             _toolTip.SetToolTip(_c.labelRepairListCaption, new RepairShipCount(Sniffer.RepairList).ToString());\r
451         }\r
452 \r
453         private void UpdateQuestList()\r
454         {\r
455             _c.questPanel.Update(Sniffer.Quests);\r
456             _c.labelQuestCount.Text = Sniffer.Quests.Length.ToString();\r
457             Notifier.NotifyQuestComplete();\r
458         }\r
459 \r
460         private void FlashWindow()\r
461         {\r
462             Win32API.FlashWindow(Form.Handle);\r
463         }\r
464 \r
465         private void ShowTaster(string title, string message)\r
466         {\r
467             _c.notifyIconMain.ShowBalloonTip(20000, title, message, ToolTipIcon.Info);\r
468         }\r
469 \r
470         private void PlaySound(string file, int volume)\r
471         {\r
472             SoundPlayer.PlaySound(Form.Handle, file, volume);\r
473         }\r
474 \r
475         private void labelRepairListButton_Click(object sender, EventArgs e)\r
476         {\r
477             if (_c.panelRepairList.Visible)\r
478             {\r
479                 _c.panelRepairList.Visible = false;\r
480                 _c.dropDownButtonRepairList.BackColor = Control.DefaultBackColor;\r
481             }\r
482             else\r
483             {\r
484                 _c.panelRepairList.Visible = true;\r
485                 _c.panelRepairList.BringToFront();\r
486                 _c.dropDownButtonRepairList.BackColor = CustomColors.ActiveButtonColor;\r
487             }\r
488         }\r
489 \r
490         private void panelRepairList_Click(object sender, EventArgs e)\r
491         {\r
492             _c.panelRepairList.Visible = false;\r
493             _c.dropDownButtonRepairList.BackColor = Control.DefaultBackColor;\r
494         }\r
495     }\r
496 }