OSDN Git Service

設定ファイルのパスの相対化に失敗することがあるのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / ShipListPanel.cs
1 // Copyright (C) 2016 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.Drawing;\r
18 using System.Linq;\r
19 using System.Runtime.InteropServices;\r
20 using System.Windows.Forms;\r
21 using static System.Math;\r
22 \r
23 namespace KancolleSniffer\r
24 {\r
25     public class ShipListPanel : Panel\r
26     {\r
27         private const int LabelHeight = 12;\r
28         public const int LineHeight = 16;\r
29         private ShipStatus[] _shipList;\r
30         private readonly List<ShipLabel[]> _labelList = new List<ShipLabel[]>();\r
31         private readonly List<Panel> _labelPanelList = new List<Panel>();\r
32         private readonly List<CheckBox[]> _checkBoxesList = new List<CheckBox[]>();\r
33         private readonly List<ShipLabel[]> _groupingLabelList = new List<ShipLabel[]>();\r
34         private readonly List<Panel> _groupingPanelList = new List<Panel>();\r
35         private readonly List<ShipLabel[]> _repairLabelList = new List<ShipLabel[]>();\r
36         private readonly List<Panel> _repairPanelList = new List<Panel>();\r
37         private readonly List<ShipLabel> _hpLabels = new List<ShipLabel>();\r
38         private string _mode;\r
39         public bool ShowHpInPercent { get; private set; }\r
40 \r
41         public const int GroupCount = 4;\r
42         public HashSet<int>[] GroupSettings { get; } = new HashSet<int>[GroupCount];\r
43 \r
44         public ScrollBar ScrollBar { get; }\r
45 \r
46         public ShipListPanel()\r
47         {\r
48             ScrollBar = new VScrollBar {Dock = DockStyle.Right, Visible = false};\r
49             ScrollBar.ValueChanged += ScrollBarOnValueChanged;\r
50             Controls.Add(ScrollBar);\r
51         }\r
52 \r
53         private void ScrollBarOnValueChanged(object sender, EventArgs eventArgs)\r
54         {\r
55             SuspendDrawing();\r
56             SetShipLabels();\r
57             ResumeDrawing();\r
58         }\r
59 \r
60         protected override void OnResize(EventArgs ev)\r
61         {\r
62             base.OnResize(ev);\r
63             if (_shipList == null || _shipList.Length == 0 || !Visible)\r
64                 return;\r
65             SuspendDrawing();\r
66             SetupLabels();\r
67             SetShipLabels();\r
68             ResumeDrawing();\r
69         }\r
70 \r
71         protected override void OnMouseWheel(MouseEventArgs e)\r
72         {\r
73             if (!ScrollBar.Visible)\r
74                 return;\r
75             ScrollBar.Value = Max(ScrollBar.Minimum, Min(ScrollBar.Maximum - ScrollBar.LargeChange + 1,\r
76                 ScrollBar.Value - e.Delta * SystemInformation.MouseWheelScrollLines / 120));\r
77         }\r
78 \r
79         public void Update(Sniffer sniffer, string mode, ListForm.SortOrder sortOrder, bool byShipType)\r
80         {\r
81             _mode = mode;\r
82             CreateShipList(sniffer, sortOrder, byShipType);\r
83             SuspendDrawing();\r
84             SetupLabels();\r
85             SetShipLabels();\r
86             ResumeDrawing();\r
87         }\r
88 \r
89         [DllImport("user32.dll")]\r
90         public static extern int SendMessage(IntPtr hWnd, int wMsg, bool wParam, IntPtr lParam);\r
91 \r
92         private void SuspendDrawing()\r
93         {\r
94             SendMessage(Handle, 11, false, IntPtr.Zero); // WM_SETREDRAW = 11\r
95             SuspendLayout();\r
96         }\r
97 \r
98         public void ResumeDrawing()\r
99         {\r
100             ResumeLayout();\r
101             SendMessage(Handle, 11, true, IntPtr.Zero);\r
102             Refresh();\r
103         }\r
104 \r
105         private void CreateShipList(Sniffer sniffer, ListForm.SortOrder sortOrder, bool byShipType)\r
106         {\r
107             var ships = _mode == "修復" ? sniffer.RepairList : FilterByGroup(sniffer.ShipList, _mode).ToArray();\r
108             var order = _mode == "修復" ? ListForm.SortOrder.Repair : sortOrder;\r
109             if (!byShipType)\r
110             {\r
111                 _shipList = ships.OrderBy(s => s, new CompareShip(false, order)).ToArray();\r
112                 return;\r
113             }\r
114             var types = ships.Select(s => new {Id = s.Spec.ShipType, Name = s.Spec.ShipTypeName})\r
115                 .Distinct()\r
116                 .Select(stype =>\r
117                     new ShipStatus\r
118                     {\r
119                         Spec = new ShipSpec {Name = stype.Name, ShipType = stype.Id},\r
120                         Level = 1000,\r
121                         NowHp = -1000,\r
122                         Cond = -1000\r
123                     });\r
124             _shipList = ships.Concat(types).OrderBy(s => s, new CompareShip(true, order)).ToArray();\r
125         }\r
126 \r
127         private IEnumerable<ShipStatus> FilterByGroup(IEnumerable<ShipStatus> ships, string group)\r
128         {\r
129             var g = Array.FindIndex(new[] {"A", "B", "C", "D"}, x => x == group);\r
130             if (g == -1)\r
131                 return ships;\r
132             return from s in ships where GroupSettings[g].Contains(s.Id) select s;\r
133         }\r
134 \r
135         public IEnumerable<ShipStatus> CurrentShipList => _shipList.Where(ship => ship.Level != 1000);\r
136 \r
137         private class CompareShip : IComparer<ShipStatus>\r
138         {\r
139             private readonly bool _shipType;\r
140             private readonly ListForm.SortOrder _order;\r
141 \r
142             public CompareShip(bool type, ListForm.SortOrder order)\r
143             {\r
144                 _shipType = type;\r
145                 _order = order;\r
146             }\r
147 \r
148             public int Compare(ShipStatus a, ShipStatus b)\r
149             {\r
150                 if (a == null || b == null)\r
151                     throw new ArgumentNullException();\r
152                 if (_shipType)\r
153                 {\r
154                     if (a.Spec.ShipType != b.Spec.ShipType)\r
155                         return a.Spec.ShipType - b.Spec.ShipType;\r
156                     if (a.Level != b.Level)\r
157                     {\r
158                         if (a.Level == 1000)\r
159                             return -1;\r
160                         if (b.Level == 1000)\r
161                             return 1;\r
162                     }\r
163                 }\r
164                 if (_order == ListForm.SortOrder.Repair && a.RepairTime != b.RepairTime)\r
165                     return (int)(b.RepairTime - a.RepairTime).TotalSeconds;\r
166                 if (a.Cond != b.Cond)\r
167                 {\r
168                     if (_order == ListForm.SortOrder.CondAscend)\r
169                         return a.Cond - b.Cond;\r
170                     if (_order == ListForm.SortOrder.CondDescend)\r
171                         return b.Cond - a.Cond;\r
172                 }\r
173                 if (a.Level != b.Level)\r
174                 {\r
175                     if (_order == ListForm.SortOrder.ExpToNextAscend)\r
176                         return b.Level - a.Level;\r
177                     if (_order == ListForm.SortOrder.ExpToNextDescend)\r
178                         return a.Level - b.Level;\r
179                     if (!_shipType) // Condが同じかSortOrder.Noneで艦種なし\r
180                         return b.Level - a.Level;\r
181                 }\r
182                 if (a.ExpToNext != b.ExpToNext)\r
183                 {\r
184                     if (_order == ListForm.SortOrder.ExpToNextAscend)\r
185                         return a.ExpToNext - b.ExpToNext;\r
186                     if (_order == ListForm.SortOrder.ExpToNextDescend)\r
187                         return b.ExpToNext - a.ExpToNext;\r
188                 }\r
189                 if (a.Spec.SortNo != b.Spec.SortNo)\r
190                     return a.Spec.SortNo - b.Spec.SortNo;\r
191                 return a.Id - b.Id;\r
192             }\r
193         }\r
194 \r
195         private void SetupLabels()\r
196         {\r
197             for (var i = _labelList.Count; i * LineHeight < Height; i++)\r
198             {\r
199                 CreateGroupingComponents(i);\r
200                 CreateRepairLabels(i);\r
201                 CreateShipLabels(i);\r
202             }\r
203             for (var i = 0; i * LineHeight < Height; i++)\r
204             {\r
205                 _labelPanelList[i].Visible = InShipStatus(_mode);\r
206                 _groupingPanelList[i].Visible = _mode == "分類";\r
207                 _repairPanelList[i].Visible = _mode == "修復";\r
208             }\r
209             SetupScrollBar();\r
210         }\r
211 \r
212         private void SetupScrollBar()\r
213         {\r
214             var needBar = _shipList.Length * LineHeight * ShipLabel.ScaleFactor.Height > Height;\r
215             if (!needBar)\r
216             {\r
217                 ScrollBar.Visible = false;\r
218                 ScrollBar.Value = 0;\r
219                 return;\r
220             }\r
221             ScrollBar.Visible = true;\r
222             ScrollBar.Minimum = 0;\r
223             var lines = Max(1, Height / (int)Round(LineHeight * ShipLabel.ScaleFactor.Height));\r
224             var max = _shipList.Length - lines;\r
225             var largeChange = Min(lines, max);\r
226             ScrollBar.LargeChange = largeChange;\r
227             ScrollBar.Maximum = Max(0, max + largeChange - 1); // ScrollBarを最大まで動かしてもmaxには届かない\r
228             ScrollBar.Value = Min(ScrollBar.Value, max);\r
229         }\r
230 \r
231         private void CreateGroupingComponents(int i)\r
232         {\r
233             var y = 3 + LineHeight * i;\r
234             var cfgp = new Panel\r
235             {\r
236                 Location = new Point(0, y - 2),\r
237                 Size = new Size(ListForm.PanelWidth, LineHeight - 1),\r
238                 BackColor = ShipLabel.ColumnColors[(i + 1) % 2],\r
239             };\r
240             cfgp.Scale(ShipLabel.ScaleFactor);\r
241             cfgp.Tag = cfgp.Location.Y;\r
242             var cfgl = new[]\r
243             {\r
244                 new ShipLabel\r
245                 {\r
246                     Location = new Point(90, 2),\r
247                     Size = new Size(24, LabelHeight),\r
248                     TextAlign = ContentAlignment.MiddleRight\r
249                 },\r
250                 new ShipLabel {Location = new Point(10, 2), AutoSize = true},\r
251                 new ShipLabel {Location = new Point(1, 2), AutoSize = true}\r
252             };\r
253 \r
254             var cb = new CheckBox[GroupCount];\r
255             for (var j = 0; j < cb.Length; j++)\r
256             {\r
257                 cb[j] = new CheckBox\r
258                 {\r
259                     Location = new Point(125 + j * 24, 2),\r
260                     FlatStyle = FlatStyle.Flat,\r
261                     Size = new Size(12, 11),\r
262                     Tag = i * 10 + j\r
263                 };\r
264                 cb[j].Scale(ShipLabel.ScaleFactor);\r
265                 cb[j].CheckedChanged += checkboxGroup_CheckedChanged;\r
266             }\r
267             _groupingLabelList.Add(cfgl);\r
268             _checkBoxesList.Add(cb);\r
269             _groupingPanelList.Add(cfgp);\r
270             // ReSharper disable CoVariantArrayConversion\r
271             cfgp.Controls.AddRange(cfgl);\r
272             cfgp.Controls.AddRange(cb);\r
273             // ReSharper restore CoVariantArrayConversion\r
274             Controls.Add(cfgp);\r
275             foreach (var label in cfgl)\r
276             {\r
277                 label.Scale();\r
278                 label.PresetColor =\r
279                     label.BackColor = ShipLabel.ColumnColors[(i + 1) % 2];\r
280             }\r
281         }\r
282 \r
283         private void checkboxGroup_CheckedChanged(object sender, EventArgs e)\r
284         {\r
285             var cb = (CheckBox)sender;\r
286             var group = (int)cb.Tag % 10;\r
287             var idx = (int)cb.Tag / 10;\r
288             if (cb.Checked)\r
289             {\r
290                 GroupSettings[group].Add(_shipList[idx + ScrollBar.Value].Id);\r
291             }\r
292             else\r
293             {\r
294                 GroupSettings[group].Remove(_shipList[idx + ScrollBar.Value].Id);\r
295             }\r
296         }\r
297 \r
298         private void CreateRepairLabels(int i)\r
299         {\r
300             var y = 3 + LineHeight * i;\r
301             const int height = LabelHeight;\r
302             var rpp = new Panel\r
303             {\r
304                 Location = new Point(0, y - 2),\r
305                 Size = new Size(ListForm.PanelWidth, LineHeight - 1),\r
306                 BackColor = ShipLabel.ColumnColors[(i + 1) % 2],\r
307             };\r
308             rpp.Scale(ShipLabel.ScaleFactor);\r
309             rpp.Tag = rpp.Location.Y;\r
310             var rpl = new[]\r
311             {\r
312                 new ShipLabel {Location = new Point(118, 2), AutoSize = true, AnchorRight = true},\r
313                 new ShipLabel\r
314                 {\r
315                     Location = new Point(116, 2),\r
316                     Size = new Size(24, height),\r
317                     TextAlign = ContentAlignment.MiddleRight\r
318                 },\r
319                 new ShipLabel {Location = new Point(141, 2), AutoSize = true},\r
320                 new ShipLabel {Location = new Point(186, 2), AutoSize = true},\r
321                 new ShipLabel {Location = new Point(10, 2), AutoSize = true},\r
322                 new ShipLabel {Location = new Point(1, 2), AutoSize = true}\r
323             };\r
324             _repairLabelList.Add(rpl);\r
325             _repairPanelList.Add(rpp);\r
326             // ReSharper disable once CoVariantArrayConversion\r
327             rpp.Controls.AddRange(rpl);\r
328             Controls.Add(rpp);\r
329             foreach (var label in rpl)\r
330             {\r
331                 label.Scale();\r
332                 label.PresetColor =\r
333                     label.BackColor = ShipLabel.ColumnColors[(i + 1) % 2];\r
334             }\r
335             if (ShowHpInPercent)\r
336                 rpl[0].ToggleHpPercent();\r
337             _hpLabels.Add(rpl[0]);\r
338         }\r
339 \r
340         private void CreateShipLabels(int i)\r
341         {\r
342             var y = 3 + LineHeight * i;\r
343             const int height = LabelHeight;\r
344             var lbp = new Panel\r
345             {\r
346                 Location = new Point(0, y - 2),\r
347                 Size = new Size(ListForm.PanelWidth, LineHeight - 1),\r
348                 BackColor = ShipLabel.ColumnColors[(i + 1) % 2]\r
349             };\r
350             lbp.Scale(ShipLabel.ScaleFactor);\r
351             var labels = new[]\r
352             {\r
353                 new ShipLabel {Location = new Point(126, 2), AutoSize = true, AnchorRight = true},\r
354                 new ShipLabel\r
355                 {\r
356                     Location = new Point(128, 2),\r
357                     Size = new Size(24, height),\r
358                     TextAlign = ContentAlignment.MiddleRight\r
359                 },\r
360                 new ShipLabel\r
361                 {\r
362                     Location = new Point(154, 2),\r
363                     Size = new Size(24, height),\r
364                     TextAlign = ContentAlignment.MiddleRight\r
365                 },\r
366                 new ShipLabel\r
367                 {\r
368                     Location = new Point(175, 2),\r
369                     Size = new Size(42, height),\r
370                     TextAlign = ContentAlignment.MiddleRight\r
371                 },\r
372                 new ShipLabel {Location = new Point(10, 2), AutoSize = true},\r
373                 new ShipLabel {Location = new Point(1, 2), AutoSize = true}\r
374             };\r
375             _labelList.Add(labels);\r
376             _labelPanelList.Add(lbp);\r
377             // ReSharper disable once CoVariantArrayConversion\r
378             lbp.Controls.AddRange(labels);\r
379             Controls.Add(lbp);\r
380             foreach (var label in labels)\r
381             {\r
382                 label.Scale();\r
383                 label.PresetColor =\r
384                     label.BackColor = ShipLabel.ColumnColors[(i + 1) % 2];\r
385             }\r
386             if (ShowHpInPercent)\r
387                 labels[0].ToggleHpPercent();\r
388             _hpLabels.Add(labels[0]);\r
389         }\r
390 \r
391         private void SetShipLabels()\r
392         {\r
393             for (var i = 0; i < (Height + LineHeight - 1) / LineHeight; i++)\r
394             {\r
395                 if (InShipStatus(_mode))\r
396                     SetShipStatus(i);\r
397                 if (_mode == "分類")\r
398                     SetGrouping(i);\r
399                 if (_mode == "修復")\r
400                     SetRepairList(i);\r
401             }\r
402         }\r
403 \r
404         private bool InShipStatus(string mode) => Array.Exists(new[] {"全員", "A", "B", "C", "D"}, x => mode == x);\r
405 \r
406         private void SetShipStatus(int i)\r
407         {\r
408             var panel = _labelPanelList[i];\r
409             if (i + ScrollBar.Value >= _shipList.Length)\r
410             {\r
411                 panel.Visible = false;\r
412                 return;\r
413             }\r
414             var s = _shipList[i + ScrollBar.Value];\r
415             var labels = _labelList[i];\r
416             if (s.Level == 1000) // 艦種の表示\r
417             {\r
418                 SetShipType(i);\r
419                 return;\r
420             }\r
421             labels[0].SetHp(s);\r
422             labels[1].SetCond(s);\r
423             labels[2].SetLevel(s);\r
424             labels[3].SetExpToNext(s);\r
425             labels[4].SetName(s, ShipNameWidth.ShipList);\r
426             labels[5].SetFleet(s);\r
427             panel.Visible = true;\r
428         }\r
429 \r
430         private void SetShipType(int i)\r
431         {\r
432             var s = _shipList[i + ScrollBar.Value];\r
433             var labels = _labelList[i];\r
434             labels[0].SetHp(null);\r
435             labels[1].SetCond(null);\r
436             labels[2].SetLevel(null);\r
437             labels[3].SetExpToNext(null);\r
438             labels[4].SetName(null);\r
439             labels[5].SetFleet(null);\r
440             labels[5].Text = s.Name;\r
441             _labelPanelList[i].Visible = true;\r
442         }\r
443 \r
444         private void SetGrouping(int i)\r
445         {\r
446             var panel = _groupingPanelList[i];\r
447             if (i + ScrollBar.Value >= _shipList.Length)\r
448             {\r
449                 panel.Visible = false;\r
450                 _labelPanelList[i].Visible = false;\r
451                 return;\r
452             }\r
453             var s = _shipList[i + ScrollBar.Value];\r
454             var labels = _groupingLabelList[i];\r
455             if (s.Level == 1000)\r
456             {\r
457                 panel.Visible = false;\r
458                 SetShipType(i);\r
459                 return;\r
460             }\r
461             labels[0].SetLevel(s);\r
462             labels[1].SetName(s, ShipNameWidth.GroupConfig);\r
463             labels[2].SetFleet(s);\r
464             var cb = _checkBoxesList[i];\r
465             for (var j = 0; j < cb.Length; j++)\r
466                 cb[j].Checked = GroupSettings[j].Contains(s.Id);\r
467             panel.Visible = true;\r
468         }\r
469 \r
470         private void SetRepairList(int i)\r
471         {\r
472             var panel = _repairPanelList[i];\r
473             if (i + ScrollBar.Value >= _shipList.Length)\r
474             {\r
475                 panel.Visible = false;\r
476                 _labelPanelList[i].Visible = false;\r
477                 return;\r
478             }\r
479             var s = _shipList[i + ScrollBar.Value];\r
480             if (s.Level == 1000)\r
481             {\r
482                 panel.Visible = false;\r
483                 SetShipType(i);\r
484                 return;\r
485             }\r
486             var rpl = _repairLabelList[i];\r
487             rpl[0].SetHp(s);\r
488             rpl[1].SetLevel(s);\r
489             rpl[2].SetRepairTime(s);\r
490             rpl[3].Text = s.RepairTimePerHp.ToString(@"mm\:ss");\r
491             rpl[4].SetName(s, ShipNameWidth.RepairListFull);\r
492             rpl[5].SetFleet(s);\r
493             panel.Visible = true;\r
494         }\r
495 \r
496         public void ToggleHpPercent()\r
497         {\r
498             ShowHpInPercent = !ShowHpInPercent;\r
499             foreach (var label in _hpLabels)\r
500                 label.ToggleHpPercent();\r
501         }\r
502 \r
503         public void ShowShip(int id)\r
504         {\r
505             var i = Array.FindIndex(_shipList, s => s.Id == id);\r
506             if (i == -1)\r
507                 return;\r
508             ScrollBar.Value = Min(i, ScrollBar.Maximum + 1 - ScrollBar.LargeChange);\r
509             SetShipLabels();\r
510         }\r
511     }\r
512 }