OSDN Git Service

4b4fc85578f0494702babd275259d2ab88e9c651
[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.Close(); // 各自で終了処理するのでシャットダウン時は不要\r
259             _listFormGroup.WaitForCloseAll();\r
260             Config.Location = (Form.WindowState == FormWindowState.Normal ? Form.Bounds : Form.RestoreBounds).Location;\r
261             Config.ShowHpInPercent = _c.fleetPanel.ShowHpInPercent;\r
262         }\r
263 \r
264         private void MainForm_Resize(object sender, EventArgs e)\r
265         {\r
266             if (_listFormGroup == null) // DPIが100%でないときにInitializeComponentから呼ばれるので\r
267                 return;\r
268             SuppressActivate.Start();\r
269             if (Form.WindowState == FormWindowState.Minimized)\r
270             {\r
271                 if (Config.HideOnMinimized)\r
272                     Form.ShowInTaskbar = false;\r
273             }\r
274             _listFormGroup.Main.ChangeWindowState(Form.WindowState);\r
275         }\r
276 \r
277         public readonly TimeOutChecker SuppressActivate = new TimeOutChecker();\r
278 \r
279         private void MainForm_Activated(object sender, EventArgs e)\r
280         {\r
281             if (SuppressActivate.Check())\r
282                 return;\r
283             if (NeedRaise)\r
284                 RaiseBothWindows();\r
285         }\r
286 \r
287         private bool NeedRaise => _listFormGroup.Main.Visible && Form.WindowState != FormWindowState.Minimized;\r
288 \r
289         private void RaiseBothWindows()\r
290         {\r
291             _listFormGroup.Main.Owner = null;\r
292             Form.Owner = _listFormGroup.Main;\r
293             Form.BringToFront();\r
294             Form.Owner = null;\r
295         }\r
296 \r
297         public class TimeOutChecker\r
298         {\r
299             private DateTime _lastCheck;\r
300             private readonly TimeSpan _timeout = TimeSpan.FromMilliseconds(500);\r
301 \r
302             public void Start()\r
303             {\r
304                 _lastCheck = DateTime.Now;\r
305             }\r
306 \r
307             public bool Check()\r
308             {\r
309                 var now = DateTime.Now;\r
310                 var last = _lastCheck;\r
311                 _lastCheck = now;\r
312                 return now - last < _timeout;\r
313             }\r
314         }\r
315 \r
316         private void RevertFromIcon()\r
317         {\r
318             Form.ShowInTaskbar = true;\r
319             Form.WindowState = FormWindowState.Normal;\r
320             Form.TopMost = _listFormGroup.TopMost = Config.TopMost; // 最前面に表示されなくなることがあるのを回避する\r
321         }\r
322 \r
323         private void notifyIconMain_MouseDoubleClick(object sender, MouseEventArgs e)\r
324         {\r
325             RevertFromIcon();\r
326         }\r
327 \r
328         private void PerformZoom()\r
329         {\r
330             if (Config.Zoom == 100)\r
331             {\r
332                 ShipLabel.Name.BaseFont = Form.Font;\r
333                 ShipLabel.Name.LatinFont = LatinFont();\r
334                 return;\r
335             }\r
336             var prev = Form.CurrentAutoScaleDimensions;\r
337             foreach (var control in new Control[]\r
338             {\r
339                 Form, _c.fleetPanel.Guide, _c.hqPanel.Login,\r
340                 _contextMenuMain\r
341             }.Concat(_main.Controls))\r
342             {\r
343                 control.Font = ZoomFont(control.Font);\r
344             }\r
345             _listFormGroup.Font = ZoomFont(_listFormGroup.Font);\r
346             foreach (var toolTip in new[] {_toolTip, _tooltipCopy})\r
347             {\r
348                 toolTip.Font = ZoomFont(toolTip.Font);\r
349             }\r
350             ShipLabel.Name.BaseFont = Form.Font;\r
351             ShipLabel.Name.LatinFont = LatinFont();\r
352             var cur = Form.CurrentAutoScaleDimensions;\r
353             Scaler.Factor = Scaler.Scale(cur.Width / prev.Width, cur.Height / prev.Height);\r
354         }\r
355 \r
356         private Font ZoomFont(Font font)\r
357         {\r
358             return new Font(font.FontFamily, font.Size * Config.Zoom / 100);\r
359         }\r
360 \r
361         private Font LatinFont()\r
362         {\r
363             return new Font("Tahoma", 8f * Config.Zoom / 100);\r
364         }\r
365 \r
366         private void RestoreLocation()\r
367         {\r
368             if (Config.Location.X == int.MinValue)\r
369                 return;\r
370             if (IsTitleBarOnAnyScreen(Config.Location))\r
371                 Form.Location = Config.Location;\r
372         }\r
373 \r
374         public void ApplyConfig()\r
375         {\r
376             if (Form.TopMost != Config.TopMost)\r
377                 Form.TopMost = _listFormGroup.TopMost = Config.TopMost;\r
378             _c.hqPanel.Update();\r
379             _c.labelAkashiRepair.Visible = _c.labelAkashiRepairTimer.Visible = Config.UsePresetAkashi;\r
380         }\r
381 \r
382         public static bool IsTitleBarOnAnyScreen(Point location)\r
383         {\r
384             var rect = new Rectangle(\r
385                 new Point(location.X + SystemInformation.IconSize.Width + SystemInformation.HorizontalFocusThickness,\r
386                     location.Y + SystemInformation.CaptionHeight), new Size(60, 1));\r
387             return Screen.AllScreens.Any(screen => screen.WorkingArea.Contains(rect));\r
388         }\r
389 \r
390         private void ShowShipOnShipList(int id)\r
391         {\r
392             _listFormGroup.ShowShip(id);\r
393         }\r
394 \r
395         public void UpdateItemInfo()\r
396         {\r
397             _c.hqPanel.Update();\r
398             Notifier.NotifyShipItemCount();\r
399             _c.materialHistoryPanel.Update();\r
400             _listFormGroup.UpdateList();\r
401         }\r
402 \r
403         private void UpdateShipInfo()\r
404         {\r
405             _c.fleetPanel.Update();\r
406             Notifier.NotifyDamagedShip();\r
407             UpdateChargeInfo();\r
408             UpdateRepairList();\r
409             UpdateMissionLabels();\r
410             _listFormGroup.UpdateList();\r
411         }\r
412 \r
413         private void UpdateBattleInfo()\r
414         {\r
415             _listFormGroup.UpdateBattleResult();\r
416             _listFormGroup.UpdateAirBattleResult();\r
417             _c.fleetPanel.UpdateBattleInfo();\r
418         }\r
419 \r
420         private void UpdateCellInfo()\r
421         {\r
422             _listFormGroup.UpdateCellInfo();\r
423         }\r
424 \r
425         private void UpdateChargeInfo()\r
426         {\r
427             _c.fleetPanel.UpdateChargeInfo();\r
428         }\r
429 \r
430         private void UpdateNDocLabels()\r
431         {\r
432             _c.ndockPanel.Update();\r
433         }\r
434 \r
435         private void UpdateMissionLabels()\r
436         {\r
437             _c.missionPanel.Update();\r
438         }\r
439 \r
440         public void UpdateTimers()\r
441         {\r
442             foreach (var timer in _timers)\r
443                 timer.UpdateTimers();\r
444         }\r
445 \r
446         private void UpdateRepairList()\r
447         {\r
448             _c.panelRepairList.SetRepairList(Sniffer.RepairList);\r
449             _toolTip.SetToolTip(_c.labelRepairListCaption, new RepairShipCount(Sniffer.RepairList).ToString());\r
450         }\r
451 \r
452         private void UpdateQuestList()\r
453         {\r
454             _c.questPanel.Update(Sniffer.Quests);\r
455             _c.labelQuestCount.Text = Sniffer.Quests.Length.ToString();\r
456             Notifier.NotifyQuestComplete();\r
457         }\r
458 \r
459         private void FlashWindow()\r
460         {\r
461             Win32API.FlashWindow(Form.Handle);\r
462         }\r
463 \r
464         private void ShowTaster(string title, string message)\r
465         {\r
466             _c.notifyIconMain.ShowBalloonTip(20000, title, message, ToolTipIcon.Info);\r
467         }\r
468 \r
469         private void PlaySound(string file, int volume)\r
470         {\r
471             SoundPlayer.PlaySound(Form.Handle, file, volume);\r
472         }\r
473 \r
474         private void labelRepairListButton_Click(object sender, EventArgs e)\r
475         {\r
476             if (_c.panelRepairList.Visible)\r
477             {\r
478                 _c.panelRepairList.Visible = false;\r
479                 _c.dropDownButtonRepairList.BackColor = Control.DefaultBackColor;\r
480             }\r
481             else\r
482             {\r
483                 _c.panelRepairList.Visible = true;\r
484                 _c.panelRepairList.BringToFront();\r
485                 _c.dropDownButtonRepairList.BackColor = CustomColors.ActiveButtonColor;\r
486             }\r
487         }\r
488 \r
489         private void panelRepairList_Click(object sender, EventArgs e)\r
490         {\r
491             _c.panelRepairList.Visible = false;\r
492             _c.dropDownButtonRepairList.BackColor = Control.DefaultBackColor;\r
493         }\r
494     }\r
495 }